github.com/rish1988/moby@v25.0.2+incompatible/cmd/dockerd/daemon.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"sort"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	containerddefaults "github.com/containerd/containerd/defaults"
    18  	"github.com/containerd/containerd/tracing"
    19  	"github.com/containerd/log"
    20  	"github.com/docker/docker/api"
    21  	apiserver "github.com/docker/docker/api/server"
    22  	buildbackend "github.com/docker/docker/api/server/backend/build"
    23  	"github.com/docker/docker/api/server/middleware"
    24  	"github.com/docker/docker/api/server/router"
    25  	"github.com/docker/docker/api/server/router/build"
    26  	checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
    27  	"github.com/docker/docker/api/server/router/container"
    28  	distributionrouter "github.com/docker/docker/api/server/router/distribution"
    29  	grpcrouter "github.com/docker/docker/api/server/router/grpc"
    30  	"github.com/docker/docker/api/server/router/image"
    31  	"github.com/docker/docker/api/server/router/network"
    32  	pluginrouter "github.com/docker/docker/api/server/router/plugin"
    33  	sessionrouter "github.com/docker/docker/api/server/router/session"
    34  	swarmrouter "github.com/docker/docker/api/server/router/swarm"
    35  	systemrouter "github.com/docker/docker/api/server/router/system"
    36  	"github.com/docker/docker/api/server/router/volume"
    37  	buildkit "github.com/docker/docker/builder/builder-next"
    38  	"github.com/docker/docker/builder/dockerfile"
    39  	"github.com/docker/docker/cli/debug"
    40  	"github.com/docker/docker/cmd/dockerd/trap"
    41  	"github.com/docker/docker/daemon"
    42  	"github.com/docker/docker/daemon/cluster"
    43  	"github.com/docker/docker/daemon/config"
    44  	"github.com/docker/docker/daemon/listeners"
    45  	"github.com/docker/docker/dockerversion"
    46  	"github.com/docker/docker/libcontainerd/supervisor"
    47  	dopts "github.com/docker/docker/opts"
    48  	"github.com/docker/docker/pkg/authorization"
    49  	"github.com/docker/docker/pkg/homedir"
    50  	"github.com/docker/docker/pkg/pidfile"
    51  	"github.com/docker/docker/pkg/plugingetter"
    52  	"github.com/docker/docker/pkg/rootless"
    53  	"github.com/docker/docker/pkg/sysinfo"
    54  	"github.com/docker/docker/pkg/system"
    55  	"github.com/docker/docker/plugin"
    56  	"github.com/docker/docker/runconfig"
    57  	"github.com/docker/go-connections/tlsconfig"
    58  	"github.com/moby/buildkit/session"
    59  	"github.com/moby/buildkit/util/tracing/detect"
    60  	swarmapi "github.com/moby/swarmkit/v2/api"
    61  	"github.com/pkg/errors"
    62  	"github.com/sirupsen/logrus"
    63  	"github.com/spf13/pflag"
    64  	"go.opentelemetry.io/otel"
    65  	"go.opentelemetry.io/otel/propagation"
    66  	"go.opentelemetry.io/otel/sdk/resource"
    67  	"tags.cncf.io/container-device-interface/pkg/cdi"
    68  )
    69  
    70  // DaemonCli represents the daemon CLI.
    71  type DaemonCli struct {
    72  	*config.Config
    73  	configFile *string
    74  	flags      *pflag.FlagSet
    75  
    76  	d               *daemon.Daemon
    77  	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
    78  
    79  	stopOnce    sync.Once
    80  	apiShutdown chan struct{}
    81  }
    82  
    83  // NewDaemonCli returns a daemon CLI
    84  func NewDaemonCli() *DaemonCli {
    85  	return &DaemonCli{
    86  		apiShutdown: make(chan struct{}),
    87  	}
    88  }
    89  
    90  func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
    91  	ctx := context.TODO()
    92  
    93  	if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
    94  		return err
    95  	}
    96  
    97  	tlsConfig, err := newAPIServerTLSConfig(cli.Config)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	if opts.Validate {
   103  		// If config wasn't OK we wouldn't have made it this far.
   104  		_, _ = fmt.Fprintln(os.Stderr, "configuration OK")
   105  		return nil
   106  	}
   107  
   108  	configureProxyEnv(cli.Config)
   109  	configureDaemonLogs(cli.Config)
   110  
   111  	log.G(ctx).Info("Starting up")
   112  
   113  	cli.configFile = &opts.configFile
   114  	cli.flags = opts.flags
   115  
   116  	if cli.Config.Debug {
   117  		debug.Enable()
   118  	}
   119  
   120  	if cli.Config.Experimental {
   121  		log.G(ctx).Warn("Running experimental build")
   122  	}
   123  
   124  	if cli.Config.IsRootless() {
   125  		log.G(ctx).Warn("Running in rootless mode. This mode has feature limitations.")
   126  	}
   127  	if rootless.RunningWithRootlessKit() {
   128  		log.G(ctx).Info("Running with RootlessKit integration")
   129  		if !cli.Config.IsRootless() {
   130  			return fmt.Errorf("rootless mode needs to be enabled for running with RootlessKit")
   131  		}
   132  	}
   133  
   134  	// return human-friendly error before creating files
   135  	if runtime.GOOS == "linux" && os.Geteuid() != 0 {
   136  		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/")
   137  	}
   138  
   139  	if err := setDefaultUmask(); err != nil {
   140  		return err
   141  	}
   142  
   143  	// Create the daemon root before we create ANY other files (PID, or migrate keys)
   144  	// to ensure the appropriate ACL is set (particularly relevant on Windows)
   145  	if err := daemon.CreateDaemonRoot(cli.Config); err != nil {
   146  		return err
   147  	}
   148  
   149  	if err := system.MkdirAll(cli.Config.ExecRoot, 0o700); err != nil {
   150  		return err
   151  	}
   152  
   153  	potentiallyUnderRuntimeDir := []string{cli.Config.ExecRoot}
   154  
   155  	if cli.Pidfile != "" {
   156  		if err = system.MkdirAll(filepath.Dir(cli.Pidfile), 0o755); err != nil {
   157  			return errors.Wrap(err, "failed to create pidfile directory")
   158  		}
   159  		if err = pidfile.Write(cli.Pidfile, os.Getpid()); err != nil {
   160  			return errors.Wrapf(err, "failed to start daemon, ensure docker is not running or delete %s", cli.Pidfile)
   161  		}
   162  		potentiallyUnderRuntimeDir = append(potentiallyUnderRuntimeDir, cli.Pidfile)
   163  		defer func() {
   164  			if err := os.Remove(cli.Pidfile); err != nil {
   165  				log.G(ctx).Error(err)
   166  			}
   167  		}()
   168  	}
   169  
   170  	if cli.Config.IsRootless() {
   171  		// Set sticky bit if XDG_RUNTIME_DIR is set && the file is actually under XDG_RUNTIME_DIR
   172  		if _, err := homedir.StickRuntimeDirContents(potentiallyUnderRuntimeDir); err != nil {
   173  			// StickRuntimeDirContents returns nil error if XDG_RUNTIME_DIR is just unset
   174  			log.G(ctx).WithError(err).Warn("cannot set sticky bit on files under XDG_RUNTIME_DIR")
   175  		}
   176  	}
   177  
   178  	lss, hosts, err := loadListeners(cli.Config, tlsConfig)
   179  	if err != nil {
   180  		return errors.Wrap(err, "failed to load listeners")
   181  	}
   182  
   183  	ctx, cancel := context.WithCancel(context.Background())
   184  	waitForContainerDShutdown, err := cli.initContainerd(ctx)
   185  	if waitForContainerDShutdown != nil {
   186  		defer waitForContainerDShutdown(10 * time.Second)
   187  	}
   188  	if err != nil {
   189  		cancel()
   190  		return err
   191  	}
   192  	defer cancel()
   193  
   194  	httpServer := &http.Server{
   195  		ReadHeaderTimeout: 5 * time.Minute, // "G112: Potential Slowloris Attack (gosec)"; not a real concern for our use, so setting a long timeout.
   196  	}
   197  	apiShutdownCtx, apiShutdownCancel := context.WithCancel(context.Background())
   198  	apiShutdownDone := make(chan struct{})
   199  	trap.Trap(cli.stop)
   200  	go func() {
   201  		// Block until cli.stop() has been called.
   202  		// It may have already been called, and that's okay.
   203  		// Any httpServer.Serve() calls made after
   204  		// httpServer.Shutdown() will return immediately,
   205  		// which is what we want.
   206  		<-cli.apiShutdown
   207  		err := httpServer.Shutdown(apiShutdownCtx)
   208  		if err != nil {
   209  			log.G(ctx).WithError(err).Error("Error shutting down http server")
   210  		}
   211  		close(apiShutdownDone)
   212  	}()
   213  	defer func() {
   214  		select {
   215  		case <-cli.apiShutdown:
   216  			// cli.stop() has been called and the daemon has completed
   217  			// shutting down. Give the HTTP server a little more time to
   218  			// finish handling any outstanding requests if needed.
   219  			tmr := time.AfterFunc(5*time.Second, apiShutdownCancel)
   220  			defer tmr.Stop()
   221  			<-apiShutdownDone
   222  		default:
   223  			// cli.start() has returned without cli.stop() being called,
   224  			// e.g. because the daemon failed to start.
   225  			// Stop the HTTP server with no grace period.
   226  			if closeErr := httpServer.Close(); closeErr != nil {
   227  				log.G(ctx).WithError(closeErr).Error("Error closing http server")
   228  			}
   229  		}
   230  	}()
   231  
   232  	// Notify that the API is active, but before daemon is set up.
   233  	preNotifyReady()
   234  
   235  	const otelServiceNameEnv = "OTEL_SERVICE_NAME"
   236  	if _, ok := os.LookupEnv(otelServiceNameEnv); !ok {
   237  		os.Setenv(otelServiceNameEnv, filepath.Base(os.Args[0]))
   238  	}
   239  
   240  	setOTLPProtoDefault()
   241  	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
   242  
   243  	// Override BuildKit's default Resource so that it matches the semconv
   244  	// version that is used in our code.
   245  	detect.Resource = resource.Default()
   246  	detect.Recorder = detect.NewTraceRecorder()
   247  
   248  	tp, err := detect.TracerProvider()
   249  	if err != nil {
   250  		log.G(ctx).WithError(err).Warn("Failed to initialize tracing, skipping")
   251  	} else {
   252  		otel.SetTracerProvider(tp)
   253  		log.G(ctx).Logger.AddHook(tracing.NewLogrusHook())
   254  	}
   255  
   256  	pluginStore := plugin.NewStore()
   257  
   258  	var apiServer apiserver.Server
   259  	cli.authzMiddleware = initMiddlewares(&apiServer, cli.Config, pluginStore)
   260  
   261  	d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore, cli.authzMiddleware)
   262  	if err != nil {
   263  		return errors.Wrap(err, "failed to start daemon")
   264  	}
   265  
   266  	d.StoreHosts(hosts)
   267  
   268  	// validate after NewDaemon has restored enabled plugins. Don't change order.
   269  	if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, pluginStore); err != nil {
   270  		return errors.Wrap(err, "failed to validate authorization plugin")
   271  	}
   272  
   273  	// Note that CDI is not inherently linux-specific, there are some linux-specific assumptions / implementations in the code that
   274  	// queries the properties of device on the host as wel as performs the injection of device nodes and their access permissions into the OCI spec.
   275  	//
   276  	// In order to lift this restriction the following would have to be addressed:
   277  	// - Support needs to be added to the cdi package for injecting Windows devices: https://tags.cncf.io/container-device-interface/issues/28
   278  	// - The DeviceRequests API must be extended to non-linux platforms.
   279  	if runtime.GOOS == "linux" && cli.Config.Features["cdi"] {
   280  		daemon.RegisterCDIDriver(cli.Config.CDISpecDirs...)
   281  	}
   282  
   283  	cli.d = d
   284  
   285  	if err := startMetricsServer(cli.Config.MetricsAddress); err != nil {
   286  		return errors.Wrap(err, "failed to start metrics server")
   287  	}
   288  
   289  	c, err := createAndStartCluster(cli, d)
   290  	if err != nil {
   291  		log.G(ctx).Fatalf("Error starting cluster component: %v", err)
   292  	}
   293  
   294  	// Restart all autostart containers which has a swarm endpoint
   295  	// and is not yet running now that we have successfully
   296  	// initialized the cluster.
   297  	d.RestartSwarmContainers()
   298  
   299  	log.G(ctx).Info("Daemon has completed initialization")
   300  
   301  	routerCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   302  	defer cancel()
   303  
   304  	// Get a the current daemon config, because the daemon sets up config
   305  	// during initialization. We cannot user the cli.Config for that reason,
   306  	// as that only holds the config that was set by the user.
   307  	//
   308  	// FIXME(thaJeztah): better separate runtime and config data?
   309  	daemonCfg := d.Config()
   310  	routerOptions, err := newRouterOptions(routerCtx, &daemonCfg, d)
   311  	if err != nil {
   312  		return err
   313  	}
   314  
   315  	routerOptions.cluster = c
   316  
   317  	httpServer.Handler = apiServer.CreateMux(routerOptions.Build()...)
   318  
   319  	go d.ProcessClusterNotifications(ctx, c.GetWatchStream())
   320  
   321  	cli.setupConfigReloadTrap()
   322  
   323  	// after the daemon is done setting up we can notify systemd api
   324  	notifyReady()
   325  
   326  	// Daemon is fully initialized. Start handling API traffic
   327  	// and wait for serve API to complete.
   328  	var (
   329  		apiWG  sync.WaitGroup
   330  		errAPI = make(chan error, 1)
   331  	)
   332  	for _, ls := range lss {
   333  		apiWG.Add(1)
   334  		go func(ls net.Listener) {
   335  			defer apiWG.Done()
   336  			log.G(ctx).Infof("API listen on %s", ls.Addr())
   337  			if err := httpServer.Serve(ls); err != http.ErrServerClosed {
   338  				log.G(ctx).WithFields(log.Fields{
   339  					"error":    err,
   340  					"listener": ls.Addr(),
   341  				}).Error("ServeAPI error")
   342  
   343  				select {
   344  				case errAPI <- err:
   345  				default:
   346  				}
   347  			}
   348  		}(ls)
   349  	}
   350  	apiWG.Wait()
   351  	close(errAPI)
   352  
   353  	c.Cleanup()
   354  
   355  	// notify systemd that we're shutting down
   356  	notifyStopping()
   357  	shutdownDaemon(ctx, d)
   358  
   359  	if err := routerOptions.buildkit.Close(); err != nil {
   360  		log.G(ctx).WithError(err).Error("Failed to close buildkit")
   361  	}
   362  
   363  	// Stop notification processing and any background processes
   364  	cancel()
   365  
   366  	if err, ok := <-errAPI; ok {
   367  		return errors.Wrap(err, "shutting down due to ServeAPI error")
   368  	}
   369  
   370  	detect.Shutdown(context.Background())
   371  
   372  	log.G(ctx).Info("Daemon shutdown complete")
   373  	return nil
   374  }
   375  
   376  // The buildkit "detect" package uses grpc as the default proto, which is in conformance with the old spec.
   377  // For a little while now http/protobuf is the default spec, so this function sets the protocol to http/protobuf when the env var is unset
   378  // so that the detect package will use http/protobuf as a default.
   379  // TODO: This can be removed after buildkit is updated to use http/protobuf as the default.
   380  func setOTLPProtoDefault() {
   381  	const (
   382  		tracesEnv = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
   383  		protoEnv  = "OTEL_EXPORTER_OTLP_PROTOCOL"
   384  	)
   385  
   386  	if os.Getenv(tracesEnv) == "" && os.Getenv(protoEnv) == "" {
   387  		os.Setenv(tracesEnv, "http/protobuf")
   388  	}
   389  }
   390  
   391  type routerOptions struct {
   392  	sessionManager *session.Manager
   393  	buildBackend   *buildbackend.Backend
   394  	features       func() map[string]bool
   395  	buildkit       *buildkit.Builder
   396  	daemon         *daemon.Daemon
   397  	cluster        *cluster.Cluster
   398  }
   399  
   400  func newRouterOptions(ctx context.Context, config *config.Config, d *daemon.Daemon) (routerOptions, error) {
   401  	opts := routerOptions{}
   402  	sm, err := session.NewManager()
   403  	if err != nil {
   404  		return opts, errors.Wrap(err, "failed to create sessionmanager")
   405  	}
   406  
   407  	manager, err := dockerfile.NewBuildManager(d.BuilderBackend(), d.IdentityMapping())
   408  	if err != nil {
   409  		return opts, err
   410  	}
   411  	cgroupParent := newCgroupParent(config)
   412  	ro := routerOptions{
   413  		sessionManager: sm,
   414  		features:       d.Features,
   415  		daemon:         d,
   416  	}
   417  
   418  	bk, err := buildkit.New(ctx, buildkit.Opt{
   419  		SessionManager:      sm,
   420  		Root:                filepath.Join(config.Root, "buildkit"),
   421  		EngineID:            d.ID(),
   422  		Dist:                d.DistributionServices(),
   423  		ImageTagger:         d.ImageService(),
   424  		NetworkController:   d.NetworkController(),
   425  		DefaultCgroupParent: cgroupParent,
   426  		RegistryHosts:       d.RegistryHosts,
   427  		BuilderConfig:       config.Builder,
   428  		Rootless:            daemon.Rootless(config),
   429  		IdentityMapping:     d.IdentityMapping(),
   430  		DNSConfig:           config.DNSConfig,
   431  		ApparmorProfile:     daemon.DefaultApparmorProfile(),
   432  		UseSnapshotter:      d.UsesSnapshotter(),
   433  		Snapshotter:         d.ImageService().StorageDriver(),
   434  		ContainerdAddress:   config.ContainerdAddr,
   435  		ContainerdNamespace: config.ContainerdNamespace,
   436  	})
   437  	if err != nil {
   438  		return opts, err
   439  	}
   440  
   441  	bb, err := buildbackend.NewBackend(d.ImageService(), manager, bk, d.EventsService)
   442  	if err != nil {
   443  		return opts, errors.Wrap(err, "failed to create buildmanager")
   444  	}
   445  
   446  	ro.buildBackend = bb
   447  	ro.buildkit = bk
   448  
   449  	return ro, nil
   450  }
   451  
   452  func (cli *DaemonCli) reloadConfig() {
   453  	ctx := context.TODO()
   454  	reload := func(c *config.Config) {
   455  		if err := validateAuthzPlugins(c.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   456  			log.G(ctx).Fatalf("Error validating authorization plugin: %v", err)
   457  			return
   458  		}
   459  
   460  		if err := cli.d.Reload(c); err != nil {
   461  			log.G(ctx).Errorf("Error reconfiguring the daemon: %v", err)
   462  			return
   463  		}
   464  
   465  		// Apply our own configuration only after the daemon reload has succeeded. We
   466  		// don't want to partially apply the config if the daemon is unhappy with it.
   467  
   468  		cli.authzMiddleware.SetPlugins(c.AuthorizationPlugins)
   469  
   470  		if c.IsValueSet("debug") {
   471  			debugEnabled := debug.IsEnabled()
   472  			switch {
   473  			case debugEnabled && !c.Debug: // disable debug
   474  				debug.Disable()
   475  			case c.Debug && !debugEnabled: // enable debug
   476  				debug.Enable()
   477  			}
   478  		}
   479  	}
   480  
   481  	if err := config.Reload(*cli.configFile, cli.flags, reload); err != nil {
   482  		log.G(ctx).Error(err)
   483  	}
   484  }
   485  
   486  func (cli *DaemonCli) stop() {
   487  	// Signal that the API server should shut down as soon as possible.
   488  	// This construct is used rather than directly shutting down the HTTP
   489  	// server to avoid any issues if this method is called before the server
   490  	// has been instantiated in cli.start(). If this method is called first,
   491  	// the HTTP server will be shut down immediately upon instantiation.
   492  	cli.stopOnce.Do(func() {
   493  		close(cli.apiShutdown)
   494  	})
   495  }
   496  
   497  // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
   498  // d.Shutdown() is waiting too long to kill container or worst it's
   499  // blocked there
   500  func shutdownDaemon(ctx context.Context, d *daemon.Daemon) {
   501  	var cancel context.CancelFunc
   502  	if timeout := d.ShutdownTimeout(); timeout >= 0 {
   503  		ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
   504  	} else {
   505  		ctx, cancel = context.WithCancel(ctx)
   506  	}
   507  
   508  	go func() {
   509  		defer cancel()
   510  		d.Shutdown(ctx)
   511  	}()
   512  
   513  	<-ctx.Done()
   514  	if errors.Is(ctx.Err(), context.DeadlineExceeded) {
   515  		log.G(ctx).Error("Force shutdown daemon")
   516  	} else {
   517  		log.G(ctx).Debug("Clean shutdown succeeded")
   518  	}
   519  }
   520  
   521  func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
   522  	if !opts.flags.Parsed() {
   523  		return nil, errors.New(`cannot load CLI config before flags are parsed`)
   524  	}
   525  	opts.setDefaultOptions()
   526  
   527  	conf := opts.daemonConfig
   528  	flags := opts.flags
   529  	conf.Debug = opts.Debug
   530  	conf.Hosts = opts.Hosts
   531  	conf.LogLevel = opts.LogLevel
   532  	conf.LogFormat = log.OutputFormat(opts.LogFormat)
   533  
   534  	// The DOCKER_MIN_API_VERSION env-var allows overriding the minimum API
   535  	// version provided by the daemon within constraints of the minimum and
   536  	// maximum (current) supported API versions.
   537  	//
   538  	// API versions older than [config.defaultMinAPIVersion] are deprecated and
   539  	// to be removed in a future release. The "DOCKER_MIN_API_VERSION" env-var
   540  	// should only be used for exceptional cases.
   541  	if ver := os.Getenv("DOCKER_MIN_API_VERSION"); ver != "" {
   542  		if err := config.ValidateMinAPIVersion(ver); err != nil {
   543  			return nil, errors.Wrap(err, "invalid DOCKER_MIN_API_VERSION")
   544  		}
   545  		conf.MinAPIVersion = ver
   546  	}
   547  
   548  	if flags.Changed(FlagTLS) {
   549  		conf.TLS = &opts.TLS
   550  	}
   551  	if flags.Changed(FlagTLSVerify) {
   552  		conf.TLSVerify = &opts.TLSVerify
   553  		v := true
   554  		conf.TLS = &v
   555  	}
   556  
   557  	if opts.TLSOptions != nil {
   558  		conf.TLSOptions = config.TLSOptions{
   559  			CAFile:   opts.TLSOptions.CAFile,
   560  			CertFile: opts.TLSOptions.CertFile,
   561  			KeyFile:  opts.TLSOptions.KeyFile,
   562  		}
   563  	} else {
   564  		conf.TLSOptions = config.TLSOptions{}
   565  	}
   566  
   567  	if opts.configFile != "" {
   568  		c, err := config.MergeDaemonConfigurations(conf, flags, opts.configFile)
   569  		if err != nil {
   570  			if flags.Changed("config-file") || !os.IsNotExist(err) {
   571  				return nil, errors.Wrapf(err, "unable to configure the Docker daemon with file %s", opts.configFile)
   572  			}
   573  		}
   574  
   575  		// the merged configuration can be nil if the config file didn't exist.
   576  		// leave the current configuration as it is if when that happens.
   577  		if c != nil {
   578  			conf = c
   579  		}
   580  	}
   581  
   582  	if err := normalizeHosts(conf); err != nil {
   583  		return nil, err
   584  	}
   585  
   586  	if err := config.Validate(conf); err != nil {
   587  		return nil, err
   588  	}
   589  
   590  	// Check if duplicate label-keys with different values are found
   591  	newLabels, err := config.GetConflictFreeLabels(conf.Labels)
   592  	if err != nil {
   593  		return nil, err
   594  	}
   595  	conf.Labels = newLabels
   596  
   597  	// Regardless of whether the user sets it to true or false, if they
   598  	// specify TLSVerify at all then we need to turn on TLS
   599  	if conf.IsValueSet(FlagTLSVerify) {
   600  		v := true
   601  		conf.TLS = &v
   602  	}
   603  
   604  	if conf.TLSVerify == nil && conf.TLS != nil {
   605  		conf.TLSVerify = conf.TLS
   606  	}
   607  
   608  	err = validateCPURealtimeOptions(conf)
   609  	if err != nil {
   610  		return nil, err
   611  	}
   612  
   613  	if conf.CDISpecDirs == nil {
   614  		// If the CDISpecDirs is not set at this stage, we set it to the default.
   615  		conf.CDISpecDirs = append([]string(nil), cdi.DefaultSpecDirs...)
   616  	} else if len(conf.CDISpecDirs) == 1 && conf.CDISpecDirs[0] == "" {
   617  		// If CDISpecDirs is set to an empty string, we clear it to ensure that CDI is disabled.
   618  		conf.CDISpecDirs = nil
   619  	}
   620  	if !conf.Features["cdi"] {
   621  		// If the CDI feature is not enabled, we clear the CDISpecDirs to ensure that CDI is disabled.
   622  		conf.CDISpecDirs = nil
   623  	}
   624  
   625  	if err := loadCLIPlatformConfig(conf); err != nil {
   626  		return nil, err
   627  	}
   628  
   629  	return conf, nil
   630  }
   631  
   632  // normalizeHosts normalizes the configured config.Hosts and remove duplicates.
   633  // It returns an error if it fails to parse a host.
   634  func normalizeHosts(config *config.Config) error {
   635  	if len(config.Hosts) == 0 {
   636  		// if no hosts are configured, create a single entry slice, so that the
   637  		// default is used.
   638  		//
   639  		// TODO(thaJeztah) implement a cleaner way for this; this depends on a
   640  		//                 side-effect of how we parse empty/partial hosts.
   641  		config.Hosts = make([]string, 1)
   642  	}
   643  	hosts := make([]string, 0, len(config.Hosts))
   644  	seen := make(map[string]struct{}, len(config.Hosts))
   645  
   646  	useTLS := DefaultTLSValue
   647  	if config.TLS != nil {
   648  		useTLS = *config.TLS
   649  	}
   650  
   651  	for _, h := range config.Hosts {
   652  		host, err := dopts.ParseHost(useTLS, honorXDG, h)
   653  		if err != nil {
   654  			return err
   655  		}
   656  		if _, ok := seen[host]; ok {
   657  			continue
   658  		}
   659  		seen[host] = struct{}{}
   660  		hosts = append(hosts, host)
   661  	}
   662  	sort.Strings(hosts)
   663  	config.Hosts = hosts
   664  	return nil
   665  }
   666  
   667  func (opts routerOptions) Build() []router.Router {
   668  	decoder := runconfig.ContainerDecoder{
   669  		GetSysInfo: func() *sysinfo.SysInfo {
   670  			return opts.daemon.RawSysInfo()
   671  		},
   672  	}
   673  
   674  	routers := []router.Router{
   675  		// we need to add the checkpoint router before the container router or the DELETE gets masked
   676  		checkpointrouter.NewRouter(opts.daemon, decoder),
   677  		container.NewRouter(opts.daemon, decoder, opts.daemon.RawSysInfo().CgroupUnified),
   678  		image.NewRouter(
   679  			opts.daemon.ImageService(),
   680  			opts.daemon.RegistryService(),
   681  			opts.daemon.ReferenceStore,
   682  			opts.daemon.ImageService().DistributionServices().ImageStore,
   683  			opts.daemon.ImageService().DistributionServices().LayerStore,
   684  		),
   685  		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildkit, opts.daemon.Features),
   686  		volume.NewRouter(opts.daemon.VolumesService(), opts.cluster),
   687  		build.NewRouter(opts.buildBackend, opts.daemon),
   688  		sessionrouter.NewRouter(opts.sessionManager),
   689  		swarmrouter.NewRouter(opts.cluster),
   690  		pluginrouter.NewRouter(opts.daemon.PluginManager()),
   691  		distributionrouter.NewRouter(opts.daemon.ImageBackend()),
   692  	}
   693  
   694  	if opts.buildBackend != nil {
   695  		routers = append(routers, grpcrouter.NewRouter(opts.buildBackend))
   696  	}
   697  
   698  	if opts.daemon.NetworkControllerEnabled() {
   699  		routers = append(routers, network.NewRouter(opts.daemon, opts.cluster))
   700  	}
   701  
   702  	if opts.daemon.HasExperimental() {
   703  		for _, r := range routers {
   704  			for _, route := range r.Routes() {
   705  				if experimental, ok := route.(router.ExperimentalRoute); ok {
   706  					experimental.Enable()
   707  				}
   708  			}
   709  		}
   710  	}
   711  
   712  	return routers
   713  }
   714  
   715  func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugingetter.PluginGetter) *authorization.Middleware {
   716  	v := dockerversion.Version
   717  
   718  	exp := middleware.NewExperimentalMiddleware(cfg.Experimental)
   719  	s.UseMiddleware(exp)
   720  
   721  	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, cfg.MinAPIVersion)
   722  	s.UseMiddleware(vm)
   723  
   724  	if cfg.CorsHeaders != "" {
   725  		c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
   726  		s.UseMiddleware(c)
   727  	}
   728  
   729  	authzMiddleware := authorization.NewMiddleware(cfg.AuthorizationPlugins, pluginStore)
   730  	s.UseMiddleware(authzMiddleware)
   731  	return authzMiddleware
   732  }
   733  
   734  func (cli *DaemonCli) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
   735  	var opts []supervisor.DaemonOpt
   736  	if cli.Debug {
   737  		opts = append(opts, supervisor.WithLogLevel("debug"))
   738  	} else {
   739  		opts = append(opts, supervisor.WithLogLevel(cli.LogLevel))
   740  	}
   741  
   742  	if logFormat := cli.Config.LogFormat; logFormat != "" {
   743  		opts = append(opts, supervisor.WithLogFormat(logFormat))
   744  	}
   745  
   746  	if !cli.CriContainerd {
   747  		// CRI support in the managed daemon is currently opt-in.
   748  		//
   749  		// It's disabled by default, originally because it was listening on
   750  		// a TCP connection at 0.0.0.0:10010, which was considered a security
   751  		// risk, and could conflict with user's container ports.
   752  		//
   753  		// Current versions of containerd started now listen on localhost on
   754  		// an ephemeral port instead, but could still conflict with container
   755  		// ports, and running kubernetes using the static binaries is not a
   756  		// common scenario, so we (for now) continue disabling it by default.
   757  		//
   758  		// Also see https://github.com/containerd/containerd/issues/2483#issuecomment-407530608
   759  		opts = append(opts, supervisor.WithCRIDisabled())
   760  	}
   761  
   762  	return opts, nil
   763  }
   764  
   765  func newAPIServerTLSConfig(config *config.Config) (*tls.Config, error) {
   766  	var tlsConfig *tls.Config
   767  	if config.TLS != nil && *config.TLS {
   768  		var (
   769  			clientAuth tls.ClientAuthType
   770  			err        error
   771  		)
   772  		if config.TLSVerify == nil || *config.TLSVerify {
   773  			// server requires and verifies client's certificate
   774  			clientAuth = tls.RequireAndVerifyClientCert
   775  		}
   776  		tlsConfig, err = tlsconfig.Server(tlsconfig.Options{
   777  			CAFile:             config.TLSOptions.CAFile,
   778  			CertFile:           config.TLSOptions.CertFile,
   779  			KeyFile:            config.TLSOptions.KeyFile,
   780  			ExclusiveRootPools: true,
   781  			ClientAuth:         clientAuth,
   782  		})
   783  		if err != nil {
   784  			return nil, errors.Wrap(err, "invalid TLS configuration")
   785  		}
   786  	}
   787  
   788  	return tlsConfig, nil
   789  }
   790  
   791  // checkTLSAuthOK checks basically for an explicitly disabled TLS/TLSVerify
   792  // Going forward we do not want to support a scenario where dockerd listens
   793  // on TCP without either TLS client auth (or an explicit opt-in to disable it)
   794  func checkTLSAuthOK(c *config.Config) bool {
   795  	if c.TLS == nil {
   796  		// Either TLS is enabled by default, in which case TLS verification should be enabled by default, or explicitly disabled
   797  		// Or TLS is disabled by default... in any of these cases, we can just take the default value as to how to proceed
   798  		return DefaultTLSValue
   799  	}
   800  
   801  	if !*c.TLS {
   802  		// TLS is explicitly disabled, which is supported
   803  		return true
   804  	}
   805  
   806  	if c.TLSVerify == nil {
   807  		// this actually shouldn't happen since we set TLSVerify on the config object anyway
   808  		// But in case it does get here, be cautious and assume this is not supported.
   809  		return false
   810  	}
   811  
   812  	// Either TLSVerify is explicitly enabled or disabled, both cases are supported
   813  	return true
   814  }
   815  
   816  func loadListeners(cfg *config.Config, tlsConfig *tls.Config) ([]net.Listener, []string, error) {
   817  	ctx := context.TODO()
   818  
   819  	if len(cfg.Hosts) == 0 {
   820  		return nil, nil, errors.New("no hosts configured")
   821  	}
   822  	var (
   823  		hosts []string
   824  		lss   []net.Listener
   825  	)
   826  
   827  	for i := 0; i < len(cfg.Hosts); i++ {
   828  		protoAddr := cfg.Hosts[i]
   829  		proto, addr, ok := strings.Cut(protoAddr, "://")
   830  		if !ok {
   831  			return nil, nil, fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
   832  		}
   833  
   834  		// It's a bad idea to bind to TCP without tlsverify.
   835  		authEnabled := tlsConfig != nil && tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert
   836  		if proto == "tcp" && !authEnabled {
   837  			log.G(ctx).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.")
   838  			log.G(ctx).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!")
   839  			time.Sleep(time.Second)
   840  
   841  			// If TLSVerify is explicitly set to false we'll take that as "Please let me shoot myself in the foot"
   842  			// 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
   843  			if !checkTLSAuthOK(cfg) {
   844  				ipAddr, _, err := net.SplitHostPort(addr)
   845  				if err != nil {
   846  					return nil, nil, errors.Wrap(err, "error parsing tcp address")
   847  				}
   848  
   849  				// shortcut all this extra stuff for literal "localhost"
   850  				// -H supports specifying hostnames, since we want to bypass this on loopback interfaces we'll look it up here.
   851  				if ipAddr != "localhost" {
   852  					ip := net.ParseIP(ipAddr)
   853  					if ip == nil {
   854  						ipA, err := net.ResolveIPAddr("ip", ipAddr)
   855  						if err != nil {
   856  							log.G(ctx).WithError(err).WithField("host", ipAddr).Error("Error looking up specified host address")
   857  						}
   858  						if ipA != nil {
   859  							ip = ipA.IP
   860  						}
   861  					}
   862  					if ip == nil || !ip.IsLoopback() {
   863  						log.G(ctx).WithField("host", protoAddr).Warn("Binding to an IP address without --tlsverify is deprecated. Startup is intentionally being slowed down to show this message")
   864  						log.G(ctx).WithField("host", protoAddr).Warn("Please consider generating tls certificates with client validation to prevent exposing unauthenticated root access to your network")
   865  						log.G(ctx).WithField("host", protoAddr).Warnf("You can override this by explicitly specifying '--%s=false' or '--%s=false'", FlagTLS, FlagTLSVerify)
   866  						log.G(ctx).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")
   867  
   868  						time.Sleep(15 * time.Second)
   869  					}
   870  				}
   871  			}
   872  		}
   873  		// If we're binding to a TCP port, make sure that a container doesn't try to use it.
   874  		if proto == "tcp" {
   875  			if err := allocateDaemonPort(addr); err != nil {
   876  				return nil, nil, err
   877  			}
   878  		}
   879  		ls, err := listeners.Init(proto, addr, cfg.SocketGroup, tlsConfig)
   880  		if err != nil {
   881  			return nil, nil, err
   882  		}
   883  		log.G(ctx).Debugf("Listener created for HTTP on %s (%s)", proto, addr)
   884  		hosts = append(hosts, addr)
   885  		lss = append(lss, ls...)
   886  	}
   887  
   888  	return lss, hosts, nil
   889  }
   890  
   891  func createAndStartCluster(cli *DaemonCli, d *daemon.Daemon) (*cluster.Cluster, error) {
   892  	name, _ := os.Hostname()
   893  
   894  	// Use a buffered channel to pass changes from store watch API to daemon
   895  	// A buffer allows store watch API and daemon processing to not wait for each other
   896  	watchStream := make(chan *swarmapi.WatchMessage, 32)
   897  
   898  	c, err := cluster.New(cluster.Config{
   899  		Root:                   cli.Config.Root,
   900  		Name:                   name,
   901  		Backend:                d,
   902  		VolumeBackend:          d.VolumesService(),
   903  		ImageBackend:           d.ImageBackend(),
   904  		PluginBackend:          d.PluginManager(),
   905  		NetworkSubnetsProvider: d,
   906  		DefaultAdvertiseAddr:   cli.Config.SwarmDefaultAdvertiseAddr,
   907  		RaftHeartbeatTick:      cli.Config.SwarmRaftHeartbeatTick,
   908  		RaftElectionTick:       cli.Config.SwarmRaftElectionTick,
   909  		RuntimeRoot:            cli.getSwarmRunRoot(),
   910  		WatchStream:            watchStream,
   911  	})
   912  	if err != nil {
   913  		return nil, err
   914  	}
   915  	d.SetCluster(c)
   916  	err = c.Start()
   917  
   918  	return c, err
   919  }
   920  
   921  // validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver
   922  // plugins present on the host and available to the daemon
   923  func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error {
   924  	for _, reqPlugin := range requestedPlugins {
   925  		if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.Lookup); err != nil {
   926  			return err
   927  		}
   928  	}
   929  	return nil
   930  }
   931  
   932  func systemContainerdRunning(honorXDG bool) (string, bool, error) {
   933  	addr := containerddefaults.DefaultAddress
   934  	if honorXDG {
   935  		runtimeDir, err := homedir.GetRuntimeDir()
   936  		if err != nil {
   937  			return "", false, err
   938  		}
   939  		addr = filepath.Join(runtimeDir, "containerd", "containerd.sock")
   940  	}
   941  	_, err := os.Lstat(addr)
   942  	return addr, err == nil, nil
   943  }
   944  
   945  // configureDaemonLogs sets the logging level and formatting. It expects
   946  // the passed configuration to already be validated, and ignores invalid options.
   947  func configureDaemonLogs(conf *config.Config) {
   948  	switch conf.LogFormat {
   949  	case log.JSONFormat:
   950  		if err := log.SetFormat(log.JSONFormat); err != nil {
   951  			panic(err.Error())
   952  		}
   953  	case log.TextFormat, "":
   954  		if err := log.SetFormat(log.TextFormat); err != nil {
   955  			panic(err.Error())
   956  		}
   957  		if conf.RawLogs {
   958  			// FIXME(thaJeztah): this needs a better solution: containerd doesn't allow disabling colors, and this code is depending on internal knowledge of "log.SetFormat"
   959  			if l, ok := log.L.Logger.Formatter.(*logrus.TextFormatter); ok {
   960  				l.DisableColors = true
   961  			}
   962  		}
   963  	default:
   964  		panic("unsupported log format " + conf.LogFormat)
   965  	}
   966  
   967  	logLevel := conf.LogLevel
   968  	if logLevel == "" {
   969  		logLevel = "info"
   970  	}
   971  	if err := log.SetLevel(logLevel); err != nil {
   972  		log.G(context.TODO()).WithError(err).Warn("configure log level")
   973  	}
   974  }
   975  
   976  func configureProxyEnv(conf *config.Config) {
   977  	if p := conf.HTTPProxy; p != "" {
   978  		overrideProxyEnv("HTTP_PROXY", p)
   979  		overrideProxyEnv("http_proxy", p)
   980  	}
   981  	if p := conf.HTTPSProxy; p != "" {
   982  		overrideProxyEnv("HTTPS_PROXY", p)
   983  		overrideProxyEnv("https_proxy", p)
   984  	}
   985  	if p := conf.NoProxy; p != "" {
   986  		overrideProxyEnv("NO_PROXY", p)
   987  		overrideProxyEnv("no_proxy", p)
   988  	}
   989  }
   990  
   991  func overrideProxyEnv(name, val string) {
   992  	if oldVal := os.Getenv(name); oldVal != "" && oldVal != val {
   993  		log.G(context.TODO()).WithFields(log.Fields{
   994  			"name":      name,
   995  			"old-value": config.MaskCredentials(oldVal),
   996  			"new-value": config.MaskCredentials(val),
   997  		}).Warn("overriding existing proxy variable with value from configuration")
   998  	}
   999  	_ = os.Setenv(name, val)
  1000  }