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 <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin committed 2026-10-08 15:18:12 -07:00
1 parent a5b58c94d3
commit 9ebf533c56
4 files changed
+11 -30

No files matched your search

+7 -1
View File
@@ -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") {
+4 -2
View File
@@ -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")
}
-21
View File
@@ -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
}
}
-6
View File
@@ -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()