From 828708bac2d48d947c86a0162cdb1767c51082b3 Mon Sep 17 00:00:00 2001 From: danishprakash Date: Thu, 9 Feb 2023 18:20:36 +0530 Subject: [PATCH] create: add support for --group-entry * add test * update documentation Signed-off-by: danishprakash --- cmd/podman/common/create.go | 4 ++++ docs/source/markdown/options/group-entry.md | 9 +++++++++ docs/source/markdown/podman-create.1.md.in | 2 ++ docs/source/markdown/podman-run.1.md.in | 2 ++ libpod/container_config.go | 2 ++ libpod/container_internal_common.go | 22 ++++++++++++++++----- libpod/options.go | 13 ++++++++++++ pkg/domain/entities/pods.go | 1 + pkg/specgen/generate/container_create.go | 3 +++ pkg/specgen/specgen.go | 4 +++- pkg/specgenutil/specgen.go | 4 ++++ test/e2e/run_passwd_test.go | 12 +++++++++++ 12 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 docs/source/markdown/options/group-entry.md diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 5ed8ae8b61..d6ae87c5e9 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -604,6 +604,10 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, createFlags.StringVar(&cf.PasswdEntry, passwdEntryName, "", "Entry to write to /etc/passwd") _ = cmd.RegisterFlagCompletionFunc(passwdEntryName, completion.AutocompleteNone) + groupEntryName := "group-entry" + createFlags.StringVar(&cf.GroupEntry, groupEntryName, "", "Entry to write to /etc/group") + _ = cmd.RegisterFlagCompletionFunc(groupEntryName, completion.AutocompleteNone) + decryptionKeysFlagName := "decryption-key" createFlags.StringSliceVar( &cf.DecryptionKeys, diff --git a/docs/source/markdown/options/group-entry.md b/docs/source/markdown/options/group-entry.md new file mode 100644 index 0000000000..28b364be1b --- /dev/null +++ b/docs/source/markdown/options/group-entry.md @@ -0,0 +1,9 @@ +####> This option file is used in: +####> podman create, run +####> If file is edited, make sure the changes +####> are applicable to all of those. +#### **--group-entry**=*ENTRY* + +Customize the entry that is written to the `/etc/group` file within the container when `--user` is used. + +The variables $GROUPNAME, $GID, and $USERLIST are automatically replaced with their value at runtime if present. diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in index 7ba7bd27b4..2d0eb86725 100644 --- a/docs/source/markdown/podman-create.1.md.in +++ b/docs/source/markdown/podman-create.1.md.in @@ -161,6 +161,8 @@ See [**Environment**](#environment) note below for precedence and examples. @@option group-add +@@option group-entry + @@option health-cmd @@option health-interval diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in index a144248a9a..b63a5b1e7b 100644 --- a/docs/source/markdown/podman-run.1.md.in +++ b/docs/source/markdown/podman-run.1.md.in @@ -194,6 +194,8 @@ See [**Environment**](#environment) note below for precedence and examples. @@option group-add +@@option group-entry + @@option health-cmd @@option health-interval diff --git a/libpod/container_config.go b/libpod/container_config.go index 1fdbe70233..f771eb9bf8 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -354,6 +354,8 @@ type ContainerMiscConfig struct { CgroupsMode string `json:"cgroupsMode,omitempty"` // Cgroup parent of the container. CgroupParent string `json:"cgroupParent"` + // GroupEntry specifies arbitrary data to append to a file. + GroupEntry string `json:"group_entry,omitempty"` // LogPath log location LogPath string `json:"logPath"` // LogTag is the tag used for logging diff --git a/libpod/container_internal_common.go b/libpod/container_internal_common.go index 4d1cabb7d5..d184570678 100644 --- a/libpod/container_internal_common.go +++ b/libpod/container_internal_common.go @@ -2226,7 +2226,7 @@ func (c *Container) generateGroupEntry() (string, error) { groupString += entry addedGID = gid } - if c.config.User != "" { + if c.config.User != "" || c.config.GroupEntry != "" { entry, err := c.generateUserGroupEntry(addedGID) if err != nil { return "", err @@ -2281,7 +2281,7 @@ func (c *Container) generateCurrentUserGroupEntry() (string, int, error) { // Make an entry in /etc/group for the group the container was specified to run // as. func (c *Container) generateUserGroupEntry(addedGID int) (string, error) { - if c.config.User == "" { + if c.config.User == "" && c.config.GroupEntry == "" { return "", nil } @@ -2301,14 +2301,26 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, error) { } // Check if the group already exists - _, err = lookup.GetGroup(c.state.Mountpoint, group) + g, err := lookup.GetGroup(c.state.Mountpoint, group) if err != runcuser.ErrNoGroupEntries { return "", err } + if c.config.GroupEntry != "" { + return c.groupEntry(g.Name, strconv.Itoa(g.Gid), g.List), nil + } + return fmt.Sprintf("%d:x:%d:%s\n", gid, gid, splitUser[0]), nil } +func (c *Container) groupEntry(groupname, gid string, list []string) string { + s := c.config.GroupEntry + s = strings.ReplaceAll(s, "$GROUPNAME", groupname) + s = strings.ReplaceAll(s, "$GID", gid) + s = strings.ReplaceAll(s, "$USERLIST", strings.Join(list, ",")) + return s + "\n" +} + // generatePasswdEntry generates an entry or entries into /etc/passwd as // required by container configuration. // Generally speaking, we will make an entry under two circumstances: @@ -2488,7 +2500,7 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, error) { return fmt.Sprintf("%d:*:%d:%d:container user:%s:/bin/sh\n", uid, uid, gid, c.WorkingDir()), nil } -func (c *Container) passwdEntry(username string, uid, gid, name, homeDir string) string { +func (c *Container) passwdEntry(username, uid, gid, name, homeDir string) string { s := c.config.PasswdEntry s = strings.ReplaceAll(s, "$USERNAME", username) s = strings.ReplaceAll(s, "$UID", uid) @@ -2512,7 +2524,7 @@ func (c *Container) passwdEntry(username string, uid, gid, name, homeDir string) // read-only. In this case, the function will return nothing ("", "", nil). func (c *Container) generatePasswdAndGroup() (string, string, error) { if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" && - len(c.config.HostUsers) == 0 { + len(c.config.HostUsers) == 0 && c.config.GroupEntry == "" { return "", "", nil } diff --git a/libpod/options.go b/libpod/options.go index e8ffd9a7bc..cd61fe85b9 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -2277,6 +2277,19 @@ func WithPasswdEntry(passwdEntry string) CtrCreateOption { } } +// WithGroupEntry sets the entry to write to the /etc/group file. +func WithGroupEntry(groupEntry string) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.GroupEntry = groupEntry + + return nil + } +} + // WithMountAllDevices sets the option to mount all of a privileged container's // host devices func WithMountAllDevices() CtrCreateOption { diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 1ee975da39..7c05c653d2 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -301,6 +301,7 @@ type ContainerCreateOptions struct { CgroupConf []string + GroupEntry string PasswdEntry string } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 2e78267ef8..27305884aa 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -312,6 +312,9 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l if s.PasswdEntry != "" { options = append(options, libpod.WithPasswdEntry(s.PasswdEntry)) } + if s.GroupEntry != "" { + options = append(options, libpod.WithGroupEntry(s.GroupEntry)) + } if s.Privileged { options = append(options, libpod.WithMountAllDevices()) diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 8905000248..d7c3a1d653 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -160,7 +160,7 @@ type ContainerBasicConfig struct { // Conflicts with UtsNS if UtsNS is not set to private. // Optional. Hostname string `json:"hostname,omitempty"` - // HostUses is a list of host usernames or UIDs to add to the container + // HostUsers is a list of host usernames or UIDs to add to the container // /etc/passwd file HostUsers []string `json:"hostusers,omitempty"` // Sysctl sets kernel parameters for the container @@ -219,6 +219,8 @@ type ContainerBasicConfig struct { Passwd *bool `json:"manage_password,omitempty"` // PasswdEntry specifies arbitrary data to append to a file. PasswdEntry string `json:"passwd_entry,omitempty"` + // GroupEntry specifies arbitrary data to append to a file. + GroupEntry string `json:"group_entry,omitempty"` } // ContainerStorageConfig contains information on the storage configuration of a diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 8e2a490b28..befae27b42 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -848,6 +848,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.PasswdEntry = c.PasswdEntry } + if len(s.GroupEntry) == 0 || len(c.GroupEntry) != 0 { + s.GroupEntry = c.GroupEntry + } + return nil } diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go index 20a6ee3b1d..fde91c7020 100644 --- a/test/e2e/run_passwd_test.go +++ b/test/e2e/run_passwd_test.go @@ -153,4 +153,16 @@ USER 1000`, ALPINE) Expect(run).Should(Exit(0)) Expect(run.OutputToString()).To(ContainSubstring("12345-12346-container user-/etc-12345")) }) + + It("podman run --group-entry flag", func() { + // Test that the line we add doesn't contain anything else than what is specified + run := podmanTest.Podman([]string{"run", "--user", "1234:1234", "--group-entry=FOO", ALPINE, "grep", "^FOO$", "/etc/group"}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + + run = podmanTest.Podman([]string{"run", "--user", "12345:12346", "--group-entry=$GID", ALPINE, "tail", "/etc/group"}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + Expect(run.OutputToString()).To(ContainSubstring("12346")) + }) })