2015-08-07 12:33:29 -04:00
// +build linux freebsd
2015-05-15 19:34:26 -04:00
package daemon
import (
"fmt"
2015-12-02 05:26:30 -05:00
"io/ioutil"
2015-05-15 19:34:26 -04:00
"net"
"os"
"path/filepath"
2016-01-07 22:43:11 -05:00
"runtime"
2015-12-02 05:26:30 -05:00
"runtime/debug"
2015-10-10 12:43:03 -04:00
"strconv"
2015-05-15 19:34:26 -04:00
"strings"
2015-06-16 14:06:53 -04:00
"syscall"
2015-05-15 19:34:26 -04:00
"github.com/Sirupsen/logrus"
2015-11-12 14:55:17 -05:00
"github.com/docker/docker/container"
2015-09-08 14:40:55 -04:00
derr "github.com/docker/docker/errors"
2015-11-18 17:20:54 -05:00
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
2015-10-08 11:51:41 -04:00
"github.com/docker/docker/pkg/idtools"
2016-01-22 21:15:09 -05:00
"github.com/docker/docker/pkg/parsers"
2015-05-15 19:34:26 -04:00
"github.com/docker/docker/pkg/parsers/kernel"
2015-08-06 07:54:48 -04:00
"github.com/docker/docker/pkg/sysinfo"
2015-12-04 16:55:15 -05:00
"github.com/docker/docker/reference"
2015-05-15 19:34:26 -04:00
"github.com/docker/docker/runconfig"
2015-12-21 20:05:55 -05:00
runconfigopts "github.com/docker/docker/runconfig/opts"
2016-01-04 19:05:26 -05:00
pblkiodev "github.com/docker/engine-api/types/blkiodev"
containertypes "github.com/docker/engine-api/types/container"
2015-05-15 19:34:26 -04:00
"github.com/docker/libnetwork"
2015-05-20 08:20:19 -04:00
nwconfig "github.com/docker/libnetwork/config"
2015-10-10 12:43:03 -04:00
"github.com/docker/libnetwork/drivers/bridge"
"github.com/docker/libnetwork/ipamutils"
2015-05-15 19:34:26 -04:00
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
2015-10-10 12:43:03 -04:00
"github.com/docker/libnetwork/types"
2015-06-11 20:34:20 -04:00
blkiodev "github.com/opencontainers/runc/libcontainer/configs"
2015-07-16 19:00:55 -04:00
"github.com/opencontainers/runc/libcontainer/label"
2016-01-07 22:43:11 -05:00
"github.com/opencontainers/runc/libcontainer/user"
2015-05-15 19:34:26 -04:00
)
2015-08-05 20:15:14 -04:00
const (
// See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
linuxMinCPUShares = 2
linuxMaxCPUShares = 262144
2015-08-07 12:33:29 -04:00
platformSupported = true
2015-12-09 01:26:41 -05:00
// It's not kernel limit, we want this 4M limit to supply a reasonable functional container
linuxMinMemory = 4194304
2016-01-07 22:43:11 -05:00
// constants for remapped root settings
defaultIDSpecifier string = "default"
defaultRemappedID string = "dockremap"
2015-08-05 20:15:14 -04:00
)
2015-12-18 13:36:17 -05:00
func getBlkioWeightDevices ( config * containertypes . HostConfig ) ( [ ] * blkiodev . WeightDevice , error ) {
2015-06-11 20:34:20 -04:00
var stat syscall . Stat_t
2015-12-14 20:50:16 -05:00
var blkioWeightDevices [ ] * blkiodev . WeightDevice
2015-06-11 20:34:20 -04:00
for _ , weightDevice := range config . BlkioWeightDevice {
if err := syscall . Stat ( weightDevice . Path , & stat ) ; err != nil {
return nil , err
}
2015-12-14 20:50:16 -05:00
weightDevice := blkiodev . NewWeightDevice ( int64 ( stat . Rdev / 256 ) , int64 ( stat . Rdev % 256 ) , weightDevice . Weight , 0 )
blkioWeightDevices = append ( blkioWeightDevices , weightDevice )
2015-06-11 20:34:20 -04:00
}
2015-12-14 20:50:16 -05:00
return blkioWeightDevices , nil
2015-06-11 20:34:20 -04:00
}
2015-12-18 13:36:17 -05:00
func parseSecurityOpt ( container * container . Container , config * containertypes . HostConfig ) error {
2015-05-15 19:34:26 -04:00
var (
labelOpts [ ] string
err error
)
for _ , opt := range config . SecurityOpt {
con := strings . SplitN ( opt , ":" , 2 )
if len ( con ) == 1 {
return fmt . Errorf ( "Invalid --security-opt: %q" , opt )
}
switch con [ 0 ] {
case "label" :
labelOpts = append ( labelOpts , con [ 1 ] )
case "apparmor" :
container . AppArmorProfile = con [ 1 ]
2015-11-14 21:02:26 -05:00
case "seccomp" :
container . SeccompProfile = con [ 1 ]
2015-05-15 19:34:26 -04:00
default :
return fmt . Errorf ( "Invalid --security-opt: %q" , opt )
}
}
container . ProcessLabel , container . MountLabel , err = label . InitLabels ( labelOpts )
return err
}
2015-12-18 13:36:17 -05:00
func getBlkioReadIOpsDevices ( config * containertypes . HostConfig ) ( [ ] * blkiodev . ThrottleDevice , error ) {
2015-07-08 07:06:48 -04:00
var blkioReadIOpsDevice [ ] * blkiodev . ThrottleDevice
var stat syscall . Stat_t
for _ , iopsDevice := range config . BlkioDeviceReadIOps {
if err := syscall . Stat ( iopsDevice . Path , & stat ) ; err != nil {
return nil , err
}
readIOpsDevice := blkiodev . NewThrottleDevice ( int64 ( stat . Rdev / 256 ) , int64 ( stat . Rdev % 256 ) , iopsDevice . Rate )
blkioReadIOpsDevice = append ( blkioReadIOpsDevice , readIOpsDevice )
}
return blkioReadIOpsDevice , nil
}
2015-12-18 13:36:17 -05:00
func getBlkioWriteIOpsDevices ( config * containertypes . HostConfig ) ( [ ] * blkiodev . ThrottleDevice , error ) {
2015-07-08 07:06:48 -04:00
var blkioWriteIOpsDevice [ ] * blkiodev . ThrottleDevice
var stat syscall . Stat_t
for _ , iopsDevice := range config . BlkioDeviceWriteIOps {
if err := syscall . Stat ( iopsDevice . Path , & stat ) ; err != nil {
return nil , err
}
writeIOpsDevice := blkiodev . NewThrottleDevice ( int64 ( stat . Rdev / 256 ) , int64 ( stat . Rdev % 256 ) , iopsDevice . Rate )
blkioWriteIOpsDevice = append ( blkioWriteIOpsDevice , writeIOpsDevice )
}
return blkioWriteIOpsDevice , nil
}
2015-12-18 13:36:17 -05:00
func getBlkioReadBpsDevices ( config * containertypes . HostConfig ) ( [ ] * blkiodev . ThrottleDevice , error ) {
2015-12-14 20:50:16 -05:00
var blkioReadBpsDevice [ ] * blkiodev . ThrottleDevice
2015-07-08 07:06:48 -04:00
var stat syscall . Stat_t
for _ , bpsDevice := range config . BlkioDeviceReadBps {
if err := syscall . Stat ( bpsDevice . Path , & stat ) ; err != nil {
return nil , err
}
2015-12-14 20:50:16 -05:00
readBpsDevice := blkiodev . NewThrottleDevice ( int64 ( stat . Rdev / 256 ) , int64 ( stat . Rdev % 256 ) , bpsDevice . Rate )
blkioReadBpsDevice = append ( blkioReadBpsDevice , readBpsDevice )
2015-07-08 07:06:48 -04:00
}
2015-12-14 20:50:16 -05:00
return blkioReadBpsDevice , nil
2015-07-08 07:06:48 -04:00
}
2015-12-18 13:36:17 -05:00
func getBlkioWriteBpsDevices ( config * containertypes . HostConfig ) ( [ ] * blkiodev . ThrottleDevice , error ) {
2015-12-14 20:50:16 -05:00
var blkioWriteBpsDevice [ ] * blkiodev . ThrottleDevice
2015-07-08 07:06:48 -04:00
var stat syscall . Stat_t
for _ , bpsDevice := range config . BlkioDeviceWriteBps {
if err := syscall . Stat ( bpsDevice . Path , & stat ) ; err != nil {
return nil , err
}
2015-12-14 20:50:16 -05:00
writeBpsDevice := blkiodev . NewThrottleDevice ( int64 ( stat . Rdev / 256 ) , int64 ( stat . Rdev % 256 ) , bpsDevice . Rate )
blkioWriteBpsDevice = append ( blkioWriteBpsDevice , writeBpsDevice )
2015-07-08 07:06:48 -04:00
}
2015-12-14 20:50:16 -05:00
return blkioWriteBpsDevice , nil
2015-07-08 07:06:48 -04:00
}
2015-07-30 17:01:53 -04:00
func checkKernelVersion ( k , major , minor int ) bool {
2015-08-18 22:40:22 -04:00
if v , err := kernel . GetKernelVersion ( ) ; err != nil {
logrus . Warnf ( "%s" , err )
} else {
if kernel . CompareKernelVersion ( * v , kernel . VersionInfo { Kernel : k , Major : major , Minor : minor } ) < 0 {
return false
}
}
return true
}
2015-05-15 19:34:26 -04:00
func checkKernel ( ) error {
// Check for unsupported kernel versions
// FIXME: it would be cleaner to not test for specific versions, but rather
// test for specific functionalities.
// Unfortunately we can't test for the feature "does not cause a kernel panic"
// without actually causing a kernel panic, so we need this workaround until
// the circumstances of pre-3.10 crashes are clearer.
// For details see https://github.com/docker/docker/issues/407
2015-07-30 17:01:53 -04:00
if ! checkKernelVersion ( 3 , 10 , 0 ) {
2015-08-18 22:40:22 -04:00
v , _ := kernel . GetKernelVersion ( )
if os . Getenv ( "DOCKER_NOWARN_KERNEL_VERSION" ) == "" {
logrus . Warnf ( "Your Linux kernel version %s can be unstable running docker. Please upgrade your kernel to 3.10.0." , v . String ( ) )
2015-05-15 19:34:26 -04:00
}
}
return nil
}
2015-07-30 18:28:11 -04:00
// adaptContainerSettings is called during container creation to modify any
// settings necessary in the HostConfig structure.
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) adaptContainerSettings ( hostConfig * containertypes . HostConfig , adjustCPUShares bool ) error {
2015-08-05 20:15:14 -04:00
if adjustCPUShares && hostConfig . CPUShares > 0 {
// Handle unsupported CPUShares
if hostConfig . CPUShares < linuxMinCPUShares {
logrus . Warnf ( "Changing requested CPUShares of %d to minimum allowed of %d" , hostConfig . CPUShares , linuxMinCPUShares )
hostConfig . CPUShares = linuxMinCPUShares
} else if hostConfig . CPUShares > linuxMaxCPUShares {
logrus . Warnf ( "Changing requested CPUShares of %d to maximum allowed of %d" , hostConfig . CPUShares , linuxMaxCPUShares )
hostConfig . CPUShares = linuxMaxCPUShares
}
}
2015-07-13 03:17:43 -04:00
if hostConfig . Memory > 0 && hostConfig . MemorySwap == 0 {
// By default, MemorySwap is set to twice the size of Memory.
hostConfig . MemorySwap = hostConfig . Memory * 2
}
2015-12-29 15:49:17 -05:00
if hostConfig . ShmSize == 0 {
hostConfig . ShmSize = container . DefaultSHMSize
2015-11-26 07:14:09 -05:00
}
2015-11-30 00:10:18 -05:00
var err error
if hostConfig . SecurityOpt == nil {
hostConfig . SecurityOpt , err = daemon . generateSecurityOpt ( hostConfig . IpcMode , hostConfig . PidMode )
if err != nil {
return err
}
}
2015-12-01 21:53:52 -05:00
if hostConfig . MemorySwappiness == nil {
defaultSwappiness := int64 ( - 1 )
hostConfig . MemorySwappiness = & defaultSwappiness
}
2015-12-31 01:17:18 -05:00
if hostConfig . OomKillDisable == nil {
defaultOomKillDisable := false
hostConfig . OomKillDisable = & defaultOomKillDisable
}
2015-11-30 00:10:18 -05:00
return nil
2015-07-13 03:17:43 -04:00
}
2016-01-20 20:08:10 -05:00
func verifyContainerResources ( resources * containertypes . Resources , sysInfo * sysinfo . SysInfo ) ( [ ] string , error ) {
2015-08-06 07:54:48 -04:00
warnings := [ ] string { }
2015-05-15 19:34:26 -04:00
2015-08-06 07:55:56 -04:00
// memory subsystem checks and adjustments
2015-12-10 21:59:29 -05:00
if resources . Memory != 0 && resources . Memory < linuxMinMemory {
2015-05-15 19:34:26 -04:00
return warnings , fmt . Errorf ( "Minimum memory limit allowed is 4MB" )
}
2015-12-10 21:59:29 -05:00
if resources . Memory > 0 && ! sysInfo . MemoryLimit {
2015-05-15 19:34:26 -04:00
warnings = append ( warnings , "Your kernel does not support memory limit capabilities. Limitation discarded." )
logrus . Warnf ( "Your kernel does not support memory limit capabilities. Limitation discarded." )
2015-12-10 21:59:29 -05:00
resources . Memory = 0
resources . MemorySwap = - 1
2015-05-15 19:34:26 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . Memory > 0 && resources . MemorySwap != - 1 && ! sysInfo . SwapLimit {
2015-05-15 19:34:26 -04:00
warnings = append ( warnings , "Your kernel does not support swap limit capabilities, memory limited without swap." )
logrus . Warnf ( "Your kernel does not support swap limit capabilities, memory limited without swap." )
2015-12-10 21:59:29 -05:00
resources . MemorySwap = - 1
2015-05-15 19:34:26 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . Memory > 0 && resources . MemorySwap > 0 && resources . MemorySwap < resources . Memory {
2015-05-15 19:34:26 -04:00
return warnings , fmt . Errorf ( "Minimum memoryswap limit should be larger than memory limit, see usage." )
}
2015-12-10 21:59:29 -05:00
if resources . Memory == 0 && resources . MemorySwap > 0 {
2015-05-15 19:34:26 -04:00
return warnings , fmt . Errorf ( "You should always set the Memory limit when using Memoryswap limit, see usage." )
}
2015-12-10 21:59:29 -05:00
if resources . MemorySwappiness != nil && * resources . MemorySwappiness != - 1 && ! sysInfo . MemorySwappiness {
2015-07-14 01:52:57 -04:00
warnings = append ( warnings , "Your kernel does not support memory swappiness capabilities, memory swappiness discarded." )
logrus . Warnf ( "Your kernel does not support memory swappiness capabilities, memory swappiness discarded." )
2015-12-10 21:59:29 -05:00
resources . MemorySwappiness = nil
2015-07-14 01:52:57 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . MemorySwappiness != nil {
swappiness := * resources . MemorySwappiness
2015-07-29 16:04:12 -04:00
if swappiness < - 1 || swappiness > 100 {
return warnings , fmt . Errorf ( "Invalid value: %v, valid memory swappiness range is 0-100." , swappiness )
}
2015-07-14 01:52:57 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . MemoryReservation > 0 && ! sysInfo . MemoryReservation {
2015-09-23 02:02:45 -04:00
warnings = append ( warnings , "Your kernel does not support memory soft limit capabilities. Limitation discarded." )
logrus . Warnf ( "Your kernel does not support memory soft limit capabilities. Limitation discarded." )
2015-12-10 21:59:29 -05:00
resources . MemoryReservation = 0
2015-09-23 02:02:45 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . Memory > 0 && resources . MemoryReservation > 0 && resources . Memory < resources . MemoryReservation {
2015-09-23 02:02:45 -04:00
return warnings , fmt . Errorf ( "Minimum memory limit should be larger than memory reservation limit, see usage." )
}
2015-12-10 21:59:29 -05:00
if resources . KernelMemory > 0 && ! sysInfo . KernelMemory {
2015-08-19 11:56:55 -04:00
warnings = append ( warnings , "Your kernel does not support kernel memory limit capabilities. Limitation discarded." )
logrus . Warnf ( "Your kernel does not support kernel memory limit capabilities. Limitation discarded." )
2015-12-10 21:59:29 -05:00
resources . KernelMemory = 0
2015-08-19 11:56:55 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . KernelMemory > 0 && resources . KernelMemory < linuxMinMemory {
2015-12-09 01:26:41 -05:00
return warnings , fmt . Errorf ( "Minimum kernel memory limit allowed is 4MB" )
}
2015-12-10 21:59:29 -05:00
if resources . KernelMemory > 0 && ! checkKernelVersion ( 4 , 0 , 0 ) {
2015-08-19 11:56:55 -04:00
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." )
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." )
}
2015-12-31 01:17:18 -05:00
if resources . OomKillDisable != nil && ! sysInfo . OomKillDisable {
2016-01-13 14:53:44 -05:00
// only produce warnings if the setting wasn't to *disable* the OOM Kill; no point
// warning the caller if they already wanted the feature to be off
if * resources . OomKillDisable {
warnings = append ( warnings , "Your kernel does not support OomKillDisable, OomKillDisable discarded." )
logrus . Warnf ( "Your kernel does not support OomKillDisable, OomKillDisable discarded." )
}
2015-12-31 01:17:18 -05:00
resources . OomKillDisable = nil
2015-12-22 03:08:04 -05:00
}
2015-12-10 21:59:29 -05:00
// cpu subsystem checks and adjustments
if resources . CPUShares > 0 && ! sysInfo . CPUShares {
2015-08-05 10:35:18 -04:00
warnings = append ( warnings , "Your kernel does not support CPU shares. Shares discarded." )
logrus . Warnf ( "Your kernel does not support CPU shares. Shares discarded." )
2015-12-10 21:59:29 -05:00
resources . CPUShares = 0
2015-08-05 10:35:18 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . CPUPeriod > 0 && ! sysInfo . CPUCfsPeriod {
2015-05-15 19:34:26 -04:00
warnings = append ( warnings , "Your kernel does not support CPU cfs period. Period discarded." )
logrus . Warnf ( "Your kernel does not support CPU cfs period. Period discarded." )
2015-12-10 21:59:29 -05:00
resources . CPUPeriod = 0
2015-05-15 19:34:26 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . CPUQuota > 0 && ! sysInfo . CPUCfsQuota {
2015-05-15 19:34:26 -04:00
warnings = append ( warnings , "Your kernel does not support CPU cfs quota. Quota discarded." )
logrus . Warnf ( "Your kernel does not support CPU cfs quota. Quota discarded." )
2015-12-10 21:59:29 -05:00
resources . CPUQuota = 0
2015-05-15 19:34:26 -04:00
}
2015-12-10 21:59:29 -05:00
// cpuset subsystem checks and adjustments
if ( resources . CpusetCpus != "" || resources . CpusetMems != "" ) && ! sysInfo . Cpuset {
2015-08-05 10:35:18 -04:00
warnings = append ( warnings , "Your kernel does not support cpuset. Cpuset discarded." )
logrus . Warnf ( "Your kernel does not support cpuset. Cpuset discarded." )
2015-12-10 21:59:29 -05:00
resources . CpusetCpus = ""
resources . CpusetMems = ""
2015-08-05 10:35:18 -04:00
}
2015-12-10 21:59:29 -05:00
cpusAvailable , err := sysInfo . IsCpusetCpusAvailable ( resources . CpusetCpus )
2015-09-08 14:40:55 -04:00
if err != nil {
2015-12-10 21:59:29 -05:00
return warnings , derr . ErrorCodeInvalidCpusetCpus . WithArgs ( resources . CpusetCpus )
2015-09-08 14:40:55 -04:00
}
if ! cpusAvailable {
2015-12-10 21:59:29 -05:00
return warnings , derr . ErrorCodeNotAvailableCpusetCpus . WithArgs ( resources . CpusetCpus , sysInfo . Cpus )
2015-09-08 14:40:55 -04:00
}
2015-12-10 21:59:29 -05:00
memsAvailable , err := sysInfo . IsCpusetMemsAvailable ( resources . CpusetMems )
2015-09-08 14:40:55 -04:00
if err != nil {
2015-12-10 21:59:29 -05:00
return warnings , derr . ErrorCodeInvalidCpusetMems . WithArgs ( resources . CpusetMems )
2015-09-08 14:40:55 -04:00
}
if ! memsAvailable {
2015-12-10 21:59:29 -05:00
return warnings , derr . ErrorCodeNotAvailableCpusetMems . WithArgs ( resources . CpusetMems , sysInfo . Mems )
2015-09-08 14:40:55 -04:00
}
2015-12-10 21:59:29 -05:00
// blkio subsystem checks and adjustments
if resources . BlkioWeight > 0 && ! sysInfo . BlkioWeight {
2015-08-05 10:35:18 -04:00
warnings = append ( warnings , "Your kernel does not support Block I/O weight. Weight discarded." )
logrus . Warnf ( "Your kernel does not support Block I/O weight. Weight discarded." )
2015-12-10 21:59:29 -05:00
resources . BlkioWeight = 0
2015-08-05 10:35:18 -04:00
}
2015-12-10 21:59:29 -05:00
if resources . BlkioWeight > 0 && ( resources . BlkioWeight < 10 || resources . BlkioWeight > 1000 ) {
2015-05-15 19:34:26 -04:00
return warnings , fmt . Errorf ( "Range of blkio weight is from 10 to 1000." )
}
2015-12-10 21:59:29 -05:00
if len ( resources . BlkioWeightDevice ) > 0 && ! sysInfo . BlkioWeightDevice {
2015-06-11 20:34:20 -04:00
warnings = append ( warnings , "Your kernel does not support Block I/O weight_device." )
logrus . Warnf ( "Your kernel does not support Block I/O weight_device. Weight-device discarded." )
2015-12-10 21:59:29 -05:00
resources . BlkioWeightDevice = [ ] * pblkiodev . WeightDevice { }
2015-06-11 20:34:20 -04:00
}
2015-12-10 21:59:29 -05:00
if len ( resources . BlkioDeviceReadBps ) > 0 && ! sysInfo . BlkioReadBpsDevice {
2015-07-08 07:06:48 -04:00
warnings = append ( warnings , "Your kernel does not support Block read limit in bytes per second." )
logrus . Warnf ( "Your kernel does not support Block I/O read limit in bytes per second. --device-read-bps discarded." )
2015-12-10 21:59:29 -05:00
resources . BlkioDeviceReadBps = [ ] * pblkiodev . ThrottleDevice { }
2015-07-08 07:06:48 -04:00
}
2015-12-10 21:59:29 -05:00
if len ( resources . BlkioDeviceWriteBps ) > 0 && ! sysInfo . BlkioWriteBpsDevice {
2015-07-08 07:06:48 -04:00
warnings = append ( warnings , "Your kernel does not support Block write limit in bytes per second." )
logrus . Warnf ( "Your kernel does not support Block I/O write limit in bytes per second. --device-write-bps discarded." )
2015-12-10 21:59:29 -05:00
resources . BlkioDeviceWriteBps = [ ] * pblkiodev . ThrottleDevice { }
2015-07-08 07:06:48 -04:00
}
2015-07-08 07:06:48 -04:00
if len ( resources . BlkioDeviceReadIOps ) > 0 && ! sysInfo . BlkioReadIOpsDevice {
warnings = append ( warnings , "Your kernel does not support Block read limit in IO per second." )
logrus . Warnf ( "Your kernel does not support Block I/O read limit in IO per second. -device-read-iops discarded." )
resources . BlkioDeviceReadIOps = [ ] * pblkiodev . ThrottleDevice { }
}
if len ( resources . BlkioDeviceWriteIOps ) > 0 && ! sysInfo . BlkioWriteIOpsDevice {
warnings = append ( warnings , "Your kernel does not support Block write limit in IO per second." )
logrus . Warnf ( "Your kernel does not support Block I/O write limit in IO per second. --device-write-iops discarded." )
resources . BlkioDeviceWriteIOps = [ ] * pblkiodev . ThrottleDevice { }
}
2015-12-10 21:59:29 -05:00
return warnings , nil
}
2016-01-22 21:15:09 -05:00
func usingSystemd ( config * Config ) bool {
for _ , option := range config . ExecOptions {
key , val , err := parsers . ParseKeyValueOpt ( option )
if err != nil || ! strings . EqualFold ( key , "native.cgroupdriver" ) {
continue
}
if val == "systemd" {
return true
}
}
return false
}
func ( daemon * Daemon ) usingSystemd ( ) bool {
return usingSystemd ( daemon . configStore )
}
2015-12-10 21:59:29 -05:00
// verifyPlatformContainerSettings performs platform-specific validation of the
// hostconfig and config structures.
2015-12-18 13:36:17 -05:00
func verifyPlatformContainerSettings ( daemon * Daemon , hostConfig * containertypes . HostConfig , config * containertypes . Config ) ( [ ] string , error ) {
2015-12-10 21:59:29 -05:00
warnings := [ ] string { }
sysInfo := sysinfo . New ( true )
warnings , err := daemon . verifyExperimentalContainerSettings ( hostConfig , config )
if err != nil {
return warnings , err
}
2016-01-20 20:08:10 -05:00
w , err := verifyContainerResources ( & hostConfig . Resources , sysInfo )
2015-12-10 21:59:29 -05:00
if err != nil {
return warnings , err
}
warnings = append ( warnings , w ... )
2015-12-29 15:49:17 -05:00
if hostConfig . ShmSize < 0 {
2015-12-10 21:59:29 -05:00
return warnings , fmt . Errorf ( "SHM size must be greater then 0" )
}
2015-10-13 05:26:27 -04:00
if hostConfig . OomScoreAdj < - 1000 || hostConfig . OomScoreAdj > 1000 {
return warnings , fmt . Errorf ( "Invalid value %d, range for oom score adj is [-1000, 1000]." , hostConfig . OomScoreAdj )
}
2015-08-06 07:54:48 -04:00
if sysInfo . IPv4ForwardingDisabled {
2015-05-15 19:34:26 -04:00
warnings = append ( warnings , "IPv4 forwarding is disabled. Networking will not work." )
logrus . Warnf ( "IPv4 forwarding is disabled. Networking will not work" )
}
2016-01-08 09:03:17 -05:00
// check for various conflicting options with user namespaces
if daemon . configStore . RemappedRoot != "" {
if hostConfig . Privileged {
2016-02-03 09:56:34 -05:00
return warnings , fmt . Errorf ( "Privileged mode is incompatible with user namespaces" )
2016-01-08 09:03:17 -05:00
}
if hostConfig . NetworkMode . IsHost ( ) || hostConfig . NetworkMode . IsContainer ( ) {
2016-02-03 09:56:34 -05:00
return warnings , fmt . Errorf ( "Cannot share the host or a container's network namespace when user namespaces are enabled" )
2016-01-08 09:03:17 -05:00
}
if hostConfig . PidMode . IsHost ( ) {
2016-02-03 09:56:34 -05:00
return warnings , fmt . Errorf ( "Cannot share the host PID namespace when user namespaces are enabled" )
2016-01-08 09:03:17 -05:00
}
if hostConfig . IpcMode . IsContainer ( ) {
2016-02-03 09:56:34 -05:00
return warnings , fmt . Errorf ( "Cannot share a container's IPC namespace when user namespaces are enabled" )
2016-01-08 09:03:17 -05:00
}
if hostConfig . ReadonlyRootfs {
2016-02-03 09:56:34 -05:00
return warnings , fmt . Errorf ( "Cannot use the --read-only option when user namespaces are enabled" )
2016-01-08 09:03:17 -05:00
}
2016-01-07 22:43:11 -05:00
}
2016-01-22 21:15:09 -05:00
if hostConfig . CgroupParent != "" && daemon . usingSystemd ( ) {
// CgroupParent for systemd cgroup should be named as "xxx.slice"
if len ( hostConfig . CgroupParent ) <= 6 || ! strings . HasSuffix ( hostConfig . CgroupParent , ".slice" ) {
return warnings , fmt . Errorf ( "cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"" )
}
}
2015-05-15 19:34:26 -04:00
return warnings , nil
}
2016-01-22 21:15:09 -05:00
// verifyDaemonSettings performs validation of daemon config struct
func verifyDaemonSettings ( config * Config ) error {
2015-05-15 19:34:26 -04:00
// Check for mutually incompatible config options
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . Iface != "" && config . bridgeConfig . IP != "" {
2016-02-03 09:56:34 -05:00
return fmt . Errorf ( "You specified -b & --bip, mutually exclusive options. Please specify only one" )
2015-05-15 19:34:26 -04:00
}
2016-01-25 16:30:33 -05:00
if ! config . bridgeConfig . EnableIPTables && ! config . bridgeConfig . InterContainerCommunication {
2016-02-03 09:56:34 -05:00
return fmt . Errorf ( "You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true" )
2015-05-15 19:34:26 -04:00
}
2016-01-25 16:30:33 -05:00
if ! config . bridgeConfig . EnableIPTables && config . bridgeConfig . EnableIPMasq {
config . bridgeConfig . EnableIPMasq = false
2015-05-15 19:34:26 -04:00
}
2016-01-22 21:15:09 -05:00
if config . CgroupParent != "" && usingSystemd ( config ) {
if len ( config . CgroupParent ) <= 6 || ! strings . HasSuffix ( config . CgroupParent , ".slice" ) {
return fmt . Errorf ( "cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"" )
}
}
2015-05-15 19:34:26 -04:00
return nil
}
2015-07-11 15:32:08 -04:00
// checkSystem validates platform-specific requirements
2015-05-15 19:34:26 -04:00
func checkSystem ( ) error {
if os . Geteuid ( ) != 0 {
return fmt . Errorf ( "The Docker daemon needs to be run as root" )
}
2015-09-08 14:40:55 -04:00
return checkKernel ( )
2015-05-15 19:34:26 -04:00
}
2015-12-02 05:26:30 -05:00
// configureMaxThreads sets the Go runtime max threads threshold
// which is 90% of the kernel setting from /proc/sys/kernel/threads-max
func configureMaxThreads ( config * Config ) error {
mt , err := ioutil . ReadFile ( "/proc/sys/kernel/threads-max" )
if err != nil {
return err
}
mtint , err := strconv . Atoi ( strings . TrimSpace ( string ( mt ) ) )
if err != nil {
return err
}
maxThreads := ( mtint / 100 ) * 90
debug . SetMaxThreads ( maxThreads )
logrus . Debugf ( "Golang's threads limit set to %d" , maxThreads )
return nil
}
2015-05-15 19:34:26 -04:00
// configureKernelSecuritySupport configures and validate security support for the kernel
func configureKernelSecuritySupport ( config * Config , driverName string ) error {
if config . EnableSelinuxSupport {
if selinuxEnabled ( ) {
2015-10-28 09:19:51 -04:00
// As Docker on overlayFS and SELinux are incompatible at present, error on overlayfs being enabled
if driverName == "overlay" {
2015-09-02 15:09:53 -04:00
return fmt . Errorf ( "SELinux is not supported with the %s graph driver" , driverName )
2015-05-15 19:34:26 -04:00
}
logrus . Debug ( "SELinux enabled successfully" )
} else {
logrus . Warn ( "Docker could not enable SELinux on the host system" )
}
} else {
selinuxSetDisabled ( )
}
return nil
}
2015-06-30 13:34:15 -04:00
func isBridgeNetworkDisabled ( config * Config ) bool {
2016-01-25 16:30:33 -05:00
return config . bridgeConfig . Iface == disableNetworkBridge
2015-05-15 19:34:26 -04:00
}
2015-09-21 08:04:36 -04:00
func ( daemon * Daemon ) networkOptions ( dconfig * Config ) ( [ ] nwconfig . Option , error ) {
2015-05-20 08:20:19 -04:00
options := [ ] nwconfig . Option { }
if dconfig == nil {
return options , nil
}
2015-10-10 12:43:03 -04:00
options = append ( options , nwconfig . OptionDataDir ( dconfig . Root ) )
2015-10-22 06:16:53 -04:00
dd := runconfig . DefaultDaemonNetworkMode ( )
dn := runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( )
options = append ( options , nwconfig . OptionDefaultDriver ( string ( dd ) ) )
options = append ( options , nwconfig . OptionDefaultNetwork ( dn ) )
2015-06-20 20:08:36 -04:00
2015-09-10 19:12:00 -04:00
if strings . TrimSpace ( dconfig . ClusterStore ) != "" {
kv := strings . Split ( dconfig . ClusterStore , "://" )
2015-11-04 09:27:23 -05:00
if len ( kv ) != 2 {
2015-09-24 23:00:05 -04:00
return nil , fmt . Errorf ( "kv store daemon config must be of the form KV-PROVIDER://KV-URL" )
2015-06-20 20:08:36 -04:00
}
options = append ( options , nwconfig . OptionKVProvider ( kv [ 0 ] ) )
2015-11-04 09:27:23 -05:00
options = append ( options , nwconfig . OptionKVProviderURL ( kv [ 1 ] ) )
2015-06-20 20:08:36 -04:00
}
2015-10-21 12:31:09 -04:00
if len ( dconfig . ClusterOpts ) > 0 {
options = append ( options , nwconfig . OptionKVOpts ( dconfig . ClusterOpts ) )
}
2015-06-20 20:08:36 -04:00
2015-09-21 08:04:36 -04:00
if daemon . discoveryWatcher != nil {
options = append ( options , nwconfig . OptionDiscoveryWatcher ( daemon . discoveryWatcher ) )
}
if dconfig . ClusterAdvertise != "" {
options = append ( options , nwconfig . OptionDiscoveryAddress ( dconfig . ClusterAdvertise ) )
}
2015-05-20 08:20:19 -04:00
options = append ( options , nwconfig . OptionLabels ( dconfig . Labels ) )
2015-09-24 23:00:05 -04:00
options = append ( options , driverOptions ( dconfig ) ... )
2015-05-20 08:20:19 -04:00
return options , nil
}
2015-09-21 08:04:36 -04:00
func ( daemon * Daemon ) initNetworkController ( config * Config ) ( libnetwork . NetworkController , error ) {
netOptions , err := daemon . networkOptions ( config )
2015-05-20 08:20:19 -04:00
if err != nil {
return nil , err
}
controller , err := libnetwork . New ( netOptions ... )
2015-05-15 19:34:26 -04:00
if err != nil {
return nil , fmt . Errorf ( "error obtaining controller instance: %v" , err )
}
// Initialize default network on "null"
2015-09-24 23:00:05 -04:00
if _ , err := controller . NewNetwork ( "null" , "none" , libnetwork . NetworkOptionPersist ( false ) ) ; err != nil {
2015-05-15 19:34:26 -04:00
return nil , fmt . Errorf ( "Error creating default \"null\" network: %v" , err )
}
// Initialize default network on "host"
2015-09-24 23:00:05 -04:00
if _ , err := controller . NewNetwork ( "host" , "host" , libnetwork . NetworkOptionPersist ( false ) ) ; err != nil {
2015-05-15 19:34:26 -04:00
return nil , fmt . Errorf ( "Error creating default \"host\" network: %v" , err )
}
2015-06-30 13:34:15 -04:00
if ! config . DisableBridge {
// Initialize default driver "bridge"
if err := initBridgeDriver ( controller , config ) ; err != nil {
return nil , err
}
}
return controller , nil
}
2015-09-24 23:00:05 -04:00
func driverOptions ( config * Config ) [ ] nwconfig . Option {
bridgeConfig := options . Generic {
2016-01-25 16:30:33 -05:00
"EnableIPForwarding" : config . bridgeConfig . EnableIPForward ,
"EnableIPTables" : config . bridgeConfig . EnableIPTables ,
"EnableUserlandProxy" : config . bridgeConfig . EnableUserlandProxy }
2015-09-24 23:00:05 -04:00
bridgeOption := options . Generic { netlabel . GenericData : bridgeConfig }
2015-05-15 19:34:26 -04:00
2015-09-24 23:00:05 -04:00
dOptions := [ ] nwconfig . Option { }
dOptions = append ( dOptions , nwconfig . OptionDriverConfig ( "bridge" , bridgeOption ) )
return dOptions
}
2015-05-15 19:34:26 -04:00
2015-09-24 23:00:05 -04:00
func initBridgeDriver ( controller libnetwork . NetworkController , config * Config ) error {
2015-10-10 12:43:03 -04:00
if n , err := controller . NetworkByName ( "bridge" ) ; err == nil {
if err = n . Delete ( ) ; err != nil {
return fmt . Errorf ( "could not delete the default bridge network: %v" , err )
}
}
bridgeName := bridge . DefaultBridgeName
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . Iface != "" {
bridgeName = config . bridgeConfig . Iface
2015-10-10 12:43:03 -04:00
}
netOption := map [ string ] string {
bridge . BridgeName : bridgeName ,
bridge . DefaultBridge : strconv . FormatBool ( true ) ,
netlabel . DriverMTU : strconv . Itoa ( config . Mtu ) ,
2016-01-25 16:30:33 -05:00
bridge . EnableIPMasquerade : strconv . FormatBool ( config . bridgeConfig . EnableIPMasq ) ,
bridge . EnableICC : strconv . FormatBool ( config . bridgeConfig . InterContainerCommunication ) ,
2015-10-10 12:43:03 -04:00
}
// --ip processing
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . DefaultIP != nil {
netOption [ bridge . DefaultBindingIP ] = config . bridgeConfig . DefaultIP . String ( )
2015-10-10 12:43:03 -04:00
}
2016-01-12 02:47:44 -05:00
var (
ipamV4Conf * libnetwork . IpamConf
ipamV6Conf * libnetwork . IpamConf
)
2015-10-10 12:43:03 -04:00
2016-01-12 02:47:44 -05:00
ipamV4Conf = & libnetwork . IpamConf { AuxAddresses : make ( map [ string ] string ) }
2015-10-10 12:43:03 -04:00
2016-01-12 02:47:44 -05:00
nw , nw6List , err := ipamutils . ElectInterfaceAddresses ( bridgeName )
if err == nil {
ipamV4Conf . PreferredPool = types . GetIPNetCanonical ( nw ) . String ( )
2015-10-10 12:43:03 -04:00
hip , _ := types . GetHostPartIP ( nw . IP , nw . Mask )
if hip . IsGlobalUnicast ( ) {
ipamV4Conf . Gateway = nw . IP . String ( )
}
2015-05-15 19:34:26 -04:00
}
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . IP != "" {
ipamV4Conf . PreferredPool = config . bridgeConfig . IP
ip , _ , err := net . ParseCIDR ( config . bridgeConfig . IP )
2015-05-15 19:34:26 -04:00
if err != nil {
2015-06-30 13:34:15 -04:00
return err
2015-05-15 19:34:26 -04:00
}
2015-10-10 12:43:03 -04:00
ipamV4Conf . Gateway = ip . String ( )
2015-10-26 14:46:20 -04:00
} else if bridgeName == bridge . DefaultBridgeName && ipamV4Conf . PreferredPool != "" {
logrus . Infof ( "Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address" , bridgeName , ipamV4Conf . PreferredPool )
2015-05-15 19:34:26 -04:00
}
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . FixedCIDR != "" {
_ , fCIDR , err := net . ParseCIDR ( config . bridgeConfig . FixedCIDR )
2015-05-15 19:34:26 -04:00
if err != nil {
2015-06-30 13:34:15 -04:00
return err
2015-05-15 19:34:26 -04:00
}
2015-10-10 12:43:03 -04:00
ipamV4Conf . SubPool = fCIDR . String ( )
2015-05-15 19:34:26 -04:00
}
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . DefaultGatewayIPv4 != nil {
ipamV4Conf . AuxAddresses [ "DefaultGatewayIPv4" ] = config . bridgeConfig . DefaultGatewayIPv4 . String ( )
2015-10-10 12:43:03 -04:00
}
2016-01-12 02:47:44 -05:00
var deferIPv6Alloc bool
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . FixedCIDRv6 != "" {
_ , fCIDRv6 , err := net . ParseCIDR ( config . bridgeConfig . FixedCIDRv6 )
2015-05-15 19:34:26 -04:00
if err != nil {
2015-06-30 13:34:15 -04:00
return err
2015-05-15 19:34:26 -04:00
}
2015-11-11 00:14:05 -05:00
// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
// at least 48 host bits, we need to guarantee the current behavior where the containers'
// IPv6 addresses will be constructed based on the containers' interface MAC address.
// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
// on this network until after the driver has created the endpoint and returned the
// constructed address. Libnetwork will then reserve this address with the ipam driver.
ones , _ := fCIDRv6 . Mask . Size ( )
deferIPv6Alloc = ones <= 80
2015-10-10 12:43:03 -04:00
if ipamV6Conf == nil {
2015-12-30 17:51:51 -05:00
ipamV6Conf = & libnetwork . IpamConf { AuxAddresses : make ( map [ string ] string ) }
2015-10-10 12:43:03 -04:00
}
ipamV6Conf . PreferredPool = fCIDRv6 . String ( )
2016-01-12 02:47:44 -05:00
// In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6
// address belongs to the same network, we need to inform libnetwork about it, so
// that it can be reserved with IPAM and it will not be given away to somebody else
for _ , nw6 := range nw6List {
if fCIDRv6 . Contains ( nw6 . IP ) {
ipamV6Conf . Gateway = nw6 . IP . String ( )
break
}
}
2015-05-15 19:34:26 -04:00
}
2016-01-25 16:30:33 -05:00
if config . bridgeConfig . DefaultGatewayIPv6 != nil {
2015-10-10 12:43:03 -04:00
if ipamV6Conf == nil {
2015-12-30 17:51:51 -05:00
ipamV6Conf = & libnetwork . IpamConf { AuxAddresses : make ( map [ string ] string ) }
2015-10-10 12:43:03 -04:00
}
2016-01-25 16:30:33 -05:00
ipamV6Conf . AuxAddresses [ "DefaultGatewayIPv6" ] = config . bridgeConfig . DefaultGatewayIPv6 . String ( )
2015-05-15 19:34:26 -04:00
}
2016-01-12 02:47:44 -05:00
v4Conf := [ ] * libnetwork . IpamConf { ipamV4Conf }
2015-10-10 12:43:03 -04:00
v6Conf := [ ] * libnetwork . IpamConf { }
if ipamV6Conf != nil {
v6Conf = append ( v6Conf , ipamV6Conf )
2015-05-15 19:34:26 -04:00
}
// Initialize default network on "bridge" with the same name
2016-01-12 02:47:44 -05:00
_ , err = controller . NewNetwork ( "bridge" , "bridge" ,
2015-05-15 19:34:26 -04:00
libnetwork . NetworkOptionGeneric ( options . Generic {
netlabel . GenericData : netOption ,
2016-01-25 16:30:33 -05:00
netlabel . EnableIPv6 : config . bridgeConfig . EnableIPv6 ,
2015-09-24 23:00:05 -04:00
} ) ,
2016-01-08 16:38:52 -05:00
libnetwork . NetworkOptionIpam ( "default" , "" , v4Conf , v6Conf , nil ) ,
2015-11-11 00:14:05 -05:00
libnetwork . NetworkOptionDeferIPv6Alloc ( deferIPv6Alloc ) )
2015-05-15 19:34:26 -04:00
if err != nil {
2015-06-30 13:34:15 -04:00
return fmt . Errorf ( "Error creating default \"bridge\" network: %v" , err )
2015-05-15 19:34:26 -04:00
}
2015-06-30 13:34:15 -04:00
return nil
2015-05-15 19:34:26 -04:00
}
2015-06-16 14:06:53 -04:00
// setupInitLayer populates a directory with mountpoints suitable
2016-01-20 06:53:54 -05:00
// for bind-mounting things into the container.
2015-06-16 14:06:53 -04:00
//
// This extra layer is used by all containers as the top-most ro layer. It protects
// the container from unwanted side-effects on the rw layer.
2015-10-08 11:51:41 -04:00
func setupInitLayer ( initLayer string , rootUID , rootGID int ) error {
2015-06-16 14:06:53 -04:00
for pth , typ := range map [ string ] string {
"/dev/pts" : "dir" ,
"/dev/shm" : "dir" ,
"/proc" : "dir" ,
"/sys" : "dir" ,
"/.dockerenv" : "file" ,
"/etc/resolv.conf" : "file" ,
"/etc/hosts" : "file" ,
"/etc/hostname" : "file" ,
"/dev/console" : "file" ,
"/etc/mtab" : "/proc/mounts" ,
} {
parts := strings . Split ( pth , "/" )
prev := "/"
for _ , p := range parts [ 1 : ] {
prev = filepath . Join ( prev , p )
syscall . Unlink ( filepath . Join ( initLayer , prev ) )
}
if _ , err := os . Stat ( filepath . Join ( initLayer , pth ) ) ; err != nil {
if os . IsNotExist ( err ) {
2015-12-04 12:18:05 -05:00
if err := idtools . MkdirAllNewAs ( filepath . Join ( initLayer , filepath . Dir ( pth ) ) , 0755 , rootUID , rootGID ) ; err != nil {
2015-06-16 14:06:53 -04:00
return err
}
switch typ {
case "dir" :
2015-12-04 12:18:05 -05:00
if err := idtools . MkdirAllNewAs ( filepath . Join ( initLayer , pth ) , 0755 , rootUID , rootGID ) ; err != nil {
2015-06-16 14:06:53 -04:00
return err
}
case "file" :
f , err := os . OpenFile ( filepath . Join ( initLayer , pth ) , os . O_CREATE , 0755 )
if err != nil {
return err
}
2015-10-08 11:51:41 -04:00
f . Chown ( rootUID , rootGID )
2015-12-04 12:18:05 -05:00
f . Close ( )
2015-06-16 14:06:53 -04:00
default :
if err := os . Symlink ( typ , filepath . Join ( initLayer , pth ) ) ; err != nil {
return err
}
}
} else {
return err
}
}
}
// Layer is ready to use, if it wasn't before.
return nil
}
2015-05-20 08:20:19 -04:00
2016-01-07 22:43:11 -05:00
// Parse the remapped root (user namespace) option, which can be one of:
// username - valid username from /etc/passwd
// username:groupname - valid username; valid groupname from /etc/group
// uid - 32-bit unsigned int valid Linux UID value
// uid:gid - uid value; 32-bit unsigned int Linux GID value
//
// If no groupname is specified, and a username is specified, an attempt
// will be made to lookup a gid for that username as a groupname
//
// If names are used, they are verified to exist in passwd/group
func parseRemappedRoot ( usergrp string ) ( string , string , error ) {
var (
userID , groupID int
username , groupname string
)
idparts := strings . Split ( usergrp , ":" )
if len ( idparts ) > 2 {
return "" , "" , fmt . Errorf ( "Invalid user/group specification in --userns-remap: %q" , usergrp )
}
if uid , err := strconv . ParseInt ( idparts [ 0 ] , 10 , 32 ) ; err == nil {
// must be a uid; take it as valid
userID = int ( uid )
luser , err := user . LookupUid ( userID )
if err != nil {
return "" , "" , fmt . Errorf ( "Uid %d has no entry in /etc/passwd: %v" , userID , err )
}
username = luser . Name
if len ( idparts ) == 1 {
// if the uid was numeric and no gid was specified, take the uid as the gid
groupID = userID
lgrp , err := user . LookupGid ( groupID )
if err != nil {
return "" , "" , fmt . Errorf ( "Gid %d has no entry in /etc/group: %v" , groupID , err )
}
groupname = lgrp . Name
}
} else {
lookupName := idparts [ 0 ]
// special case: if the user specified "default", they want Docker to create or
// use (after creation) the "dockremap" user/group for root remapping
if lookupName == defaultIDSpecifier {
lookupName = defaultRemappedID
}
luser , err := user . LookupUser ( lookupName )
if err != nil && idparts [ 0 ] != defaultIDSpecifier {
// error if the name requested isn't the special "dockremap" ID
return "" , "" , fmt . Errorf ( "Error during uid lookup for %q: %v" , lookupName , err )
} else if err != nil {
// special case-- if the username == "default", then we have been asked
// to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid}
// ranges will be used for the user and group mappings in user namespaced containers
_ , _ , err := idtools . AddNamespaceRangesUser ( defaultRemappedID )
if err == nil {
return defaultRemappedID , defaultRemappedID , nil
}
return "" , "" , fmt . Errorf ( "Error during %q user creation: %v" , defaultRemappedID , err )
}
userID = luser . Uid
username = luser . Name
if len ( idparts ) == 1 {
// we only have a string username, and no group specified; look up gid from username as group
group , err := user . LookupGroup ( lookupName )
if err != nil {
return "" , "" , fmt . Errorf ( "Error during gid lookup for %q: %v" , lookupName , err )
}
groupID = group . Gid
groupname = group . Name
}
}
if len ( idparts ) == 2 {
// groupname or gid is separately specified and must be resolved
// to a unsigned 32-bit gid
if gid , err := strconv . ParseInt ( idparts [ 1 ] , 10 , 32 ) ; err == nil {
// must be a gid, take it as valid
groupID = int ( gid )
lgrp , err := user . LookupGid ( groupID )
if err != nil {
return "" , "" , fmt . Errorf ( "Gid %d has no entry in /etc/passwd: %v" , groupID , err )
}
groupname = lgrp . Name
} else {
// not a number; attempt a lookup
group , err := user . LookupGroup ( idparts [ 1 ] )
if err != nil {
return "" , "" , fmt . Errorf ( "Error during gid lookup for %q: %v" , idparts [ 1 ] , err )
}
groupID = group . Gid
groupname = idparts [ 1 ]
}
}
return username , groupname , nil
}
func setupRemappedRoot ( config * Config ) ( [ ] idtools . IDMap , [ ] idtools . IDMap , error ) {
if runtime . GOOS != "linux" && config . RemappedRoot != "" {
return nil , nil , fmt . Errorf ( "User namespaces are only supported on Linux" )
}
// if the daemon was started with remapped root option, parse
// the config option to the int uid,gid values
var (
uidMaps , gidMaps [ ] idtools . IDMap
)
if config . RemappedRoot != "" {
username , groupname , err := parseRemappedRoot ( config . RemappedRoot )
if err != nil {
return nil , nil , err
}
if username == "root" {
// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op
// effectively
logrus . Warnf ( "User namespaces: root cannot be remapped with itself; user namespaces are OFF" )
return uidMaps , gidMaps , nil
}
logrus . Infof ( "User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s" , username , groupname )
// update remapped root setting now that we have resolved them to actual names
config . RemappedRoot = fmt . Sprintf ( "%s:%s" , username , groupname )
uidMaps , gidMaps , err = idtools . CreateIDMappings ( username , groupname )
if err != nil {
return nil , nil , fmt . Errorf ( "Can't create ID mappings: %v" , err )
}
}
return uidMaps , gidMaps , nil
}
func setupDaemonRoot ( config * Config , rootDir string , rootUID , rootGID int ) error {
config . Root = rootDir
// the docker root metadata directory needs to have execute permissions for all users (o+x)
// so that syscalls executing as non-root, operating on subdirectories of the graph root
// (e.g. mounted layers of a container) can traverse this path.
// The user namespace support will create subdirectories for the remapped root host uid:gid
// pair owned by that same uid:gid pair for proper write access to those needed metadata and
// layer content subtrees.
if _ , err := os . Stat ( rootDir ) ; err == nil {
// root current exists; verify the access bits are correct by setting them
if err = os . Chmod ( rootDir , 0701 ) ; err != nil {
return err
}
} else if os . IsNotExist ( err ) {
// no root exists yet, create it 0701 with root:root ownership
if err := os . MkdirAll ( rootDir , 0701 ) ; err != nil {
return err
}
}
// if user namespaces are enabled we will create a subtree underneath the specified root
// with any/all specified remapped root uid/gid options on the daemon creating
// a new subdirectory with ownership set to the remapped uid/gid (so as to allow
// `chdir()` to work for containers namespaced to that uid/gid)
if config . RemappedRoot != "" {
config . Root = filepath . Join ( rootDir , fmt . Sprintf ( "%d.%d" , rootUID , rootGID ) )
logrus . Debugf ( "Creating user namespaced daemon root: %s" , config . Root )
// Create the root directory if it doesn't exists
if err := idtools . MkdirAllAs ( config . Root , 0700 , rootUID , rootGID ) ; err != nil {
return fmt . Errorf ( "Cannot create daemon root: %s: %v" , config . Root , err )
}
}
return nil
}
2015-07-30 17:01:53 -04:00
// registerLinks writes the links to a file.
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) registerLinks ( container * container . Container , hostConfig * containertypes . HostConfig ) error {
2016-01-05 14:20:47 -05:00
if hostConfig == nil || hostConfig . NetworkMode . IsUserDefined ( ) {
2015-06-23 13:13:42 -04:00
return nil
}
for _ , l := range hostConfig . Links {
2015-12-21 20:05:55 -05:00
name , alias , err := runconfigopts . ParseLink ( l )
2015-06-23 13:13:42 -04:00
if err != nil {
return err
}
2015-12-11 12:39:28 -05:00
child , err := daemon . GetContainer ( name )
2015-06-23 13:13:42 -04:00
if err != nil {
2015-12-11 12:39:28 -05:00
//An error from daemon.GetContainer() means this name could not be found
2015-06-23 13:13:42 -04:00
return fmt . Errorf ( "Could not get container for %s" , name )
}
2015-11-12 14:55:17 -05:00
for child . HostConfig . NetworkMode . IsContainer ( ) {
parts := strings . SplitN ( string ( child . HostConfig . NetworkMode ) , ":" , 2 )
2015-12-11 12:39:28 -05:00
child , err = daemon . GetContainer ( parts [ 1 ] )
2015-06-23 13:13:42 -04:00
if err != nil {
return fmt . Errorf ( "Could not get container for %s" , parts [ 1 ] )
}
}
2015-11-12 14:55:17 -05:00
if child . HostConfig . NetworkMode . IsHost ( ) {
2015-06-23 13:13:42 -04:00
return runconfig . ErrConflictHostNetworkAndLinks
}
2015-07-30 17:01:53 -04:00
if err := daemon . registerLink ( container , child , alias ) ; err != nil {
2015-06-23 13:13:42 -04:00
return err
}
}
// After we load all the links into the daemon
// set them to nil on the hostconfig
2015-09-03 20:51:04 -04:00
return container . WriteHostConfig ( )
2015-06-23 13:13:42 -04:00
}
2015-07-16 17:14:58 -04:00
2015-11-02 20:06:09 -05:00
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) conditionalMountOnStart ( container * container . Container ) error {
2015-11-02 20:06:09 -05:00
return daemon . Mount ( container )
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) conditionalUnmountOnCleanup ( container * container . Container ) {
2015-11-18 17:20:54 -05:00
daemon . Unmount ( container )
2015-11-02 20:06:09 -05:00
}
2015-12-16 15:32:16 -05:00
func restoreCustomImage ( is image . Store , ls layer . Store , rs reference . Store ) error {
2015-11-18 17:20:54 -05:00
// Unix has no custom images to register
return nil
}