github.com/olljanat/moby@v1.13.1/cmd/dockerd/daemon.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/docker/distribution/uuid"
    15  	"github.com/docker/docker/api"
    16  	apiserver "github.com/docker/docker/api/server"
    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  	"github.com/docker/docker/api/server/router/image"
    23  	"github.com/docker/docker/api/server/router/network"
    24  	pluginrouter "github.com/docker/docker/api/server/router/plugin"
    25  	swarmrouter "github.com/docker/docker/api/server/router/swarm"
    26  	systemrouter "github.com/docker/docker/api/server/router/system"
    27  	"github.com/docker/docker/api/server/router/volume"
    28  	"github.com/docker/docker/builder/dockerfile"
    29  	cliflags "github.com/docker/docker/cli/flags"
    30  	"github.com/docker/docker/cliconfig"
    31  	"github.com/docker/docker/daemon"
    32  	"github.com/docker/docker/daemon/cluster"
    33  	"github.com/docker/docker/daemon/logger"
    34  	"github.com/docker/docker/dockerversion"
    35  	"github.com/docker/docker/libcontainerd"
    36  	dopts "github.com/docker/docker/opts"
    37  	"github.com/docker/docker/pkg/authorization"
    38  	"github.com/docker/docker/pkg/jsonlog"
    39  	"github.com/docker/docker/pkg/listeners"
    40  	"github.com/docker/docker/pkg/pidfile"
    41  	"github.com/docker/docker/pkg/plugingetter"
    42  	"github.com/docker/docker/pkg/signal"
    43  	"github.com/docker/docker/pkg/system"
    44  	"github.com/docker/docker/registry"
    45  	"github.com/docker/docker/runconfig"
    46  	"github.com/docker/docker/utils"
    47  	"github.com/docker/go-connections/tlsconfig"
    48  	"github.com/spf13/pflag"
    49  )
    50  
    51  const (
    52  	flagDaemonConfigFile = "config-file"
    53  )
    54  
    55  // DaemonCli represents the daemon CLI.
    56  type DaemonCli struct {
    57  	*daemon.Config
    58  	configFile *string
    59  	flags      *pflag.FlagSet
    60  
    61  	api             *apiserver.Server
    62  	d               *daemon.Daemon
    63  	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
    64  }
    65  
    66  // NewDaemonCli returns a daemon CLI
    67  func NewDaemonCli() *DaemonCli {
    68  	return &DaemonCli{}
    69  }
    70  
    71  func migrateKey(config *daemon.Config) (err error) {
    72  	// No migration necessary on Windows
    73  	if runtime.GOOS == "windows" {
    74  		return nil
    75  	}
    76  
    77  	// Migrate trust key if exists at ~/.docker/key.json and owned by current user
    78  	oldPath := filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
    79  	newPath := filepath.Join(getDaemonConfDir(config.Root), cliflags.DefaultTrustKeyFile)
    80  	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
    81  		defer func() {
    82  			// Ensure old path is removed if no error occurred
    83  			if err == nil {
    84  				err = os.Remove(oldPath)
    85  			} else {
    86  				logrus.Warnf("Key migration failed, key file not removed at %s", oldPath)
    87  				os.Remove(newPath)
    88  			}
    89  		}()
    90  
    91  		if err := system.MkdirAll(getDaemonConfDir(config.Root), os.FileMode(0644)); err != nil {
    92  			return fmt.Errorf("Unable to create daemon configuration directory: %s", err)
    93  		}
    94  
    95  		newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
    96  		if err != nil {
    97  			return fmt.Errorf("error creating key file %q: %s", newPath, err)
    98  		}
    99  		defer newFile.Close()
   100  
   101  		oldFile, err := os.Open(oldPath)
   102  		if err != nil {
   103  			return fmt.Errorf("error opening key file %q: %s", oldPath, err)
   104  		}
   105  		defer oldFile.Close()
   106  
   107  		if _, err := io.Copy(newFile, oldFile); err != nil {
   108  			return fmt.Errorf("error copying key: %s", err)
   109  		}
   110  
   111  		logrus.Infof("Migrated key from %s to %s", oldPath, newPath)
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  func (cli *DaemonCli) start(opts daemonOptions) (err error) {
   118  	stopc := make(chan bool)
   119  	defer close(stopc)
   120  
   121  	// warn from uuid package when running the daemon
   122  	uuid.Loggerf = logrus.Warnf
   123  
   124  	opts.common.SetDefaultOptions(opts.flags)
   125  
   126  	if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
   127  		return err
   128  	}
   129  	cli.configFile = &opts.configFile
   130  	cli.flags = opts.flags
   131  
   132  	if opts.common.TrustKey == "" {
   133  		opts.common.TrustKey = filepath.Join(
   134  			getDaemonConfDir(cli.Config.Root),
   135  			cliflags.DefaultTrustKeyFile)
   136  	}
   137  
   138  	if cli.Config.Debug {
   139  		utils.EnableDebug()
   140  	}
   141  
   142  	if cli.Config.Experimental {
   143  		logrus.Warn("Running experimental build")
   144  	}
   145  
   146  	logrus.SetFormatter(&logrus.TextFormatter{
   147  		TimestampFormat: jsonlog.RFC3339NanoFixed,
   148  		DisableColors:   cli.Config.RawLogs,
   149  	})
   150  
   151  	if err := setDefaultUmask(); err != nil {
   152  		return fmt.Errorf("Failed to set umask: %v", err)
   153  	}
   154  
   155  	if len(cli.LogConfig.Config) > 0 {
   156  		if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
   157  			return fmt.Errorf("Failed to set log opts: %v", err)
   158  		}
   159  	}
   160  
   161  	// Create the daemon root before we create ANY other files (PID, or migrate keys)
   162  	// to ensure the appropriate ACL is set (particularly relevant on Windows)
   163  	if err := daemon.CreateDaemonRoot(cli.Config); err != nil {
   164  		return err
   165  	}
   166  
   167  	if cli.Pidfile != "" {
   168  		pf, err := pidfile.New(cli.Pidfile)
   169  		if err != nil {
   170  			return fmt.Errorf("Error starting daemon: %v", err)
   171  		}
   172  		defer func() {
   173  			if err := pf.Remove(); err != nil {
   174  				logrus.Error(err)
   175  			}
   176  		}()
   177  	}
   178  
   179  	serverConfig := &apiserver.Config{
   180  		Logging:     true,
   181  		SocketGroup: cli.Config.SocketGroup,
   182  		Version:     dockerversion.Version,
   183  		EnableCors:  cli.Config.EnableCors,
   184  		CorsHeaders: cli.Config.CorsHeaders,
   185  	}
   186  
   187  	if cli.Config.TLS {
   188  		tlsOptions := tlsconfig.Options{
   189  			CAFile:   cli.Config.CommonTLSOptions.CAFile,
   190  			CertFile: cli.Config.CommonTLSOptions.CertFile,
   191  			KeyFile:  cli.Config.CommonTLSOptions.KeyFile,
   192  		}
   193  
   194  		if cli.Config.TLSVerify {
   195  			// server requires and verifies client's certificate
   196  			tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert
   197  		}
   198  		tlsConfig, err := tlsconfig.Server(tlsOptions)
   199  		if err != nil {
   200  			return err
   201  		}
   202  		serverConfig.TLSConfig = tlsConfig
   203  	}
   204  
   205  	if len(cli.Config.Hosts) == 0 {
   206  		cli.Config.Hosts = make([]string, 1)
   207  	}
   208  
   209  	api := apiserver.New(serverConfig)
   210  	cli.api = api
   211  
   212  	for i := 0; i < len(cli.Config.Hosts); i++ {
   213  		var err error
   214  		if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
   215  			return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err)
   216  		}
   217  
   218  		protoAddr := cli.Config.Hosts[i]
   219  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
   220  		if len(protoAddrParts) != 2 {
   221  			return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
   222  		}
   223  
   224  		proto := protoAddrParts[0]
   225  		addr := protoAddrParts[1]
   226  
   227  		// It's a bad idea to bind to TCP without tlsverify.
   228  		if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) {
   229  			logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]")
   230  		}
   231  		ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
   232  		if err != nil {
   233  			return err
   234  		}
   235  		ls = wrapListeners(proto, ls)
   236  		// If we're binding to a TCP port, make sure that a container doesn't try to use it.
   237  		if proto == "tcp" {
   238  			if err := allocateDaemonPort(addr); err != nil {
   239  				return err
   240  			}
   241  		}
   242  		logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr)
   243  		api.Accept(addr, ls...)
   244  	}
   245  
   246  	if err := migrateKey(cli.Config); err != nil {
   247  		return err
   248  	}
   249  
   250  	// FIXME: why is this down here instead of with the other TrustKey logic above?
   251  	cli.TrustKeyPath = opts.common.TrustKey
   252  
   253  	registryService := registry.NewService(cli.Config.ServiceOptions)
   254  	containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
   255  	if err != nil {
   256  		return err
   257  	}
   258  	signal.Trap(func() {
   259  		cli.stop()
   260  		<-stopc // wait for daemonCli.start() to return
   261  	})
   262  
   263  	d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote)
   264  	if err != nil {
   265  		return fmt.Errorf("Error starting daemon: %v", err)
   266  	}
   267  
   268  	if cli.Config.MetricsAddress != "" {
   269  		if !d.HasExperimental() {
   270  			return fmt.Errorf("metrics-addr is only supported when experimental is enabled")
   271  		}
   272  		if err := startMetricsServer(cli.Config.MetricsAddress); err != nil {
   273  			return err
   274  		}
   275  	}
   276  
   277  	name, _ := os.Hostname()
   278  
   279  	c, err := cluster.New(cluster.Config{
   280  		Root:                   cli.Config.Root,
   281  		Name:                   name,
   282  		Backend:                d,
   283  		NetworkSubnetsProvider: d,
   284  		DefaultAdvertiseAddr:   cli.Config.SwarmDefaultAdvertiseAddr,
   285  		RuntimeRoot:            cli.getSwarmRunRoot(),
   286  	})
   287  	if err != nil {
   288  		logrus.Fatalf("Error creating cluster component: %v", err)
   289  	}
   290  
   291  	// Restart all autostart containers which has a swarm endpoint
   292  	// and is not yet running now that we have successfully
   293  	// initialized the cluster.
   294  	d.RestartSwarmContainers()
   295  
   296  	logrus.Info("Daemon has completed initialization")
   297  
   298  	logrus.WithFields(logrus.Fields{
   299  		"version":     dockerversion.Version,
   300  		"commit":      dockerversion.GitCommit,
   301  		"graphdriver": d.GraphDriverName(),
   302  	}).Info("Docker daemon")
   303  
   304  	cli.d = d
   305  
   306  	// initMiddlewares needs cli.d to be populated. Dont change this init order.
   307  	if err := cli.initMiddlewares(api, serverConfig); err != nil {
   308  		logrus.Fatalf("Error creating middlewares: %v", err)
   309  	}
   310  	d.SetCluster(c)
   311  	initRouter(api, d, c)
   312  
   313  	cli.setupConfigReloadTrap()
   314  
   315  	// The serve API routine never exits unless an error occurs
   316  	// We need to start it as a goroutine and wait on it so
   317  	// daemon doesn't exit
   318  	serveAPIWait := make(chan error)
   319  	go api.Wait(serveAPIWait)
   320  
   321  	// after the daemon is done setting up we can notify systemd api
   322  	notifySystem()
   323  
   324  	// Daemon is fully initialized and handling API traffic
   325  	// Wait for serve API to complete
   326  	errAPI := <-serveAPIWait
   327  	c.Cleanup()
   328  	shutdownDaemon(d)
   329  	containerdRemote.Cleanup()
   330  	if errAPI != nil {
   331  		return fmt.Errorf("Shutting down due to ServeAPI error: %v", errAPI)
   332  	}
   333  
   334  	return nil
   335  }
   336  
   337  func (cli *DaemonCli) reloadConfig() {
   338  	reload := func(config *daemon.Config) {
   339  
   340  		// Revalidate and reload the authorization plugins
   341  		if err := validateAuthzPlugins(config.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   342  			logrus.Fatalf("Error validating authorization plugin: %v", err)
   343  			return
   344  		}
   345  		cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins)
   346  
   347  		if err := cli.d.Reload(config); err != nil {
   348  			logrus.Errorf("Error reconfiguring the daemon: %v", err)
   349  			return
   350  		}
   351  
   352  		if config.IsValueSet("debug") {
   353  			debugEnabled := utils.IsDebugEnabled()
   354  			switch {
   355  			case debugEnabled && !config.Debug: // disable debug
   356  				utils.DisableDebug()
   357  				cli.api.DisableProfiler()
   358  			case config.Debug && !debugEnabled: // enable debug
   359  				utils.EnableDebug()
   360  				cli.api.EnableProfiler()
   361  			}
   362  
   363  		}
   364  	}
   365  
   366  	if err := daemon.ReloadConfiguration(*cli.configFile, cli.flags, reload); err != nil {
   367  		logrus.Error(err)
   368  	}
   369  }
   370  
   371  func (cli *DaemonCli) stop() {
   372  	cli.api.Close()
   373  }
   374  
   375  // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
   376  // d.Shutdown() is waiting too long to kill container or worst it's
   377  // blocked there
   378  func shutdownDaemon(d *daemon.Daemon) {
   379  	shutdownTimeout := d.ShutdownTimeout()
   380  	ch := make(chan struct{})
   381  	go func() {
   382  		d.Shutdown()
   383  		close(ch)
   384  	}()
   385  	if shutdownTimeout < 0 {
   386  		<-ch
   387  		logrus.Debug("Clean shutdown succeeded")
   388  		return
   389  	}
   390  	select {
   391  	case <-ch:
   392  		logrus.Debug("Clean shutdown succeeded")
   393  	case <-time.After(time.Duration(shutdownTimeout) * time.Second):
   394  		logrus.Error("Force shutdown daemon")
   395  	}
   396  }
   397  
   398  func loadDaemonCliConfig(opts daemonOptions) (*daemon.Config, error) {
   399  	config := opts.daemonConfig
   400  	flags := opts.flags
   401  	config.Debug = opts.common.Debug
   402  	config.Hosts = opts.common.Hosts
   403  	config.LogLevel = opts.common.LogLevel
   404  	config.TLS = opts.common.TLS
   405  	config.TLSVerify = opts.common.TLSVerify
   406  	config.CommonTLSOptions = daemon.CommonTLSOptions{}
   407  
   408  	if opts.common.TLSOptions != nil {
   409  		config.CommonTLSOptions.CAFile = opts.common.TLSOptions.CAFile
   410  		config.CommonTLSOptions.CertFile = opts.common.TLSOptions.CertFile
   411  		config.CommonTLSOptions.KeyFile = opts.common.TLSOptions.KeyFile
   412  	}
   413  
   414  	if opts.configFile != "" {
   415  		c, err := daemon.MergeDaemonConfigurations(config, flags, opts.configFile)
   416  		if err != nil {
   417  			if flags.Changed(flagDaemonConfigFile) || !os.IsNotExist(err) {
   418  				return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", opts.configFile, err)
   419  			}
   420  		}
   421  		// the merged configuration can be nil if the config file didn't exist.
   422  		// leave the current configuration as it is if when that happens.
   423  		if c != nil {
   424  			config = c
   425  		}
   426  	}
   427  
   428  	if err := daemon.ValidateConfiguration(config); err != nil {
   429  		return nil, err
   430  	}
   431  
   432  	// Labels of the docker engine used to allow multiple values associated with the same key.
   433  	// This is deprecated in 1.13, and, be removed after 3 release cycles.
   434  	// The following will check the conflict of labels, and report a warning for deprecation.
   435  	//
   436  	// TODO: After 3 release cycles (1.16) an error will be returned, and labels will be
   437  	// sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels):
   438  	//
   439  	// newLabels, err := daemon.GetConflictFreeLabels(config.Labels)
   440  	// if err != nil {
   441  	//	return nil, err
   442  	// }
   443  	// config.Labels = newLabels
   444  	//
   445  	if _, err := daemon.GetConflictFreeLabels(config.Labels); err != nil {
   446  		logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err)
   447  	}
   448  
   449  	// Regardless of whether the user sets it to true or false, if they
   450  	// specify TLSVerify at all then we need to turn on TLS
   451  	if config.IsValueSet(cliflags.FlagTLSVerify) {
   452  		config.TLS = true
   453  	}
   454  
   455  	// ensure that the log level is the one set after merging configurations
   456  	cliflags.SetLogLevel(config.LogLevel)
   457  
   458  	return config, nil
   459  }
   460  
   461  func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
   462  	decoder := runconfig.ContainerDecoder{}
   463  
   464  	routers := []router.Router{
   465  		// we need to add the checkpoint router before the container router or the DELETE gets masked
   466  		checkpointrouter.NewRouter(d, decoder),
   467  		container.NewRouter(d, decoder),
   468  		image.NewRouter(d, decoder),
   469  		systemrouter.NewRouter(d, c),
   470  		volume.NewRouter(d),
   471  		build.NewRouter(dockerfile.NewBuildManager(d)),
   472  		swarmrouter.NewRouter(c),
   473  		pluginrouter.NewRouter(d.PluginManager()),
   474  	}
   475  
   476  	if d.NetworkControllerEnabled() {
   477  		routers = append(routers, network.NewRouter(d, c))
   478  	}
   479  
   480  	if d.HasExperimental() {
   481  		for _, r := range routers {
   482  			for _, route := range r.Routes() {
   483  				if experimental, ok := route.(router.ExperimentalRoute); ok {
   484  					experimental.Enable()
   485  				}
   486  			}
   487  		}
   488  	}
   489  
   490  	s.InitRouter(utils.IsDebugEnabled(), routers...)
   491  }
   492  
   493  func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) error {
   494  	v := cfg.Version
   495  
   496  	exp := middleware.NewExperimentalMiddleware(cli.d.HasExperimental())
   497  	s.UseMiddleware(exp)
   498  
   499  	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
   500  	s.UseMiddleware(vm)
   501  
   502  	if cfg.EnableCors {
   503  		c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
   504  		s.UseMiddleware(c)
   505  	}
   506  
   507  	if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   508  		return fmt.Errorf("Error validating authorization plugin: %v", err)
   509  	}
   510  	cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, cli.d.PluginStore)
   511  	s.UseMiddleware(cli.authzMiddleware)
   512  	return nil
   513  }
   514  
   515  // validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver
   516  // plugins present on the host and available to the daemon
   517  func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error {
   518  	for _, reqPlugin := range requestedPlugins {
   519  		if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.LOOKUP); err != nil {
   520  			return err
   521  		}
   522  	}
   523  	return nil
   524  }