configs: disallow ambiguous userns and timens configurations

For userns and timens, the mappings (and offsets, respectively) cannot
be changed after the namespace is first configured. Thus, configuring a
container with a namespace path to join means that you cannot also
provide configuration for said namespace. Previously we would silently
ignore the configuration (and just join the provided path), but we
really should be returning an error (especially when you consider that
the configuration userns mappings are used quite a bit in runc with the
assumption that they are the correct mapping for the userns -- but in
this case they are not).

In the case of userns, the mappings are also required if you _do not_
specify a path, while in the case of the time namespace you can have a
container with a timens but no mappings specified.

It should be noted that the case checking that the user has not
specified a userns path and a userns mapping needs to be handled in
specconv (as opposed to the configuration validator) because with this
patchset we now cache the mappings of path-based userns configurations
and thus the validator can't be sure whether the mapping is a cached
mapping or a user-specified one. So we do the validation in specconv,
and thus the test for this needs to be an integration test.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
Aleksa Sarai
2023-11-21 17:20:36 +11:00
parent 3bab7e9223
commit 09822c3da8
5 changed files with 92 additions and 6 deletions
+8
View File
@@ -516,6 +516,9 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
}
func toConfigIDMap(specMaps []specs.LinuxIDMapping) []configs.IDMap {
if specMaps == nil {
return nil
}
idmaps := make([]configs.IDMap, len(specMaps))
for i, id := range specMaps {
idmaps[i] = configs.IDMap{
@@ -970,6 +973,11 @@ func setupUserNamespace(spec *specs.Spec, config *configs.Config) error {
config.GIDMappings = toConfigIDMap(spec.Linux.GIDMappings)
}
if path := config.Namespaces.PathOf(configs.NEWUSER); path != "" {
// We cannot allow uid or gid mappings to be set if we are also asked
// to join a userns.
if config.UIDMappings != nil || config.GIDMappings != nil {
return errors.New("user namespaces enabled, but both namespace path and mapping specified -- you may only provide one")
}
// Cache the current userns mappings in our configuration, so that we
// can calculate uid and gid mappings within runc. These mappings are
// never used for configuring the container if the path is set.
+34
View File
@@ -608,6 +608,40 @@ func TestDupNamespaces(t *testing.T) {
}
}
func TestUserNamespaceMappingAndPath(t *testing.T) {
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
t.Skip("Test requires userns.")
}
spec := &specs.Spec{
Root: &specs.Root{
Path: "rootfs",
},
Linux: &specs.Linux{
UIDMappings: []specs.LinuxIDMapping{
{ContainerID: 0, HostID: 1000, Size: 1000},
},
GIDMappings: []specs.LinuxIDMapping{
{ContainerID: 0, HostID: 2000, Size: 1000},
},
Namespaces: []specs.LinuxNamespace{
{
Type: "user",
Path: "/proc/1/ns/user",
},
},
},
}
_, err := CreateLibcontainerConfig(&CreateOpts{
Spec: spec,
})
if !strings.Contains(err.Error(), "user namespaces enabled, but both namespace path and mapping specified") {
t.Errorf("user namespace with mapping and namespace path should be forbidden")
}
}
func TestNonZeroEUIDCompatibleSpecconvValidate(t *testing.T) {
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
t.Skip("Test requires userns.")