Skip to content

Commit 3518383

Browse files
committed
dockerd: fix rootless detection (alternative to moby#39024)
The `--rootless` flag had a couple of issues: * moby#38702: euid=0, $USER="root" but no access to cgroup ("rootful" Docker in rootless Docker) * moby#39009: euid=0 but $USER="docker" (rootful boot2docker) To fix moby#38702, XDG dirs are ignored as in rootful Docker, unless the dockerd is directly running under RootlessKit namespaces. RootlessKit detection is implemented by checking whether `$ROOTLESSKIT_STATE_DIR` is set. To fix moby#39009, the non-robust `$USER` check is now completely removed. The entire logic can be illustrated as follows: ``` withRootlessKit := getenv("ROOTLESSKIT_STATE_DIR") rootlessMode := withRootlessKit || cliFlag("--rootless") honorXDG := withRootlessKit useRootlessKitDockerProxy := withRootlessKit removeCgroupSpec := rootlessMode adjustOOMScoreAdj := rootlessMode ``` Close moby#39024 Fix moby#38702 moby#39009 Signed-off-by: Akihiro Suda <[email protected]>
1 parent 3cd54c2 commit 3518383

File tree

8 files changed

+60
-32
lines changed

8 files changed

+60
-32
lines changed

cmd/dockerd/config_common_unix.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import (
99
"github.com/docker/docker/daemon/config"
1010
"github.com/docker/docker/opts"
1111
"github.com/docker/docker/pkg/homedir"
12-
"github.com/docker/docker/rootless"
1312
"github.com/spf13/pflag"
1413
)
1514

1615
func getDefaultPidFile() (string, error) {
17-
if !rootless.RunningWithNonRootUsername() {
16+
if !honorXDG {
1817
return "/var/run/docker.pid", nil
1918
}
2019
runtimeDir, err := homedir.GetRuntimeDir()
@@ -25,7 +24,7 @@ func getDefaultPidFile() (string, error) {
2524
}
2625

2726
func getDefaultDataRoot() (string, error) {
28-
if !rootless.RunningWithNonRootUsername() {
27+
if !honorXDG {
2928
return "/var/lib/docker", nil
3029
}
3130
dataHome, err := homedir.GetDataHome()
@@ -36,7 +35,7 @@ func getDefaultDataRoot() (string, error) {
3635
}
3736

3837
func getDefaultExecRoot() (string, error) {
39-
if !rootless.RunningWithNonRootUsername() {
38+
if !honorXDG {
4039
return "/var/run/docker", nil
4140
}
4241
runtimeDir, err := homedir.GetRuntimeDir()

cmd/dockerd/config_unix.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
package main
44

55
import (
6+
"os/exec"
7+
68
"github.com/docker/docker/daemon/config"
79
"github.com/docker/docker/opts"
810
"github.com/docker/docker/rootless"
911
"github.com/docker/go-units"
12+
"github.com/pkg/errors"
1013
"github.com/spf13/pflag"
1114
)
1215

@@ -35,7 +38,16 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
3538
flags.BoolVar(&conf.BridgeConfig.EnableIPv6, "ipv6", false, "Enable IPv6 networking")
3639
flags.StringVar(&conf.BridgeConfig.FixedCIDRv6, "fixed-cidr-v6", "", "IPv6 subnet for fixed IPs")
3740
flags.BoolVar(&conf.BridgeConfig.EnableUserlandProxy, "userland-proxy", true, "Use userland proxy for loopback traffic")
38-
flags.StringVar(&conf.BridgeConfig.UserlandProxyPath, "userland-proxy-path", "", "Path to the userland proxy binary")
41+
defaultUserlandProxyPath := ""
42+
if rootless.RunningWithRootlessKit() {
43+
var err error
44+
// use rootlesskit-docker-proxy for exposing the ports in RootlessKit netns to the initial namespace.
45+
defaultUserlandProxyPath, err = exec.LookPath(rootless.RootlessKitDockerProxyBinary)
46+
if err != nil {
47+
return errors.Wrapf(err, "running with RootlessKit, but %s not installed", rootless.RootlessKitDockerProxyBinary)
48+
}
49+
}
50+
flags.StringVar(&conf.BridgeConfig.UserlandProxyPath, "userland-proxy-path", defaultUserlandProxyPath, "Path to the userland proxy binary")
3951
flags.StringVar(&conf.CgroupParent, "cgroup-parent", "", "Set parent cgroup for all containers")
4052
flags.StringVar(&conf.RemappedRoot, "userns-remap", "", "User/Group setting for user namespaces")
4153
flags.BoolVar(&conf.LiveRestoreEnabled, "live-restore", false, "Enable live restore of docker when containers are still running")
@@ -49,7 +61,8 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
4961
flags.BoolVar(&conf.NoNewPrivileges, "no-new-privileges", false, "Set no-new-privileges by default for new containers")
5062
flags.StringVar(&conf.IpcMode, "default-ipc-mode", config.DefaultIpcMode, `Default mode for containers ipc ("shareable" | "private")`)
5163
flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "Default address pools for node specific local networks")
52-
// Mostly users don't need to set this flag explicitly.
53-
flags.BoolVar(&conf.Rootless, "rootless", rootless.RunningWithNonRootUsername(), "Enable rootless mode (experimental)")
64+
// rootless needs to be explicitly specified for running "rootful" dockerd in rootless dockerd (#38702)
65+
// Note that defaultUserlandProxyPath and honorXDG are configured according to the value of rootless.RunningWithRootlessKit, not the value of --rootless.
66+
flags.BoolVar(&conf.Rootless, "rootless", rootless.RunningWithRootlessKit(), "Enable rootless mode; typically used with RootlessKit (experimental)")
5467
return nil
5568
}

cmd/dockerd/daemon.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
103103
if cli.Config.IsRootless() {
104104
logrus.Warn("Running in rootless mode. Cgroups, AppArmor, and CRIU are disabled.")
105105
}
106+
if rootless.RunningWithRootlessKit() {
107+
logrus.Info("Running with RootlessKit integration")
108+
if !cli.Config.IsRootless() {
109+
return fmt.Errorf("rootless mode needs to be enabled for running with RootlessKit")
110+
}
111+
}
106112
} else {
107113
if cli.Config.IsRootless() {
108114
return fmt.Errorf("rootless mode is supported only when running in experimental mode")
@@ -591,7 +597,7 @@ func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, er
591597
var hosts []string
592598
for i := 0; i < len(cli.Config.Hosts); i++ {
593599
var err error
594-
if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, rootless.RunningWithNonRootUsername(), cli.Config.Hosts[i]); err != nil {
600+
if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, honorXDG, cli.Config.Hosts[i]); err != nil {
595601
return nil, errors.Wrapf(err, "error parsing -H %s", cli.Config.Hosts[i])
596602
}
597603

@@ -668,9 +674,9 @@ func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGette
668674
return nil
669675
}
670676

671-
func systemContainerdRunning(isRootless bool) (string, bool, error) {
677+
func systemContainerdRunning(honorXDG bool) (string, bool, error) {
672678
addr := containerddefaults.DefaultAddress
673-
if isRootless {
679+
if honorXDG {
674680
runtimeDir, err := homedir.GetRuntimeDir()
675681
if err != nil {
676682
return "", false, err

cmd/dockerd/daemon_unix.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ import (
1818
"github.com/docker/docker/daemon/config"
1919
"github.com/docker/docker/libcontainerd/supervisor"
2020
"github.com/docker/docker/pkg/homedir"
21-
"github.com/docker/docker/rootless"
2221
"github.com/docker/libnetwork/portallocator"
2322
"github.com/pkg/errors"
2423
"golang.org/x/sys/unix"
2524
)
2625

2726
func getDefaultDaemonConfigDir() (string, error) {
28-
if !rootless.RunningWithNonRootUsername() {
27+
if !honorXDG {
2928
return "/etc/docker", nil
3029
}
3130
// NOTE: CLI uses ~/.docker while the daemon uses ~/.config/docker, because
@@ -148,7 +147,7 @@ func newCgroupParent(config *config.Config) string {
148147
func (cli *DaemonCli) initContainerD(ctx context.Context) (func(time.Duration) error, error) {
149148
var waitForShutdown func(time.Duration) error
150149
if cli.Config.ContainerdAddr == "" {
151-
systemContainerdAddr, ok, err := systemContainerdRunning(cli.Config.IsRootless())
150+
systemContainerdAddr, ok, err := systemContainerdRunning(honorXDG)
152151
if err != nil {
153152
return nil, errors.Wrap(err, "could not determine whether the system containerd is running")
154153
}

cmd/dockerd/docker.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ import (
1010
"github.com/docker/docker/pkg/jsonmessage"
1111
"github.com/docker/docker/pkg/reexec"
1212
"github.com/docker/docker/pkg/term"
13+
"github.com/docker/docker/rootless"
1314
"github.com/moby/buildkit/util/apicaps"
1415
"github.com/sirupsen/logrus"
1516
"github.com/spf13/cobra"
1617
)
1718

19+
var (
20+
honorXDG bool
21+
)
22+
1823
func newDaemonCommand() (*cobra.Command, error) {
1924
opts := newDaemonOptions(config.New())
2025

@@ -53,6 +58,14 @@ func init() {
5358
if dockerversion.ProductName != "" {
5459
apicaps.ExportedProduct = dockerversion.ProductName
5560
}
61+
// When running with RootlessKit, $XDG_RUNTIME_DIR, $XDG_DATA_HOME, and $XDG_CONFIG_HOME needs to be
62+
// honored as the default dirs, because we are unlikely to have permissions to access the system-wide
63+
// directories.
64+
//
65+
// Note that even running with --rootless, when not running with RootlessKit, honorXDG needs to be kept false,
66+
// because the system-wide directories in the current mount namespace are expected to be accessible.
67+
// ("rootful" dockerd in rootless dockerd, #38702)
68+
honorXDG = rootless.RunningWithRootlessKit()
5669
}
5770

5871
func main() {

docs/rootless.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@ penguin:231072:65536
5555
You need to run `dockerd-rootless.sh` instead of `dockerd`.
5656

5757
```console
58-
$ dockerd-rootless.sh --experimental --userland-proxy --userland-proxy-path=$(which rootlesskit-docker-proxy)"
58+
$ dockerd-rootless.sh --experimental
5959
```
6060
As Rootless mode is experimental per se, currently you always need to run `dockerd-rootless.sh` with `--experimental`.
61-
Also, to expose ports, you need to set `--userland-proxy-path` to the path of `rootlesskit-docker-proxy` binary.
6261

6362
Remarks:
6463
* The socket path is set to `$XDG_RUNTIME_DIR/docker.sock` by default. `$XDG_RUNTIME_DIR` is typically set to `/run/user/$UID`.

opts/hosts.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ func ValidateHost(val string) (string, error) {
4545
}
4646

4747
// ParseHost and set defaults for a Daemon host string.
48-
// defaultToTLS is preferred over defaultToUnixRootless.
49-
func ParseHost(defaultToTLS, defaultToUnixRootless bool, val string) (string, error) {
48+
// defaultToTLS is preferred over defaultToUnixXDG.
49+
func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error) {
5050
host := strings.TrimSpace(val)
5151
if host == "" {
5252
if defaultToTLS {
5353
host = DefaultTLSHost
54-
} else if defaultToUnixRootless {
54+
} else if defaultToUnixXDG {
5555
runtimeDir, err := homedir.GetRuntimeDir()
5656
if err != nil {
5757
return "", err

rootless/rootless.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,21 @@ import (
55
"sync"
66
)
77

8+
const (
9+
// RootlessKitDockerProxyBinary is the binary name of rootlesskit-docker-proxy
10+
RootlessKitDockerProxyBinary = "rootlesskit-docker-proxy"
11+
)
12+
813
var (
9-
runningWithNonRootUsername bool
10-
runningWithNonRootUsernameOnce sync.Once
14+
runningWithRootlessKit bool
15+
runningWithRootlessKitOnce sync.Once
1116
)
1217

13-
// RunningWithNonRootUsername returns true if we $USER is set to a non-root value,
14-
// regardless to the UID/EUID value.
15-
//
16-
// The value of this variable is mostly used for configuring default paths.
17-
// If the value is true, $HOME and $XDG_RUNTIME_DIR should be honored for setting up the default paths.
18-
// If false (not only EUID==0 but also $USER==root), $HOME and $XDG_RUNTIME_DIR should be ignored
19-
// even if we are in a user namespace.
20-
func RunningWithNonRootUsername() bool {
21-
runningWithNonRootUsernameOnce.Do(func() {
22-
u := os.Getenv("USER")
23-
runningWithNonRootUsername = u != "" && u != "root"
18+
// RunningWithRootlessKit returns true if running under RootlessKit namespaces.
19+
func RunningWithRootlessKit() bool {
20+
runningWithRootlessKitOnce.Do(func() {
21+
u := os.Getenv("ROOTLESSKIT_STATE_DIR")
22+
runningWithRootlessKit = u != ""
2423
})
25-
return runningWithNonRootUsername
24+
return runningWithRootlessKit
2625
}

0 commit comments

Comments
 (0)