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