diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/images_build.go | 203 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/play.go | 2 | ||||
-rw-r--r-- | pkg/bindings/images/build.go | 32 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 22 | ||||
-rw-r--r-- | pkg/domain/filters/containers.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/runtime_libpod.go | 6 | ||||
-rw-r--r-- | pkg/machine/config.go | 3 | ||||
-rw-r--r-- | pkg/machine/ignition.go | 14 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 71 | ||||
-rw-r--r-- | pkg/netns/netns_linux.go | 26 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 58 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 3 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 16 |
13 files changed, 342 insertions, 116 deletions
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index fd310711f..ab92434b1 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "time" "github.com/containers/buildah" @@ -63,52 +64,55 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { }() query := struct { - AddHosts string `schema:"extrahosts"` - AdditionalCapabilities string `schema:"addcaps"` - Annotations string `schema:"annotations"` - BuildArgs string `schema:"buildargs"` - CacheFrom string `schema:"cachefrom"` - Compression uint64 `schema:"compression"` - ConfigureNetwork string `schema:"networkmode"` - CpuPeriod uint64 `schema:"cpuperiod"` // nolint - CpuQuota int64 `schema:"cpuquota"` // nolint - CpuSetCpus string `schema:"cpusetcpus"` // nolint - CpuShares uint64 `schema:"cpushares"` // nolint - Devices string `schema:"devices"` - Dockerfile string `schema:"dockerfile"` - DropCapabilities string `schema:"dropcaps"` - DNSServers string `schema:"dnsservers"` - DNSOptions string `schema:"dnsoptions"` - DNSSearch string `schema:"dnssearch"` - Excludes string `schema:"excludes"` - ForceRm bool `schema:"forcerm"` - From string `schema:"from"` - HTTPProxy bool `schema:"httpproxy"` - Isolation string `schema:"isolation"` - Ignore bool `schema:"ignore"` - Jobs int `schema:"jobs"` // nolint - Labels string `schema:"labels"` - Layers bool `schema:"layers"` - LogRusage bool `schema:"rusage"` - Manifest string `schema:"manifest"` - MemSwap int64 `schema:"memswap"` - Memory int64 `schema:"memory"` - NamespaceOptions string `schema:"nsoptions"` - NoCache bool `schema:"nocache"` - OutputFormat string `schema:"outputformat"` - Platform string `schema:"platform"` - Pull bool `schema:"pull"` - PullPolicy string `schema:"pullpolicy"` - Quiet bool `schema:"q"` - Registry string `schema:"registry"` - Rm bool `schema:"rm"` - //FIXME SecurityOpt in remote API is not handled - SecurityOpt string `schema:"securityopt"` - ShmSize int `schema:"shmsize"` - Squash bool `schema:"squash"` - Tag []string `schema:"t"` - Target string `schema:"target"` - Timestamp int64 `schema:"timestamp"` + AddHosts string `schema:"extrahosts"` + AdditionalCapabilities string `schema:"addcaps"` + Annotations string `schema:"annotations"` + AppArmor string `schema:"apparmor"` + BuildArgs string `schema:"buildargs"` + CacheFrom string `schema:"cachefrom"` + Compression uint64 `schema:"compression"` + ConfigureNetwork string `schema:"networkmode"` + CpuPeriod uint64 `schema:"cpuperiod"` // nolint + CpuQuota int64 `schema:"cpuquota"` // nolint + CpuSetCpus string `schema:"cpusetcpus"` // nolint + CpuShares uint64 `schema:"cpushares"` // nolint + DNSOptions string `schema:"dnsoptions"` + DNSSearch string `schema:"dnssearch"` + DNSServers string `schema:"dnsservers"` + Devices string `schema:"devices"` + Dockerfile string `schema:"dockerfile"` + DropCapabilities string `schema:"dropcaps"` + Excludes string `schema:"excludes"` + ForceRm bool `schema:"forcerm"` + From string `schema:"from"` + HTTPProxy bool `schema:"httpproxy"` + Ignore bool `schema:"ignore"` + Isolation string `schema:"isolation"` + Jobs int `schema:"jobs"` // nolint + LabelOpts string `schema:"labelopts"` + Labels string `schema:"labels"` + Layers bool `schema:"layers"` + LogRusage bool `schema:"rusage"` + Manifest string `schema:"manifest"` + MemSwap int64 `schema:"memswap"` + Memory int64 `schema:"memory"` + NamespaceOptions string `schema:"nsoptions"` + NoCache bool `schema:"nocache"` + OutputFormat string `schema:"outputformat"` + Platform string `schema:"platform"` + Pull bool `schema:"pull"` + PullPolicy string `schema:"pullpolicy"` + Quiet bool `schema:"q"` + Registry string `schema:"registry"` + Rm bool `schema:"rm"` + Seccomp string `schema:"seccomp"` + SecurityOpt string `schema:"securityopt"` + ShmSize int `schema:"shmsize"` + Squash bool `schema:"squash"` + Tag []string `schema:"t"` + Target string `schema:"target"` + Timestamp int64 `schema:"timestamp"` + Ulimits string `schema:"ulimits"` }{ Dockerfile: "Dockerfile", Registry: "docker.io", @@ -123,7 +127,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { return } - // convert label formats + // convert addcaps formats var addCaps = []string{} if _, found := r.URL.Query()["addcaps"]; found { var m = []string{} @@ -133,6 +137,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } addCaps = m } + addhosts := []string{} if _, found := r.URL.Query()["extrahosts"]; found { if err := json.Unmarshal([]byte(query.AddHosts), &addhosts); err != nil { @@ -142,7 +147,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } compression := archive.Compression(query.Compression) - // convert label formats + + // convert dropcaps formats var dropCaps = []string{} if _, found := r.URL.Query()["dropcaps"]; found { var m = []string{} @@ -153,7 +159,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { dropCaps = m } - // convert label formats + // convert devices formats var devices = []string{} if _, found := r.URL.Query()["devices"]; found { var m = []string{} @@ -233,7 +239,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } - // convert label formats + // convert annotations formats var annotations = []string{} if _, found := r.URL.Query()["annotations"]; found { if err := json.Unmarshal([]byte(query.Annotations), &annotations); err != nil { @@ -242,7 +248,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } - // convert label formats + // convert nsoptions formats nsoptions := buildah.NamespaceOptions{} if _, found := r.URL.Query()["nsoptions"]; found { if err := json.Unmarshal([]byte(query.NamespaceOptions), &nsoptions); err != nil { @@ -271,11 +277,75 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } } + jobs := 1 if _, found := r.URL.Query()["jobs"]; found { jobs = query.Jobs } + var ( + labelOpts = []string{} + seccomp string + apparmor string + ) + + if utils.IsLibpodRequest(r) { + seccomp = query.Seccomp + apparmor = query.AppArmor + // convert labelopts formats + if _, found := r.URL.Query()["labelopts"]; found { + var m = []string{} + if err := json.Unmarshal([]byte(query.LabelOpts), &m); err != nil { + utils.BadRequest(w, "labelopts", query.LabelOpts, err) + return + } + labelOpts = m + } + } else { + // handle security-opt + if _, found := r.URL.Query()["securityopt"]; found { + var securityOpts = []string{} + if err := json.Unmarshal([]byte(query.SecurityOpt), &securityOpts); err != nil { + utils.BadRequest(w, "securityopt", query.SecurityOpt, err) + return + } + for _, opt := range securityOpts { + if opt == "no-new-privileges" { + utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.New("no-new-privileges is not supported")) + return + } + con := strings.SplitN(opt, "=", 2) + if len(con) != 2 { + utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt name=value pair: %q", opt)) + return + } + + switch con[0] { + case "label": + labelOpts = append(labelOpts, con[1]) + case "apparmor": + apparmor = con[1] + case "seccomp": + seccomp = con[1] + default: + utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt 2: %q", opt)) + return + } + } + } + } + + // convert ulimits formats + var ulimits = []string{} + if _, found := r.URL.Query()["ulimits"]; found { + var m = []string{} + if err := json.Unmarshal([]byte(query.Ulimits), &m); err != nil { + utils.BadRequest(w, "ulimits", query.Ulimits, err) + return + } + ulimits = m + } + pullPolicy := buildahDefine.PullIfMissing if utils.IsLibpodRequest(r) { pullPolicy = buildahDefine.PolicyMap[query.PullPolicy] @@ -320,18 +390,22 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Annotations: annotations, Args: buildArgs, CommonBuildOpts: &buildah.CommonBuildOptions{ - AddHost: addhosts, - CPUPeriod: query.CpuPeriod, - CPUQuota: query.CpuQuota, - CPUShares: query.CpuShares, - CPUSetCPUs: query.CpuSetCpus, - DNSServers: dnsservers, - DNSOptions: dnsoptions, - DNSSearch: dnssearch, - HTTPProxy: query.HTTPProxy, - Memory: query.Memory, - MemorySwap: query.MemSwap, - ShmSize: strconv.Itoa(query.ShmSize), + AddHost: addhosts, + ApparmorProfile: apparmor, + CPUPeriod: query.CpuPeriod, + CPUQuota: query.CpuQuota, + CPUSetCPUs: query.CpuSetCpus, + CPUShares: query.CpuShares, + DNSOptions: dnsoptions, + DNSSearch: dnssearch, + DNSServers: dnsservers, + HTTPProxy: query.HTTPProxy, + LabelOpts: labelOpts, + Memory: query.Memory, + MemorySwap: query.MemSwap, + SeccompProfilePath: seccomp, + ShmSize: strconv.Itoa(query.ShmSize), + Ulimit: ulimits, }, CNIConfigDir: rtc.Network.CNIPluginDirs[0], CNIPluginPath: util.DefaultCNIPluginPath, @@ -349,6 +423,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Jobs: &jobs, Labels: labels, Layers: query.Layers, + LogRusage: query.LogRusage, Manifest: query.Manifest, MaxPullPushRetries: 3, NamespaceOptions: nsoptions, @@ -363,11 +438,11 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { RemoveIntermediateCtrs: query.Rm, ReportWriter: reporter, Squash: query.Squash, + Target: query.Target, SystemContext: &types.SystemContext{ AuthFilePath: authfile, DockerAuthConfig: creds, }, - Target: query.Target, } if _, found := r.URL.Query()["timestamp"]; found { diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index ee2d3c00d..eba5386b6 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -20,7 +20,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Network string `schema:"reference"` + Network string `schema:"network"` TLSVerify bool `schema:"tlsVerify"` LogDriver string `schema:"logDriver"` Start bool `schema:"start"` diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index c79d79136..c47a16551 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -120,6 +120,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if options.ForceRmIntermediateCtrs { params.Set("forcerm", "1") } + if options.RemoveIntermediateCtrs { + params.Set("rm", "1") + } if len(options.From) > 0 { params.Set("from", options.From) } @@ -140,6 +143,23 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } params.Set("labels", l) } + + if opt := options.CommonBuildOpts.LabelOpts; len(opt) > 0 { + o, err := jsoniter.MarshalToString(opt) + if err != nil { + return nil, err + } + params.Set("labelopts", o) + } + + if len(options.CommonBuildOpts.SeccompProfilePath) > 0 { + params.Set("seccomp", options.CommonBuildOpts.SeccompProfilePath) + } + + if len(options.CommonBuildOpts.ApparmorProfile) > 0 { + params.Set("apparmor", options.CommonBuildOpts.ApparmorProfile) + } + if options.Layers { params.Set("layers", "1") } @@ -174,6 +194,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if len(platform) > 0 { params.Set("platform", platform) } + params.Set("pullpolicy", options.PullPolicy.String()) if options.Quiet { @@ -182,6 +203,10 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if options.RemoveIntermediateCtrs { params.Set("rm", "1") } + if len(options.Target) > 0 { + params.Set("target", options.Target) + } + if hosts := options.CommonBuildOpts.AddHost; len(hosts) > 0 { h, err := jsoniter.MarshalToString(hosts) if err != nil { @@ -212,6 +237,13 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Set("timestamp", strconv.FormatInt(t.Unix(), 10)) } + if len(options.CommonBuildOpts.Ulimit) > 0 { + ulimitsJSON, err := json.Marshal(options.CommonBuildOpts.Ulimit) + if err != nil { + return nil, err + } + params.Set("ulimits", string(ulimitsJSON)) + } var ( headers map[string]string err error diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index cb9e0721b..4d1361746 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -571,7 +571,7 @@ var _ = Describe("Podman containers ", func() { // Valid filter params container should be pruned now. filters := map[string][]string{ - "until": {"0s"}, + "until": {"5000000000"}, //Friday, June 11, 2128 } pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filters)) Expect(err).To(BeNil()) @@ -579,6 +579,26 @@ var _ = Describe("Podman containers ", func() { Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(1)) }) + It("podman list containers with until filter", func() { + var name = "top" + _, err := bt.RunTopContainer(&name, nil) + Expect(err).To(BeNil()) + + filters := map[string][]string{ + "until": {"5000000000"}, //Friday, June 11, 2128 + } + c, err := containers.List(bt.conn, new(containers.ListOptions).WithFilters(filters).WithAll(true)) + Expect(err).To(BeNil()) + Expect(len(c)).To(Equal(1)) + + filters = map[string][]string{ + "until": {"500000"}, // Tuesday, January 6, 1970 + } + c, err = containers.List(bt.conn, new(containers.ListOptions).WithFilters(filters).WithAll(true)) + Expect(err).To(BeNil()) + Expect(len(c)).To(Equal(0)) + }) + It("podman prune running containers", func() { // Start the container. var name = "top" diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 19d704da1..45791cd84 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -237,7 +237,7 @@ func prepareUntilFilterFunc(filterValues []string) (func(container *libpod.Conta return nil, err } return func(c *libpod.Container) bool { - if !until.IsZero() && c.CreatedTime().After((until)) { + if !until.IsZero() && c.CreatedTime().Before(until) { return true } return false diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index b0d9dc797..a98c9168a 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -146,7 +146,11 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // This should always be checked after storage-driver is checked if len(cfg.StorageOpts) > 0 { storageSet = true - storageOpts.GraphDriverOptions = cfg.StorageOpts + if len(cfg.StorageOpts) == 1 && cfg.StorageOpts[0] == "" { + storageOpts.GraphDriverOptions = []string{} + } else { + storageOpts.GraphDriverOptions = cfg.StorageOpts + } } if opts.migrate { options = append(options, libpod.WithMigrate()) diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 554ea7c97..32b3b5c2b 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -56,8 +56,7 @@ type ListResponse struct { } type SSHOptions struct { - Execute bool - Args []string + Args []string } type StartOptions struct{} diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index a68d68ac3..cc5c01de6 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -55,10 +55,16 @@ func NewIgnitionFile(ign DynamicIgnition) error { } ignPassword := Passwd{ - Users: []PasswdUser{{ - Name: ign.Name, - SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)}, - }}, + Users: []PasswdUser{ + { + Name: ign.Name, + SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)}, + }, + { + Name: "root", + SSHAuthorizedKeys: []SSHAuthorizedKey{SSHAuthorizedKey(ign.Key)}, + }, + }, } ignStorage := Storage{ diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index b4427f160..fd22f465b 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -37,12 +37,8 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { if len(opts.Name) > 0 { vm.Name = opts.Name } - vm.IgnitionFilePath = opts.IgnitionPath - // If no ignitionfilepath was provided, use defaults - if len(vm.IgnitionFilePath) < 1 { - ignitionFile := filepath.Join(vmConfigDir, vm.Name+".ign") - vm.IgnitionFilePath = ignitionFile - } + ignitionFile := filepath.Join(vmConfigDir, vm.Name+".ign") + vm.IgnitionFilePath = ignitionFile // An image was specified if len(opts.ImagePath) > 0 { @@ -125,6 +121,9 @@ func LoadVMByName(name string) (machine.VM, error) { // Init writes the json configuration file to the filesystem for // other verbs (start, stop) func (v *MachineVM) Init(opts machine.InitOptions) error { + var ( + key string + ) sshDir := filepath.Join(homedir.Get(), ".ssh") // GetConfDir creates the directory so no need to check for // its existence @@ -164,9 +163,18 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { // Add location of bootable image v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath) // This kind of stinks but no other way around this r/n - uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername) - if err := machine.AddConnection(&uri, v.Name, filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil { - return err + if len(opts.IgnitionPath) < 1 { + uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername) + if err := machine.AddConnection(&uri, v.Name, filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil { + return err + } + + uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root") + if err := machine.AddConnection(&uriRoot, v.Name+"-root", filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil { + return err + } + } else { + fmt.Println("An ignition path was provided. No SSH connection was added to Podman") } // Write the JSON file b, err := json.MarshalIndent(v, "", " ") @@ -176,9 +184,14 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil { return err } - key, err := machine.CreateSSHKeys(v.IdentityPath) - if err != nil { - return err + + // User has provided ignition file so keygen + // will be skipped. + if len(opts.IgnitionPath) < 1 { + key, err = machine.CreateSSHKeys(v.IdentityPath) + if err != nil { + return err + } } // Run arch specific things that need to be done if err := v.prepare(); err != nil { @@ -200,6 +213,15 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { return errors.Errorf("error resizing image: %q", err) } } + // If the user provides an ignition file, we need to + // copy it into the conf dir + if len(opts.IgnitionPath) > 0 { + inputIgnition, err := ioutil.ReadFile(opts.IgnitionPath) + if err != nil { + return err + } + return ioutil.WriteFile(v.IgnitionFilePath, inputIgnition, 0644) + } // Write the ignition file ign := machine.DynamicIgnition{ Name: opts.Username, @@ -340,6 +362,10 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun if err := machine.RemoveConnection(v.Name); err != nil { logrus.Error(err) } + if err := machine.RemoveConnection(v.Name + "-root"); err != nil { + logrus.Error(err) + } + vmConfigDir, err := machine.GetConfDir(vmtype) if err != nil { return "", nil, err @@ -383,7 +409,7 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { port := strconv.Itoa(v.Port) args := []string{"-i", v.IdentityPath, "-p", port, sshDestination} - if opts.Execute { + if len(opts.Args) > 0 { args = append(args, opts.Args...) } else { fmt.Printf("Connecting to vm %s. To close connection, use `~.` or `exit`\n", v.Name) @@ -429,7 +455,11 @@ func getDiskSize(path string) (uint64, error) { } // List lists all vm's that use qemu virtualization -func List(opts machine.ListOptions) ([]*machine.ListResponse, error) { +func List(_ machine.ListOptions) ([]*machine.ListResponse, error) { + return GetVMInfos() +} + +func GetVMInfos() ([]*machine.ListResponse, error) { vmConfigDir, err := machine.GetConfDir(vmtype) if err != nil { return nil, err @@ -476,3 +506,16 @@ func List(opts machine.ListOptions) ([]*machine.ListResponse, error) { } return listed, err } + +func IsValidVMName(name string) (bool, error) { + infos, err := GetVMInfos() + if err != nil { + return false, err + } + for _, vm := range infos { + if vm.Name == name { + return true, nil + } + } + return false, nil +} diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index 0b7d1782c..ecefb65ff 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -35,9 +35,9 @@ import ( "golang.org/x/sys/unix" ) -// get NSRunDir returns the dir of where to create the netNS. When running +// GetNSRunDir returns the dir of where to create the netNS. When running // rootless, it needs to be at a location writable by user. -func getNSRunDir() (string, error) { +func GetNSRunDir() (string, error) { if rootless.IsRootless() { rootlessDir, err := util.GetRuntimeDir() if err != nil { @@ -51,15 +51,21 @@ func getNSRunDir() (string, error) { // NewNS creates a new persistent (bind-mounted) network namespace and returns // an object representing that namespace, without switching to it. func NewNS() (ns.NetNS, error) { - nsRunDir, err := getNSRunDir() + b := make([]byte, 16) + _, err := rand.Reader.Read(b) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to generate random netns name: %v", err) } + nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + return NewNSWithName(nsName) +} - b := make([]byte, 16) - _, err = rand.Reader.Read(b) +// NewNSWithName creates a new persistent (bind-mounted) network namespace and returns +// an object representing that namespace, without switching to it. +func NewNSWithName(name string) (ns.NetNS, error) { + nsRunDir, err := GetNSRunDir() if err != nil { - return nil, fmt.Errorf("failed to generate random netns name: %v", err) + return nil, err } // Create the directory for mounting network namespaces @@ -93,10 +99,8 @@ func NewNS() (ns.NetNS, error) { } } - nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) - // create an empty file at the mount point - nsPath := path.Join(nsRunDir, nsName) + nsPath := path.Join(nsRunDir, name) mountPointFd, err := os.Create(nsPath) if err != nil { return nil, err @@ -177,7 +181,7 @@ func NewNS() (ns.NetNS, error) { // UnmountNS unmounts the NS held by the netns object func UnmountNS(ns ns.NetNS) error { - nsRunDir, err := getNSRunDir() + nsRunDir, err := GetNSRunDir() if err != nil { return err } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 7a2bf0377..918b9a7e6 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -61,6 +61,10 @@ static int open_files_max_fd; static fd_set *open_files_set; static uid_t rootless_uid_init; static gid_t rootless_gid_init; +static bool do_socket_activation = false; +static char *saved_systemd_listen_fds; +static char *saved_systemd_listen_pid; +static char *saved_systemd_listen_fdnames; static int syscall_setresuid (uid_t ruid, uid_t euid, uid_t suid) @@ -242,6 +246,10 @@ static void __attribute__((constructor)) init() { const char *xdg_runtime_dir; const char *pause; + const char *listen_pid; + const char *listen_fds; + const char *listen_fdnames; + DIR *d; pause = getenv ("_PODMAN_PAUSE"); @@ -293,6 +301,26 @@ static void __attribute__((constructor)) init() closedir (d); } + listen_pid = getenv("LISTEN_PID"); + listen_fds = getenv("LISTEN_FDS"); + listen_fdnames = getenv("LISTEN_FDNAMES"); + + if (listen_pid != NULL && listen_fds != NULL && strtol(listen_pid, NULL, 10) == getpid()) + { + // save systemd socket environment for rootless child + do_socket_activation = true; + saved_systemd_listen_pid = strdup(listen_pid); + saved_systemd_listen_fds = strdup(listen_fds); + saved_systemd_listen_fdnames = strdup(listen_fdnames); + if (saved_systemd_listen_pid == NULL + || saved_systemd_listen_fds == NULL + || saved_systemd_listen_fdnames == NULL) + { + fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + } + /* Shortcut. If we are able to join the pause pid file, do it now so we don't need to re-exec. */ xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); @@ -635,6 +663,12 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) for (f = 3; f <= open_files_max_fd; f++) if (is_fd_inherited (f)) close (f); + if (do_socket_activation) + { + unsetenv ("LISTEN_PID"); + unsetenv ("LISTEN_FDS"); + unsetenv ("LISTEN_FDNAMES"); + } return pid; } @@ -660,6 +694,15 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) _exit (EXIT_FAILURE); } + if (do_socket_activation) + { + char s[32]; + sprintf (s, "%d", getpid()); + setenv ("LISTEN_PID", s, true); + setenv ("LISTEN_FDS", saved_systemd_listen_fds, true); + setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); + } + setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1); setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1); @@ -777,9 +820,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re char **argv; char uid[16]; char gid[16]; - char *listen_fds = NULL; - char *listen_pid = NULL; - bool do_socket_activation = false; char *cwd = getcwd (NULL, 0); sigset_t sigset, oldsigset; @@ -789,14 +829,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re _exit (EXIT_FAILURE); } - listen_pid = getenv("LISTEN_PID"); - listen_fds = getenv("LISTEN_FDS"); - - if (listen_pid != NULL && listen_fds != NULL) - { - if (strtol(listen_pid, NULL, 10) == getpid()) - do_socket_activation = true; - } sprintf (uid, "%d", geteuid ()); sprintf (gid, "%d", getegid ()); @@ -814,7 +846,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re { long num_fds; - num_fds = strtol (listen_fds, NULL, 10); + num_fds = strtol (saved_systemd_listen_fds, NULL, 10); if (num_fds != LONG_MIN && num_fds != LONG_MAX) { int f; @@ -863,6 +895,8 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re char s[32]; sprintf (s, "%d", getpid()); setenv ("LISTEN_PID", s, true); + setenv ("LISTEN_FDS", saved_systemd_listen_fds, true); + setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); } setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index b87375a92..845dfdad7 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -236,9 +236,6 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. case specgen.Private: fallthrough case specgen.Bridge: - if postConfigureNetNS && rootless.IsRootless() { - return nil, errors.New("CNI networks not supported with user namespaces") - } portMappings, err := createPortMappings(ctx, s, img) if err != nil { return nil, err diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 5d7bf1930..20151f016 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -4,6 +4,7 @@ import ( "context" "github.com/containers/podman/v3/libpod" + "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -94,8 +95,19 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod } switch p.NetNS.NSMode { - case specgen.Bridge, specgen.Default, "": - logrus.Debugf("Pod using default network mode") + case specgen.Default, "": + if p.NoInfra { + logrus.Debugf("No networking because the infra container is missing") + break + } + if rootless.IsRootless() { + logrus.Debugf("Pod will use slirp4netns") + options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) + } else { + logrus.Debugf("Pod using bridge network mode") + } + case specgen.Bridge: + logrus.Debugf("Pod using bridge network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") options = append(options, libpod.WithPodHostNetwork()) |