Skip to content

Commit f73b05d

Browse files
authored
Merge pull request #3717 from kinvolk/rata/idmap
Support idmap mounts for volumes
2 parents 4338e97 + b460dc3 commit f73b05d

File tree

17 files changed

+953
-100
lines changed

17 files changed

+953
-100
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ vendor/pkg
44
contrib/cmd/recvtty/recvtty
55
contrib/cmd/sd-helper/sd-helper
66
contrib/cmd/seccompagent/seccompagent
7+
contrib/cmd/fs-idmap/fs-idmap
78
man/man8
89
release
910
Vagrantfile

Makefile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ endif
6060
runc:
6161
$(GO_BUILD) -o runc .
6262

63-
all: runc recvtty sd-helper seccompagent
63+
all: runc recvtty sd-helper seccompagent fs-idmap
6464

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

6868
static:
@@ -151,6 +151,7 @@ clean:
151151
rm -f contrib/cmd/recvtty/recvtty
152152
rm -f contrib/cmd/sd-helper/sd-helper
153153
rm -f contrib/cmd/seccompagent/seccompagent
154+
rm -f contrib/cmd/fs-idmap/fs-idmap
154155
rm -rf release
155156
rm -rf man/man8
156157

@@ -191,7 +192,7 @@ verify-dependencies: vendor
191192
validate-keyring:
192193
script/keyring_validate.sh
193194

194-
.PHONY: runc all recvtty sd-helper seccompagent static releaseall release \
195+
.PHONY: runc all recvtty sd-helper seccompagent fs-idmap static releaseall release \
195196
localrelease dbuild lint man runcimage \
196197
test localtest unittest localunittest integration localintegration \
197198
rootlessintegration localrootlessintegration shell install install-bash \

contrib/cmd/fs-idmap/fs-idmap.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"os/exec"
8+
"syscall"
9+
10+
"golang.org/x/sys/unix"
11+
)
12+
13+
func main() {
14+
if len(os.Args) < 2 {
15+
log.Fatalf("usage: %s path_to_mount_set_attr", os.Args[0])
16+
}
17+
18+
src := os.Args[1]
19+
treeFD, err := unix.OpenTree(-1, src, uint(unix.OPEN_TREE_CLONE|unix.OPEN_TREE_CLOEXEC|unix.AT_EMPTY_PATH|unix.AT_RECURSIVE))
20+
if err != nil {
21+
log.Fatalf("error calling open_tree %q: %v", src, err)
22+
}
23+
defer unix.Close(treeFD)
24+
25+
cmd := exec.Command("/usr/bin/sleep", "5")
26+
cmd.SysProcAttr = &syscall.SysProcAttr{
27+
Cloneflags: syscall.CLONE_NEWUSER,
28+
UidMappings: []syscall.SysProcIDMap{{ContainerID: 0, HostID: 65536, Size: 65536}},
29+
GidMappings: []syscall.SysProcIDMap{{ContainerID: 0, HostID: 65536, Size: 65536}},
30+
}
31+
if err := cmd.Start(); err != nil {
32+
log.Fatalf("failed to run the helper binary: %v", err)
33+
}
34+
35+
path := fmt.Sprintf("/proc/%d/ns/user", cmd.Process.Pid)
36+
var userNsFile *os.File
37+
if userNsFile, err = os.Open(path); err != nil {
38+
log.Fatalf("unable to get user ns file descriptor: %v", err)
39+
return
40+
}
41+
defer userNsFile.Close()
42+
43+
attr := unix.MountAttr{
44+
Attr_set: unix.MOUNT_ATTR_IDMAP,
45+
Userns_fd: uint64(userNsFile.Fd()),
46+
}
47+
if err := unix.MountSetattr(treeFD, "", unix.AT_EMPTY_PATH|unix.AT_RECURSIVE, &attr); err != nil {
48+
log.Fatalf("error calling mount_setattr: %v", err)
49+
}
50+
}

libcontainer/configs/mount_linux.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,24 @@ type Mount struct {
2929

3030
// Extensions are additional flags that are specific to runc.
3131
Extensions int `json:"extensions"`
32+
33+
// UIDMappings is used to changing file user owners w/o calling chown.
34+
// Note that, the underlying filesystem should support this feature to be
35+
// used.
36+
// Every mount point could have its own mapping.
37+
UIDMappings []IDMap `json:"uidMappings,omitempty"`
38+
39+
// GIDMappings is used to changing file group owners w/o calling chown.
40+
// Note that, the underlying filesystem should support this feature to be
41+
// used.
42+
// Every mount point could have its own mapping.
43+
GIDMappings []IDMap `json:"gidMappings,omitempty"`
3244
}
3345

