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