Skip to content

Commit

Permalink
config: allow setting security_opt option (#382)
Browse files Browse the repository at this point in the history
* config: allow setting security_opt option

* change security-opt after https://github.com/containers/podman/blob/main/pkg/specgenutil/specgen.go#L700 specifications

* typo

* create Annotations map
  • Loading branch information
ruspaul013 authored Dec 12, 2024
1 parent 917eebb commit fc11e23
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 2 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

IMPROVEMENTS:

* config: Add `socket` stanza to allow multiple Podman sockets to be used. [[GH-371](https://github.com/hashicorp/nomad-driver-podman/pull/371)]

* config: Allow setting `security_opt` option. [[GH-382](https://github.com/hashicorp/nomad-driver-podman/pull/382)]
* config: Add `socket` stanza to allow multiple Podman sockets to be used. [[GH-371](https://github.com/hashicorp/nomad-driver-podman/pull/371)]

## 0.6.1 (July 15, 2024)

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@ config {
}
```

* **security_opt** - (Optional) A list of security-related options that are set in the container.

```hcl
config {
security_opt = [
"no-new-privileges"
]
}
```

* **selinux_opts** - (Optional) A list of process labels the container will use.

```
Expand Down
14 changes: 13 additions & 1 deletion api/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ type ContainerBasicConfig struct {
// container.
// Optional.
Env map[string]string `json:"env,omitempty"`
// Labels nested indicates whether or not the container is allowed
// to run fully nested containers including SELinux labelling.
// Optional.
LabelsNested bool `json:"labels_nested,omitempty"`
// Labels are key-value pairs that are used to add metadata to
// containers.
// Optional.
Expand Down Expand Up @@ -298,7 +302,15 @@ type ContainerSecurityConfig struct {
// privileges flag on create, which disables gaining additional
// privileges (e.g. via setuid) in the container.
NoNewPrivileges bool `json:"no_new_privileges,omitempty"`

// Mask is the path we want to mask in the container. This masks the paths
// given in addition to the default list.
// Optional
Mask []string `json:"mask,omitempty"`
// Unmask a path in the container. Some paths are masked by default,
// preventing them from being accessed within the container; this undoes that masking.
// If ALL is passed, all paths will be unmasked.
// Optional.
Unmask []string `json:"unmask,omitempty"`
// IDMappings are UID and GID mappings that will be used by user
// namespaces.
// Required if UserNS is private.
Expand Down
3 changes: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ var (
"port_map": hclspec.NewAttr("port_map", "list(map(number))", false),
"ports": hclspec.NewAttr("ports", "list(string)", false),
"privileged": hclspec.NewAttr("privileged", "bool", false),
"security_opt": hclspec.NewAttr("security_opt", "list(string)", false),
"socket": hclspec.NewDefault(
hclspec.NewAttr("socket", "string", false),
hclspec.NewLiteral(`"default"`),
Expand All @@ -130,6 +131,7 @@ var (
"readonly_rootfs": hclspec.NewAttr("readonly_rootfs", "bool", false),
"userns": hclspec.NewAttr("userns", "string", false),
"shm_size": hclspec.NewAttr("shm_size", "string", false),

})
)

Expand Down Expand Up @@ -244,4 +246,5 @@ type TaskConfig struct {
ReadOnlyRootfs bool `codec:"readonly_rootfs"`
UserNS string `codec:"userns"`
ShmSize string `codec:"shm_size"`
SecurityOpt []string `codec:"security_opt"`
}
57 changes: 57 additions & 0 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,11 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
createOpts.ContainerSecurityConfig.ReadOnlyFilesystem = podmanTaskConfig.ReadOnlyRootfs
createOpts.ContainerSecurityConfig.ApparmorProfile = podmanTaskConfig.ApparmorProfile

// add security_opt if configured
if securiyOptsErr := parseSecurityOpt(podmanTaskConfig.SecurityOpt, &createOpts); securiyOptsErr != nil {
return nil, nil, fmt.Errorf("failed to parse security_opt configuration: %v", securiyOptsErr)

Check failure on line 733 in driver.go

View workflow job for this annotation

GitHub Actions / run-tests

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}

// Populate --userns mode only if configured
if podmanTaskConfig.UserNS != "" {
userns := strings.SplitN(podmanTaskConfig.UserNS, ":", 2)
Expand Down Expand Up @@ -1648,3 +1653,55 @@ func setExtraHosts(hosts []string, createOpts *api.SpecGenerator) error {
createOpts.ContainerNetworkConfig.HostAdd = slices.Clone(hosts)
return nil
}

func parseSecurityOpt(securityOpt []string, createOpts *api.SpecGenerator) error {
createOpts.Annotations = make(map[string]string)
for _, opt := range securityOpt {
con := strings.SplitN(opt, "=", 2)
if len(con) == 1 && con[0] != "no-new-privileges" {
if strings.Contains(opt, ":") {
con = strings.SplitN(opt, ":", 2)
} else {
return fmt.Errorf("invalid security_opt: %q", opt)
}
}

switch con[0] {
case "no-new-privileges":
createOpts.ContainerSecurityConfig.NoNewPrivileges = true
continue
case "label":
if con[1] == "nested" {
createOpts.ContainerBasicConfig.LabelsNested = true
continue
}
createOpts.ContainerSecurityConfig.SelinuxOpts = append(createOpts.ContainerSecurityConfig.SelinuxOpts, con[1])
createOpts.Annotations["io.podman.annotations.label"] = strings.Join(createOpts.ContainerSecurityConfig.SelinuxOpts, ",label=")
case "apparmor":
createOpts.ContainerSecurityConfig.ApparmorProfile = con[1]
createOpts.Annotations["io.podman.annotations.apparmor"] = con[1]
case "seccomp":
createOpts.ContainerSecurityConfig.SeccompProfilePath = con[1]
createOpts.Annotations["io.podman.annotations.seccomp"] = con[1]
case "proc-opts":
createOpts.ContainerSecurityConfig.ProcOpts = strings.Split(con[1], ",")
case "mask":
createOpts.ContainerSecurityConfig.Mask = append(createOpts.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...)
case "unmask":
if con[1] == "ALL" {
createOpts.ContainerSecurityConfig.Unmask = append(createOpts.ContainerSecurityConfig.Unmask, con[1])
continue
}
createOpts.ContainerSecurityConfig.Unmask = append(createOpts.ContainerSecurityConfig.Unmask, strings.Split(con[1], ":")...)
case "systempaths":
if con[1] == "unconfined" {
createOpts.ContainerSecurityConfig.Unmask = append(createOpts.ContainerSecurityConfig.Unmask, []string{"ALL"}...)
} else {
return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1])
}
default:
return fmt.Errorf("invalid security_opt: %q", opt)
}
}
return nil
}
10 changes: 10 additions & 0 deletions driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,16 @@ func TestPodmanDriver_Caps(t *testing.T) {
}
}

// check security_opt option
func TestPodmanDriver_SecurityOpt(t *testing.T) {
taskCfg := newTaskConfig("", busyboxLongRunningCmd)
// add a security_opt
taskCfg.SecurityOpt = []string{"no-new-privileges"}
inspectData := startDestroyInspect(t, taskCfg, "securityopt")
// and compare it
must.SliceContains(t, inspectData.HostConfig.SecurityOpt, "no-new-privileges")
}

// check enabled tty option
func TestPodmanDriver_Tty(t *testing.T) {
ci.Parallel(t)
Expand Down

0 comments on commit fc11e23

Please sign in to comment.