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