3446
func (m *Mount) IsBind() bool {
3547
return m.Flags&unix.MS_BIND != 0
3648
}
49+
50+
func (m *Mount) IsIDMapped() bool {
51+
return len(m.UIDMappings) > 0 || len(m.GIDMappings) > 0
52+
}

libcontainer/configs/validate/validator.go

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"github.com/opencontainers/runc/libcontainer/configs"
1313
"github.com/opencontainers/runc/libcontainer/intelrdt"
1414
selinux "github.com/opencontainers/selinux/go-selinux"
15-
"github.com/sirupsen/logrus"
1615
"golang.org/x/sys/unix"
1716
)
1817

@@ -29,21 +28,13 @@ func Validate(config *configs.Config) error {
2928
sysctl,
3029
intelrdtCheck,
3130
rootlessEUIDCheck,
31+
mounts,
3232
}
3333
for _, c := range checks {
3434
if err := c(config); err != nil {
3535
return err
3636
}
3737
}
38-
// Relaxed validation rules for backward compatibility
39-
warns := []check{
40-
mounts, // TODO (runc v1.x.x): make this an error instead of a warning
41-
}
42-
for _, c := range warns {
43-
if err := c(config); err != nil {
44-
logrus.WithError(err).Warn("invalid configuration")
45-
}
46-
}
4738
return nil
4839
}
4940

@@ -262,16 +253,72 @@ func cgroupsCheck(config *configs.Config) error {
262253
return nil
263254
}
264255

256+
func checkIDMapMounts(config *configs.Config, m *configs.Mount) error {
257+
if !m.IsIDMapped() {
258+
return nil
259+
}
260+
261+
if !m.IsBind() {
262+
return fmt.Errorf("gidMappings/uidMappings is supported only for mounts with the option 'bind'")
263+
}
264+
if config.RootlessEUID {
265+
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")
266+
}
267+
if len(config.UidMappings) == 0 || len(config.GidMappings) == 0 {
268+
return fmt.Errorf("not yet supported to use gidMappings/uidMappings in a mount without also using a user namespace")
269+
}
270+
if !sameMapping(config.UidMappings, m.UIDMappings) {
271+
return fmt.Errorf("not yet supported for the mount uidMappings to be different than user namespace uidMapping")
272+
}
273+
if !sameMapping(config.GidMappings, m.GIDMappings) {
274+
return fmt.Errorf("not yet supported for the mount gidMappings to be different than user namespace gidMapping")
275+
}
276+
if !filepath.IsAbs(m.Source) {
277+
return fmt.Errorf("mount source not absolute")
278+
}
279+
280+
return nil
281+
}
282+
265283
func mounts(config *configs.Config) error {
266284
for _, m := range config.Mounts {
285+
// We upgraded this to an error in runc 1.2. We might need to
286+
// revert this change if some users haven't still moved to use
287+
// abs paths, in that please move this check inside
288+
// checkIDMapMounts() as we do want to ensure that for idmap
289+
// mounts anyways.
267290
if !filepath.IsAbs(m.Destination) {
268291
return fmt.Errorf("invalid mount %+v: mount destination not absolute", m)
269292
}
293+
if err := checkIDMapMounts(config, m); err != nil {
294+
return fmt.Errorf("invalid mount %+v: %w", m, err)
295+
}
270296
}
271297

272298
return nil
273299
}
274300

301+
// sameMapping checks if the mappings are the same. If the mappings are the same
302+
// but in different order, it returns false.
303+
func sameMapping(a, b []configs.IDMap) bool {
304+
if len(a) != len(b) {
305+
return false
306+
}
307+
308+
for i := range a {
309+
if a[i].ContainerID != b[i].ContainerID {
310+
return false
311+
}
312+
if a[i].HostID != b[i].HostID {
313+
return false
314+
}
315+
if a[i].Size != b[i].Size {
316+
return false
317+
}
318+
}
319+
return true
320+
}
321+
275322
func isHostNetNS(path string) (bool, error) {
276323
const currentProcessNetns = "/proc/self/ns/net"
277324

0 commit comments

Comments
 (0)