github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/run.go (about) 1 // Copyright 2014 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //+build linux 16 17 package main 18 19 import ( 20 "bufio" 21 "errors" 22 "fmt" 23 "net" 24 "os" 25 "strconv" 26 "strings" 27 28 "github.com/appc/spec/schema/types" 29 cnitypes "github.com/containernetworking/cni/pkg/types" 30 "github.com/hashicorp/errwrap" 31 "github.com/opencontainers/selinux/go-selinux/label" 32 "github.com/rkt/rkt/common" 33 "github.com/rkt/rkt/pkg/lock" 34 pkgPod "github.com/rkt/rkt/pkg/pod" 35 "github.com/rkt/rkt/pkg/user" 36 "github.com/rkt/rkt/rkt/image" 37 "github.com/rkt/rkt/stage0" 38 "github.com/rkt/rkt/store/imagestore" 39 "github.com/rkt/rkt/store/treestore" 40 "github.com/spf13/cobra" 41 ) 42 43 var ( 44 cmdRun = &cobra.Command{ 45 Use: "run [--volume=name,kind=host,...] [--mount volume=VOL,target=PATH] IMAGE [-- image-args...[---]]...", 46 Short: "Run image(s) in a pod in rkt", 47 Long: `IMAGE should be a string referencing an image; either a hash, local file on 48 disk, or URL. They will be checked in that order and the first match will be 49 used. 50 51 Volumes are made available to the container via --volume. Mounts bind volumes 52 into each image's root within the container via --mount. --mount is 53 position-sensitive; occurring before any images applies to all images, occurring 54 after any images applies only to the nearest preceding image. Per-app mounts 55 take precedence over global ones if they have the same path. 56 57 An "--" may be used to inhibit rkt run's parsing of subsequent arguments, which 58 will instead be appended to the preceding image app's exec arguments. End the 59 image arguments with a lone "---" to resume argument parsing.`, 60 Run: ensureSuperuser(runWrapper(runRun)), 61 } 62 flagPorts portList 63 flagNet common.NetList 64 flagPrivateUsers bool 65 flagInheritEnv bool 66 flagExplicitEnv kvMap 67 flagEnvFromFile envFileMap 68 flagInteractive bool 69 flagDNS flagStringList 70 flagDNSSearch flagStringList 71 flagDNSOpt flagStringList 72 flagDNSDomain string 73 flagNoOverlay bool 74 flagStoreOnly bool 75 flagNoStore bool 76 flagPodManifest string 77 flagMDSRegister bool 78 flagUUIDFileSave string 79 flagHostname string 80 flagHostsEntries flagStringList 81 flagPullPolicy string 82 flagIPCMode string 83 ) 84 85 func addIsolatorFlags(cmd *cobra.Command, compat bool) { 86 cmd.Flags().Var((*appMemoryLimit)(&rktApps), "memory", "memory limit for the preceding image (example: '--memory=16Mi', '--memory=50M', '--memory=1G')") 87 cmd.Flags().Var((*appCPULimit)(&rktApps), "cpu", "cpu limit for the preceding image (example: '--cpu=500m')") 88 cmd.Flags().Var((*appCPUShares)(&rktApps), "cpu-shares", "cpu-shares assigns the specified CPU time share weight (example: '--cpu-shares=2048')") 89 cmd.Flags().Var((*appCapsRetain)(&rktApps), "caps-retain", "capability to retain (example: '--caps-retain=CAP_SYS_ADMIN')") 90 cmd.Flags().Var((*appCapsRemove)(&rktApps), "caps-remove", "capability to remove (example: '--caps-remove=CAP_MKNOD')") 91 cmd.Flags().Var((*appSeccompFilter)(&rktApps), "seccomp", "seccomp filter override (example: '--seccomp mode=retain,errno=EPERM,chmod,chown')") 92 cmd.Flags().Var((*appOOMScoreAdj)(&rktApps), "oom-score-adj", "oom-score-adj isolator override") 93 94 // For backwards compatibility 95 if compat { 96 cmd.Flags().Var((*appCapsRetain)(&rktApps), "cap-retain", "capability to retain (example: '--caps-retain=CAP_SYS_ADMIN')") 97 cmd.Flags().Var((*appCapsRemove)(&rktApps), "cap-remove", "capability to remove (example: '--caps-remove=CAP_MKNOD')") 98 cmd.Flags().MarkDeprecated("cap-retain", "use --caps-retain instead") 99 cmd.Flags().MarkDeprecated("cap-remove", "use --caps-remove instead") 100 } 101 } 102 103 func addAppFlags(cmd *cobra.Command) { 104 cmd.Flags().Var((*appExec)(&rktApps), "exec", "override the exec command for the preceding image") 105 cmd.Flags().Var((*appWorkingDir)(&rktApps), "working-dir", "override the working directory of the preceding image") 106 cmd.Flags().Var((*appReadOnlyRootFS)(&rktApps), "readonly-rootfs", "if set, the app's rootfs will be mounted read-only") 107 cmd.Flags().Var((*appMount)(&rktApps), "mount", "mount point binding a volume to a path within an app") 108 cmd.Flags().Var((*appUser)(&rktApps), "user", "user override for the preceding image (example: '--user=user')") 109 cmd.Flags().Var((*appGroup)(&rktApps), "group", "group override for the preceding image (example: '--group=group')") 110 cmd.Flags().Var((*appSupplementaryGIDs)(&rktApps), "supplementary-gids", "supplementary group IDs override for the preceding image (examples: '--supplementary-gids=1024,2048'") 111 cmd.Flags().Var((*appName)(&rktApps), "name", "set the name of the app (example: '--name=foo'). If not set, then the app name default to the image's name") 112 cmd.Flags().Var((*appAnnotation)(&rktApps), "annotation", "set the app's annotations (example: '--annotation=foo=bar')") 113 cmd.Flags().Var((*appUserAnnotation)(&rktApps), "user-annotation", "set the app's annotations (example: '--user-annotation=foo=bar')") 114 cmd.Flags().Var((*appLabel)(&rktApps), "user-label", "set the app's labels (example: '--user-label=foo=bar')") 115 cmd.Flags().Var((*appEnv)(&rktApps), "environment", "set the app's environment variables (example: '--environment=foo=bar')") 116 if common.IsExperimentEnabled("attach") { 117 cmd.Flags().Var((*appStdin)(&rktApps), "stdin", "stdin mode for the preceding application (example: '--stdin=null')") 118 cmd.Flags().MarkHidden("stdin") 119 cmd.Flags().Var((*appStdout)(&rktApps), "stdout", "stdout mode for the preceding application (example: '--stdout=log')") 120 cmd.Flags().MarkHidden("stdout") 121 cmd.Flags().Var((*appStderr)(&rktApps), "stderr", "stderr mode for the preceding application (example: '--stderr=log')") 122 cmd.Flags().MarkHidden("stderr") 123 } 124 } 125 126 func init() { 127 cmdRkt.AddCommand(cmdRun) 128 129 /* 130 Careful! 131 Be sure to add common flags to run-prepared as well! 132 */ 133 134 addStage1ImageFlags(cmdRun.Flags()) 135 cmdRun.Flags().Var(&flagPorts, "port", "ports to expose on the host (requires contained network). Syntax: --port=NAME:[HOSTIP:]HOSTPORT") 136 cmdRun.Flags().Var(&flagNet, "net", "configure the pod's networking. Optionally, pass a list of user-configured networks to load and set arguments to pass to each network, respectively. Syntax: --net[=n[:args], ...]") 137 cmdRun.Flags().Lookup("net").NoOptDefVal = "default" 138 cmdRun.Flags().BoolVar(&flagInheritEnv, "inherit-env", false, "inherit all environment variables not set by apps") 139 cmdRun.Flags().BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem") 140 cmdRun.Flags().BoolVar(&flagPrivateUsers, "private-users", false, "run within user namespaces.") 141 cmdRun.Flags().Var(&flagExplicitEnv, "set-env", "environment variable to set for all the apps in the form key=value, this will be overridden by --environment") 142 cmdRun.Flags().Var(&flagEnvFromFile, "set-env-file", "path to an environment variables file") 143 cmdRun.Flags().BoolVar(&flagInteractive, "interactive", false, "run pod interactively. If true, only one image may be supplied.") 144 cmdRun.Flags().Var(&flagDNS, "dns", "name servers to write in /etc/resolv.conf. Pass 'host' to use host's resolv.conf. Pass 'none' to ignore CNI DNS config") 145 cmdRun.Flags().Var(&flagDNSSearch, "dns-search", "DNS search domains to write in /etc/resolv.conf") 146 cmdRun.Flags().Var(&flagDNSOpt, "dns-opt", "DNS options to write in /etc/resolv.conf") 147 cmdRun.Flags().StringVar(&flagDNSDomain, "dns-domain", "", "DNS domain to write in /etc/resolv.conf") 148 cmdRun.Flags().Var(&flagHostsEntries, "hosts-entry", "Entries to add to the pod-wide /etc/hosts. Pass 'host' to use the host's /etc/hosts") 149 cmdRun.Flags().BoolVar(&flagStoreOnly, "store-only", false, "use only available images in the store (do not discover or download from remote URLs)") 150 cmdRun.Flags().MarkDeprecated("store-only", "please use --pull-policy=never") 151 cmdRun.Flags().BoolVar(&flagNoStore, "no-store", false, "fetch images ignoring the local store") 152 cmdRun.Flags().MarkDeprecated("no-store", "please use --pull-policy=update") 153 cmdRun.Flags().StringVar(&flagPullPolicy, "pull-policy", image.PullPolicyNew, "when to pull an image") 154 cmdRun.Flags().StringVar(&flagPodManifest, "pod-manifest", "", "the path to the pod manifest. If it's non-empty, then only '--net', '--no-overlay' and '--interactive' will have effect") 155 cmdRun.Flags().BoolVar(&flagMDSRegister, "mds-register", false, "register pod with metadata service. needs network connectivity to the host (--net=(default|default-restricted|host)") 156 cmdRun.Flags().StringVar(&flagUUIDFileSave, "uuid-file-save", "", "write out pod UUID to specified file") 157 cmdRun.Flags().StringVar(&flagHostname, "hostname", "", `pod's hostname. If empty, it will be "rkt-$PODUUID"`) 158 cmdRun.Flags().Var((*appsVolume)(&rktApps), "volume", "volumes to make available in the pod") 159 cmdRun.Flags().StringVar(&flagIPCMode, "ipc", "", `whether to stay in the host IPC namespace. Syntax: --ipc=[auto|private|parent]`) 160 161 // per-app flags 162 cmdRun.Flags().Var((*appAsc)(&rktApps), "signature", "local signature file to use in validating the preceding image") 163 addAppFlags(cmdRun) 164 addIsolatorFlags(cmdRun, true) 165 166 flagPorts = portList{} 167 flagDNS = flagStringList{} 168 flagDNSSearch = flagStringList{} 169 flagDNSOpt = flagStringList{} 170 flagHostsEntries = flagStringList{} 171 172 // Disable interspersed flags to stop parsing after the first non flag 173 // argument. All the subsequent parsing will be done by parseApps. 174 // This is needed to correctly handle image args 175 cmdRun.Flags().SetInterspersed(false) 176 } 177 178 func runRun(cmd *cobra.Command, args []string) (exit int) { 179 privateUsers := user.NewBlankUidRange() 180 err := parseApps(&rktApps, args, cmd.Flags(), true) 181 if err != nil { 182 stderr.PrintE("error parsing app image arguments", err) 183 return 254 184 } 185 186 if flagStoreOnly && flagNoStore { 187 stderr.Print("both --store-only and --no-store specified") 188 return 254 189 } 190 if flagStoreOnly { 191 flagPullPolicy = image.PullPolicyNever 192 } 193 if flagNoStore { 194 flagPullPolicy = image.PullPolicyUpdate 195 } 196 197 if flagPrivateUsers { 198 if !common.SupportsUserNS() { 199 stderr.Print("--private-users is not supported, kernel compiled without user namespace support") 200 return 254 201 } 202 privateUsers.SetRandomUidRange(user.DefaultRangeCount) 203 } 204 205 if len(flagPorts) > 0 && flagNet.None() { 206 stderr.Print("--port flag does not work with 'none' networking") 207 return 254 208 } 209 if len(flagPorts) > 0 && flagNet.Host() { 210 stderr.Print("--port flag does not work with 'host' networking") 211 return 254 212 } 213 214 if flagMDSRegister && flagNet.None() { 215 stderr.Print("--mds-register flag does not work with --net=none. Please use 'host', 'default' or an equivalent network") 216 return 254 217 } 218 219 if len(flagPodManifest) > 0 && (rktApps.Count() > 0 || 220 (*appsVolume)(&rktApps).String() != "" || (*appMount)(&rktApps).String() != "" || 221 len(flagPorts) > 0 || flagPullPolicy == image.PullPolicyNever || 222 flagPullPolicy == image.PullPolicyUpdate || flagInheritEnv || 223 !flagExplicitEnv.IsEmpty() || !flagEnvFromFile.IsEmpty()) { 224 stderr.Print("conflicting flags set with --pod-manifest (see --help)") 225 return 254 226 } 227 228 if flagInteractive && rktApps.Count() > 1 { 229 stderr.Print("interactive option only supports one image") 230 return 254 231 } 232 233 if rktApps.Count() < 1 && len(flagPodManifest) == 0 { 234 stderr.Print("must provide at least one image or specify the pod manifest") 235 return 254 236 } 237 238 s, err := imagestore.NewStore(storeDir()) 239 if err != nil { 240 stderr.PrintE("cannot open store", err) 241 return 254 242 } 243 244 ts, err := treestore.NewStore(treeStoreDir(), s) 245 if err != nil { 246 stderr.PrintE("cannot open treestore", err) 247 return 254 248 } 249 250 config, err := getConfig() 251 if err != nil { 252 stderr.PrintE("cannot get configuration", err) 253 return 254 254 } 255 256 s1img, err := getStage1Hash(s, ts, config) 257 if err != nil { 258 stderr.Error(err) 259 return 254 260 } 261 262 fn := &image.Finder{ 263 S: s, 264 Ts: ts, 265 Ks: getKeystore(), 266 Headers: config.AuthPerHost, 267 DockerAuth: config.DockerCredentialsPerRegistry, 268 InsecureFlags: globalFlags.InsecureFlags, 269 Debug: globalFlags.Debug, 270 TrustKeysFromHTTPS: globalFlags.TrustKeysFromHTTPS, 271 272 PullPolicy: flagPullPolicy, 273 WithDeps: true, 274 } 275 if err := fn.FindImages(&rktApps); err != nil { 276 stderr.Error(err) 277 return 254 278 } 279 280 p, err := pkgPod.NewPod(getDataDir()) 281 if err != nil { 282 stderr.PrintE("error creating new pod", err) 283 return 254 284 } 285 286 // if requested, write out pod UUID early so "rkt rm" can 287 // clean it up even if something goes wrong 288 if flagUUIDFileSave != "" { 289 if err := pkgPod.WriteUUIDToFile(p.UUID, flagUUIDFileSave); err != nil { 290 stderr.PrintE("error saving pod UUID to file", err) 291 return 254 292 } 293 } 294 295 processLabel, mountLabel, err := label.InitLabels([]string{}) 296 if err != nil { 297 stderr.PrintE("error initialising SELinux", err) 298 return 254 299 } 300 p.MountLabel = mountLabel 301 302 cfg := stage0.CommonConfig{ 303 DataDir: getDataDir(), 304 MountLabel: mountLabel, 305 ProcessLabel: processLabel, 306 Store: s, 307 TreeStore: ts, 308 Stage1Image: *s1img, 309 UUID: p.UUID, 310 Debug: globalFlags.Debug, 311 Mutable: false, 312 } 313 314 ovlOk := true 315 if err := common.PathSupportsOverlay(getDataDir()); err != nil { 316 if oerr, ok := err.(common.ErrOverlayUnsupported); ok { 317 stderr.Printf("disabling overlay support: %q", oerr.Error()) 318 ovlOk = false 319 } else { 320 stderr.PrintE("error determining overlay support", err) 321 return 254 322 } 323 } 324 325 useOverlay := !flagNoOverlay && ovlOk 326 327 pcfg := stage0.PrepareConfig{ 328 CommonConfig: &cfg, 329 UseOverlay: useOverlay, 330 PrivateUsers: privateUsers, 331 } 332 333 if len(flagPodManifest) > 0 { 334 pcfg.PodManifest = flagPodManifest 335 } else { 336 pcfg.Ports = []types.ExposedPort(flagPorts) 337 pcfg.InheritEnv = flagInheritEnv 338 pcfg.ExplicitEnv = flagExplicitEnv.Strings() 339 pcfg.EnvFromFile = flagEnvFromFile.Strings() 340 pcfg.Apps = &rktApps 341 } 342 343 if globalFlags.Debug { 344 stage0.InitDebug() 345 } 346 347 keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock) 348 if err != nil { 349 stderr.PrintE("cannot get shared prepare lock", err) 350 return 254 351 } 352 err = stage0.Prepare(pcfg, p.Path(), p.UUID) 353 if err != nil { 354 stderr.PrintE("error setting up stage0", err) 355 keyLock.Close() 356 return 254 357 } 358 keyLock.Close() 359 360 // get the lock fd for run 361 lfd, err := p.Fd() 362 if err != nil { 363 stderr.PrintE("error getting pod lock fd", err) 364 return 254 365 } 366 367 // skip prepared by jumping directly to run, we own this pod 368 if err := p.ToRun(); err != nil { 369 stderr.PrintE("unable to transition to run", err) 370 return 254 371 } 372 373 rktgid, err := common.LookupGid(common.RktGroup) 374 if err != nil { 375 stderr.Printf("group %q not found, will use default gid when rendering images", common.RktGroup) 376 rktgid = -1 377 } 378 379 DNSConfMode, DNSConfig, HostsEntries, err := parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt, flagDNSDomain) 380 if err != nil { 381 stderr.PrintE("error with dns flags", err) 382 return 254 383 } 384 385 rcfg := stage0.RunConfig{ 386 CommonConfig: &cfg, 387 Net: flagNet, 388 LockFd: lfd, 389 Interactive: flagInteractive, 390 DNSConfMode: DNSConfMode, 391 DNSConfig: DNSConfig, 392 MDSRegister: flagMDSRegister, 393 LocalConfig: globalFlags.LocalConfigDir, 394 RktGid: rktgid, 395 Hostname: flagHostname, 396 InsecureCapabilities: globalFlags.InsecureFlags.SkipCapabilities(), 397 InsecurePaths: globalFlags.InsecureFlags.SkipPaths(), 398 InsecureSeccomp: globalFlags.InsecureFlags.SkipSeccomp(), 399 UseOverlay: useOverlay, 400 HostsEntries: *HostsEntries, 401 IPCMode: flagIPCMode, 402 } 403 404 _, manifest, err := p.PodManifest() 405 if err != nil { 406 stderr.PrintE("cannot get the pod manifest", err) 407 return 254 408 } 409 410 if len(manifest.Apps) == 0 { 411 stderr.Print("pod must contain at least one application") 412 return 254 413 } 414 rcfg.Apps = manifest.Apps 415 stage0.Run(rcfg, p.Path(), getDataDir()) // execs, never returns 416 417 return 254 418 } 419 420 // portList implements the flag.Value interface to contain a set of mappings 421 // from port name --> host port 422 type portList []types.ExposedPort 423 424 func (pl *portList) Set(s string) error { 425 parts := strings.SplitN(s, ":", 3) 426 if len(parts) < 2 { 427 return fmt.Errorf("%q is not in name:[ip:]port format", s) 428 } 429 430 name, err := types.NewACName(parts[0]) 431 if err != nil { 432 return errwrap.Wrap(fmt.Errorf("%q is not a valid port name", parts[0]), err) 433 } 434 435 portStr := parts[1] 436 var ip net.IP 437 if len(parts) == 3 { 438 portStr = parts[2] 439 ip = net.ParseIP(parts[1]) 440 if ip == nil { 441 return fmt.Errorf("%q is not a valid IP", parts[1]) 442 } 443 } 444 445 port, err := strconv.ParseUint(portStr, 10, 16) 446 if err != nil { 447 return fmt.Errorf("%q is not a valid port number", parts[1]) 448 } 449 450 p := types.ExposedPort{ 451 Name: *name, 452 HostPort: uint(port), 453 HostIP: ip, 454 } 455 456 *pl = append(*pl, p) 457 return nil 458 } 459 460 func (pl *portList) String() string { 461 var ps []string 462 for _, p := range []types.ExposedPort(*pl) { 463 ps = append(ps, fmt.Sprintf("%v:%v", p.Name, p.HostPort)) 464 } 465 return strings.Join(ps, " ") 466 } 467 468 func (pl *portList) Type() string { 469 return "portList" 470 } 471 472 // flagStringList implements the flag.Value interface to contain a set of strings 473 type flagStringList []string 474 475 func (dns *flagStringList) Set(s string) error { 476 *dns = append(*dns, s) 477 return nil 478 } 479 480 func (dns *flagStringList) String() string { 481 return strings.Join(*dns, " ") 482 } 483 484 func (dns *flagStringList) Type() string { 485 return "flagStringList" 486 } 487 488 // kvMap implements the flag.Value interface to contain a set of key=value mappings 489 type kvMap struct { 490 mapping map[string]string 491 } 492 493 func (e *kvMap) Set(s string) error { 494 if e.mapping == nil { 495 e.mapping = make(map[string]string) 496 } 497 pair := strings.SplitN(s, "=", 2) 498 if len(pair) != 2 { 499 return fmt.Errorf("must be specified as key=value") 500 } 501 if _, exists := e.mapping[pair[0]]; exists { 502 return fmt.Errorf("key %q already set", pair[0]) 503 } 504 e.mapping[pair[0]] = pair[1] 505 return nil 506 } 507 508 func (e *kvMap) IsEmpty() bool { 509 return len(e.mapping) == 0 510 } 511 512 func (e *kvMap) String() string { 513 return strings.Join(e.Strings(), "\n") 514 } 515 516 func (e *kvMap) Strings() []string { 517 var env []string 518 for n, v := range e.mapping { 519 env = append(env, n+"="+v) 520 } 521 return env 522 } 523 524 func (e *kvMap) Type() string { 525 return "kvMap" 526 } 527 528 // envFileMap 529 type envFileMap struct { 530 mapping map[string]string 531 } 532 533 func commentLine(s string) bool { 534 return strings.HasPrefix(s, "#") || strings.HasPrefix(s, ";") 535 } 536 537 func (e *envFileMap) Set(s string) error { 538 if e.mapping == nil { 539 e.mapping = make(map[string]string) 540 } 541 file, err := os.Open(s) 542 if err != nil { 543 return err 544 } 545 defer file.Close() 546 547 scanner := bufio.NewScanner(file) 548 for scanner.Scan() { 549 line := scanner.Text() 550 // Skip empty lines 551 if len(line) == 0 { 552 continue 553 } 554 // Skip comments 555 if commentLine(line) { 556 continue 557 } 558 pair := strings.SplitN(line, "=", 2) 559 // Malformed lines 560 if len(pair) != 2 || len(pair[0]) == 0 { 561 return fmt.Errorf("environment variable must be specified as name=value (file %q)", s) 562 } 563 if _, exists := e.mapping[pair[0]]; exists { 564 return fmt.Errorf("environment variable %q already set (file %q)", pair[0], s) 565 } 566 e.mapping[pair[0]] = pair[1] 567 } 568 return nil 569 } 570 571 func (e *envFileMap) IsEmpty() bool { 572 return len(e.mapping) == 0 573 } 574 575 func (e *envFileMap) String() string { 576 return strings.Join(e.Strings(), "\n") 577 } 578 579 func (e *envFileMap) Strings() []string { 580 var env []string 581 for n, v := range e.mapping { 582 env = append(env, n+"="+v) 583 } 584 return env 585 } 586 587 func (e *envFileMap) Type() string { 588 return "envFileMap" 589 } 590 591 /* 592 * Parse out the --hosts-entries, --dns, --dns-search, and --dns-opt flags 593 * This includes decoding the "magic" values for hosts-entries and dns. 594 * Try to detect any obvious insanity, namely invalid IPs or more than one 595 * magic option 596 */ 597 func parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt []string, flagDNSDomain string) (stage0.DNSConfMode, cnitypes.DNS, *stage0.HostsEntries, error) { 598 DNSConfMode := stage0.DNSConfMode{ 599 Resolv: "default", 600 Hosts: "default", 601 } 602 DNSConfig := cnitypes.DNS{} 603 HostsEntries := make(stage0.HostsEntries) 604 605 // Loop through --dns and look for magic option 606 // Check for obvious insanity - only one magic option allowed 607 for _, d := range flagDNS { 608 // parse magic values 609 if d == "host" || d == "none" { 610 if len(flagDNS) > 1 { 611 return DNSConfMode, DNSConfig, &HostsEntries, 612 fmt.Errorf("no other --dns options allowed when --dns=%s is passed", d) 613 } 614 DNSConfMode.Resolv = d 615 break 616 617 } else { 618 // parse list of IPS 619 for _, d := range strings.Split(d, ",") { 620 if net.ParseIP(d) == nil { 621 return DNSConfMode, DNSConfig, &HostsEntries, 622 fmt.Errorf("Invalid IP passed to --dns: %s", d) 623 } 624 DNSConfig.Nameservers = append(DNSConfig.Nameservers, d) 625 } 626 } 627 } 628 629 DNSConfig.Search = flagDNSSearch 630 DNSConfig.Options = flagDNSOpt 631 DNSConfig.Domain = flagDNSDomain 632 633 if !common.IsDNSZero(&DNSConfig) { 634 if DNSConfMode.Resolv == "default" { 635 DNSConfMode.Resolv = "stage0" 636 } 637 638 if DNSConfMode.Resolv != "stage0" { 639 return DNSConfMode, DNSConfig, &HostsEntries, 640 fmt.Errorf("Cannot call --dns-opt, --dns-search, or --dns-domain with --dns=%v", DNSConfMode.Resolv) 641 } 642 } 643 644 // Parse out --hosts-entries, also looking for the magic value "host" 645 for _, entry := range flagHostsEntries { 646 if entry == "host" { 647 DNSConfMode.Hosts = "host" 648 continue 649 } 650 for _, entry := range strings.Split(entry, ",") { 651 vals := strings.SplitN(entry, "=", 2) 652 if len(vals) != 2 { 653 return DNSConfMode, DNSConfig, &HostsEntries, 654 fmt.Errorf("Did not understand --hosts-entry %s", entry) 655 } 656 ipStr := vals[0] 657 hostname := vals[1] 658 659 // validate IP address 660 ip := net.ParseIP(ipStr) 661 if ip == nil { 662 return DNSConfMode, DNSConfig, &HostsEntries, 663 fmt.Errorf("Invalid IP passed to --hosts-entry: %s", ipStr) 664 } 665 666 _, exists := HostsEntries[ipStr] 667 if !exists { 668 HostsEntries[ipStr] = []string{hostname} 669 } else { 670 HostsEntries[ipStr] = append(HostsEntries[ipStr], hostname) 671 } 672 } 673 } 674 675 if len(HostsEntries) > 0 { 676 if DNSConfMode.Hosts == "host" { 677 return DNSConfMode, DNSConfig, &HostsEntries, 678 errors.New("cannot pass --hosts-entry=host with multiple hosts-entries") 679 } 680 DNSConfMode.Hosts = "stage0" 681 } 682 683 return DNSConfMode, DNSConfig, &HostsEntries, nil 684 }