github.com/torfuzx/docker@v1.8.1/daemon/daemon_unix.go (about) 1 // +build !windows 2 3 package daemon 4 5 import ( 6 "fmt" 7 "net" 8 "net/http" 9 "os" 10 "path/filepath" 11 "strings" 12 "syscall" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/docker/autogen/dockerversion" 16 "github.com/docker/docker/daemon/graphdriver" 17 "github.com/docker/docker/pkg/archive" 18 "github.com/docker/docker/pkg/fileutils" 19 "github.com/docker/docker/pkg/nat" 20 "github.com/docker/docker/pkg/parsers" 21 "github.com/docker/docker/pkg/parsers/kernel" 22 "github.com/docker/docker/pkg/system" 23 "github.com/docker/docker/runconfig" 24 "github.com/docker/docker/utils" 25 volumedrivers "github.com/docker/docker/volume/drivers" 26 "github.com/docker/docker/volume/local" 27 "github.com/docker/libnetwork" 28 nwapi "github.com/docker/libnetwork/api" 29 nwconfig "github.com/docker/libnetwork/config" 30 "github.com/docker/libnetwork/netlabel" 31 "github.com/docker/libnetwork/options" 32 "github.com/opencontainers/runc/libcontainer/label" 33 ) 34 35 func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) { 36 initID := fmt.Sprintf("%s-init", container.ID) 37 return daemon.driver.Changes(container.ID, initID) 38 } 39 40 func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) { 41 initID := fmt.Sprintf("%s-init", container.ID) 42 return daemon.driver.Diff(container.ID, initID) 43 } 44 45 func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { 46 var ( 47 labelOpts []string 48 err error 49 ) 50 51 for _, opt := range config.SecurityOpt { 52 con := strings.SplitN(opt, ":", 2) 53 if len(con) == 1 { 54 return fmt.Errorf("Invalid --security-opt: %q", opt) 55 } 56 switch con[0] { 57 case "label": 58 labelOpts = append(labelOpts, con[1]) 59 case "apparmor": 60 container.AppArmorProfile = con[1] 61 default: 62 return fmt.Errorf("Invalid --security-opt: %q", opt) 63 } 64 } 65 66 container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) 67 return err 68 } 69 70 func (daemon *Daemon) createRootfs(container *Container) error { 71 // Step 1: create the container directory. 72 // This doubles as a barrier to avoid race conditions. 73 if err := os.Mkdir(container.root, 0700); err != nil { 74 return err 75 } 76 initID := fmt.Sprintf("%s-init", container.ID) 77 if err := daemon.driver.Create(initID, container.ImageID); err != nil { 78 return err 79 } 80 initPath, err := daemon.driver.Get(initID, "") 81 if err != nil { 82 return err 83 } 84 85 if err := setupInitLayer(initPath); err != nil { 86 daemon.driver.Put(initID) 87 return err 88 } 89 90 // We want to unmount init layer before we take snapshot of it 91 // for the actual container. 92 daemon.driver.Put(initID) 93 94 if err := daemon.driver.Create(container.ID, initID); err != nil { 95 return err 96 } 97 return nil 98 } 99 100 func checkKernel() error { 101 // Check for unsupported kernel versions 102 // FIXME: it would be cleaner to not test for specific versions, but rather 103 // test for specific functionalities. 104 // Unfortunately we can't test for the feature "does not cause a kernel panic" 105 // without actually causing a kernel panic, so we need this workaround until 106 // the circumstances of pre-3.10 crashes are clearer. 107 // For details see https://github.com/docker/docker/issues/407 108 if k, err := kernel.GetKernelVersion(); err != nil { 109 logrus.Warnf("%s", err) 110 } else { 111 if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 { 112 if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { 113 logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String()) 114 } 115 } 116 } 117 return nil 118 } 119 120 func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig, config *runconfig.Config) ([]string, error) { 121 var warnings []string 122 123 if config != nil { 124 // The check for a valid workdir path is made on the server rather than in the 125 // client. This is because we don't know the type of path (Linux or Windows) 126 // to validate on the client. 127 if config.WorkingDir != "" && !filepath.IsAbs(config.WorkingDir) { 128 return warnings, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path.", config.WorkingDir) 129 } 130 } 131 132 if hostConfig == nil { 133 return warnings, nil 134 } 135 136 for port := range hostConfig.PortBindings { 137 _, portStr := nat.SplitProtoPort(string(port)) 138 if _, err := nat.ParsePort(portStr); err != nil { 139 return warnings, fmt.Errorf("Invalid port specification: %q", portStr) 140 } 141 for _, pb := range hostConfig.PortBindings[port] { 142 _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) 143 if err != nil { 144 return warnings, fmt.Errorf("Invalid port specification: %q", pb.HostPort) 145 } 146 } 147 } 148 if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") { 149 return warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name()) 150 } 151 if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 { 152 return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB") 153 } 154 if hostConfig.Memory > 0 && !daemon.SystemConfig().MemoryLimit { 155 warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") 156 logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") 157 hostConfig.Memory = 0 158 } 159 if hostConfig.Memory > 0 && hostConfig.MemorySwap != -1 && !daemon.SystemConfig().SwapLimit { 160 warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") 161 logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.") 162 hostConfig.MemorySwap = -1 163 } 164 if hostConfig.Memory > 0 && hostConfig.MemorySwap > 0 && hostConfig.MemorySwap < hostConfig.Memory { 165 return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.") 166 } 167 if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 { 168 return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.") 169 } 170 if hostConfig.MemorySwappiness != nil && !daemon.SystemConfig().MemorySwappiness { 171 warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") 172 logrus.Warnf("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") 173 hostConfig.MemorySwappiness = nil 174 } 175 if hostConfig.MemorySwappiness != nil { 176 swappiness := *hostConfig.MemorySwappiness 177 if swappiness < -1 || swappiness > 100 { 178 return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100.", swappiness) 179 } 180 } 181 if hostConfig.CpuPeriod > 0 && !daemon.SystemConfig().CpuCfsPeriod { 182 warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") 183 logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.") 184 hostConfig.CpuPeriod = 0 185 } 186 if hostConfig.CpuQuota > 0 && !daemon.SystemConfig().CpuCfsQuota { 187 warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") 188 logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.") 189 hostConfig.CpuQuota = 0 190 } 191 if hostConfig.BlkioWeight > 0 && (hostConfig.BlkioWeight < 10 || hostConfig.BlkioWeight > 1000) { 192 return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000.") 193 } 194 if hostConfig.OomKillDisable && !daemon.SystemConfig().OomKillDisable { 195 hostConfig.OomKillDisable = false 196 return warnings, fmt.Errorf("Your kernel does not support oom kill disable.") 197 } 198 if daemon.SystemConfig().IPv4ForwardingDisabled { 199 warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") 200 logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") 201 } 202 return warnings, nil 203 } 204 205 // checkConfigOptions checks for mutually incompatible config options 206 func checkConfigOptions(config *Config) error { 207 // Check for mutually incompatible config options 208 if config.Bridge.Iface != "" && config.Bridge.IP != "" { 209 return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.") 210 } 211 if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication { 212 return fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.") 213 } 214 if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq { 215 config.Bridge.EnableIPMasq = false 216 } 217 return nil 218 } 219 220 // checkSystem validates platform-specific requirements 221 func checkSystem() error { 222 if os.Geteuid() != 0 { 223 return fmt.Errorf("The Docker daemon needs to be run as root") 224 } 225 if err := checkKernel(); err != nil { 226 return err 227 } 228 return nil 229 } 230 231 // configureKernelSecuritySupport configures and validate security support for the kernel 232 func configureKernelSecuritySupport(config *Config, driverName string) error { 233 if config.EnableSelinuxSupport { 234 if selinuxEnabled() { 235 // As Docker on btrfs and SELinux are incompatible at present, error on both being enabled 236 if driverName == "btrfs" { 237 return fmt.Errorf("SELinux is not supported with the BTRFS graph driver") 238 } 239 logrus.Debug("SELinux enabled successfully") 240 } else { 241 logrus.Warn("Docker could not enable SELinux on the host system") 242 } 243 } else { 244 selinuxSetDisabled() 245 } 246 return nil 247 } 248 249 // MigrateIfDownlevel is a wrapper for AUFS migration for downlevel 250 func migrateIfDownlevel(driver graphdriver.Driver, root string) error { 251 return migrateIfAufs(driver, root) 252 } 253 254 func configureVolumes(config *Config) error { 255 volumesDriver, err := local.New(config.Root) 256 if err != nil { 257 return err 258 } 259 volumedrivers.Register(volumesDriver, volumesDriver.Name()) 260 return nil 261 } 262 263 func configureSysInit(config *Config) (string, error) { 264 localCopy := filepath.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION)) 265 sysInitPath := utils.DockerInitPath(localCopy) 266 if sysInitPath == "" { 267 return "", fmt.Errorf("Could not locate dockerinit: This usually means docker was built incorrectly. See https://docs.docker.com/contributing/devenvironment for official build instructions.") 268 } 269 270 if sysInitPath != localCopy { 271 // When we find a suitable dockerinit binary (even if it's our local binary), we copy it into config.Root at localCopy for future use (so that the original can go away without that being a problem, for example during a package upgrade). 272 if err := os.Mkdir(filepath.Dir(localCopy), 0700); err != nil && !os.IsExist(err) { 273 return "", err 274 } 275 if _, err := fileutils.CopyFile(sysInitPath, localCopy); err != nil { 276 return "", err 277 } 278 if err := os.Chmod(localCopy, 0700); err != nil { 279 return "", err 280 } 281 sysInitPath = localCopy 282 } 283 return sysInitPath, nil 284 } 285 286 func isBridgeNetworkDisabled(config *Config) bool { 287 return config.Bridge.Iface == disableNetworkBridge 288 } 289 290 func networkOptions(dconfig *Config) ([]nwconfig.Option, error) { 291 options := []nwconfig.Option{} 292 if dconfig == nil { 293 return options, nil 294 } 295 if strings.TrimSpace(dconfig.DefaultNetwork) != "" { 296 dn := strings.Split(dconfig.DefaultNetwork, ":") 297 if len(dn) < 2 { 298 return nil, fmt.Errorf("default network daemon config must be of the form NETWORKDRIVER:NETWORKNAME") 299 } 300 options = append(options, nwconfig.OptionDefaultDriver(dn[0])) 301 options = append(options, nwconfig.OptionDefaultNetwork(strings.Join(dn[1:], ":"))) 302 } else { 303 dd := runconfig.DefaultDaemonNetworkMode() 304 dn := runconfig.DefaultDaemonNetworkMode().NetworkName() 305 options = append(options, nwconfig.OptionDefaultDriver(string(dd))) 306 options = append(options, nwconfig.OptionDefaultNetwork(dn)) 307 } 308 309 if strings.TrimSpace(dconfig.NetworkKVStore) != "" { 310 kv := strings.Split(dconfig.NetworkKVStore, ":") 311 if len(kv) < 2 { 312 return nil, fmt.Errorf("kv store daemon config must be of the form KV-PROVIDER:KV-URL") 313 } 314 options = append(options, nwconfig.OptionKVProvider(kv[0])) 315 options = append(options, nwconfig.OptionKVProviderURL(strings.Join(kv[1:], ":"))) 316 } 317 318 options = append(options, nwconfig.OptionLabels(dconfig.Labels)) 319 return options, nil 320 } 321 322 func initNetworkController(config *Config) (libnetwork.NetworkController, error) { 323 netOptions, err := networkOptions(config) 324 if err != nil { 325 return nil, err 326 } 327 328 controller, err := libnetwork.New(netOptions...) 329 if err != nil { 330 return nil, fmt.Errorf("error obtaining controller instance: %v", err) 331 } 332 333 // Initialize default driver "null" 334 335 if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil { 336 return nil, fmt.Errorf("Error initializing null driver: %v", err) 337 } 338 339 // Initialize default network on "null" 340 if _, err := controller.NewNetwork("null", "none"); err != nil { 341 return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) 342 } 343 344 // Initialize default driver "host" 345 if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil { 346 return nil, fmt.Errorf("Error initializing host driver: %v", err) 347 } 348 349 // Initialize default network on "host" 350 if _, err := controller.NewNetwork("host", "host"); err != nil { 351 return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) 352 } 353 354 if !config.DisableBridge { 355 // Initialize default driver "bridge" 356 if err := initBridgeDriver(controller, config); err != nil { 357 return nil, err 358 } 359 } 360 361 return controller, nil 362 } 363 364 func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { 365 option := options.Generic{ 366 "EnableIPForwarding": config.Bridge.EnableIPForward} 367 368 if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil { 369 return fmt.Errorf("Error initializing bridge driver: %v", err) 370 } 371 372 netOption := options.Generic{ 373 "BridgeName": config.Bridge.Iface, 374 "Mtu": config.Mtu, 375 "EnableIPTables": config.Bridge.EnableIPTables, 376 "EnableIPMasquerade": config.Bridge.EnableIPMasq, 377 "EnableICC": config.Bridge.InterContainerCommunication, 378 "EnableUserlandProxy": config.Bridge.EnableUserlandProxy, 379 } 380 381 if config.Bridge.IP != "" { 382 ip, bipNet, err := net.ParseCIDR(config.Bridge.IP) 383 if err != nil { 384 return err 385 } 386 387 bipNet.IP = ip 388 netOption["AddressIPv4"] = bipNet 389 } 390 391 if config.Bridge.FixedCIDR != "" { 392 _, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR) 393 if err != nil { 394 return err 395 } 396 397 netOption["FixedCIDR"] = fCIDR 398 } 399 400 if config.Bridge.FixedCIDRv6 != "" { 401 _, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6) 402 if err != nil { 403 return err 404 } 405 406 netOption["FixedCIDRv6"] = fCIDRv6 407 } 408 409 if config.Bridge.DefaultGatewayIPv4 != nil { 410 netOption["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4 411 } 412 413 if config.Bridge.DefaultGatewayIPv6 != nil { 414 netOption["DefaultGatewayIPv6"] = config.Bridge.DefaultGatewayIPv6 415 } 416 417 // --ip processing 418 if config.Bridge.DefaultIP != nil { 419 netOption["DefaultBindingIP"] = config.Bridge.DefaultIP 420 } 421 422 // Initialize default network on "bridge" with the same name 423 _, err := controller.NewNetwork("bridge", "bridge", 424 libnetwork.NetworkOptionGeneric(options.Generic{ 425 netlabel.GenericData: netOption, 426 netlabel.EnableIPv6: config.Bridge.EnableIPv6, 427 })) 428 if err != nil { 429 return fmt.Errorf("Error creating default \"bridge\" network: %v", err) 430 } 431 return nil 432 } 433 434 // setupInitLayer populates a directory with mountpoints suitable 435 // for bind-mounting dockerinit into the container. The mountpoint is simply an 436 // empty file at /.dockerinit 437 // 438 // This extra layer is used by all containers as the top-most ro layer. It protects 439 // the container from unwanted side-effects on the rw layer. 440 func setupInitLayer(initLayer string) error { 441 for pth, typ := range map[string]string{ 442 "/dev/pts": "dir", 443 "/dev/shm": "dir", 444 "/proc": "dir", 445 "/sys": "dir", 446 "/.dockerinit": "file", 447 "/.dockerenv": "file", 448 "/etc/resolv.conf": "file", 449 "/etc/hosts": "file", 450 "/etc/hostname": "file", 451 "/dev/console": "file", 452 "/etc/mtab": "/proc/mounts", 453 } { 454 parts := strings.Split(pth, "/") 455 prev := "/" 456 for _, p := range parts[1:] { 457 prev = filepath.Join(prev, p) 458 syscall.Unlink(filepath.Join(initLayer, prev)) 459 } 460 461 if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil { 462 if os.IsNotExist(err) { 463 if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil { 464 return err 465 } 466 switch typ { 467 case "dir": 468 if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil { 469 return err 470 } 471 case "file": 472 f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755) 473 if err != nil { 474 return err 475 } 476 f.Close() 477 default: 478 if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil { 479 return err 480 } 481 } 482 } else { 483 return err 484 } 485 } 486 } 487 488 // Layer is ready to use, if it wasn't before. 489 return nil 490 } 491 492 func (daemon *Daemon) NetworkApiRouter() func(w http.ResponseWriter, req *http.Request) { 493 return nwapi.NewHTTPHandler(daemon.netController) 494 } 495 496 func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error { 497 if hostConfig == nil || hostConfig.Links == nil { 498 return nil 499 } 500 501 for _, l := range hostConfig.Links { 502 name, alias, err := parsers.ParseLink(l) 503 if err != nil { 504 return err 505 } 506 child, err := daemon.Get(name) 507 if err != nil { 508 //An error from daemon.Get() means this name could not be found 509 return fmt.Errorf("Could not get container for %s", name) 510 } 511 for child.hostConfig.NetworkMode.IsContainer() { 512 parts := strings.SplitN(string(child.hostConfig.NetworkMode), ":", 2) 513 child, err = daemon.Get(parts[1]) 514 if err != nil { 515 return fmt.Errorf("Could not get container for %s", parts[1]) 516 } 517 } 518 if child.hostConfig.NetworkMode.IsHost() { 519 return runconfig.ErrConflictHostNetworkAndLinks 520 } 521 if err := daemon.RegisterLink(container, child, alias); err != nil { 522 return err 523 } 524 } 525 526 // After we load all the links into the daemon 527 // set them to nil on the hostconfig 528 hostConfig.Links = nil 529 if err := container.WriteHostConfig(); err != nil { 530 return err 531 } 532 533 return nil 534 }