Merge pull request #8408 from umohnani8/sec-opt

Add mask and unmask option to --security-opt
This commit is contained in:
OpenShift Merge Robot
2020-12-03 00:49:23 +01:00
committed by GitHub
7 changed files with 114 additions and 22 deletions

View File

@ -517,18 +517,22 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
} }
switch con[0] { switch con[0] {
case "proc-opts": case "apparmor":
s.ProcOpts = strings.Split(con[1], ",") s.ContainerSecurityConfig.ApparmorProfile = con[1]
s.Annotations[define.InspectAnnotationApparmor] = con[1]
case "label": case "label":
// TODO selinux opts and label opts are the same thing // TODO selinux opts and label opts are the same thing
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=")
case "apparmor": case "mask":
s.ContainerSecurityConfig.ApparmorProfile = con[1] s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...)
s.Annotations[define.InspectAnnotationApparmor] = con[1] case "proc-opts":
s.ProcOpts = strings.Split(con[1], ",")
case "seccomp": case "seccomp":
s.SeccompProfilePath = con[1] s.SeccompProfilePath = con[1]
s.Annotations[define.InspectAnnotationSeccomp] = con[1] s.Annotations[define.InspectAnnotationSeccomp] = con[1]
case "unmask":
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, strings.Split(con[1], ":")...)
default: default:
return fmt.Errorf("invalid --security-opt 2: %q", opt) return fmt.Errorf("invalid --security-opt 2: %q", opt)
} }

View File

@ -885,11 +885,16 @@ Security Options
- **label=level:**_LEVEL_: Set the label level for the container processes - **label=level:**_LEVEL_: Set the label level for the container processes
- **label=filetype:**TYPE_: Set the label file type for the container files - **label=filetype:**TYPE_: Set the label file type for the container files
- **label=disable**: Turn off label separation for the container - **label=disable**: Turn off label separation for the container
- **mask**=_/path/1:/path/2_: The paths to mask separated by a colon. A masked path
cannot be accessed inside the container.
- **no-new-privileges**: Disable container processes from gaining additional privileges - **no-new-privileges**: Disable container processes from gaining additional privileges
- **seccomp=unconfined**: Turn off seccomp confinement for the container - **seccomp=unconfined**: Turn off seccomp confinement for the container
- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter - **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter
- **proc-opts**=_OPTIONS_ : Comma separated list of options to use for the /proc mount. More details - **proc-opts**=_OPTIONS_ : Comma separated list of options to use for the /proc mount. More details
for the possible mount options are specified at **proc(5)** man page. for the possible mount options are specified at **proc(5)** man page.
- **unmask**=_ALL_ or _/path/1:/path/2_: Paths to unmask separated by a colon. If set to **ALL**, it will
unmask all the paths that are masked by default.
The default masked paths are **/proc/acpi, /proc/kcore, /proc/keys, /proc/latency_stats, /proc/sched_debug, /proc/scsi, /proc/timer_list, /proc/timer_stats, /sys/firmware, and /sys/fs/selinux.**
Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file. Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file.
@ -1479,6 +1484,26 @@ $ podman run --security-opt label=type:svirt_apache_t -i -t centos bash
Note you would have to write policy defining a **svirt_apache_t** type. Note you would have to write policy defining a **svirt_apache_t** type.
To mask additional specific paths in the container, specify the paths
separated by a colon using the **mask** option with the **--security-opt**
flag.
```
$ podman run --security-opt mask=/foo/bar:/second/path fedora bash
```
To unmask all the paths that are masked by default, set the **unmask** option to
**ALL**. Or to only unmask specific paths, specify the paths as shown above with
the **mask** option.
```
$ podman run --security-opt unmask=ALL fedora bash
```
```
$ podman run --security-opt unmask=/foo/bar:/sys/firmware fedora bash
```
### Setting device weight ### Setting device weight
If you want to set _/dev/sda_ device weight to **200**, you can specify the device If you want to set _/dev/sda_ device weight to **200**, you can specify the device

