Skip to content

Commit 7562868

Browse files
committed
runsc: Make identity user mapping work for filesystem
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 501a663 commit 7562868

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-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{
@@ -784,6 +787,26 @@ func waitForFD(fd int, fdName string) error {
784787
return nil
785788
}
786789

790+
func waitForID(fd int, fdName string) (uint32, uint32, error) {
791+
log.Debugf("Waiting on %s %d...", fdName, fd)
792+
f := os.NewFile(uintptr(fd), fdName)
793+
defer f.Close()
794+
795+
var uid uint32
796+
var gid uint32
797+
buf := make([]byte, 8)
798+
799+
if n, err := f.Read(buf); n != 8 || err != nil {
800+
e := fmt.Errorf("failed to convert to int:%v :%v", uid, err)
801+
return 0, 0, e
802+
}
803+
uid = binary.BigEndian.Uint32(buf[0:4])
804+
gid = binary.BigEndian.Uint32(buf[4:8])
805+
806+
807+
return uid, gid, nil
808+
}
809+
787810
// spawnProcMounter executes the /proc unmounter process.
788811
// It returns a function to wait on the proc unmounter process, which
789812
// should be called (via defer) in case of errors in order to clean up the
@@ -838,17 +861,22 @@ func (g *goferSyncFDs) syncUsernsForRootless() {
838861
//
839862
// Postcondition: All callers must re-exec themselves after this returns.
840863
func syncUsernsForRootless(fd int) {
841-
if err := waitForFD(fd, "userns sync FD"); err != nil {
842-
util.Fatalf("failed to sync on userns FD: %v", err)
864+
var uid uint32
865+
var gid uint32
866+
var err error
867+
868+
if uid, gid, err = waitForID(fd, "userns sync FD"); err != nil {
869+
util.Fatalf("failed to sync on userns FD:%v: %v %v", uid, gid, err)
843870
}
844871

872+
845873
// SETUID changes UID on the current system thread, so we have
846874
// to re-execute current binary.
847875
runtime.LockOSThread()
848-
if _, _, errno := unix.RawSyscall(unix.SYS_SETUID, 0, 0, 0); errno != 0 {
876+
if _, _, errno := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0); errno != 0 {
849877
util.Fatalf("failed to set UID: %v", errno)
850878
}
851-
if _, _, errno := unix.RawSyscall(unix.SYS_SETGID, 0, 0, 0); errno != 0 {
879+
if _, _, errno := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0); errno != 0 {
852880
util.Fatalf("failed to set GID: %v", errno)
853881
}
854882
}

runsc/container/container.go

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

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

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

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

runsc/sandbox/sandbox.go

Lines changed: 42 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"
@@ -1029,6 +1030,13 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
10291030
// configured.
10301031
rootlessEUID := unix.Geteuid() != 0
10311032
setUserMappings := false
1033+
var gSyncFile *os.File
1034+
defer func() {
1035+
if gSyncFile != nil {
1036+
gSyncFile.Close()
1037+
}
1038+
}()
1039+
10321040
if conf.Network == config.NetworkHost || conf.DirectFS {
10331041
if userns, ok := specutils.GetNS(specs.UserNamespace, args.Spec); ok {
10341042
log.Infof("Sandbox will be started in container's user namespace: %+v", userns)
@@ -1038,7 +1046,7 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
10381046
if err != nil {
10391047
return err
10401048
}
1041-
defer syncFile.Close()
1049+
gSyncFile = syncFile
10421050
setUserMappings = true
10431051
} else {
10441052
specutils.SetUIDGIDMappings(cmd, args.Spec)
@@ -1283,6 +1291,9 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
12831291
if err := SetUserMappings(args.Spec, cmd.Process.Pid); err != nil {
12841292
return err
12851293
}
1294+
if err := SendIDToSandbox(gSyncFile, args.Spec); err != nil {
1295+
return err
1296+
}
12861297
}
12871298

12881299
s.child = true
@@ -1292,6 +1303,36 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
12921303
return nil
12931304
}
12941305

1306+
func SendIDToSandbox(syncFile *os.File, spec *specs.Spec) error {
1307+
euid := uint32(os.Geteuid())
1308+
egid := uint32(os.Getegid())
1309+
1310+
var cuid uint32
1311+
var cgid uint32
1312+
1313+
for _, idMap := range spec.Linux.UIDMappings {
1314+
if euid >= idMap.HostID && euid < idMap.Size + idMap.HostID {
1315+
cuid = euid - idMap.HostID + idMap.ContainerID
1316+
break
1317+
}
1318+
}
1319+
1320+
for _, idMap := range spec.Linux.GIDMappings {
1321+
if egid >= idMap.HostID && euid < idMap.Size + idMap.HostID {
1322+
cgid = egid - idMap.HostID + idMap.ContainerID
1323+
break
1324+
}
1325+
}
1326+
buf := make([]byte, 8)
1327+
binary.BigEndian.PutUint32(buf[0:4], cuid)
1328+
binary.BigEndian.PutUint32(buf[4:8], cgid)
1329+
if _, err := syncFile.Write(buf); err != nil {
1330+
return fmt.Errorf("write uid&gid to sandbox error: %w", err)
1331+
}
1332+
1333+
return nil
1334+
}
1335+
12951336
// Wait waits for the containerized process to exit, and returns its WaitStatus.
12961337
func (s *Sandbox) Wait(cid string) (unix.WaitStatus, error) {
12971338
log.Debugf("Waiting for container %q in sandbox %q", cid, s.ID)

0 commit comments

Comments
 (0)