github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/cmd/dockerd/daemon.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"sort"
    12  	"strings"
    13  	"time"
    14  
    15  	containerddefaults "github.com/containerd/containerd/defaults"
    16  	"github.com/docker/docker/api"
    17  	apiserver "github.com/docker/docker/api/server"
    18  	buildbackend "github.com/docker/docker/api/server/backend/build"
    19  	"github.com/docker/docker/api/server/middleware"
    20  	"github.com/docker/docker/api/server/router"
    21  	"github.com/docker/docker/api/server/router/build"
    22  	checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
    23  	"github.com/docker/docker/api/server/router/container"
    24  	distributionrouter "github.com/docker/docker/api/server/router/distribution"
    25  	grpcrouter "github.com/docker/docker/api/server/router/grpc"
    26  	"github.com/docker/docker/api/server/router/image"
    27  	"github.com/docker/docker/api/server/router/network"
    28  	pluginrouter "github.com/docker/docker/api/server/router/plugin"
    29  	sessionrouter "github.com/docker/docker/api/server/router/session"
    30  	swarmrouter "github.com/docker/docker/api/server/router/swarm"
    31  	systemrouter "github.com/docker/docker/api/server/router/system"
    32  	"github.com/docker/docker/api/server/router/volume"
    33  	buildkit "github.com/docker/docker/builder/builder-next"
    34  	"github.com/docker/docker/builder/dockerfile"
    35  	"github.com/docker/docker/cli/debug"
    36  	"github.com/docker/docker/cmd/dockerd/trap"
    37  	"github.com/docker/docker/daemon"
    38  	"github.com/docker/docker/daemon/cluster"
    39  	"github.com/docker/docker/daemon/config"
    40  	"github.com/docker/docker/daemon/listeners"
    41  	"github.com/docker/docker/dockerversion"
    42  	"github.com/docker/docker/libcontainerd/supervisor"
    43  	dopts "github.com/docker/docker/opts"
    44  	"github.com/docker/docker/pkg/authorization"
    45  	"github.com/docker/docker/pkg/homedir"
    46  	"github.com/docker/docker/pkg/jsonmessage"
    47  	"github.com/docker/docker/pkg/pidfile"
    48  	"github.com/docker/docker/pkg/plugingetter"
    49  	"github.com/docker/docker/pkg/sysinfo"
    50  	"github.com/docker/docker/pkg/system"
    51  	"github.com/docker/docker/plugin"
    52  	"github.com/docker/docker/rootless"
    53  	"github.com/docker/docker/runconfig"
    54  	"github.com/docker/go-connections/tlsconfig"
    55  	"github.com/moby/buildkit/session"
    56  	swarmapi "github.com/moby/swarmkit/v2/api"
    57  	"github.com/pkg/errors"
    58  	"github.com/sirupsen/logrus"
    59  	"github.com/spf13/pflag"
    60  )
    61  
    62  // DaemonCli represents the daemon CLI.
    63  type DaemonCli struct {
    64  	*config.Config
    65  	configFile *string
    66  	flags      *pflag.FlagSet
    67  
    68  	api             *apiserver.Server
    69  	d               *daemon.Daemon
    70  	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
    71  }
    72  
    73  // NewDaemonCli returns a daemon CLI
    74  func NewDaemonCli() *DaemonCli {
    75  	return &DaemonCli{}
    76  }
    77  
    78  func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
    79  	if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
    80  		return err
    81  	}
    82  
    83  	serverConfig, err := newAPIServerConfig(cli.Config)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	if opts.Validate {
    89  		// If config wasn't OK we wouldn't have made it this far.
    90  		_, _ = fmt.Fprintln(os.Stderr, "configuration OK")
    91  		return nil
    92  	}
    93  
    94  	configureProxyEnv(cli.Config)
    95  	configureDaemonLogs(cli.Config)
    96  
    97  	logrus.Info("Starting up")
    98  
    99  	cli.configFile = &opts.configFile
   100  	cli.flags = opts.flags
   101  
   102  	if cli.Config.Debug {
   103  		debug.Enable()
   104  	}
   105  
   106  	if cli.Config.Experimental {
   107  		logrus.Warn("Running experimental build")
   108  	}
   109  
   110  	if cli.Config.IsRootless() {
   111  		logrus.Warn("Running in rootless mode. This mode has feature limitations.")
   112  	}
   113  	if rootless.RunningWithRootlessKit() {
   114  		logrus.Info("Running with RootlessKit integration")
   115  		if !cli.Config.IsRootless() {
   116  			return fmt.Errorf("rootless mode needs to be enabled for running with RootlessKit")
   117  		}
   118  	}
   119  
   120  	// return human-friendly error before creating files
   121  	if runtime.GOOS == "linux" && os.Geteuid() != 0 {
   122  		return fmt.Errorf("dockerd needs to be started with root privileges. To run dockerd in rootless mode as an unprivileged user, see https://docs.docker.com/go/rootless/")
   123  	}
   124  
   125  	if err := setDefaultUmask(); err != nil {
   126  		return err
   127  	}
   128  
   129  	// Create the daemon root before we create ANY other files (PID, or migrate keys)
   130  	// to ensure the appropriate ACL is set (particularly relevant on Windows)
   131  	if err := daemon.CreateDaemonRoot(cli.Config); err != nil {
   132  		return err
   133  	}
   134  
   135  	if err := system.MkdirAll(cli.Config.ExecRoot, 0700); err != nil {
   136  		return err
   137  	}
   138  
   139  	potentiallyUnderRuntimeDir := []string{cli.Config.ExecRoot}
   140  
   141  	if cli.Pidfile != "" {
   142  		if err = system.MkdirAll(filepath.Dir(cli.Pidfile), 0o755); err != nil {
   143  			return errors.Wrap(err, "failed to create pidfile directory")
   144  		}
   145  		if err = pidfile.Write(cli.Pidfile, os.Getpid()); err != nil {
   146  			return errors.Wrapf(err, "failed to start daemon, ensure docker is not running or delete %s", cli.Pidfile)
   147  		}
   148  		potentiallyUnderRuntimeDir = append(potentiallyUnderRuntimeDir, cli.Pidfile)
   149  		defer func() {
   150  			if err := os.Remove(cli.Pidfile); err != nil {
   151  				logrus.Error(err)
   152  			}
   153  		}()
   154  	}
   155  
   156  	if cli.Config.IsRootless() {
   157  		// Set sticky bit if XDG_RUNTIME_DIR is set && the file is actually under XDG_RUNTIME_DIR
   158  		if _, err := homedir.StickRuntimeDirContents(potentiallyUnderRuntimeDir); err != nil {
   159  			// StickRuntimeDirContents returns nil error if XDG_RUNTIME_DIR is just unset
   160  			logrus.WithError(err).Warn("cannot set sticky bit on files under XDG_RUNTIME_DIR")
   161  		}
   162  	}
   163  
   164  	cli.api = apiserver.New(serverConfig)
   165  
   166  	hosts, err := loadListeners(cli, serverConfig)
   167  	if err != nil {
   168  		return errors.Wrap(err, "failed to load listeners")
   169  	}
   170  
   171  	ctx, cancel := context.WithCancel(context.Background())
   172  	waitForContainerDShutdown, err := cli.initContainerd(ctx)
   173  	if waitForContainerDShutdown != nil {
   174  		defer waitForContainerDShutdown(10 * time.Second)
   175  	}
   176  	if err != nil {
   177  		cancel()
   178  		return err
   179  	}
   180  	defer cancel()
   181  
   182  	stopc := make(chan bool)
   183  	defer close(stopc)
   184  
   185  	trap.Trap(func() {
   186  		cli.stop()
   187  		<-stopc // wait for daemonCli.start() to return
   188  	}, logrus.StandardLogger())
   189  
   190  	// Notify that the API is active, but before daemon is set up.
   191  	preNotifyReady()
   192  
   193  	pluginStore := plugin.NewStore()
   194  
   195  	if err := cli.initMiddlewares(cli.api, serverConfig, pluginStore); err != nil {
   196  		logrus.Fatalf("Error creating middlewares: %v", err)
   197  	}
   198  
   199  	d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore)
   200  	if err != nil {
   201  		return errors.Wrap(err, "failed to start daemon")
   202  	}
   203  
   204  	d.StoreHosts(hosts)
   205  
   206  	// validate after NewDaemon has restored enabled plugins. Don't change order.
   207  	if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, pluginStore); err != nil {
   208  		return errors.Wrap(err, "failed to validate authorization plugin")
   209  	}
   210  
   211  	cli.d = d
   212  
   213  	if err := startMetricsServer(cli.Config.MetricsAddress); err != nil {
   214  		return errors.Wrap(err, "failed to start metrics server")
   215  	}
   216  
   217  	c, err := createAndStartCluster(cli, d)
   218  	if err != nil {
   219  		logrus.Fatalf("Error starting cluster component: %v", err)
   220  	}
   221  
   222  	// Restart all autostart containers which has a swarm endpoint
   223  	// and is not yet running now that we have successfully
   224  	// initialized the cluster.
   225  	d.RestartSwarmContainers()
   226  
   227  	logrus.Info("Daemon has completed initialization")
   228  
   229  	routerOptions, err := newRouterOptions(cli.Config, d)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	routerOptions.api = cli.api
   234  	routerOptions.cluster = c
   235  
   236  	initRouter(routerOptions)
   237  
   238  	go d.ProcessClusterNotifications(ctx, c.GetWatchStream())
   239  
   240  	cli.setupConfigReloadTrap()
   241  
   242  	// The serve API routine never exits unless an error occurs
   243  	// We need to start it as a goroutine and wait on it so
   244  	// daemon doesn't exit
   245  	serveAPIWait := make(chan error)
   246  	go cli.api.Wait(serveAPIWait)
   247  
   248  	// after the daemon is done setting up we can notify systemd api
   249  	notifyReady()
   250  
   251  	// Daemon is fully initialized and handling API traffic
   252  	// Wait for serve API to complete
   253  	errAPI := <-serveAPIWait
   254  	c.Cleanup()
   255  
   256  	// notify systemd that we're shutting down
   257  	notifyStopping()
   258  	shutdownDaemon(ctx, d)
   259  
   260  	// Stop notification processing and any background processes
   261  	cancel()
   262  
   263  	if errAPI != nil {
   264  		return errors.Wrap(errAPI, "shutting down due to ServeAPI error")
   265  	}
   266  
   267  	logrus.Info("Daemon shutdown complete")
   268  	return nil
   269  }
   270  
   271  type routerOptions struct {
   272  	sessionManager *session.Manager
   273  	buildBackend   *buildbackend.Backend
   274  	features       *map[string]bool
   275  	buildkit       *buildkit.Builder
   276  	daemon         *daemon.Daemon
   277  	api            *apiserver.Server
   278  	cluster        *cluster.Cluster
   279  }
   280  
   281  func newRouterOptions(config *config.Config, d *daemon.Daemon) (routerOptions, error) {
   282  	opts := routerOptions{}
   283  	sm, err := session.NewManager()
   284  	if err != nil {
   285  		return opts, errors.Wrap(err, "failed to create sessionmanager")
   286  	}
   287  
   288  	manager, err := dockerfile.NewBuildManager(d.BuilderBackend(), d.IdentityMapping())
   289  	if err != nil {
   290  		return opts, err
   291  	}
   292  	cgroupParent := newCgroupParent(config)
   293  	ro := routerOptions{
   294  		sessionManager: sm,
   295  		features:       d.Features(),
   296  		daemon:         d,
   297  	}
   298  	if !d.UsesSnapshotter() {
   299  		bk, err := buildkit.New(buildkit.Opt{
   300  			SessionManager:      sm,
   301  			Root:                filepath.Join(config.Root, "buildkit"),
   302  			Dist:                d.DistributionServices(),
   303  			NetworkController:   d.NetworkController(),
   304  			DefaultCgroupParent: cgroupParent,
   305  			RegistryHosts:       d.RegistryHosts(),
   306  			BuilderConfig:       config.Builder,
   307  			Rootless:            d.Rootless(),
   308  			IdentityMapping:     d.IdentityMapping(),
   309  			DNSConfig:           config.DNSConfig,
   310  			ApparmorProfile:     daemon.DefaultApparmorProfile(),
   311  		})
   312  		if err != nil {
   313  			return opts, err
   314  		}
   315  
   316  		bb, err := buildbackend.NewBackend(d.ImageService(), manager, bk, d.EventsService)
   317  		if err != nil {
   318  			return opts, errors.Wrap(err, "failed to create buildmanager")
   319  		}
   320  
   321  		ro.buildBackend = bb
   322  		ro.buildkit = bk
   323  	}
   324  
   325  	return ro, nil
   326  }
   327  
   328  func (cli *DaemonCli) reloadConfig() {
   329  	reload := func(c *config.Config) {
   330  		// Revalidate and reload the authorization plugins
   331  		if err := validateAuthzPlugins(c.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   332  			logrus.Fatalf("Error validating authorization plugin: %v", err)
   333  			return
   334  		}
   335  		cli.authzMiddleware.SetPlugins(c.AuthorizationPlugins)
   336  
   337  		if err := cli.d.Reload(c); err != nil {
   338  			logrus.Errorf("Error reconfiguring the daemon: %v", err)
   339  			return
   340  		}
   341  
   342  		if c.IsValueSet("debug") {
   343  			debugEnabled := debug.IsEnabled()
   344  			switch {
   345  			case debugEnabled && !c.Debug: // disable debug
   346  				debug.Disable()
   347  			case c.Debug && !debugEnabled: // enable debug
   348  				debug.Enable()
   349  			}
   350  		}
   351  	}
   352  
   353  	if err := config.Reload(*cli.configFile, cli.flags, reload); err != nil {
   354  		logrus.Error(err)
   355  	}
   356  }
   357  
   358  func (cli *DaemonCli) stop() {
   359  	cli.api.Close()
   360  }
   361  
   362  // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
   363  // d.Shutdown() is waiting too long to kill container or worst it's
   364  // blocked there
   365  func shutdownDaemon(ctx context.Context, d *daemon.Daemon) {
   366  	var cancel context.CancelFunc
   367  	if timeout := d.ShutdownTimeout(); timeout >= 0 {
   368  		ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
   369  	} else {
   370  		ctx, cancel = context.WithCancel(ctx)
   371  	}
   372  
   373  	go func() {
   374  		defer cancel()
   375  		d.Shutdown(ctx)
   376  	}()
   377  
   378  	<-ctx.Done()
   379  	if errors.Is(ctx.Err(), context.DeadlineExceeded) {
   380  		logrus.Error("Force shutdown daemon")
   381  	} else {
   382  		logrus.Debug("Clean shutdown succeeded")
   383  	}
   384  }
   385  
   386  func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
   387  	if !opts.flags.Parsed() {
   388  		return nil, errors.New(`cannot load CLI config before flags are parsed`)
   389  	}
   390  	opts.setDefaultOptions()
   391  
   392  	conf := opts.daemonConfig
   393  	flags := opts.flags
   394  	conf.Debug = opts.Debug
   395  	conf.Hosts = opts.Hosts
   396  	conf.LogLevel = opts.LogLevel
   397  
   398  	if flags.Changed(FlagTLS) {
   399  		conf.TLS = &opts.TLS
   400  	}
   401  	if flags.Changed(FlagTLSVerify) {
   402  		conf.TLSVerify = &opts.TLSVerify
   403  		v := true
   404  		conf.TLS = &v
   405  	}
   406  
   407  	if opts.TLSOptions != nil {
   408  		conf.CommonTLSOptions = config.CommonTLSOptions{
   409  			CAFile:   opts.TLSOptions.CAFile,
   410  			CertFile: opts.TLSOptions.CertFile,
   411  			KeyFile:  opts.TLSOptions.KeyFile,
   412  		}
   413  	} else {
   414  		conf.CommonTLSOptions = config.CommonTLSOptions{}
   415  	}
   416  
   417  	if conf.TrustKeyPath == "" {
   418  		daemonConfDir, err := getDaemonConfDir(conf.Root)
   419  		if err != nil {
   420  			return nil, err
   421  		}
   422  		conf.TrustKeyPath = filepath.Join(daemonConfDir, defaultTrustKeyFile)
   423  	}
   424  
   425  	if opts.configFile != "" {
   426  		c, err := config.MergeDaemonConfigurations(conf, flags, opts.configFile)
   427  		if err != nil {
   428  			if flags.Changed("config-file") || !os.IsNotExist(err) {
   429  				return nil, errors.Wrapf(err, "unable to configure the Docker daemon with file %s", opts.configFile)
   430  			}
   431  		}
   432  
   433  		// the merged configuration can be nil if the config file didn't exist.
   434  		// leave the current configuration as it is if when that happens.
   435  		if c != nil {
   436  			conf = c
   437  		}
   438  	}
   439  
   440  	if err := normalizeHosts(conf); err != nil {
   441  		return nil, err
   442  	}
   443  
   444  	if err := config.Validate(conf); err != nil {
   445  		return nil, err
   446  	}
   447  
   448  	// Check if duplicate label-keys with different values are found
   449  	newLabels, err := config.GetConflictFreeLabels(conf.Labels)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  	conf.Labels = newLabels
   454  
   455  	// Regardless of whether the user sets it to true or false, if they
   456  	// specify TLSVerify at all then we need to turn on TLS
   457  	if conf.IsValueSet(FlagTLSVerify) {
   458  		v := true
   459  		conf.TLS = &v
   460  	}
   461  
   462  	if conf.TLSVerify == nil && conf.TLS != nil {
   463  		conf.TLSVerify = conf.TLS
   464  	}
   465  
   466  	err = validateCPURealtimeOptions(conf)
   467  	if err != nil {
   468  		return nil, err
   469  	}
   470  
   471  	return conf, nil
   472  }
   473  
   474  // normalizeHosts normalizes the configured config.Hosts and remove duplicates.
   475  // It returns an error if it fails to parse a host.
   476  func normalizeHosts(config *config.Config) error {
   477  	if len(config.Hosts) == 0 {
   478  		// if no hosts are configured, create a single entry slice, so that the
   479  		// default is used.
   480  		//
   481  		// TODO(thaJeztah) implement a cleaner way for this; this depends on a
   482  		//                 side-effect of how we parse empty/partial hosts.
   483  		config.Hosts = make([]string, 1)
   484  	}
   485  	hosts := make([]string, 0, len(config.Hosts))
   486  	seen := make(map[string]struct{}, len(config.Hosts))
   487  
   488  	useTLS := DefaultTLSValue
   489  	if config.TLS != nil {
   490  		useTLS = *config.TLS
   491  	}
   492  
   493  	for _, h := range config.Hosts {
   494  		host, err := dopts.ParseHost(useTLS, honorXDG, h)
   495  		if err != nil {
   496  			return err
   497  		}
   498  		if _, ok := seen[host]; ok {
   499  			continue
   500  		}
   501  		seen[host] = struct{}{}
   502  		hosts = append(hosts, host)
   503  	}
   504  	sort.Strings(hosts)
   505  	config.Hosts = hosts
   506  	return nil
   507  }
   508  
   509  func initRouter(opts routerOptions) {
   510  	decoder := runconfig.ContainerDecoder{
   511  		GetSysInfo: func() *sysinfo.SysInfo {
   512  			return opts.daemon.RawSysInfo()
   513  		},
   514  	}
   515  
   516  	routers := []router.Router{
   517  		// we need to add the checkpoint router before the container router or the DELETE gets masked
   518  		checkpointrouter.NewRouter(opts.daemon, decoder),
   519  		container.NewRouter(opts.daemon, decoder, opts.daemon.RawSysInfo().CgroupUnified),
   520  		image.NewRouter(
   521  			opts.daemon.ImageService(),
   522  			opts.daemon.ReferenceStore,
   523  			opts.daemon.ImageService().DistributionServices().ImageStore,
   524  			opts.daemon.ImageService().DistributionServices().LayerStore,
   525  		),
   526  		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildkit, opts.features),
   527  		volume.NewRouter(opts.daemon.VolumesService(), opts.cluster),
   528  		build.NewRouter(opts.buildBackend, opts.daemon, opts.features),
   529  		sessionrouter.NewRouter(opts.sessionManager),
   530  		swarmrouter.NewRouter(opts.cluster),
   531  		pluginrouter.NewRouter(opts.daemon.PluginManager()),
   532  		distributionrouter.NewRouter(opts.daemon.ImageService()),
   533  	}
   534  
   535  	if opts.buildBackend != nil {
   536  		routers = append(routers, grpcrouter.NewRouter(opts.buildBackend))
   537  	}
   538  
   539  	if opts.daemon.NetworkControllerEnabled() {
   540  		routers = append(routers, network.NewRouter(opts.daemon, opts.cluster))
   541  	}
   542  
   543  	if opts.daemon.HasExperimental() {
   544  		for _, r := range routers {
   545  			for _, route := range r.Routes() {
   546  				if experimental, ok := route.(router.ExperimentalRoute); ok {
   547  					experimental.Enable()
   548  				}
   549  			}
   550  		}
   551  	}
   552  
   553  	opts.api.InitRouter(routers...)
   554  }
   555  
   556  // TODO: remove this from cli and return the authzMiddleware
   557  func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config, pluginStore plugingetter.PluginGetter) error {
   558  	v := cfg.Version
   559  
   560  	exp := middleware.NewExperimentalMiddleware(cli.Config.Experimental)
   561  	s.UseMiddleware(exp)
   562  
   563  	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
   564  	s.UseMiddleware(vm)
   565  
   566  	if cfg.CorsHeaders != "" {
   567  		c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
   568  		s.UseMiddleware(c)
   569  	}
   570  
   571  	cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, pluginStore)
   572  	cli.Config.AuthzMiddleware = cli.authzMiddleware
   573  	s.UseMiddleware(cli.authzMiddleware)
   574  	return nil
   575  }
   576  
   577  func (cli *DaemonCli) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
   578  	opts, err := cli.getPlatformContainerdDaemonOpts()
   579  	if err != nil {
   580  		return nil, err
   581  	}
   582  
   583  	if cli.Debug {
   584  		opts = append(opts, supervisor.WithLogLevel("debug"))
   585  	} else {
   586  		opts = append(opts, supervisor.WithLogLevel(cli.LogLevel))
   587  	}
   588  
   589  	if !cli.CriContainerd {
   590  		// CRI support in the managed daemon is currently opt-in.
   591  		//
   592  		// It's disabled by default, originally because it was listening on
   593  		// a TCP connection at 0.0.0.0:10010, which was considered a security
   594  		// risk, and could conflict with user's container ports.
   595  		//
   596  		// Current versions of containerd started now listen on localhost on
   597  		// an ephemeral port instead, but could still conflict with container
   598  		// ports, and running kubernetes using the static binaries is not a
   599  		// common scenario, so we (for now) continue disabling it by default.
   600  		//
   601  		// Also see https://github.com/containerd/containerd/issues/2483#issuecomment-407530608
   602  		opts = append(opts, supervisor.WithCRIDisabled())
   603  	}
   604  
   605  	return opts, nil
   606  }
   607  
   608  func newAPIServerConfig(config *config.Config) (*apiserver.Config, error) {
   609  	var tlsConfig *tls.Config
   610  	if config.TLS != nil && *config.TLS {
   611  		var (
   612  			clientAuth tls.ClientAuthType
   613  			err        error
   614  		)
   615  		if config.TLSVerify == nil || *config.TLSVerify {
   616  			// server requires and verifies client's certificate
   617  			clientAuth = tls.RequireAndVerifyClientCert
   618  		}
   619  		tlsConfig, err = tlsconfig.Server(tlsconfig.Options{
   620  			CAFile:             config.CommonTLSOptions.CAFile,
   621  			CertFile:           config.CommonTLSOptions.CertFile,
   622  			KeyFile:            config.CommonTLSOptions.KeyFile,
   623  			ExclusiveRootPools: true,
   624  			ClientAuth:         clientAuth,
   625  		})
   626  		if err != nil {
   627  			return nil, errors.Wrap(err, "invalid TLS configuration")
   628  		}
   629  	}
   630  
   631  	return &apiserver.Config{
   632  		SocketGroup: config.SocketGroup,
   633  		Version:     dockerversion.Version,
   634  		CorsHeaders: config.CorsHeaders,
   635  		TLSConfig:   tlsConfig,
   636  		Hosts:       config.Hosts,
   637  	}, nil
   638  }
   639  
   640  // checkTLSAuthOK checks basically for an explicitly disabled TLS/TLSVerify
   641  // Going forward we do not want to support a scenario where dockerd listens
   642  // on TCP without either TLS client auth (or an explicit opt-in to disable it)
   643  func checkTLSAuthOK(c *config.Config) bool {
   644  	if c.TLS == nil {
   645  		// Either TLS is enabled by default, in which case TLS verification should be enabled by default, or explicitly disabled
   646  		// Or TLS is disabled by default... in any of these cases, we can just take the default value as to how to proceed
   647  		return DefaultTLSValue
   648  	}
   649  
   650  	if !*c.TLS {
   651  		// TLS is explicitly disabled, which is supported
   652  		return true
   653  	}
   654  
   655  	if c.TLSVerify == nil {
   656  		// this actually shouldn't happen since we set TLSVerify on the config object anyway
   657  		// But in case it does get here, be cautious and assume this is not supported.
   658  		return false
   659  	}
   660  
   661  	// Either TLSVerify is explicitly enabled or disabled, both cases are supported
   662  	return true
   663  }
   664  
   665  func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, error) {
   666  	if len(serverConfig.Hosts) == 0 {
   667  		return nil, errors.New("no hosts configured")
   668  	}
   669  	var hosts []string
   670  
   671  	for i := 0; i < len(serverConfig.Hosts); i++ {
   672  		protoAddr := serverConfig.Hosts[i]
   673  		protoAddrParts := strings.SplitN(serverConfig.Hosts[i], "://", 2)
   674  		if len(protoAddrParts) != 2 {
   675  			return nil, fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
   676  		}
   677  
   678  		proto, addr := protoAddrParts[0], protoAddrParts[1]
   679  
   680  		// It's a bad idea to bind to TCP without tlsverify.
   681  		authEnabled := serverConfig.TLSConfig != nil && serverConfig.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert
   682  		if proto == "tcp" && !authEnabled {
   683  			logrus.WithField("host", protoAddr).Warn("Binding to IP address without --tlsverify is insecure and gives root access on this machine to everyone who has access to your network.")
   684  			logrus.WithField("host", protoAddr).Warn("Binding to an IP address, even on localhost, can also give access to scripts run in a browser. Be safe out there!")
   685  			time.Sleep(time.Second)
   686  
   687  			// If TLSVerify is explicitly set to false we'll take that as "Please let me shoot myself in the foot"
   688  			// We do not want to continue to support a default mode where tls verification is disabled, so we do some extra warnings here and eventually remove support
   689  			if !checkTLSAuthOK(cli.Config) {
   690  				ipAddr, _, err := net.SplitHostPort(addr)
   691  				if err != nil {
   692  					return nil, errors.Wrap(err, "error parsing tcp address")
   693  				}
   694  
   695  				// shortcut all this extra stuff for literal "localhost"
   696  				// -H supports specifying hostnames, since we want to bypass this on loopback interfaces we'll look it up here.
   697  				if ipAddr != "localhost" {
   698  					ip := net.ParseIP(ipAddr)
   699  					if ip == nil {
   700  						ipA, err := net.ResolveIPAddr("ip", ipAddr)
   701  						if err != nil {
   702  							logrus.WithError(err).WithField("host", ipAddr).Error("Error looking up specified host address")
   703  						}
   704  						if ipA != nil {
   705  							ip = ipA.IP
   706  						}
   707  					}
   708  					if ip == nil || !ip.IsLoopback() {
   709  						logrus.WithField("host", protoAddr).Warn("Binding to an IP address without --tlsverify is deprecated. Startup is intentionally being slowed down to show this message")
   710  						logrus.WithField("host", protoAddr).Warn("Please consider generating tls certificates with client validation to prevent exposing unauthenticated root access to your network")
   711  						logrus.WithField("host", protoAddr).Warnf("You can override this by explicitly specifying '--%s=false' or '--%s=false'", FlagTLS, FlagTLSVerify)
   712  						logrus.WithField("host", protoAddr).Warnf("Support for listening on TCP without authentication or explicit intent to run without authentication will be removed in the next release")
   713  
   714  						time.Sleep(15 * time.Second)
   715  					}
   716  				}
   717  			}
   718  		}
   719  		// If we're binding to a TCP port, make sure that a container doesn't try to use it.
   720  		if proto == "tcp" {
   721  			if err := allocateDaemonPort(addr); err != nil {
   722  				return nil, err
   723  			}
   724  		}
   725  		ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
   726  		if err != nil {
   727  			return nil, err
   728  		}
   729  		logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr)
   730  		hosts = append(hosts, protoAddrParts[1])
   731  		cli.api.Accept(addr, ls...)
   732  	}
   733  
   734  	return hosts, nil
   735  }
   736  
   737  func createAndStartCluster(cli *DaemonCli, d *daemon.Daemon) (*cluster.Cluster, error) {
   738  	name, _ := os.Hostname()
   739  
   740  	// Use a buffered channel to pass changes from store watch API to daemon
   741  	// A buffer allows store watch API and daemon processing to not wait for each other
   742  	watchStream := make(chan *swarmapi.WatchMessage, 32)
   743  
   744  	c, err := cluster.New(cluster.Config{
   745  		Root:                   cli.Config.Root,
   746  		Name:                   name,
   747  		Backend:                d,
   748  		VolumeBackend:          d.VolumesService(),
   749  		ImageBackend:           d.ImageService(),
   750  		PluginBackend:          d.PluginManager(),
   751  		NetworkSubnetsProvider: d,
   752  		DefaultAdvertiseAddr:   cli.Config.SwarmDefaultAdvertiseAddr,
   753  		RaftHeartbeatTick:      cli.Config.SwarmRaftHeartbeatTick,
   754  		RaftElectionTick:       cli.Config.SwarmRaftElectionTick,
   755  		RuntimeRoot:            cli.getSwarmRunRoot(),
   756  		WatchStream:            watchStream,
   757  	})
   758  	if err != nil {
   759  		return nil, err
   760  	}
   761  	d.SetCluster(c)
   762  	err = c.Start()
   763  
   764  	return c, err
   765  }
   766  
   767  // validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver
   768  // plugins present on the host and available to the daemon
   769  func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error {
   770  	for _, reqPlugin := range requestedPlugins {
   771  		if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.Lookup); err != nil {
   772  			return err
   773  		}
   774  	}
   775  	return nil
   776  }
   777  
   778  func systemContainerdRunning(honorXDG bool) (string, bool, error) {
   779  	addr := containerddefaults.DefaultAddress
   780  	if honorXDG {
   781  		runtimeDir, err := homedir.GetRuntimeDir()
   782  		if err != nil {
   783  			return "", false, err
   784  		}
   785  		addr = filepath.Join(runtimeDir, "containerd", "containerd.sock")
   786  	}
   787  	_, err := os.Lstat(addr)
   788  	return addr, err == nil, nil
   789  }
   790  
   791  // configureDaemonLogs sets the logrus logging level and formatting. It expects
   792  // the passed configuration to already be validated, and ignores invalid options.
   793  func configureDaemonLogs(conf *config.Config) {
   794  	if conf.LogLevel != "" {
   795  		lvl, err := logrus.ParseLevel(conf.LogLevel)
   796  		if err == nil {
   797  			logrus.SetLevel(lvl)
   798  		}
   799  	} else {
   800  		logrus.SetLevel(logrus.InfoLevel)
   801  	}
   802  	logrus.SetFormatter(&logrus.TextFormatter{
   803  		TimestampFormat: jsonmessage.RFC3339NanoFixed,
   804  		DisableColors:   conf.RawLogs,
   805  		FullTimestamp:   true,
   806  	})
   807  }
   808  
   809  func configureProxyEnv(conf *config.Config) {
   810  	if p := conf.HTTPProxy; p != "" {
   811  		overrideProxyEnv("HTTP_PROXY", p)
   812  		overrideProxyEnv("http_proxy", p)
   813  	}
   814  	if p := conf.HTTPSProxy; p != "" {
   815  		overrideProxyEnv("HTTPS_PROXY", p)
   816  		overrideProxyEnv("https_proxy", p)
   817  	}
   818  	if p := conf.NoProxy; p != "" {
   819  		overrideProxyEnv("NO_PROXY", p)
   820  		overrideProxyEnv("no_proxy", p)
   821  	}
   822  }
   823  
   824  func overrideProxyEnv(name, val string) {
   825  	if oldVal := os.Getenv(name); oldVal != "" && oldVal != val {
   826  		logrus.WithFields(logrus.Fields{
   827  			"name":      name,
   828  			"old-value": config.MaskCredentials(oldVal),
   829  			"new-value": config.MaskCredentials(val),
   830  		}).Warn("overriding existing proxy variable with value from configuration")
   831  	}
   832  	_ = os.Setenv(name, val)
   833  }