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