diff --git a/pkg/cdi/container-edits.go b/pkg/cdi/container-edits.go index f498049..0c5c568 100644 --- a/pkg/cdi/container-edits.go +++ b/pkg/cdi/container-edits.go @@ -123,8 +123,15 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { if len(e.Mounts) > 0 { for _, m := range e.Mounts { + mnt := &Mount{m} + specgen.RemoveMount(m.ContainerPath) - specgen.AddMount((&Mount{m}).toOCI()) + + if !specHasUserNamespace(spec) { + specgen.AddMount(mnt.toOCI()) + } else { + specgen.AddMount(mnt.toOCI(withIDMapForBindMount())) + } } sortMounts(&specgen) } @@ -465,3 +472,16 @@ func (m orderedMounts) Swap(i, j int) { func (m orderedMounts) parts(i int) int { return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) } + +// specHasUserNamespace returns true if the OCI Spec has a Linux UserNamespace. +func specHasUserNamespace(spec *oci.Spec) bool { + if spec == nil || spec.Linux == nil { + return false + } + for _, ns := range spec.Linux.Namespaces { + if ns.Type == oci.UserNamespace { + return true + } + } + return false +} diff --git a/pkg/cdi/container-edits_test.go b/pkg/cdi/container-edits_test.go index 2c553d9..00e20fa 100644 --- a/pkg/cdi/container-edits_test.go +++ b/pkg/cdi/container-edits_test.go @@ -800,6 +800,104 @@ func TestApplyContainerEdits(t *testing.T) { }, }, }, + { + name: "mount added to container with Linux user namespace and uid/gid mappings", + spec: &oci.Spec{ + Linux: &oci.Linux{ + UIDMappings: []oci.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 1000, + Size: 999, + }, + }, + GIDMappings: []oci.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 2000, + Size: 777, + }, + }, + Namespaces: []oci.LinuxNamespace{ + { + Type: oci.UserNamespace, + Path: "/foo/bar", + }, + }, + }, + Mounts: []oci.Mount{ + { + Source: "/some/host/path1", + Destination: "/dest/path/c", + }, + { + Source: "/some/host/path2", + Destination: "/dest/path/b", + }, + }, + }, + edits: &cdi.ContainerEdits{ + Mounts: []*cdi.Mount{ + { + HostPath: "/some/host/path3", + ContainerPath: "/dest/path/a", + Type: "bind", + }, + { + HostPath: "/some/host/path4", + ContainerPath: "/dest/path/d", + Type: "bind", + Options: []string{"rbind"}, + }, + }, + }, + result: &oci.Spec{ + Linux: &oci.Linux{ + UIDMappings: []oci.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 1000, + Size: 999, + }, + }, + GIDMappings: []oci.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 2000, + Size: 777, + }, + }, + Namespaces: []oci.LinuxNamespace{ + { + Type: oci.UserNamespace, + Path: "/foo/bar", + }, + }, + }, + Mounts: []oci.Mount{ + { + Source: "/some/host/path1", + Destination: "/dest/path/c", + }, + { + Source: "/some/host/path2", + Destination: "/dest/path/b", + }, + { + Source: "/some/host/path3", + Destination: "/dest/path/a", + Type: "bind", + Options: []string{"idmap"}, + }, + { + Source: "/some/host/path4", + Destination: "/dest/path/d", + Type: "bind", + Options: []string{"rbind", "ridmap"}, + }, + }, + }, + }, } { t.Run(tc.name, func(t *testing.T) { edits := ContainerEdits{tc.edits} diff --git a/pkg/cdi/oci.go b/pkg/cdi/oci.go index f37499f..68ffafe 100644 --- a/pkg/cdi/oci.go +++ b/pkg/cdi/oci.go @@ -30,14 +30,46 @@ func (h *Hook) toOCI() spec.Hook { } } +// Additional OCI mount option to apply to injected mounts. +type ociMountOption func(*spec.Mount) + +// withIDMapForBindMount adds any necessary ID mapping options for a bind mount. +func withIDMapForBindMount() ociMountOption { + return func(m *spec.Mount) { + option := "" + if m.Type == "bind" { + option = "idmap" + } + + for _, o := range m.Options { + switch o { + case "idmap", "ridmap": + return + case "bind": + option = "idmap" + case "rbind": + option = "ridmap" + } + } + + if option != "" { + m.Options = append(m.Options, option) + } + } +} + // toOCI returns the opencontainers runtime Spec Mount for this Mount. -func (m *Mount) toOCI() spec.Mount { - return spec.Mount{ +func (m *Mount) toOCI(options ...ociMountOption) spec.Mount { + om := spec.Mount{ Source: m.HostPath, Destination: m.ContainerPath, Options: m.Options, Type: m.Type, } + for _, o := range options { + o(&om) + } + return om } // toOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.