diff --git a/daemon/command/daemon.go b/daemon/command/daemon.go index bd4f787241..d6c17f59a9 100644 --- a/daemon/command/daemon.go +++ b/daemon/command/daemon.go @@ -639,6 +639,30 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { } } + // TODO(thaJeztah): consider making empty strings an error. Existing behavior allowed for empty strings to be used as default, even if explicitly set (`dockerd -H ""`). + conf.Hosts = slices.DeleteFunc(conf.Hosts, func(h string) bool { + return strings.TrimSpace(h) == "" + }) + if len(conf.Hosts) == 0 { + // Set the default host if no hosts are configured. + // TODO(thaJeztah) can set defaults in config.New() instead? + if conf.TLS != nil && *conf.TLS { + // If no host is configured, but the "--tls" flag is set, we + // default to using a TCP connection instead of a unix-socket + // or named pipe. + // + // See https://github.com/moby/moby/commit/0906195fbbd6f379c163b80f23e4c5a60bcfc5f0 + conf.Hosts = append(conf.Hosts, dopts.DefaultTLSHost) + } else { + // Otherwise use the default unix-socket (Linux) or named pipe (Windows). + h, err := defaultAPISocketPath(honorXDG) + if err != nil { + return nil, err + } + conf.Hosts = append(conf.Hosts, h) + } + } + if err := normalizeHosts(conf); err != nil { return nil, err } @@ -718,27 +742,37 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { return conf, nil } +// defaultAPISocketPath returns the default path for the Unix socket (Linux) +// or named pipe (Windows). +// +// When running with rootlessKit, XDG dirs should be preferred, and the +// default is to listen on an unprivileged socket in [XDG_RUNTIME_DIR]. +// +// [XDG_RUNTIME_DIR]: https://specifications.freedesktop.org/basedir/0.8/#variables +func defaultAPISocketPath(honorXDG bool) (string, error) { + if honorXDG { + runtimeDir, err := homedir.GetRuntimeDir() + if err != nil { + return "", err + } + return "unix://" + filepath.Join(runtimeDir, "docker.sock"), nil + } + + // default unix-socket (Linux) or named pipe (Windows). + return dopts.DefaultHost, nil +} + // normalizeHosts normalizes the configured config.Hosts and removes duplicates. // It returns an error if it fails to parse a host. func normalizeHosts(cfg *config.Config) error { + if len(cfg.Hosts) == 0 { + return errors.New("no hosts specified") + } + hosts := slices.Clone(cfg.Hosts) - if len(hosts) == 0 { - // if no hosts are configured, create a single entry slice, so that the - // default is used. - // - // TODO(thaJeztah) implement a cleaner way for this; this depends on a - // side-effect of how we parse empty/partial hosts. - hosts = make([]string, 1) - } - - useTLS := DefaultTLSValue - if cfg.TLS != nil { - useTLS = *cfg.TLS - } - for i, h := range hosts { var err error - hosts[i], err = dopts.ParseHost(useTLS, honorXDG, h) + hosts[i], err = dopts.ParseDaemonHost(h) if err != nil { return err }