Skip to content

Commit 9ee1e33

Browse files
committed
cdi: inject mount UID/GID mappings if user NS is in use.
When injecting mounts to a container with user namespaces in use, inject the mounts with UID and GID mappings taken from the OCI Spec. Signed-off-by: Krisztian Litkey <[email protected]>
1 parent 139ffec commit 9ee1e33

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

pkg/cdi/container-edits.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,18 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
114114
}
115115

116116
if len(e.Mounts) > 0 {
117+
var (
118+
uids []oci.LinuxIDMapping
119+
gids []oci.LinuxIDMapping
120+
)
121+
122+
if specHasUserNamespace(spec) {
123+
uids = cloneIDMappings(spec.Linux.UIDMappings)
124+
gids = cloneIDMappings(spec.Linux.GIDMappings)
125+
}
117126
for _, m := range e.Mounts {
118127
specgen.RemoveMount(m.ContainerPath)
119-
specgen.AddMount((&Mount{m}).toOCI())
128+
specgen.AddMount((&Mount{m}).toOCI(withMountIDMappings(uids, gids)))
120129
}
121130
sortMounts(&specgen)
122131
}
@@ -389,3 +398,26 @@ func (m orderedMounts) Swap(i, j int) {
389398
func (m orderedMounts) parts(i int) int {
390399
return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
391400
}
401+
402+
// specHasUserNamespace returns true if the OCI Spec has a Linux UserNamespace.
403+
func specHasUserNamespace(spec *oci.Spec) bool {
404+
if spec == nil || spec.Linux == nil {
405+
return false
406+
}
407+
for _, ns := range spec.Linux.Namespaces {
408+
if ns.Type == oci.UserNamespace {
409+
return true
410+
}
411+
}
412+
return false
413+
}
414+
415+
// cloneIDMappings clones a slice of OCI LinuxIDMappings.
416+
func cloneIDMappings(mappings []oci.LinuxIDMapping) []oci.LinuxIDMapping {
417+
if mappings == nil {
418+
return nil
419+
}
420+
clone := make([]oci.LinuxIDMapping, len(mappings))
421+
copy(clone, mappings)
422+
return clone
423+
}

pkg/cdi/container-edits_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,103 @@ func TestApplyContainerEdits(t *testing.T) {
695695
},
696696
},
697697
},
698+
{
699+
name: "mount added to container with Linux user namespace and uid/gid mappings",
700+
spec: &oci.Spec{
701+
Linux: &oci.Linux{
702+
UIDMappings: []oci.LinuxIDMapping{
703+
{
704+
ContainerID: 0,
705+
HostID: 1000,
706+
Size: 999,
707+
},
708+
},
709+
GIDMappings: []oci.LinuxIDMapping{
710+
{
711+
ContainerID: 0,
712+
HostID: 2000,
713+
Size: 777,
714+
},
715+
},
716+
Namespaces: []oci.LinuxNamespace{
717+
{
718+
Type: oci.UserNamespace,
719+
Path: "/foo/bar",
720+
},
721+
},
722+
},
723+
Mounts: []oci.Mount{
724+
{
725+
Source: "/some/host/path1",
726+
Destination: "/dest/path/c",
727+
},
728+
{
729+
Source: "/some/host/path2",
730+
Destination: "/dest/path/b",
731+
},
732+
},
733+
},
734+
edits: &cdi.ContainerEdits{
735+
Mounts: []*cdi.Mount{
736+
{
737+
HostPath: "/some/host/path3",
738+
ContainerPath: "/dest/path/a",
739+
},
740+
},
741+
},
742+
result: &oci.Spec{
743+
Linux: &oci.Linux{
744+
UIDMappings: []oci.LinuxIDMapping{
745+
{
746+
ContainerID: 0,
747+
HostID: 1000,
748+
Size: 999,
749+
},
750+
},
751+
GIDMappings: []oci.LinuxIDMapping{
752+
{
753+
ContainerID: 0,
754+
HostID: 2000,
755+
Size: 777,
756+
},
757+
},
758+
Namespaces: []oci.LinuxNamespace{
759+
{
760+
Type: oci.UserNamespace,
761+
Path: "/foo/bar",
762+
},
763+
},
764+
},
765+
Mounts: []oci.Mount{
766+
{
767+
Source: "/some/host/path1",
768+
Destination: "/dest/path/c",
769+
},
770+
{
771+
Source: "/some/host/path2",
772+
Destination: "/dest/path/b",
773+
},
774+
{
775+
Source: "/some/host/path3",
776+
Destination: "/dest/path/a",
777+
UIDMappings: []oci.LinuxIDMapping{
778+
{
779+
ContainerID: 0,
780+
HostID: 1000,
781+
Size: 999,
782+
},
783+
},
784+
GIDMappings: []oci.LinuxIDMapping{
785+
{
786+
ContainerID: 0,
787+
HostID: 2000,
788+
Size: 777,
789+
},
790+
},
791+
},
792+
},
793+
},
794+
},
698795
} {
699796
t.Run(tc.name, func(t *testing.T) {
700797
edits := ContainerEdits{tc.edits}

pkg/cdi/oci.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,33 @@ 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+
// withMountIDMappings adds UID and GID mappings for the given mount.
37+
func withMountIDMappings(uid, gid []spec.LinuxIDMapping) ociMountOption {
38+
return func(m *spec.Mount) {
39+
if uid != nil {
40+
m.UIDMappings = uid
41+
}
42+
if gid != nil {
43+
m.GIDMappings = gid
44+
}
45+
}
46+
}
47+
3348
// toOCI returns the opencontainers runtime Spec Mount for this Mount.
34-
func (m *Mount) toOCI() spec.Mount {
35-
return spec.Mount{
49+
func (m *Mount) toOCI(options ...ociMountOption) spec.Mount {
50+
om := spec.Mount{
3651
Source: m.HostPath,
3752
Destination: m.ContainerPath,
3853
Options: m.Options,
3954
Type: m.Type,
4055
}
56+
for _, o := range options {
57+
o(&om)
58+
}
59+
return om
4160
}
4261

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

0 commit comments

Comments
 (0)