From ac84f9c96365bce233b6a8395756e588fd50e8f1 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Thu, 2 Mar 2023 14:56:54 -0800 Subject: [PATCH] Run sandbox is a new user namespace with identity mapping when using directfs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running in a new user namespace is helpful in isolating the sandbox process’s capabilities from the host. Ideally we would want to run the sandbox with random UID/GID mappings, so that the sandbox is running with UID/GID = 0 inside its own userns which maps to a non-root user on the host. However, due to the lack of ID-mapped mount support in various filesystems (incl overlayfs), this option is currently not feasible. PiperOrigin-RevId: 513648154 --- runsc/container/container.go | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/runsc/container/container.go b/runsc/container/container.go index 9d4352935..304c14347 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -191,6 +191,10 @@ func New(conf *config.Config, args Args) (*Container, error) { return nil, fmt.Errorf("creating container root directory %q: %v", conf.RootDir, err) } + if err := modifySpecForDirectfs(conf, args.Spec); err != nil { + return nil, fmt.Errorf("failed to modify spec for directfs: %v", err) + } + sandboxID := args.ID if !isRoot(args.Spec) { var ok bool @@ -1566,3 +1570,50 @@ func cgroupInstall(conf *config.Config, cg cgroup.Cgroup, res *specs.LinuxResour } return cg, nil } + +func modifySpecForDirectfs(conf *config.Config, spec *specs.Spec) error { + if !conf.DirectFS || conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { + return nil + } + if conf.Network == config.NetworkHost { + // Hostnet feature requires the sandbox to run in the current user + // namespace, in which the network namespace is configured. + return nil + } + if _, ok := specutils.GetNS(specs.UserNamespace, spec); ok { + // If the spec already defines a userns, use that. + return nil + } + if spec.Linux == nil { + spec.Linux = &specs.Linux{} + } + if len(spec.Linux.UIDMappings) > 0 || len(spec.Linux.GIDMappings) > 0 { + // The spec can only define UID/GID mappings with a userns (checked above). + return fmt.Errorf("spec defines UID/GID mappings without defining userns") + } + // Run the sandbox in a new user namespace with identity UID/GID mappings. + spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{Type: specs.UserNamespace}) + // The maximum range of UID/GID mapping should be the largest uint32 integer. + // This is similar to what Linux does for identity mappings. Also note: + // "This leaves 4294967295 (the 32-bit signed -1 value) unmapped. This is + // deliberate: (uid_t) -1 is used in several interfaces (e.g., setreuid(2)) + // as a way to specify "no user ID". Leaving (uid_t) -1 unmapped and + // unusable guarantees that there will be no confusion when using these + // interfaces." -- user_namespaces(7). + maxRange := ^uint32(0) + spec.Linux.UIDMappings = []specs.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: maxRange, + }, + } + spec.Linux.GIDMappings = []specs.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: maxRange, + }, + } + return nil +}