Skip to content

Commit b799444

Browse files
committed
runsc: Allow map host user to non-root user in rootless mode
Fix #9918. Currently, the rootless mode(runsc is called by no-root user) is not working well with the filesystem if we uses a non-root user in runsc container. This is because the runsc is mapping the host non-root user to root-user in container. In some cases we need to map the host non-root user to runsc container non-root user (with the same uid). After this patch, the following filesystem operations works well. test@test-virtual-machine:~/test$ ./runsc -ignore-cgroups --network host run abc id uid=1000(test) gid=1000(test) groups=1000(test) touch /tmp/runsctest echo aaa > /tmp/runsctest ls -lh /tmp/runsctest -rw-r--r-- 1 test test 4 Jun 29 18:46 /tmp/runsctest exit test@test-virtual-machine:~/test$ ls -lh /tmp/runsctest -rw-r--r-- 1 test test 4 6月 29 18:46 /tmp/runsctest test@test-virtual-machine:~/test$ cat /tmp/runsctest
1 parent 8a89a2f commit b799444

File tree

3 files changed

+113
-6
lines changed

3 files changed

+113
-6
lines changed

runsc/cmd/gofer.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package cmd
1717
import (
1818
"context"
1919
"encoding/json"
20+
"encoding/binary"
2021
"fmt"
2122
"io"
2223
"os"
@@ -64,6 +65,8 @@ var goferCaps = &specs.LinuxCapabilities{
6465
Bounding: caps,
6566
Effective: caps,
6667
Permitted: caps,
68+
Inheritable: caps,
69+
Ambient: caps,
6770
}
6871

6972
var goferUdsOpenCaps = &specs.LinuxCapabilities{
@@ -818,6 +821,26 @@ func waitForFD(fd int, fdName string) error {
818821
return nil
819822
}
820823

824+
func waitForID(fd int, fdName string) (uint32, uint32, error) {
825+
log.Debugf("Waiting on %s %d...", fdName, fd)
826+
f := os.NewFile(uintptr(fd), fdName)
827+
defer f.Close()
828+
829+
var uid uint32
830+
var gid uint32
831+
buf := make([]byte, 8)
832+
833+
if n, err := f.Read(buf); n != 8 || err != nil {
834+
e := fmt.Errorf("failed to convert to int:%v :%v", uid, err)
835+
return 0, 0, e
836+
}
837+
uid = binary.BigEndian.Uint32(buf[0:4])
838+
gid = binary.BigEndian.Uint32(buf[4:8])
839+
840+
841+
return uid, gid, nil
842+
}
843+
821844
// spawnProcMounter executes the /proc unmounter process.
822845
// It returns a function to wait on the proc unmounter process, which
823846
// should be called (via defer) in case of errors in order to clean up the
@@ -872,17 +895,22 @@ func (g *goferSyncFDs) syncUsernsForRootless() {
872895
//
873896
// Postcondition: All callers must re-exec themselves after this returns.
874897
func syncUsernsForRootless(fd int) {
875-
if err := waitForFD(fd, "userns sync FD"); err != nil {
876-
util.Fatalf("failed to sync on userns FD: %v", err)
898+
var uid uint32
899+
var gid uint32
900+
var err error
901+
902+
if uid, gid, err = waitForID(fd, "userns sync FD"); err != nil {
903+
util.Fatalf("failed to sync on userns FD:%v: %v %v", uid, gid, err)
877904
}
878905

906+
879907
// SETUID changes UID on the current system thread, so we have
880908
// to re-execute current binary.
881909
runtime.LockOSThread()
882-
if _, _, errno := unix.RawSyscall(unix.SYS_SETUID, 0, 0, 0); errno != 0 {
910+
if _, _, errno := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0); errno != 0 {
883911
util.Fatalf("failed to set UID: %v", errno)
884912
}
885-
if _, _, errno := unix.RawSyscall(unix.SYS_SETGID, 0, 0, 0); errno != 0 {
913+
if _, _, errno := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0); errno != 0 {
886914
util.Fatalf("failed to set GID: %v", errno)
887915
}
888916
}

runsc/container/container.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,13 @@ func (c *Container) createGoferProcess(conf *config.Config, mountHints *boot.Pod
13921392
{Type: specs.UTSNamespace},
13931393
}
13941394

1395+
var gSyncFile *os.File
1396+
defer func() {
1397+
if gSyncFile != nil {
1398+
gSyncFile.Close()
1399+
}
1400+
}()
1401+
13951402
rootlessEUID := unix.Geteuid() != 0
13961403
// Setup any uid/gid mappings, and create or join the configured user
13971404
// namespace so the gofer's view of the filesystem aligns with the
@@ -1413,7 +1420,7 @@ func (c *Container) createGoferProcess(conf *config.Config, mountHints *boot.Pod
14131420
if err != nil {
14141421
return nil, nil, nil, nil, err
14151422
}
1416-
defer syncFile.Close()
1423+
gSyncFile = syncFile
14171424
}
14181425

14191426
// Create synchronization FD for chroot.
@@ -1459,6 +1466,13 @@ func (c *Container) createGoferProcess(conf *config.Config, mountHints *boot.Pod
14591466
return nil, nil, nil, nil, fmt.Errorf("creating gofer filestore files: %w", err)
14601467
}
14611468

1469+
if rootlessEUID {
1470+
chrootSyncSandEnd.Close()
1471+
if err := sandbox.SendIDToSandbox(gSyncFile, c.Spec); err != nil {
1472+
return nil, nil, nil, nil, err
1473+
}
1474+
}
1475+
14621476
return sandEnds, goferFilestores, devSandEnd, mountsSand, nil
14631477
}
14641478

runsc/sandbox/sandbox.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package sandbox
1717

1818
import (
1919
"context"
20+
"encoding/binary"
2021
"encoding/json"
2122
"errors"
2223
"fmt"
@@ -1030,6 +1031,13 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
10301031
// configured.
10311032
rootlessEUID := unix.Geteuid() != 0
10321033
setUserMappings := false
1034+
var gSyncFile *os.File
1035+
defer func() {
1036+
if gSyncFile != nil {
1037+
gSyncFile.Close()
1038+
}
1039+
}()
1040+
10331041
if conf.Network == config.NetworkHost || conf.DirectFS {
10341042
if userns, ok := specutils.GetNS(specs.UserNamespace, args.Spec); ok {
10351043
log.Infof("Sandbox will be started in container's user namespace: %+v", userns)
@@ -1039,7 +1047,7 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
10391047
if err != nil {
10401048
return err
10411049
}
1042-
defer syncFile.Close()
1050+
gSyncFile = syncFile
10431051
setUserMappings = true
10441052
} else {
10451053
specutils.SetUIDGIDMappings(cmd, args.Spec)
@@ -1284,6 +1292,9 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
12841292
if err := SetUserMappings(args.Spec, cmd.Process.Pid); err != nil {
12851293
return err
12861294
}
1295+
if err := SendIDToSandbox(gSyncFile, args.Spec); err != nil {
1296+
return err
1297+
}
12871298
}
12881299

12891300
s.child = true
@@ -1293,6 +1304,60 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
12931304
return nil
12941305
}
12951306

1307+
// Send the UID & GID to the sandbox and gofer process
1308+
// This UID & GID is the ID for container init process
1309+
func SendIDToSandbox(syncFile *os.File, spec *specs.Spec) error {
1310+
1311+
specUID := uint32(spec.Process.User.UID)
1312+
specGID := uint32(spec.Process.User.GID)
1313+
1314+
euid := uint32(os.Geteuid())
1315+
egid := uint32(os.Getegid())
1316+
1317+
var cuid uint32
1318+
var cgid uint32
1319+
var uidFound, gidFound bool
1320+
1321+
// Find which container UID & GID the host user map to
1322+
for _, idMap := range spec.Linux.UIDMappings {
1323+
if euid >= idMap.HostID && euid < idMap.Size + idMap.HostID {
1324+
cuid = euid - idMap.HostID + idMap.ContainerID
1325+
uidFound = true
1326+
break
1327+
}
1328+
}
1329+
1330+
for _, idMap := range spec.Linux.GIDMappings {
1331+
if egid >= idMap.HostID && euid < idMap.Size + idMap.HostID {
1332+
cgid = egid - idMap.HostID + idMap.ContainerID
1333+
gidFound = true
1334+
break
1335+
}
1336+
}
1337+
1338+
if !uidFound || !gidFound {
1339+
return fmt.Errorf("current host uid(%d) or gid(%d) has no mapping in container", euid, egid)
1340+
}
1341+
1342+
// Make sure the specified UID & GID is the same as the host user map to
1343+
if cuid != specUID {
1344+
return fmt.Errorf("host uid %d can't map to container uid %d, expected %d", euid, specUID, cuid)
1345+
}
1346+
1347+
if cgid != specGID {
1348+
return fmt.Errorf("host gid %d can't map to container gid %d, expected %d", egid, specGID, cgid)
1349+
}
1350+
1351+
buf := make([]byte, 8)
1352+
binary.BigEndian.PutUint32(buf[0:4], cuid)
1353+
binary.BigEndian.PutUint32(buf[4:8], cgid)
1354+
if _, err := syncFile.Write(buf); err != nil {
1355+
return fmt.Errorf("write uid&gid to sandbox error: %w", err)
1356+
}
1357+
1358+
return nil
1359+
}
1360+
12961361
// Wait waits for the containerized process to exit, and returns its WaitStatus.
12971362
func (s *Sandbox) Wait(cid string) (unix.WaitStatus, error) {
12981363
log.Debugf("Waiting for container %q in sandbox %q", cid, s.ID)

0 commit comments

Comments
 (0)