Skip to content

Commit d4d5861

Browse files
committed
cdi: use [r]idmap for bind mounts if user NS is in use.
When injecting bind mounts to a container with user namespaces, add the idmap or ridmap option depending on whether we have a bind or rbind mount. This should cause the mount to happen with ID mapping set up for proper access. Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
1 parent 98a7d73 commit d4d5861

File tree

3 files changed

+153
-3
lines changed

3 files changed

+153
-3
lines changed

pkg/cdi/container-edits.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,15 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
123123

124124
if len(e.Mounts) > 0 {
125125
for _, m := range e.Mounts {
126+
mnt := &Mount{m}
127+
126128
specgen.RemoveMount(m.ContainerPath)
127-
specgen.AddMount((&Mount{m}).toOCI())
129+
130+
if !specHasUserNamespace(spec) {
131+
specgen.AddMount(mnt.toOCI())
132+
} else {
133+
specgen.AddMount(mnt.toOCI(withIDMapForBindMount()))
134+
}
128135
}
129136
sortMounts(&specgen)
130137
}
@@ -465,3 +472,16 @@ func (m orderedMounts) Swap(i, j int) {
465472
func (m orderedMounts) parts(i int) int {
466473
return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
467474
}
475+
476+
// specHasUserNamespace returns true if the OCI Spec has a Linux UserNamespace.
477+
func specHasUserNamespace(spec *oci.Spec) bool {
478+
if spec == nil || spec.Linux == nil {
479+
return false
480+
}
481+
for _, ns := range spec.Linux.Namespaces {
482+
if ns.Type == oci.UserNamespace {
483+
return true
484+
}
485+
}
486+
return false
487+
}

pkg/cdi/container-edits_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,104 @@ func TestApplyContainerEdits(t *testing.T) {
800800
},
801801
},
802802
},
803+
{
804+
name: "mount added to container with Linux user namespace and uid/gid mappings",
805+
spec: &oci.Spec{
806+
Linux: &oci.Linux{
807+
UIDMappings: []oci.LinuxIDMapping{
808+
{
809+
ContainerID: 0,
810+
HostID: 1000,
811+
Size: 999,
812+
},
813+
},
814+
GIDMappings: []oci.LinuxIDMapping{
815+
{
816+
ContainerID: 0,
817+
HostID: 2000,
818+
Size: 777,
819+
},
820+
},
821+
Namespaces: []oci.LinuxNamespace{
822+
{
823+
Type: oci.UserNamespace,
824+
Path: "/foo/bar",
825+
},
826+
},
827+
},
828+
Mounts: []oci.Mount{
829+
{
830+
Source: "/some/host/path1",
831+
Destination: "/dest/path/c",
832+
},
833+
{
834+
Source: "/some/host/path2",
835+
Destination: "/dest/path/b",
836+
},
837+
},
838+
},
839+
edits: &cdi.ContainerEdits{
840+
Mounts: []*cdi.Mount{
841+
{
842+
HostPath: "/some/host/path3",
843+
ContainerPath: "/dest/path/a",
844+
Type: "bind",
845+
},
846+
{
847+
HostPath: "/some/host/path4",
848+
ContainerPath: "/dest/path/d",
849+
Type: "bind",
850+
Options: []string{"rbind"},
851+
},
852+
},
853+
},
854+
result: &oci.Spec{
855+
Linux: &oci.Linux{
856+
UIDMappings: []oci.LinuxIDMapping{
857+
{
858+
ContainerID: 0,
859+
HostID: 1000,
860+
Size: 999,
861+
},
862+
},
863+
GIDMappings: []oci.LinuxIDMapping{
864+
{
865+
ContainerID: 0,
866+
HostID: 2000,
867+
Size: 777,
868+
},
869+
},
870+
Namespaces: []oci.LinuxNamespace{
871+
{
872+
Type: oci.UserNamespace,
873+
Path: "/foo/bar",
874+
},
875+
},
876+
},
877+
Mounts: []oci.Mount{
878+
{
879+
Source: "/some/host/path1",
880+
Destination: "/dest/path/c",
881+
},
882+
{
883+
Source: "/some/host/path2",
884+
Destination: "/dest/path/b",
885+
},
886+
{
887+
Source: "/some/host/path3",
888+
Destination: "/dest/path/a",
889+
Type: "bind",
890+
Options: []string{"idmap"},
891+
},
892+
{
893+
Source: "/some/host/path4",
894+
Destination: "/dest/path/d",
895+
Type: "bind",
896+
Options: []string{"rbind", "ridmap"},
897+
},
898+
},
899+
},
900+
},
803901
} {
804902
t.Run(tc.name, func(t *testing.T) {
805903
edits := ContainerEdits{tc.edits}

pkg/cdi/oci.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,46 @@ func (h *Hook) toOCI() spec.Hook {
3030
}
3131
}
3232

33+
// Additional OCI mount option to apply to injected mounts.
34+
type ociMountOption func(*spec.Mount)
35+
36+
// withIDMapForBindMount adds any necessary ID mapping options for a bind mount.
37+
func withIDMapForBindMount() ociMountOption {
38+
return func(m *spec.Mount) {
39+
option := ""
40+
if m.Type == "bind" {
41+
option = "idmap"
42+
}
43+
44+
for _, o := range m.Options {
45+
switch o {
46+
case "idmap", "ridmap":
47+
return
48+
case "bind":
49+
option = "idmap"
50+
case "rbind":
51+
option = "ridmap"
52+
}
53+
}
54+
55+
if option != "" {
56+
m.Options = append(m.Options, option)
57+
}
58+
}
59+
}
60+
3361
// toOCI returns the opencontainers runtime Spec Mount for this Mount.
34-
func (m *Mount) toOCI() spec.Mount {
35-
return spec.Mount{
62+
func (m *Mount) toOCI(options ...ociMountOption) spec.Mount {
63+
om := spec.Mount{
3664
Source: m.HostPath,
3765
Destination: m.ContainerPath,
3866
Options: m.Options,
3967
Type: m.Type,
4068
}
69+
for _, o := range options {
70+
o(&om)
71+
}
72+
return om
4173
}
4274

4375
// toOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.

0 commit comments

Comments
 (0)