Skip to content

Commit

Permalink
Merge pull request #3717 from kinvolk/rata/idmap
Browse files Browse the repository at this point in the history
Support idmap mounts for volumes
  • Loading branch information
lifubang authored Jul 17, 2023
2 parents 4338e97 + b460dc3 commit f73b05d
Show file tree
Hide file tree
Showing 17 changed files with 953 additions and 100 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ vendor/pkg
contrib/cmd/recvtty/recvtty
contrib/cmd/sd-helper/sd-helper
contrib/cmd/seccompagent/seccompagent
contrib/cmd/fs-idmap/fs-idmap
man/man8
release
Vagrantfile
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ endif
runc:
$(GO_BUILD) -o runc .

all: runc recvtty sd-helper seccompagent
all: runc recvtty sd-helper seccompagent fs-idmap

recvtty sd-helper seccompagent:
recvtty sd-helper seccompagent fs-idmap:
$(GO_BUILD) -o contrib/cmd/$@/$@ ./contrib/cmd/$@

static:
Expand Down Expand Up @@ -151,6 +151,7 @@ clean:
rm -f contrib/cmd/recvtty/recvtty
rm -f contrib/cmd/sd-helper/sd-helper
rm -f contrib/cmd/seccompagent/seccompagent
rm -f contrib/cmd/fs-idmap/fs-idmap
rm -rf release
rm -rf man/man8

Expand Down Expand Up @@ -191,7 +192,7 @@ verify-dependencies: vendor
validate-keyring:
script/keyring_validate.sh

.PHONY: runc all recvtty sd-helper seccompagent static releaseall release \
.PHONY: runc all recvtty sd-helper seccompagent fs-idmap static releaseall release \
localrelease dbuild lint man runcimage \
test localtest unittest localunittest integration localintegration \
rootlessintegration localrootlessintegration shell install install-bash \
Expand Down
50 changes: 50 additions & 0 deletions contrib/cmd/fs-idmap/fs-idmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"fmt"
"log"
"os"
"os/exec"
"syscall"

"golang.org/x/sys/unix"
)

func main() {
if len(os.Args) < 2 {
log.Fatalf("usage: %s path_to_mount_set_attr", os.Args[0])
}

src := os.Args[1]
treeFD, err := unix.OpenTree(-1, src, uint(unix.OPEN_TREE_CLONE|unix.OPEN_TREE_CLOEXEC|unix.AT_EMPTY_PATH|unix.AT_RECURSIVE))
if err != nil {
log.Fatalf("error calling open_tree %q: %v", src, err)
}
defer unix.Close(treeFD)

cmd := exec.Command("/usr/bin/sleep", "5")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUSER,
UidMappings: []syscall.SysProcIDMap{{ContainerID: 0, HostID: 65536, Size: 65536}},
GidMappings: []syscall.SysProcIDMap{{ContainerID: 0, HostID: 65536, Size: 65536}},
}
if err := cmd.Start(); err != nil {
log.Fatalf("failed to run the helper binary: %v", err)
}

path := fmt.Sprintf("/proc/%d/ns/user", cmd.Process.Pid)
var userNsFile *os.File
if userNsFile, err = os.Open(path); err != nil {
log.Fatalf("unable to get user ns file descriptor: %v", err)
return
}
defer userNsFile.Close()

attr := unix.MountAttr{
Attr_set: unix.MOUNT_ATTR_IDMAP,
Userns_fd: uint64(userNsFile.Fd()),
}
if err := unix.MountSetattr(treeFD, "", unix.AT_EMPTY_PATH|unix.AT_RECURSIVE, &attr); err != nil {
log.Fatalf("error calling mount_setattr: %v", err)
}
}
16 changes: 16 additions & 0 deletions libcontainer/configs/mount_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,24 @@ type Mount struct {

// Extensions are additional flags that are specific to runc.
Extensions int `json:"extensions"`

// UIDMappings is used to changing file user owners w/o calling chown.
// Note that, the underlying filesystem should support this feature to be
// used.
// Every mount point could have its own mapping.
UIDMappings []IDMap `json:"uidMappings,omitempty"`

// GIDMappings is used to changing file group owners w/o calling chown.
// Note that, the underlying filesystem should support this feature to be
// used.
// Every mount point could have its own mapping.
GIDMappings []IDMap `json:"gidMappings,omitempty"`
}

func (m *Mount) IsBind() bool {
return m.Flags&unix.MS_BIND != 0
}

func (m *Mount) IsIDMapped() bool {
return len(m.UIDMappings) > 0 || len(m.GIDMappings) > 0
}
67 changes: 57 additions & 10 deletions libcontainer/configs/validate/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/intelrdt"
selinux "github.com/opencontainers/selinux/go-selinux"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

Expand All @@ -29,21 +28,13 @@ func Validate(config *configs.Config) error {
sysctl,
intelrdtCheck,
rootlessEUIDCheck,
mounts,
}
for _, c := range checks {
if err := c(config); err != nil {
return err
}
}
// Relaxed validation rules for backward compatibility
warns := []check{
mounts, // TODO (runc v1.x.x): make this an error instead of a warning
}
for _, c := range warns {
if err := c(config); err != nil {
logrus.WithError(err).Warn("invalid configuration")
}
}
return nil
}

Expand Down Expand Up @@ -262,16 +253,72 @@ func cgroupsCheck(config *configs.Config) error {
return nil
}

func checkIDMapMounts(config *configs.Config, m *configs.Mount) error {
if !m.IsIDMapped() {
return nil
}

if !m.IsBind() {
return fmt.Errorf("gidMappings/uidMappings is supported only for mounts with the option 'bind'")
}
if config.RootlessEUID {
return fmt.Errorf("gidMappings/uidMappings is not supported when runc is being launched with EUID != 0, needs CAP_SYS_ADMIN on the runc parent's user namespace")
}
if len(config.UidMappings) == 0 || len(config.GidMappings) == 0 {
return fmt.Errorf("not yet supported to use gidMappings/uidMappings in a mount without also using a user namespace")
}
if !sameMapping(config.UidMappings, m.UIDMappings) {
return fmt.Errorf("not yet supported for the mount uidMappings to be different than user namespace uidMapping")
}
if !sameMapping(config.GidMappings, m.GIDMappings) {
return fmt.Errorf("not yet supported for the mount gidMappings to be different than user namespace gidMapping")
}
if !filepath.IsAbs(m.Source) {
return fmt.Errorf("mount source not absolute")
}

return nil
}

func mounts(config *configs.Config) error {
for _, m := range config.Mounts {
// We upgraded this to an error in runc 1.2. We might need to
// revert this change if some users haven't still moved to use
// abs paths, in that please move this check inside
// checkIDMapMounts() as we do want to ensure that for idmap
// mounts anyways.
if !filepath.IsAbs(m.Destination) {
return fmt.Errorf("invalid mount %+v: mount destination not absolute", m)
}
if err := checkIDMapMounts(config, m); err != nil {
return fmt.Errorf("invalid mount %+v: %w", m, err)
}
}

return nil
}

// sameMapping checks if the mappings are the same. If the mappings are the same
// but in different order, it returns false.
func sameMapping(a, b []configs.IDMap) bool {
if len(a) != len(b) {
return false
}

for i := range a {
if a[i].ContainerID != b[i].ContainerID {
return false
}
if a[i].HostID != b[i].HostID {
return false
}
if a[i].Size != b[i].Size {
return false
}
}
return true
}

func isHostNetNS(path string) (bool, error) {
const currentProcessNetns = "/proc/self/ns/net"

Expand Down
Loading

0 comments on commit f73b05d

Please sign in to comment.