github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/docker/daemon.go (about) 1 // +build daemon 2 3 package main 4 5 import ( 6 "crypto/tls" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "time" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/distribution/uuid" 17 "github.com/docker/docker/api" 18 apiserver "github.com/docker/docker/api/server" 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 "github.com/docker/docker/api/server/router/container" 23 "github.com/docker/docker/api/server/router/image" 24 "github.com/docker/docker/api/server/router/network" 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/cli" 29 "github.com/docker/docker/cliconfig" 30 "github.com/docker/docker/daemon" 31 "github.com/docker/docker/daemon/logger" 32 "github.com/docker/docker/dockerversion" 33 "github.com/docker/docker/libcontainerd" 34 "github.com/docker/docker/opts" 35 "github.com/docker/docker/pkg/authorization" 36 "github.com/docker/docker/pkg/jsonlog" 37 "github.com/docker/docker/pkg/listeners" 38 flag "github.com/docker/docker/pkg/mflag" 39 "github.com/docker/docker/pkg/pidfile" 40 "github.com/docker/docker/pkg/signal" 41 "github.com/docker/docker/pkg/system" 42 "github.com/docker/docker/pkg/version" 43 "github.com/docker/docker/registry" 44 "github.com/docker/docker/runconfig" 45 "github.com/docker/docker/utils" 46 "github.com/docker/go-connections/tlsconfig" 47 ) 48 49 const ( 50 daemonUsage = " docker daemon [ --help | ... ]\n" 51 daemonConfigFileFlag = "-config-file" 52 ) 53 54 var ( 55 daemonCli cli.Handler = NewDaemonCli() 56 ) 57 58 // DaemonCli represents the daemon CLI. 59 type DaemonCli struct { 60 *daemon.Config 61 flags *flag.FlagSet 62 } 63 64 func presentInHelp(usage string) string { return usage } 65 func absentFromHelp(string) string { return "" } 66 67 // NewDaemonCli returns a pre-configured daemon CLI 68 func NewDaemonCli() *DaemonCli { 69 daemonFlags := cli.Subcmd("daemon", nil, "Enable daemon mode", true) 70 71 // TODO(tiborvass): remove InstallFlags? 72 daemonConfig := new(daemon.Config) 73 daemonConfig.LogConfig.Config = make(map[string]string) 74 daemonConfig.ClusterOpts = make(map[string]string) 75 76 if runtime.GOOS != "linux" { 77 daemonConfig.V2Only = true 78 } 79 80 daemonConfig.InstallFlags(daemonFlags, presentInHelp) 81 daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) 82 daemonFlags.Require(flag.Exact, 0) 83 84 return &DaemonCli{ 85 Config: daemonConfig, 86 flags: daemonFlags, 87 } 88 } 89 90 func migrateKey() (err error) { 91 // Migrate trust key if exists at ~/.docker/key.json and owned by current user 92 oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) 93 newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 94 if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { 95 defer func() { 96 // Ensure old path is removed if no error occurred 97 if err == nil { 98 err = os.Remove(oldPath) 99 } else { 100 logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) 101 os.Remove(newPath) 102 } 103 }() 104 105 if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { 106 return fmt.Errorf("Unable to create daemon configuration directory: %s", err) 107 } 108 109 newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 110 if err != nil { 111 return fmt.Errorf("error creating key file %q: %s", newPath, err) 112 } 113 defer newFile.Close() 114 115 oldFile, err := os.Open(oldPath) 116 if err != nil { 117 return fmt.Errorf("error opening key file %q: %s", oldPath, err) 118 } 119 defer oldFile.Close() 120 121 if _, err := io.Copy(newFile, oldFile); err != nil { 122 return fmt.Errorf("error copying key: %s", err) 123 } 124 125 logrus.Infof("Migrated key from %s to %s", oldPath, newPath) 126 } 127 128 return nil 129 } 130 131 func getGlobalFlag() (globalFlag *flag.Flag) { 132 defer func() { 133 if x := recover(); x != nil { 134 switch f := x.(type) { 135 case *flag.Flag: 136 globalFlag = f 137 default: 138 panic(x) 139 } 140 } 141 }() 142 visitor := func(f *flag.Flag) { panic(f) } 143 commonFlags.FlagSet.Visit(visitor) 144 clientFlags.FlagSet.Visit(visitor) 145 return 146 } 147 148 // CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. 149 func (cli *DaemonCli) CmdDaemon(args ...string) error { 150 // warn from uuid package when running the daemon 151 uuid.Loggerf = logrus.Warnf 152 153 if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { 154 // deny `docker -D daemon` 155 illegalFlag := getGlobalFlag() 156 fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) 157 os.Exit(1) 158 } else { 159 // allow new form `docker daemon -D` 160 flag.Merge(cli.flags, commonFlags.FlagSet) 161 } 162 163 configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") 164 165 cli.flags.ParseFlags(args, true) 166 commonFlags.PostParse() 167 168 if commonFlags.TrustKey == "" { 169 commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 170 } 171 cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) 172 if err != nil { 173 fmt.Fprint(os.Stderr, err) 174 os.Exit(1) 175 } 176 cli.Config = cliConfig 177 178 if cli.Config.Debug { 179 utils.EnableDebug() 180 } 181 182 if utils.ExperimentalBuild() { 183 logrus.Warn("Running experimental build") 184 } 185 186 logrus.SetFormatter(&logrus.TextFormatter{ 187 TimestampFormat: jsonlog.RFC3339NanoFixed, 188 DisableColors: cli.Config.RawLogs, 189 }) 190 191 if err := setDefaultUmask(); err != nil { 192 logrus.Fatalf("Failed to set umask: %v", err) 193 } 194 195 if len(cli.LogConfig.Config) > 0 { 196 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 197 logrus.Fatalf("Failed to set log opts: %v", err) 198 } 199 } 200 201 var pfile *pidfile.PIDFile 202 if cli.Pidfile != "" { 203 pf, err := pidfile.New(cli.Pidfile) 204 if err != nil { 205 logrus.Fatalf("Error starting daemon: %v", err) 206 } 207 pfile = pf 208 defer func() { 209 if err := pfile.Remove(); err != nil { 210 logrus.Error(err) 211 } 212 }() 213 } 214 215 serverConfig := &apiserver.Config{ 216 Logging: true, 217 SocketGroup: cli.Config.SocketGroup, 218 Version: dockerversion.Version, 219 } 220 serverConfig = setPlatformServerConfig(serverConfig, cli.Config) 221 222 if cli.Config.TLS { 223 tlsOptions := tlsconfig.Options{ 224 CAFile: cli.Config.CommonTLSOptions.CAFile, 225 CertFile: cli.Config.CommonTLSOptions.CertFile, 226 KeyFile: cli.Config.CommonTLSOptions.KeyFile, 227 } 228 229 if cli.Config.TLSVerify { 230 // server requires and verifies client's certificate 231 tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert 232 } 233 tlsConfig, err := tlsconfig.Server(tlsOptions) 234 if err != nil { 235 logrus.Fatal(err) 236 } 237 serverConfig.TLSConfig = tlsConfig 238 } 239 240 if len(cli.Config.Hosts) == 0 { 241 cli.Config.Hosts = make([]string, 1) 242 } 243 244 api := apiserver.New(serverConfig) 245 246 for i := 0; i < len(cli.Config.Hosts); i++ { 247 var err error 248 if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { 249 logrus.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) 250 } 251 252 protoAddr := cli.Config.Hosts[i] 253 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 254 if len(protoAddrParts) != 2 { 255 logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) 256 } 257 258 proto := protoAddrParts[0] 259 addr := protoAddrParts[1] 260 261 // It's a bad idea to bind to TCP without tlsverify. 262 if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { 263 logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") 264 } 265 l, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) 266 if err != nil { 267 logrus.Fatal(err) 268 } 269 // If we're binding to a TCP port, make sure that a container doesn't try to use it. 270 if proto == "tcp" { 271 if err := allocateDaemonPort(addr); err != nil { 272 logrus.Fatal(err) 273 } 274 } 275 logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) 276 api.Accept(protoAddrParts[1], l...) 277 } 278 279 if err := migrateKey(); err != nil { 280 logrus.Fatal(err) 281 } 282 cli.TrustKeyPath = commonFlags.TrustKey 283 284 registryService := registry.NewService(cli.Config.ServiceOptions) 285 containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) 286 if err != nil { 287 logrus.Fatal(err) 288 } 289 290 d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) 291 if err != nil { 292 if pfile != nil { 293 if err := pfile.Remove(); err != nil { 294 logrus.Error(err) 295 } 296 } 297 logrus.Fatalf("Error starting daemon: %v", err) 298 } 299 300 logrus.Info("Daemon has completed initialization") 301 302 logrus.WithFields(logrus.Fields{ 303 "version": dockerversion.Version, 304 "commit": dockerversion.GitCommit, 305 "graphdriver": d.GraphDriverName(), 306 }).Info("Docker daemon") 307 308 cli.initMiddlewares(api, serverConfig) 309 initRouter(api, d) 310 311 reload := func(config *daemon.Config) { 312 if err := d.Reload(config); err != nil { 313 logrus.Errorf("Error reconfiguring the daemon: %v", err) 314 return 315 } 316 if config.IsValueSet("debug") { 317 debugEnabled := utils.IsDebugEnabled() 318 switch { 319 case debugEnabled && !config.Debug: // disable debug 320 utils.DisableDebug() 321 api.DisableProfiler() 322 case config.Debug && !debugEnabled: // enable debug 323 utils.EnableDebug() 324 api.EnableProfiler() 325 } 326 327 } 328 } 329 330 setupConfigReloadTrap(*configFile, cli.flags, reload) 331 332 // The serve API routine never exits unless an error occurs 333 // We need to start it as a goroutine and wait on it so 334 // daemon doesn't exit 335 serveAPIWait := make(chan error) 336 go api.Wait(serveAPIWait) 337 338 signal.Trap(func() { 339 api.Close() 340 <-serveAPIWait 341 shutdownDaemon(d, 15) 342 if pfile != nil { 343 if err := pfile.Remove(); err != nil { 344 logrus.Error(err) 345 } 346 } 347 }) 348 349 // after the daemon is done setting up we can notify systemd api 350 notifySystem() 351 352 // Daemon is fully initialized and handling API traffic 353 // Wait for serve API to complete 354 errAPI := <-serveAPIWait 355 shutdownDaemon(d, 15) 356 containerdRemote.Cleanup() 357 if errAPI != nil { 358 if pfile != nil { 359 if err := pfile.Remove(); err != nil { 360 logrus.Error(err) 361 } 362 } 363 logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) 364 } 365 return nil 366 } 367 368 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 369 // d.Shutdown() is waiting too long to kill container or worst it's 370 // blocked there 371 func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { 372 ch := make(chan struct{}) 373 go func() { 374 d.Shutdown() 375 close(ch) 376 }() 377 select { 378 case <-ch: 379 logrus.Debug("Clean shutdown succeeded") 380 case <-time.After(timeout * time.Second): 381 logrus.Error("Force shutdown daemon") 382 } 383 } 384 385 func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commonConfig *cli.CommonFlags, configFile string) (*daemon.Config, error) { 386 config.Debug = commonConfig.Debug 387 config.Hosts = commonConfig.Hosts 388 config.LogLevel = commonConfig.LogLevel 389 config.TLS = commonConfig.TLS 390 config.TLSVerify = commonConfig.TLSVerify 391 config.CommonTLSOptions = daemon.CommonTLSOptions{} 392 393 if commonConfig.TLSOptions != nil { 394 config.CommonTLSOptions.CAFile = commonConfig.TLSOptions.CAFile 395 config.CommonTLSOptions.CertFile = commonConfig.TLSOptions.CertFile 396 config.CommonTLSOptions.KeyFile = commonConfig.TLSOptions.KeyFile 397 } 398 399 if configFile != "" { 400 c, err := daemon.MergeDaemonConfigurations(config, daemonFlags, configFile) 401 if err != nil { 402 if daemonFlags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) { 403 return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err) 404 } 405 } 406 // the merged configuration can be nil if the config file didn't exist. 407 // leave the current configuration as it is if when that happens. 408 if c != nil { 409 config = c 410 } 411 } 412 413 // Regardless of whether the user sets it to true or false, if they 414 // specify TLSVerify at all then we need to turn on TLS 415 if config.IsValueSet(tlsVerifyKey) { 416 config.TLS = true 417 } 418 419 // ensure that the log level is the one set after merging configurations 420 setDaemonLogLevel(config.LogLevel) 421 422 return config, nil 423 } 424 425 func initRouter(s *apiserver.Server, d *daemon.Daemon) { 426 decoder := runconfig.ContainerDecoder{} 427 428 routers := []router.Router{ 429 container.NewRouter(d, decoder), 430 image.NewRouter(d, decoder), 431 systemrouter.NewRouter(d), 432 volume.NewRouter(d), 433 build.NewRouter(dockerfile.NewBuildManager(d)), 434 } 435 if d.NetworkControllerEnabled() { 436 routers = append(routers, network.NewRouter(d)) 437 } 438 439 s.InitRouter(utils.IsDebugEnabled(), routers...) 440 } 441 442 func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) { 443 v := version.Version(cfg.Version) 444 445 vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion) 446 s.UseMiddleware(vm) 447 448 if cfg.EnableCors { 449 c := middleware.NewCORSMiddleware(cfg.CorsHeaders) 450 s.UseMiddleware(c) 451 } 452 453 u := middleware.NewUserAgentMiddleware(v) 454 s.UseMiddleware(u) 455 456 if len(cli.Config.AuthorizationPlugins) > 0 { 457 authZPlugins := authorization.NewPlugins(cli.Config.AuthorizationPlugins) 458 handleAuthorization := authorization.NewMiddleware(authZPlugins) 459 s.UseMiddleware(handleAuthorization) 460 } 461 }