From 9ebf533c56ea2e6836649354f6c0589fd1c2df23 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 8 Oct 2026 15:18:12 -0700 Subject: [PATCH] daemon: use OCI domainname field Setting the container domain name via the kernel.domainname sysctl does not work with user namespaces (including rootless mode), as /proc/sys/kernel/domainname is owned by the global root and thus can't be written to from inside a user namespace: write sysctl key kernel.domainname: open /proc/sys/kernel/domainname: permission denied Since runtime-spec v1.1.0, there is a dedicated domainname field, which is implemented by OCI runtimes (runc >= v1.2.0, crun >= 1.7, youki) using setdomainname(2), which works with user namespaces. Use it instead of the sysctl. An explicit kernel.domainname sysctl (set via --sysctl) is still passed to the runtime as is. Enable TestNISDomainname for rootless and user namespaces. Signed-off-by: Kir Kolyshkin --- daemon/oci_linux.go | 8 +++++++- daemon/oci_linux_test.go | 6 ++++-- daemon/oci_utils.go | 21 --------------------- integration/container/run_linux_test.go | 6 ------ 4 files changed, 11 insertions(+), 30 deletions(-) delete mode 100644 daemon/oci_utils.go diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 1b3bbc57ba..e8147a2389 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -749,7 +749,7 @@ func withCommonOptions(daemon *Daemon, daemonCfg *dconfig.Config, c *container.C s.Process.Terminal = c.Config.Tty s.Hostname = c.Config.Hostname - setLinuxDomainname(c, s) + s.Domainname = c.Config.Domainname // Add default sysctls that are generally safe and useful; currently we // grant the capabilities to allow these anyway. You can override if @@ -757,6 +757,12 @@ func withCommonOptions(daemon *Daemon, daemonCfg *dconfig.Config, c *container.C // We do not set network sysctls if network namespace is host, or if we are // joining an existing namespace, only if we create a new net namespace. if c.HostConfig.NetworkMode.IsPrivate() { + if s.Linux == nil { + s.Linux = &specs.Linux{} + } + if s.Linux.Sysctl == nil { + s.Linux.Sysctl = make(map[string]string) + } // We cannot set up ping socket support in a user namespace userNS := daemonCfg.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate() if !userNS && !userns.RunningInUserNS() && sysctlExists("net.ipv4.ping_group_range") { diff --git a/daemon/oci_linux_test.go b/daemon/oci_linux_test.go index 09b80de5e5..598ff552f4 100644 --- a/daemon/oci_linux_test.go +++ b/daemon/oci_linux_test.go @@ -290,11 +290,13 @@ func TestSysctlOverride(t *testing.T) { } d := setupFakeDaemon(t, c) - // Ensure that the implicit sysctl is set correctly. + // Ensure that the domainname is set, and the sysctl is not. s, err := d.createSpec(ctx, &configStore{}, c, nil) assert.NilError(t, err) assert.Equal(t, s.Hostname, "foobar") - assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname) + assert.Equal(t, s.Domainname, c.Config.Domainname) + _, hasDomainnameSysctl := s.Linux.Sysctl["kernel.domainname"] + assert.Check(t, !hasDomainnameSysctl) if sysctlExists("net.ipv4.ip_unprivileged_port_start") { assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "0") } diff --git a/daemon/oci_utils.go b/daemon/oci_utils.go deleted file mode 100644 index 2a045c581e..0000000000 --- a/daemon/oci_utils.go +++ /dev/null @@ -1,21 +0,0 @@ -package daemon - -import ( - "github.com/moby/moby/v2/daemon/container" - "github.com/opencontainers/runtime-spec/specs-go" -) - -func setLinuxDomainname(c *container.Container, s *specs.Spec) { - // There isn't a field in the OCI for the NIS domainname, but luckily there - // is a sysctl which has an identical effect to setdomainname(2) so there's - // no explicit need for runtime support. - if s.Linux == nil { - s.Linux = &specs.Linux{} - } - if s.Linux.Sysctl == nil { - s.Linux.Sysctl = make(map[string]string) - } - if c.Config.Domainname != "" { - s.Linux.Sysctl["kernel.domainname"] = c.Config.Domainname - } -} diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index 9797093f84..cd37c8482c 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -34,12 +34,6 @@ import ( func TestNISDomainname(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") - skip.If(t, testEnv.IsUserNamespace, "user namespaces cannot write the kernel domainname sysctl") - - // Rootless supports custom Hostname but doesn't support custom Domainname - // OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \ - // "write sysctl key kernel.domainname: open /proc/sys/kernel/domainname: permission denied\"": unknown. - skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting Domainname (TODO: https://github.com/moby/moby/issues/40632)") ctx := setupTest(t) apiClient := testEnv.APIClient()