Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var (
"devices": hclspec.NewAttr("devices", "list(string)", false),
"entrypoint": hclspec.NewAttr("entrypoint", "any", false), // any for compat
"working_dir": hclspec.NewAttr("working_dir", "string", false),
"group_add": hclspec.NewAttr("group_add", "list(string)", false),
"hostname": hclspec.NewAttr("hostname", "string", false),
"image": hclspec.NewAttr("image", "string", true),
"image_pull_timeout": hclspec.NewDefault(
Expand Down Expand Up @@ -225,6 +226,7 @@ type TaskConfig struct {
Devices []string `codec:"devices"`
Entrypoint any `codec:"entrypoint"` // any for compat
WorkingDir string `codec:"working_dir"`
GroupAdd []string `codec:"group_add"`
Hostname string `codec:"hostname"`
Image string `codec:"image"`
ImagePullTimeout string `codec:"image_pull_timeout"`
Expand Down
17 changes: 17 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,20 @@ func TestConfig_PodmanOOMScoreAdj(t *testing.T) {
parser.ParseHCL(t, validHCL, &tc)
must.Eq(t, "default", tc.Socket)
}

func TestConfig_GroupAdd(t *testing.T) {
ci.Parallel(t)

parser := hclutils.NewConfigParser(taskConfigSpec)
expectedGroups := []string{"audio", "video"}
validHCL := `
config {
image = "docker://redis"
group_add = ["audio", "video"]
}
`

var tc *TaskConfig
parser.ParseHCL(t, validHCL, &tc)
must.SliceContainsAll(t, expectedGroups, tc.GroupAdd)
}
36 changes: 35 additions & 1 deletion driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,11 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
createOpts.ContainerSecurityConfig.ReadOnlyFilesystem = podmanTaskConfig.ReadOnlyRootfs
createOpts.ContainerSecurityConfig.ApparmorProfile = podmanTaskConfig.ApparmorProfile

// add group_add if configured
if groupAddErr := parseGroupAdd(podmanTaskConfig.GroupAdd, &createOpts); groupAddErr != nil {
return nil, nil, fmt.Errorf("failed to parse group_add configuration: %w", groupAddErr)
}

// add security_opt if configured
if securiyOptsErr := parseSecurityOpt(podmanTaskConfig.SecurityOpt, &createOpts); securiyOptsErr != nil {
return nil, nil, fmt.Errorf("failed to parse security_opt configuration: %w", securiyOptsErr)
Expand Down Expand Up @@ -1744,8 +1749,37 @@ func setExtraHosts(hosts []string, createOpts *api.SpecGenerator) error {
return nil
}

// ensureAnnotations initializes the Annotations map if it's nil
func ensureAnnotations(createOpts *api.SpecGenerator) {
if createOpts.Annotations == nil {
createOpts.Annotations = make(map[string]string)
}
}

// parseGroupAdd parses group-add options and sets them in the container creation specification.
func parseGroupAdd(groupAdd []string, createOpts *api.SpecGenerator) error {
ensureAnnotations(createOpts)

if slices.Contains(groupAdd, "keep-groups") {
// "keep-groups" is a special value that is interpreted by crun via annotations
// to retain the original user's supplementary groups.
// This is mutually exclusive with any other group-add options.
if len(groupAdd) > 1 {
return fmt.Errorf("the '--group-add keep-groups' option is not allowed with any other --group-add options")
}
createOpts.Annotations["run.oci.keep_original_groups"] = "1"
} else {
// Regular group additions as a list of group names.
createOpts.ContainerSecurityConfig.Groups = groupAdd
}

return nil
}


func parseSecurityOpt(securityOpt []string, createOpts *api.SpecGenerator) error {
createOpts.Annotations = make(map[string]string)
ensureAnnotations(createOpts)

for _, opt := range securityOpt {
con := strings.SplitN(opt, "=", 2)
if len(con) == 1 && con[0] != "no-new-privileges" {
Expand Down
49 changes: 49 additions & 0 deletions driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,55 @@ func TestPodmanDriver_Caps(t *testing.T) {
}
}

// check group_add option
func TestPodmanDriver_GroupAdd(t *testing.T) {
taskCfg := newTaskConfig("", busyboxLongRunningCmd)
// add a group_add
taskCfg.GroupAdd = []string{"audio", "video"}
inspectData := startDestroyInspect(t, taskCfg, "groupadd")
// and compare it
must.SliceContains(t, inspectData.HostConfig.GroupAdd, "audio")
must.SliceContains(t, inspectData.HostConfig.GroupAdd, "video")
}

// check group_add option with keep-groups special case
func TestPodmanDriver_GroupAdd_KeepGroups(t *testing.T) {
taskCfg := newTaskConfig("", busyboxLongRunningCmd)
// add a group_add
taskCfg.GroupAdd = []string{"keep-groups"}
inspectData := startDestroyInspect(t, taskCfg, "groupadd-keep-groups")
// and compare it
must.NotNil(t, inspectData.Config.Annotations["run.oci.keep_original_groups"])
must.Eq(t, inspectData.Config.Annotations["run.oci.keep_original_groups"], "1")
}

// check group_add option with keep-groups special case
// with other groups - should error
func TestPodmanDriver_GroupAdd_KeepGroupsWithOthersError(t *testing.T) {
ci.Parallel(t)

taskCfg := newTaskConfig("", busyboxLongRunningCmd)
// try to combine keep-groups with other groups - should error
taskCfg.GroupAdd = []string{"keep-groups", "audio"}

task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "groupadd-error",
AllocID: uuid.Generate(),
Resources: createBasicResources(),
}
must.NoError(t, task.EncodeConcreteDriverConfig(&taskCfg))

d := podmanDriverHarness(t, nil)
cleanup := d.MkAllocDir(task, false)
defer cleanup()

_, _, err := d.StartTask(task)
// should fail with error about keep-groups being mutually exclusive
must.Error(t, err)
must.StrContains(t, err.Error(), "keep-groups")
}

// check security_opt option
func TestPodmanDriver_SecurityOpt(t *testing.T) {
taskCfg := newTaskConfig("", busyboxLongRunningCmd)
Expand Down