diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/bindings/images/images.go | 4 | ||||
-rw-r--r-- | pkg/bindings/images/pull.go | 4 | ||||
-rw-r--r-- | pkg/bindings/images/push.go | 5 | ||||
-rw-r--r-- | pkg/bindings/images/types.go | 10 | ||||
-rw-r--r-- | pkg/bindings/internal/util/util.go | 3 | ||||
-rw-r--r-- | pkg/bindings/kube/kube.go | 4 | ||||
-rw-r--r-- | pkg/bindings/kube/types.go | 2 | ||||
-rw-r--r-- | pkg/bindings/manifests/manifests.go | 10 | ||||
-rw-r--r-- | pkg/bindings/manifests/types.go | 4 | ||||
-rw-r--r-- | pkg/bindings/test/types_test.go | 66 | ||||
-rw-r--r-- | pkg/domain/infra/runtime_libpod.go | 51 | ||||
-rw-r--r-- | pkg/machine/e2e/config_init_test.go | 10 | ||||
-rw-r--r-- | pkg/machine/e2e/init_test.go | 20 | ||||
-rw-r--r-- | pkg/namespaces/namespaces.go | 49 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 11 | ||||
-rw-r--r-- | pkg/specgen/namespaces.go | 15 | ||||
-rw-r--r-- | pkg/util/utils.go | 8 |
17 files changed, 198 insertions, 78 deletions
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index bb7867c4e..ea7d445db 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -282,9 +282,9 @@ func Search(ctx context.Context, term string, options *SearchOptions) ([]entitie } params.Set("term", term) - // Note: we have to verify if skipped is false. + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index 109981c63..8caf45c0e 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -35,9 +35,9 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, } params.Set("reference", rawImage) + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") - // Note: we have to verify if skipped is false. params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } diff --git a/pkg/bindings/images/push.go b/pkg/bindings/images/push.go index f1e059f8c..0e1309e91 100644 --- a/pkg/bindings/images/push.go +++ b/pkg/bindings/images/push.go @@ -38,10 +38,9 @@ func Push(ctx context.Context, source string, destination string, options *PushO if err != nil { return err } - // SkipTLSVerify is special. We need to delete the param added by - // toparams and change the key and flip the bool + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } params.Set("destination", destination) diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 3ecfb9e09..f8630926e 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -136,9 +136,9 @@ type PushOptions struct { // ProgressWriter is a writer where push progress are sent. // Since API handler for image push is quiet by default, WithQuiet(false) is necessary for // the writer to receive progress messages. - ProgressWriter *io.Writer + ProgressWriter *io.Writer `schema:"-"` // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // RemoveSignatures Discard any pre-existing signatures in the image. RemoveSignatures *bool // Username for authenticating against the registry. @@ -158,7 +158,7 @@ type SearchOptions struct { // Limit the number of results. Limit *int // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // ListTags search the available tags of the repository ListTags *bool } @@ -183,12 +183,12 @@ type PullOptions struct { // Password for authenticating against the registry. Password *string // ProgressWriter is a writer where pull progress are sent. - ProgressWriter *io.Writer + ProgressWriter *io.Writer `schema:"-"` // Quiet can be specified to suppress pull progress when pulling. Ignored // for remote calls. Quiet *bool // SkipTLSVerify to skip HTTPS and certificate verification. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // Username for authenticating against the registry. Username *string // Variant will overwrite the local variant for image pulls. diff --git a/pkg/bindings/internal/util/util.go b/pkg/bindings/internal/util/util.go index f8f99d6c1..52ce14738 100644 --- a/pkg/bindings/internal/util/util.go +++ b/pkg/bindings/internal/util/util.go @@ -74,6 +74,9 @@ func ToParams(o interface{}) (url.Values, error) { } paramName := fieldName if pn, ok := sType.Field(i).Tag.Lookup("schema"); ok { + if pn == "-" { + continue + } paramName = pn } switch { diff --git a/pkg/bindings/kube/kube.go b/pkg/bindings/kube/kube.go index e727439cf..1b9f888ef 100644 --- a/pkg/bindings/kube/kube.go +++ b/pkg/bindings/kube/kube.go @@ -40,8 +40,10 @@ func PlayWithBody(ctx context.Context, body io.Reader, options *PlayOptions) (*e if err != nil { return nil, err } + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Set("tlsVerify", strconv.FormatBool(options.GetSkipTLSVerify())) + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } if options.Start != nil { params.Set("start", strconv.FormatBool(options.GetStart())) diff --git a/pkg/bindings/kube/types.go b/pkg/bindings/kube/types.go index 783d1912a..279a9f8f3 100644 --- a/pkg/bindings/kube/types.go +++ b/pkg/bindings/kube/types.go @@ -27,7 +27,7 @@ type PlayOptions struct { SignaturePolicy *string // SkipTLSVerify - skip https and certificate validation when // contacting container registries. - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` // SeccompProfileRoot - path to a directory containing seccomp // profiles. SeccompProfileRoot *string diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 0163d21a0..752366937 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -165,10 +165,9 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt if err != nil { return "", err } - // SkipTLSVerify is special. We need to delete the param added by - // ToParams() and change the key and flip the bool + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } @@ -246,10 +245,9 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp if err != nil { return "", err } - // SkipTLSVerify is special. We need to delete the param added by - // ToParams() and change the key and flip the bool + // SkipTLSVerify is special. It's not being serialized by ToParams() + // because we need to flip the boolean. if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go index 5f2557fe1..fec3f9d13 100644 --- a/pkg/bindings/manifests/types.go +++ b/pkg/bindings/manifests/types.go @@ -32,7 +32,7 @@ type AddOptions struct { Authfile *string Password *string Username *string - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` } //go:generate go run ../generator/generator.go RemoveOptions @@ -60,5 +60,5 @@ type ModifyOptions struct { Authfile *string Password *string Username *string - SkipTLSVerify *bool + SkipTLSVerify *bool `schema:"-"` } diff --git a/pkg/bindings/test/types_test.go b/pkg/bindings/test/types_test.go new file mode 100644 index 000000000..bc98c8b7d --- /dev/null +++ b/pkg/bindings/test/types_test.go @@ -0,0 +1,66 @@ +package bindings_test + +import ( + "bytes" + + "github.com/containers/podman/v4/pkg/bindings/images" + "github.com/containers/podman/v4/pkg/bindings/kube" + "github.com/containers/podman/v4/pkg/bindings/manifests" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Binding types", func() { + It("serialize image pull options", func() { + var writer bytes.Buffer + opts := new(images.PullOptions).WithOS("foo").WithProgressWriter(&writer).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("os")).To(Equal("foo")) + Expect(params.Has("progresswriter")).To(BeFalse()) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize image push options", func() { + var writer bytes.Buffer + opts := new(images.PushOptions).WithAll(true).WithProgressWriter(&writer).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("all")).To(Equal("true")) + Expect(params.Has("progresswriter")).To(BeFalse()) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize image search options", func() { + opts := new(images.SearchOptions).WithLimit(123).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("limit")).To(Equal("123")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize manifest modify options", func() { + opts := new(manifests.ModifyOptions).WithOS("foo").WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("os")).To(Equal("foo")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize manifest add options", func() { + opts := new(manifests.AddOptions).WithAll(true).WithOS("foo").WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("all")).To(Equal("true")) + Expect(params.Get("os")).To(Equal("foo")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) + + It("serialize kube play options", func() { + opts := new(kube.PlayOptions).WithQuiet(true).WithSkipTLSVerify(true) + params, err := opts.ToParams() + Expect(err).ToNot(HaveOccurred()) + Expect(params.Get("quiet")).To(Equal("true")) + Expect(params.Has("skiptlsverify")).To(BeFalse()) + }) +}) diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index f76fab4ea..a23a23653 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -294,57 +294,6 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin options.AutoUserNsOpts = *opts return &options, nil } - if mode.IsKeepID() { - if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 { - return nil, errors.New("cannot specify custom mappings with --userns=keep-id") - } - if len(subUIDMap) > 0 || len(subGIDMap) > 0 { - return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id") - } - if !rootless.IsRootless() { - return nil, errors.New("keep-id is only supported in rootless mode") - } - min := func(a, b int) int { - if a < b { - return a - } - return b - } - - uid := rootless.GetRootlessUID() - gid := rootless.GetRootlessGID() - - uids, gids, err := rootless.GetConfiguredMappings() - if err != nil { - return nil, fmt.Errorf("cannot read mappings: %w", err) - } - maxUID, maxGID := 0, 0 - for _, u := range uids { - maxUID += u.Size - } - for _, g := range gids { - maxGID += g.Size - } - - options.UIDMap, options.GIDMap = nil, nil - - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)}) - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1}) - if maxUID > uid { - options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid}) - } - - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)}) - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1}) - if maxGID > gid { - options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid}) - } - - options.HostUIDMapping = false - options.HostGIDMapping = false - // Simply ignore the setting and do not set up an inner namespace for root as it is a no-op - return &options, nil - } if subGIDMap == "" && subUIDMap != "" { subGIDMap = subUIDMap diff --git a/pkg/machine/e2e/config_init_test.go b/pkg/machine/e2e/config_init_test.go index d6c7990b0..305d101a3 100644 --- a/pkg/machine/e2e/config_init_test.go +++ b/pkg/machine/e2e/config_init_test.go @@ -9,6 +9,7 @@ type initMachine struct { --cpus uint Number of CPUs (default 1) --disk-size uint Disk size in GB (default 100) --ignition-path string Path to ignition file + --username string Username of the remote user (default "core" for FCOS, "user" for Fedora) --image-path string Path to qcow image (default "testing") -m, --memory uint Memory in MB (default 2048) --now Start machine now @@ -21,6 +22,7 @@ type initMachine struct { cpus *uint diskSize *uint ignitionPath string + username string imagePath string memory *uint now bool @@ -42,6 +44,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string { if l := len(i.ignitionPath); l > 0 { cmd = append(cmd, "--ignition-path", i.ignitionPath) } + if l := len(i.username); l > 0 { + cmd = append(cmd, "--username", i.username) + } if l := len(i.imagePath); l > 0 { cmd = append(cmd, "--image-path", i.imagePath) } @@ -76,6 +81,11 @@ func (i *initMachine) withIgnitionPath(path string) *initMachine { //nolint:unus return i } +func (i *initMachine) withUsername(username string) *initMachine { + i.username = username + return i +} + func (i *initMachine) withImagePath(path string) *initMachine { i.imagePath = path return i diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index 859a3ca46..c298d3b14 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -77,6 +77,26 @@ var _ = Describe("podman machine init", func() { Expect(inspectAfter[0].State).To(Equal(machine.Running)) }) + It("simple init with username", func() { + i := new(initMachine) + remoteUsername := "remoteuser" + session, err := mb.setCmd(i.withImagePath(mb.imagePath).withUsername(remoteUsername)).run() + Expect(err).To(BeNil()) + Expect(session).To(Exit(0)) + + inspectBefore, ec, err := mb.toQemuInspectInfo() + Expect(err).To(BeNil()) + Expect(ec).To(BeZero()) + + Expect(len(inspectBefore)).To(BeNumerically(">", 0)) + testMachine := inspectBefore[0] + Expect(testMachine.Name).To(Equal(mb.names[0])) + Expect(testMachine.Resources.CPUs).To(Equal(uint64(1))) + Expect(testMachine.Resources.Memory).To(Equal(uint64(2048))) + Expect(testMachine.SSHConfig.RemoteUsername).To((Equal(remoteUsername))) + + }) + It("machine init with cpus, disk size, memory, timezone", func() { name := randomString() i := new(initMachine) diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go index 8eacb8da7..6dd576ea5 100644 --- a/pkg/namespaces/namespaces.go +++ b/pkg/namespaces/namespaces.go @@ -21,6 +21,14 @@ const ( slirpType = "slirp4netns" ) +// KeepIDUserNsOptions defines how to keepIDmatically create a user namespace. +type KeepIDUserNsOptions struct { + // UID is the target uid in the user namespace. + UID *uint32 + // GID is the target uid in the user namespace. + GID *uint32 +} + // CgroupMode represents cgroup mode in the container. type CgroupMode string @@ -93,7 +101,8 @@ func (n UsernsMode) IsHost() bool { // IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is kept inside of the namespace. func (n UsernsMode) IsKeepID() bool { - return n == "keep-id" + parts := strings.Split(string(n), ":") + return parts[0] == "keep-id" } // IsNoMap indicates whether container uses a mapping where the (uid, gid) on the host is not present in the namespace. @@ -154,6 +163,44 @@ func (n UsernsMode) GetAutoOptions() (*types.AutoUserNsOptions, error) { return &options, nil } +// GetKeepIDOptions returns a KeepIDUserNsOptions with the settings to keepIDmatically set up +// a user namespace. +func (n UsernsMode) GetKeepIDOptions() (*KeepIDUserNsOptions, error) { + parts := strings.SplitN(string(n), ":", 2) + if parts[0] != "keep-id" { + return nil, fmt.Errorf("wrong user namespace mode") + } + options := KeepIDUserNsOptions{} + if len(parts) == 1 { + return &options, nil + } + for _, o := range strings.Split(parts[1], ",") { + v := strings.SplitN(o, "=", 2) + if len(v) != 2 { + return nil, fmt.Errorf("invalid option specified: %q", o) + } + switch v[0] { + case "uid": + s, err := strconv.ParseUint(v[1], 10, 32) + if err != nil { + return nil, err + } + v := uint32(s) + options.UID = &v + case "gid": + s, err := strconv.ParseUint(v[1], 10, 32) + if err != nil { + return nil, err + } + v := uint32(s) + options.GID = &v + default: + return nil, fmt.Errorf("unknown option specified: %q", v[0]) + } + } + return &options, nil +} + // IsPrivate indicates whether the container uses the a private userns. func (n UsernsMode) IsPrivate() bool { return !(n.IsHost() || n.IsContainer()) diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index f0d4e9153..e27a3abac 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -11,6 +11,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/namespaces" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" @@ -198,12 +199,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if !rootless.IsRootless() { return nil, errors.New("keep-id is only supported in rootless mode") } - toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) + opts, err := namespaces.UsernsMode(s.UserNS.String()).GetKeepIDOptions() + if err != nil { + return nil, err + } + if opts.UID == nil && opts.GID == nil { + toReturn = append(toReturn, libpod.WithAddCurrentUserPasswdEntry()) + } // If user is not overridden, set user in the container // to user running Podman. if s.User == "" { - _, uid, gid, err := util.GetKeepIDMapping() + _, uid, gid, err := util.GetKeepIDMapping(opts) if err != nil { return nil, err } diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 03a2049f6..8cc0fe6a9 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -11,6 +11,7 @@ import ( "github.com/containers/common/pkg/cgroups" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/namespaces" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -308,6 +309,14 @@ func ParseUserNamespace(ns string) (Namespace, error) { case ns == "keep-id": toReturn.NSMode = KeepID return toReturn, nil + case strings.HasPrefix(ns, "keep-id:"): + split := strings.SplitN(ns, ":", 2) + if len(split) != 2 { + return toReturn, errors.New("invalid setting for keep-id: mode") + } + toReturn.NSMode = KeepID + toReturn.Value = split[1] + return toReturn, nil case ns == "nomap": toReturn.NSMode = NoMap return toReturn, nil @@ -490,7 +499,11 @@ func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *gene return user, err } case KeepID: - mappings, uid, gid, err := util.GetKeepIDMapping() + opts, err := namespaces.UsernsMode(userns.String()).GetKeepIDOptions() + if err != nil { + return user, err + } + mappings, uid, gid, err := util.GetKeepIDMapping(opts) if err != nil { return user, err } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 33c11d611..87e403986 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -342,7 +342,7 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) { } // GetKeepIDMapping returns the mappings and the user to use when keep-id is used -func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) { +func GetKeepIDMapping(opts *namespaces.KeepIDUserNsOptions) (*stypes.IDMappingOptions, int, int, error) { if !rootless.IsRootless() { return nil, -1, -1, errors.New("keep-id is only supported in rootless mode") } @@ -359,6 +359,12 @@ func GetKeepIDMapping() (*stypes.IDMappingOptions, int, int, error) { uid := rootless.GetRootlessUID() gid := rootless.GetRootlessGID() + if opts.UID != nil { + uid = int(*opts.UID) + } + if opts.GID != nil { + gid = int(*opts.GID) + } uids, gids, err := rootless.GetConfiguredMappings() if err != nil { |