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