View File

@ -4,13 +4,16 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -137,22 +140,33 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error {
return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":")) return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
} }
func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) { func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask []string, g *generate.Generator) {
defaultMaskPaths := []string{"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
"/sys/fs/selinux",
"/sys/dev/block",
}
unmaskAll := false
if unmask != nil && unmask[0] == "ALL" {
unmaskAll = true
}
if !privileged { if !privileged {
for _, mp := range []string{ if !unmaskAll {
"/proc/acpi", for _, mp := range defaultMaskPaths {
"/proc/kcore", // check that the path to mask is not in the list of paths to unmask
"/proc/keys", if !util.StringInSlice(mp, unmask) {
"/proc/latency_stats", g.AddLinuxMaskedPaths(mp)
"/proc/timer_list", }
"/proc/timer_stats", }
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
"/sys/fs/selinux",
"/sys/dev",
} {
g.AddLinuxMaskedPaths(mp)
} }
if pidModeIsHost && rootless.IsRootless() { if pidModeIsHost && rootless.IsRootless() {
@ -170,6 +184,15 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.
g.AddLinuxReadonlyPaths(rp) g.AddLinuxReadonlyPaths(rp)
} }
} }
// mask the paths provided by the user
for _, mp := range mask {
if !path.IsAbs(mp) && mp != "" {
logrus.Errorf("Path %q is not an absolute path, skipping...", mp)
continue
}
g.AddLinuxMaskedPaths(mp)
}
} }
// based on getDevices from runc (libcontainer/devices/devices.go) // based on getDevices from runc (libcontainer/devices/devices.go)

View File

@ -298,7 +298,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
} }
} }
BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g) BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g)
for name, val := range s.Env { for name, val := range s.Env {
g.AddProcessEnv(name, val) g.AddProcessEnv(name, val)

View File

@ -307,6 +307,13 @@ type ContainerSecurityConfig struct {
Umask string `json:"umask,omitempty"` Umask string `json:"umask,omitempty"`
// ProcOpts are the options used for the proc mount. // ProcOpts are the options used for the proc mount.
ProcOpts []string `json:"procfs_opts,omitempty"` ProcOpts []string `json:"procfs_opts,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 is the path we want to unmask in the container. To override
// all the default paths that are masked, set unmask=ALL.
Unmask []string `json:"unmask,omitempty"`
} }
// ContainerCgroupConfig contains configuration information about a container's // ContainerCgroupConfig contains configuration information about a container's

View File

@ -233,6 +233,39 @@ var _ = Describe("Podman run", func() {
return jsonFile return jsonFile
} }
It("podman run mask and unmask path test", func() {
session := podmanTest.Podman([]string{"run", "-d", "--name=maskCtr1", "--security-opt", "unmask=ALL", "--security-opt", "mask=/proc/acpi", ALPINE, "sleep", "200"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/sys/firmware"})
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(Not(BeEmpty()))
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/proc/acpi"})
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(BeEmpty())
session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr2", "--security-opt", "unmask=/proc/acpi:/sys/firmware", ALPINE, "sleep", "200"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/sys/firmware"})
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(Not(BeEmpty()))
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/proc/acpi"})
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(Not(BeEmpty()))
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr3", "--security-opt", "mask=/sys/power/disk", ALPINE, "sleep", "200"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "maskCtr3", "cat", "/sys/power/disk"})
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(BeEmpty())
Expect(session.ExitCode()).To(Equal(0))
})
It("podman run seccomp test", func() { It("podman run seccomp test", func() {
session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"}) session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()

View File

@ -118,7 +118,7 @@ EOF
/proc/scsi /proc/scsi
/sys/firmware /sys/firmware
/sys/fs/selinux /sys/fs/selinux
/sys/dev /sys/dev/block
) )
# Some of the above may not exist on our host. Find only the ones that do. # Some of the above may not exist on our host. Find only the ones that do.