daemon.go 17 KB

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