github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/cmd/dockerd/daemon.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/docker/distribution/uuid"
    14  	"github.com/docker/docker/api"
    15  	apiserver "github.com/docker/docker/api/server"
    16  	buildbackend "github.com/docker/docker/api/server/backend/build"
    17  	"github.com/docker/docker/api/server/middleware"
    18  	"github.com/docker/docker/api/server/router"
    19  	"github.com/docker/docker/api/server/router/build"
    20  	checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
    21  	"github.com/docker/docker/api/server/router/container"
    22  	distributionrouter "github.com/docker/docker/api/server/router/distribution"
    23  	"github.com/docker/docker/api/server/router/image"
    24  	"github.com/docker/docker/api/server/router/network"
    25  	pluginrouter "github.com/docker/docker/api/server/router/plugin"
    26  	sessionrouter "github.com/docker/docker/api/server/router/session"
    27  	swarmrouter "github.com/docker/docker/api/server/router/swarm"
    28  	systemrouter "github.com/docker/docker/api/server/router/system"
    29  	"github.com/docker/docker/api/server/router/volume"
    30  	"github.com/docker/docker/builder/dockerfile"
    31  	"github.com/docker/docker/builder/fscache"
    32  	"github.com/docker/docker/cli/debug"
    33  	"github.com/docker/docker/daemon"
    34  	"github.com/docker/docker/daemon/cluster"
    35  	"github.com/docker/docker/daemon/config"
    36  	"github.com/docker/docker/daemon/listeners"
    37  	"github.com/docker/docker/daemon/logger"
    38  	"github.com/docker/docker/dockerversion"
    39  	"github.com/docker/docker/libcontainerd"
    40  	dopts "github.com/docker/docker/opts"
    41  	"github.com/docker/docker/pkg/authorization"
    42  	"github.com/docker/docker/pkg/jsonmessage"
    43  	"github.com/docker/docker/pkg/pidfile"
    44  	"github.com/docker/docker/pkg/plugingetter"
    45  	"github.com/docker/docker/pkg/signal"
    46  	"github.com/docker/docker/pkg/system"
    47  	"github.com/docker/docker/plugin"
    48  	"github.com/docker/docker/registry"
    49  	"github.com/docker/docker/runconfig"
    50  	"github.com/docker/go-connections/tlsconfig"
    51  	swarmapi "github.com/docker/swarmkit/api"
    52  	"github.com/moby/buildkit/session"
    53  	"github.com/pkg/errors"
    54  	"github.com/sirupsen/logrus"
    55  	"github.com/spf13/pflag"
    56  )
    57  
    58  // DaemonCli represents the daemon CLI.
    59  type DaemonCli struct {
    60  	*config.Config
    61  	configFile *string
    62  	flags      *pflag.FlagSet
    63  
    64  	api             *apiserver.Server
    65  	d               *daemon.Daemon
    66  	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
    67  }
    68  
    69  // NewDaemonCli returns a daemon CLI
    70  func NewDaemonCli() *DaemonCli {
    71  	return &DaemonCli{}
    72  }
    73  
    74  func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
    75  	stopc := make(chan bool)
    76  	defer close(stopc)
    77  
    78  	// warn from uuid package when running the daemon
    79  	uuid.Loggerf = logrus.Warnf
    80  
    81  	opts.SetDefaultOptions(opts.flags)
    82  
    83  	if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
    84  		return err
    85  	}
    86  	cli.configFile = &opts.configFile
    87  	cli.flags = opts.flags
    88  
    89  	if cli.Config.Debug {
    90  		debug.Enable()
    91  	}
    92  
    93  	if cli.Config.Experimental {
    94  		logrus.Warn("Running experimental build")
    95  	}
    96  
    97  	logrus.SetFormatter(&logrus.TextFormatter{
    98  		TimestampFormat: jsonmessage.RFC3339NanoFixed,
    99  		DisableColors:   cli.Config.RawLogs,
   100  		FullTimestamp:   true,
   101  	})
   102  
   103  	system.InitLCOW(cli.Config.Experimental)
   104  
   105  	if err := setDefaultUmask(); err != nil {
   106  		return fmt.Errorf("Failed to set umask: %v", err)
   107  	}
   108  
   109  	if len(cli.LogConfig.Config) > 0 {
   110  		if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
   111  			return fmt.Errorf("Failed to set log opts: %v", err)
   112  		}
   113  	}
   114  
   115  	// Create the daemon root before we create ANY other files (PID, or migrate keys)
   116  	// to ensure the appropriate ACL is set (particularly relevant on Windows)
   117  	if err := daemon.CreateDaemonRoot(cli.Config); err != nil {
   118  		return err
   119  	}
   120  
   121  	if cli.Pidfile != "" {
   122  		pf, err := pidfile.New(cli.Pidfile)
   123  		if err != nil {
   124  			return fmt.Errorf("Error starting daemon: %v", err)
   125  		}
   126  		defer func() {
   127  			if err := pf.Remove(); err != nil {
   128  				logrus.Error(err)
   129  			}
   130  		}()
   131  	}
   132  
   133  	// TODO: extract to newApiServerConfig()
   134  	serverConfig := &apiserver.Config{
   135  		Logging:     true,
   136  		SocketGroup: cli.Config.SocketGroup,
   137  		Version:     dockerversion.Version,
   138  		CorsHeaders: cli.Config.CorsHeaders,
   139  	}
   140  
   141  	if cli.Config.TLS {
   142  		tlsOptions := tlsconfig.Options{
   143  			CAFile:             cli.Config.CommonTLSOptions.CAFile,
   144  			CertFile:           cli.Config.CommonTLSOptions.CertFile,
   145  			KeyFile:            cli.Config.CommonTLSOptions.KeyFile,
   146  			ExclusiveRootPools: true,
   147  		}
   148  
   149  		if cli.Config.TLSVerify {
   150  			// server requires and verifies client's certificate
   151  			tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert
   152  		}
   153  		tlsConfig, err := tlsconfig.Server(tlsOptions)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		serverConfig.TLSConfig = tlsConfig
   158  	}
   159  
   160  	if len(cli.Config.Hosts) == 0 {
   161  		cli.Config.Hosts = make([]string, 1)
   162  	}
   163  
   164  	cli.api = apiserver.New(serverConfig)
   165  
   166  	var hosts []string
   167  
   168  	for i := 0; i < len(cli.Config.Hosts); i++ {
   169  		var err error
   170  		if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
   171  			return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err)
   172  		}
   173  
   174  		protoAddr := cli.Config.Hosts[i]
   175  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
   176  		if len(protoAddrParts) != 2 {
   177  			return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
   178  		}
   179  
   180  		proto := protoAddrParts[0]
   181  		addr := protoAddrParts[1]
   182  
   183  		// It's a bad idea to bind to TCP without tlsverify.
   184  		if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) {
   185  			logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]")
   186  		}
   187  		ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
   188  		if err != nil {
   189  			return err
   190  		}
   191  		ls = wrapListeners(proto, ls)
   192  		// If we're binding to a TCP port, make sure that a container doesn't try to use it.
   193  		if proto == "tcp" {
   194  			if err := allocateDaemonPort(addr); err != nil {
   195  				return err
   196  			}
   197  		}
   198  		logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr)
   199  		hosts = append(hosts, protoAddrParts[1])
   200  		cli.api.Accept(addr, ls...)
   201  	}
   202  
   203  	registryService, err := registry.NewService(cli.Config.ServiceOptions)
   204  	if err != nil {
   205  		return err
   206  	}
   207  
   208  	rOpts, err := cli.getRemoteOptions()
   209  	if err != nil {
   210  		return fmt.Errorf("Failed to generate containerd options: %s", err)
   211  	}
   212  	containerdRemote, err := libcontainerd.New(filepath.Join(cli.Config.Root, "containerd"), filepath.Join(cli.Config.ExecRoot, "containerd"), rOpts...)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	signal.Trap(func() {
   217  		cli.stop()
   218  		<-stopc // wait for daemonCli.start() to return
   219  	}, logrus.StandardLogger())
   220  
   221  	// Notify that the API is active, but before daemon is set up.
   222  	preNotifySystem()
   223  
   224  	pluginStore := plugin.NewStore()
   225  
   226  	if err := cli.initMiddlewares(cli.api, serverConfig, pluginStore); err != nil {
   227  		logrus.Fatalf("Error creating middlewares: %v", err)
   228  	}
   229  
   230  	d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote, pluginStore)
   231  	if err != nil {
   232  		return fmt.Errorf("Error starting daemon: %v", err)
   233  	}
   234  
   235  	d.StoreHosts(hosts)
   236  
   237  	// validate after NewDaemon has restored enabled plugins. Dont change order.
   238  	if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, pluginStore); err != nil {
   239  		return fmt.Errorf("Error validating authorization plugin: %v", err)
   240  	}
   241  
   242  	// TODO: move into startMetricsServer()
   243  	if cli.Config.MetricsAddress != "" {
   244  		if !d.HasExperimental() {
   245  			return fmt.Errorf("metrics-addr is only supported when experimental is enabled")
   246  		}
   247  		if err := startMetricsServer(cli.Config.MetricsAddress); err != nil {
   248  			return err
   249  		}
   250  	}
   251  
   252  	// TODO: createAndStartCluster()
   253  	name, _ := os.Hostname()
   254  
   255  	// Use a buffered channel to pass changes from store watch API to daemon
   256  	// A buffer allows store watch API and daemon processing to not wait for each other
   257  	watchStream := make(chan *swarmapi.WatchMessage, 32)
   258  
   259  	c, err := cluster.New(cluster.Config{
   260  		Root:                   cli.Config.Root,
   261  		Name:                   name,
   262  		Backend:                d,
   263  		PluginBackend:          d.PluginManager(),
   264  		NetworkSubnetsProvider: d,
   265  		DefaultAdvertiseAddr:   cli.Config.SwarmDefaultAdvertiseAddr,
   266  		RuntimeRoot:            cli.getSwarmRunRoot(),
   267  		WatchStream:            watchStream,
   268  	})
   269  	if err != nil {
   270  		logrus.Fatalf("Error creating cluster component: %v", err)
   271  	}
   272  	d.SetCluster(c)
   273  	err = c.Start()
   274  	if err != nil {
   275  		logrus.Fatalf("Error starting cluster component: %v", err)
   276  	}
   277  
   278  	// Restart all autostart containers which has a swarm endpoint
   279  	// and is not yet running now that we have successfully
   280  	// initialized the cluster.
   281  	d.RestartSwarmContainers()
   282  
   283  	logrus.Info("Daemon has completed initialization")
   284  
   285  	cli.d = d
   286  
   287  	routerOptions, err := newRouterOptions(cli.Config, d)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	routerOptions.api = cli.api
   292  	routerOptions.cluster = c
   293  
   294  	initRouter(routerOptions)
   295  
   296  	// process cluster change notifications
   297  	watchCtx, cancel := context.WithCancel(context.Background())
   298  	defer cancel()
   299  	go d.ProcessClusterNotifications(watchCtx, watchStream)
   300  
   301  	cli.setupConfigReloadTrap()
   302  
   303  	// The serve API routine never exits unless an error occurs
   304  	// We need to start it as a goroutine and wait on it so
   305  	// daemon doesn't exit
   306  	serveAPIWait := make(chan error)
   307  	go cli.api.Wait(serveAPIWait)
   308  
   309  	// after the daemon is done setting up we can notify systemd api
   310  	notifySystem()
   311  
   312  	// Daemon is fully initialized and handling API traffic
   313  	// Wait for serve API to complete
   314  	errAPI := <-serveAPIWait
   315  	c.Cleanup()
   316  	shutdownDaemon(d)
   317  	containerdRemote.Cleanup()
   318  	if errAPI != nil {
   319  		return fmt.Errorf("Shutting down due to ServeAPI error: %v", errAPI)
   320  	}
   321  
   322  	return nil
   323  }
   324  
   325  type routerOptions struct {
   326  	sessionManager *session.Manager
   327  	buildBackend   *buildbackend.Backend
   328  	buildCache     *fscache.FSCache
   329  	daemon         *daemon.Daemon
   330  	api            *apiserver.Server
   331  	cluster        *cluster.Cluster
   332  }
   333  
   334  func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptions, error) {
   335  	opts := routerOptions{}
   336  	sm, err := session.NewManager()
   337  	if err != nil {
   338  		return opts, errors.Wrap(err, "failed to create sessionmanager")
   339  	}
   340  
   341  	builderStateDir := filepath.Join(config.Root, "builder")
   342  
   343  	buildCache, err := fscache.NewFSCache(fscache.Opt{
   344  		Backend: fscache.NewNaiveCacheBackend(builderStateDir),
   345  		Root:    builderStateDir,
   346  		GCPolicy: fscache.GCPolicy{ // TODO: expose this in config
   347  			MaxSize:         1024 * 1024 * 512,  // 512MB
   348  			MaxKeepDuration: 7 * 24 * time.Hour, // 1 week
   349  		},
   350  	})
   351  	if err != nil {
   352  		return opts, errors.Wrap(err, "failed to create fscache")
   353  	}
   354  
   355  	manager, err := dockerfile.NewBuildManager(daemon, sm, buildCache, daemon.IDMappings())
   356  	if err != nil {
   357  		return opts, err
   358  	}
   359  
   360  	bb, err := buildbackend.NewBackend(daemon, manager, buildCache)
   361  	if err != nil {
   362  		return opts, errors.Wrap(err, "failed to create buildmanager")
   363  	}
   364  
   365  	return routerOptions{
   366  		sessionManager: sm,
   367  		buildBackend:   bb,
   368  		buildCache:     buildCache,
   369  		daemon:         daemon,
   370  	}, nil
   371  }
   372  
   373  func (cli *DaemonCli) reloadConfig() {
   374  	reload := func(config *config.Config) {
   375  
   376  		// Revalidate and reload the authorization plugins
   377  		if err := validateAuthzPlugins(config.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   378  			logrus.Fatalf("Error validating authorization plugin: %v", err)
   379  			return
   380  		}
   381  		cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins)
   382  
   383  		if err := cli.d.Reload(config); err != nil {
   384  			logrus.Errorf("Error reconfiguring the daemon: %v", err)
   385  			return
   386  		}
   387  
   388  		if config.IsValueSet("debug") {
   389  			debugEnabled := debug.IsEnabled()
   390  			switch {
   391  			case debugEnabled && !config.Debug: // disable debug
   392  				debug.Disable()
   393  			case config.Debug && !debugEnabled: // enable debug
   394  				debug.Enable()
   395  			}
   396  
   397  		}
   398  	}
   399  
   400  	if err := config.Reload(*cli.configFile, cli.flags, reload); err != nil {
   401  		logrus.Error(err)
   402  	}
   403  }
   404  
   405  func (cli *DaemonCli) stop() {
   406  	cli.api.Close()
   407  }
   408  
   409  // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
   410  // d.Shutdown() is waiting too long to kill container or worst it's
   411  // blocked there
   412  func shutdownDaemon(d *daemon.Daemon) {
   413  	shutdownTimeout := d.ShutdownTimeout()
   414  	ch := make(chan struct{})
   415  	go func() {
   416  		d.Shutdown()
   417  		close(ch)
   418  	}()
   419  	if shutdownTimeout < 0 {
   420  		<-ch
   421  		logrus.Debug("Clean shutdown succeeded")
   422  		return
   423  	}
   424  	select {
   425  	case <-ch:
   426  		logrus.Debug("Clean shutdown succeeded")
   427  	case <-time.After(time.Duration(shutdownTimeout) * time.Second):
   428  		logrus.Error("Force shutdown daemon")
   429  	}
   430  }
   431  
   432  func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
   433  	conf := opts.daemonConfig
   434  	flags := opts.flags
   435  	conf.Debug = opts.Debug
   436  	conf.Hosts = opts.Hosts
   437  	conf.LogLevel = opts.LogLevel
   438  	conf.TLS = opts.TLS
   439  	conf.TLSVerify = opts.TLSVerify
   440  	conf.CommonTLSOptions = config.CommonTLSOptions{}
   441  
   442  	if opts.TLSOptions != nil {
   443  		conf.CommonTLSOptions.CAFile = opts.TLSOptions.CAFile
   444  		conf.CommonTLSOptions.CertFile = opts.TLSOptions.CertFile
   445  		conf.CommonTLSOptions.KeyFile = opts.TLSOptions.KeyFile
   446  	}
   447  
   448  	if conf.TrustKeyPath == "" {
   449  		conf.TrustKeyPath = filepath.Join(
   450  			getDaemonConfDir(conf.Root),
   451  			defaultTrustKeyFile)
   452  	}
   453  
   454  	if flags.Changed("graph") && flags.Changed("data-root") {
   455  		return nil, fmt.Errorf(`cannot specify both "--graph" and "--data-root" option`)
   456  	}
   457  
   458  	if opts.configFile != "" {
   459  		c, err := config.MergeDaemonConfigurations(conf, flags, opts.configFile)
   460  		if err != nil {
   461  			if flags.Changed("config-file") || !os.IsNotExist(err) {
   462  				return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v", opts.configFile, err)
   463  			}
   464  		}
   465  		// the merged configuration can be nil if the config file didn't exist.
   466  		// leave the current configuration as it is if when that happens.
   467  		if c != nil {
   468  			conf = c
   469  		}
   470  	}
   471  
   472  	if err := config.Validate(conf); err != nil {
   473  		return nil, err
   474  	}
   475  
   476  	if runtime.GOOS != "windows" {
   477  		if flags.Changed("disable-legacy-registry") {
   478  			// TODO: Remove this error after 3 release cycles (18.03)
   479  			return nil, errors.New("ERROR: The '--disable-legacy-registry' flag has been removed. Interacting with legacy (v1) registries is no longer supported")
   480  		}
   481  		if !conf.V2Only {
   482  			// TODO: Remove this error after 3 release cycles (18.03)
   483  			return nil, errors.New("ERROR: The 'disable-legacy-registry' configuration option has been removed. Interacting with legacy (v1) registries is no longer supported")
   484  		}
   485  	}
   486  
   487  	if flags.Changed("graph") {
   488  		logrus.Warnf(`The "-g / --graph" flag is deprecated. Please use "--data-root" instead`)
   489  	}
   490  
   491  	// Check if duplicate label-keys with different values are found
   492  	newLabels, err := config.GetConflictFreeLabels(conf.Labels)
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  	conf.Labels = newLabels
   497  
   498  	// Regardless of whether the user sets it to true or false, if they
   499  	// specify TLSVerify at all then we need to turn on TLS
   500  	if conf.IsValueSet(FlagTLSVerify) {
   501  		conf.TLS = true
   502  	}
   503  
   504  	// ensure that the log level is the one set after merging configurations
   505  	setLogLevel(conf.LogLevel)
   506  
   507  	return conf, nil
   508  }
   509  
   510  func initRouter(opts routerOptions) {
   511  	decoder := runconfig.ContainerDecoder{}
   512  
   513  	routers := []router.Router{
   514  		// we need to add the checkpoint router before the container router or the DELETE gets masked
   515  		checkpointrouter.NewRouter(opts.daemon, decoder),
   516  		container.NewRouter(opts.daemon, decoder),
   517  		image.NewRouter(opts.daemon, decoder),
   518  		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache),
   519  		volume.NewRouter(opts.daemon),
   520  		build.NewRouter(opts.buildBackend, opts.daemon),
   521  		sessionrouter.NewRouter(opts.sessionManager),
   522  		swarmrouter.NewRouter(opts.cluster),
   523  		pluginrouter.NewRouter(opts.daemon.PluginManager()),
   524  		distributionrouter.NewRouter(opts.daemon),
   525  	}
   526  
   527  	if opts.daemon.NetworkControllerEnabled() {
   528  		routers = append(routers, network.NewRouter(opts.daemon, opts.cluster))
   529  	}
   530  
   531  	if opts.daemon.HasExperimental() {
   532  		for _, r := range routers {
   533  			for _, route := range r.Routes() {
   534  				if experimental, ok := route.(router.ExperimentalRoute); ok {
   535  					experimental.Enable()
   536  				}
   537  			}
   538  		}
   539  	}
   540  
   541  	opts.api.InitRouter(routers...)
   542  }
   543  
   544  // TODO: remove this from cli and return the authzMiddleware
   545  func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config, pluginStore plugingetter.PluginGetter) error {
   546  	v := cfg.Version
   547  
   548  	exp := middleware.NewExperimentalMiddleware(cli.Config.Experimental)
   549  	s.UseMiddleware(exp)
   550  
   551  	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
   552  	s.UseMiddleware(vm)
   553  
   554  	if cfg.CorsHeaders != "" {
   555  		c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
   556  		s.UseMiddleware(c)
   557  	}
   558  
   559  	cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, pluginStore)
   560  	cli.Config.AuthzMiddleware = cli.authzMiddleware
   561  	s.UseMiddleware(cli.authzMiddleware)
   562  	return nil
   563  }
   564  
   565  func (cli *DaemonCli) getRemoteOptions() ([]libcontainerd.RemoteOption, error) {
   566  	opts := []libcontainerd.RemoteOption{}
   567  
   568  	pOpts, err := cli.getPlatformRemoteOptions()
   569  	if err != nil {
   570  		return nil, err
   571  	}
   572  	opts = append(opts, pOpts...)
   573  	return opts, nil
   574  }
   575  
   576  // validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver
   577  // plugins present on the host and available to the daemon
   578  func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error {
   579  	for _, reqPlugin := range requestedPlugins {
   580  		if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.Lookup); err != nil {
   581  			return err
   582  		}
   583  	}
   584  	return nil
   585  }