1package daemon // import "github.com/docker/docker/daemon" 2 3import ( 4 "fmt" 5 "net" 6 "runtime" 7 "strings" 8 "time" 9 10 "github.com/docker/docker/api/types" 11 containertypes "github.com/docker/docker/api/types/container" 12 networktypes "github.com/docker/docker/api/types/network" 13 "github.com/docker/docker/container" 14 "github.com/docker/docker/errdefs" 15 "github.com/docker/docker/image" 16 "github.com/docker/docker/pkg/idtools" 17 "github.com/docker/docker/pkg/system" 18 "github.com/docker/docker/runconfig" 19 "github.com/opencontainers/selinux/go-selinux/label" 20 "github.com/pkg/errors" 21 "github.com/sirupsen/logrus" 22) 23 24// CreateManagedContainer creates a container that is managed by a Service 25func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { 26 return daemon.containerCreate(params, true) 27} 28 29// ContainerCreate creates a regular container 30func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { 31 return daemon.containerCreate(params, false) 32} 33 34func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { 35 start := time.Now() 36 if params.Config == nil { 37 return containertypes.ContainerCreateCreatedBody{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container")) 38 } 39 40 os := runtime.GOOS 41 if params.Config.Image != "" { 42 img, err := daemon.imageService.GetImage(params.Config.Image) 43 if err == nil { 44 os = img.OS 45 } 46 } else { 47 // This mean scratch. On Windows, we can safely assume that this is a linux 48 // container. On other platforms, it's the host OS (which it already is) 49 if runtime.GOOS == "windows" && system.LCOWSupported() { 50 os = "linux" 51 } 52 } 53 54 warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false) 55 if err != nil { 56 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) 57 } 58 59 err = verifyNetworkingConfig(params.NetworkingConfig) 60 if err != nil { 61 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) 62 } 63 64 if params.HostConfig == nil { 65 params.HostConfig = &containertypes.HostConfig{} 66 } 67 err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) 68 if err != nil { 69 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) 70 } 71 72 container, err := daemon.create(params, managed) 73 if err != nil { 74 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err 75 } 76 containerActions.WithValues("create").UpdateSince(start) 77 78 return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil 79} 80 81// Create creates a new container from the given configuration with a given name. 82func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) { 83 var ( 84 container *container.Container 85 img *image.Image 86 imgID image.ID 87 err error 88 ) 89 90 os := runtime.GOOS 91 if params.Config.Image != "" { 92 img, err = daemon.imageService.GetImage(params.Config.Image) 93 if err != nil { 94 return nil, err 95 } 96 if img.OS != "" { 97 os = img.OS 98 } else { 99 // default to the host OS except on Windows with LCOW 100 if runtime.GOOS == "windows" && system.LCOWSupported() { 101 os = "linux" 102 } 103 } 104 imgID = img.ID() 105 106 if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { 107 return nil, errors.New("operating system on which parent image was created is not Windows") 108 } 109 } else { 110 if runtime.GOOS == "windows" { 111 os = "linux" // 'scratch' case. 112 } 113 } 114 115 if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { 116 return nil, errdefs.InvalidParameter(err) 117 } 118 119 if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil { 120 return nil, errdefs.InvalidParameter(err) 121 } 122 123 if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil { 124 return nil, err 125 } 126 defer func() { 127 if retErr != nil { 128 if err := daemon.cleanupContainer(container, true, true); err != nil { 129 logrus.Errorf("failed to cleanup container on create error: %v", err) 130 } 131 } 132 }() 133 134 if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil { 135 return nil, err 136 } 137 138 container.HostConfig.StorageOpt = params.HostConfig.StorageOpt 139 140 // Fixes: https://github.com/moby/moby/issues/34074 and 141 // https://github.com/docker/for-win/issues/999. 142 // Merge the daemon's storage options if they aren't already present. We only 143 // do this on Windows as there's no effective sandbox size limit other than 144 // physical on Linux. 145 if runtime.GOOS == "windows" { 146 if container.HostConfig.StorageOpt == nil { 147 container.HostConfig.StorageOpt = make(map[string]string) 148 } 149 for _, v := range daemon.configStore.GraphOptions { 150 opt := strings.SplitN(v, "=", 2) 151 if _, ok := container.HostConfig.StorageOpt[opt[0]]; !ok { 152 container.HostConfig.StorageOpt[opt[0]] = opt[1] 153 } 154 } 155 } 156 157 // Set RWLayer for container after mount labels have been set 158 rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMappings)) 159 if err != nil { 160 return nil, errdefs.System(err) 161 } 162 container.RWLayer = rwLayer 163 164 rootIDs := daemon.idMappings.RootPair() 165 if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { 166 return nil, err 167 } 168 if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil { 169 return nil, err 170 } 171 172 if err := daemon.setHostConfig(container, params.HostConfig); err != nil { 173 return nil, err 174 } 175 176 if err := daemon.createContainerOSSpecificSettings(container, params.Config, params.HostConfig); err != nil { 177 return nil, err 178 } 179 180 var endpointsConfigs map[string]*networktypes.EndpointSettings 181 if params.NetworkingConfig != nil { 182 endpointsConfigs = params.NetworkingConfig.EndpointsConfig 183 } 184 // Make sure NetworkMode has an acceptable value. We do this to ensure 185 // backwards API compatibility. 186 runconfig.SetDefaultNetModeIfBlank(container.HostConfig) 187 188 daemon.updateContainerNetworkSettings(container, endpointsConfigs) 189 if err := daemon.Register(container); err != nil { 190 return nil, err 191 } 192 stateCtr.set(container.ID, "stopped") 193 daemon.LogContainerEvent(container, "create") 194 return container, nil 195} 196 197func toHostConfigSelinuxLabels(labels []string) []string { 198 for i, l := range labels { 199 labels[i] = "label=" + l 200 } 201 return labels 202} 203 204func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) ([]string, error) { 205 for _, opt := range hostConfig.SecurityOpt { 206 con := strings.Split(opt, "=") 207 if con[0] == "label" { 208 // Caller overrode SecurityOpts 209 return nil, nil 210 } 211 } 212 ipcMode := hostConfig.IpcMode 213 pidMode := hostConfig.PidMode 214 privileged := hostConfig.Privileged 215 if ipcMode.IsHost() || pidMode.IsHost() || privileged { 216 return toHostConfigSelinuxLabels(label.DisableSecOpt()), nil 217 } 218 219 var ipcLabel []string 220 var pidLabel []string 221 ipcContainer := ipcMode.Container() 222 pidContainer := pidMode.Container() 223 if ipcContainer != "" { 224 c, err := daemon.GetContainer(ipcContainer) 225 if err != nil { 226 return nil, err 227 } 228 ipcLabel = label.DupSecOpt(c.ProcessLabel) 229 if pidContainer == "" { 230 return toHostConfigSelinuxLabels(ipcLabel), err 231 } 232 } 233 if pidContainer != "" { 234 c, err := daemon.GetContainer(pidContainer) 235 if err != nil { 236 return nil, err 237 } 238 239 pidLabel = label.DupSecOpt(c.ProcessLabel) 240 if ipcContainer == "" { 241 return toHostConfigSelinuxLabels(pidLabel), err 242 } 243 } 244 245 if pidLabel != nil && ipcLabel != nil { 246 for i := 0; i < len(pidLabel); i++ { 247 if pidLabel[i] != ipcLabel[i] { 248 return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same") 249 } 250 } 251 return toHostConfigSelinuxLabels(pidLabel), nil 252 } 253 return nil, nil 254} 255 256func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { 257 if img != nil && img.Config != nil { 258 if err := merge(config, img.Config); err != nil { 259 return err 260 } 261 } 262 // Reset the Entrypoint if it is [""] 263 if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" { 264 config.Entrypoint = nil 265 } 266 if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { 267 return fmt.Errorf("No command specified") 268 } 269 return nil 270} 271 272// Checks if the client set configurations for more than one network while creating a container 273// Also checks if the IPAMConfig is valid 274func verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { 275 if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 { 276 return nil 277 } 278 if len(nwConfig.EndpointsConfig) == 1 { 279 for k, v := range nwConfig.EndpointsConfig { 280 if v == nil { 281 return errdefs.InvalidParameter(errors.Errorf("no EndpointSettings for %s", k)) 282 } 283 if v.IPAMConfig != nil { 284 if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { 285 return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) 286 } 287 if v.IPAMConfig.IPv6Address != "" { 288 n := net.ParseIP(v.IPAMConfig.IPv6Address) 289 // if the address is an invalid network address (ParseIP == nil) or if it is 290 // an IPv4 address (To4() != nil), then it is an invalid IPv6 address 291 if n == nil || n.To4() != nil { 292 return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) 293 } 294 } 295 } 296 } 297 return nil 298 } 299 l := make([]string, 0, len(nwConfig.EndpointsConfig)) 300 for k := range nwConfig.EndpointsConfig { 301 l = append(l, k) 302 } 303 return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) 304} 305