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] {
case "proc-opts":
s.ProcOpts = strings.Split(con[1], ",")
case "apparmor":
s.ContainerSecurityConfig.ApparmorProfile = con[1]
s.Annotations[define.InspectAnnotationApparmor] = con[1]
case "label":
// TODO selinux opts and label opts are the same thing
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=")
case "apparmor":
s.ContainerSecurityConfig.ApparmorProfile = con[1]
s.Annotations[define.InspectAnnotationApparmor] = con[1]
case "mask":
s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...)
case "proc-opts":
s.ProcOpts = strings.Split(con[1], ",")
case "seccomp":
s.SeccompProfilePath = con[1]
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
case "unmask":
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, strings.Split(con[1], ":")...)
default:
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=filetype:**TYPE_: Set the label file type for the container files
- **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
- **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
- **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.
- **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.
@ -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.
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
If you want to set _/dev/sda_ device weight to **200**, you can specify the device

View File

@ -4,13 +4,16 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"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:]...), ":"))
}
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 {
for _, mp := range []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",
} {
g.AddLinuxMaskedPaths(mp)
if !unmaskAll {
for _, mp := range defaultMaskPaths {
// check that the path to mask is not in the list of paths to unmask
if !util.StringInSlice(mp, unmask) {
g.AddLinuxMaskedPaths(mp)
}
}
}
if pidModeIsHost && rootless.IsRootless() {
@ -170,6 +184,15 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.
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)

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 {
g.AddProcessEnv(name, val)

View File

@ -307,6 +307,13 @@ type ContainerSecurityConfig struct {
Umask string `json:"umask,omitempty"`
// ProcOpts are the options used for the proc mount.
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

View File

@ -233,6 +233,39 @@ var _ = Describe("Podman run", func() {
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() {
session := podmanTest.Podman([]string{"run", "-it", "--security-opt", strings.Join([]string{"seccomp=", forbidGetCWDSeccompProfile()}, ""), ALPINE, "pwd"})
session.WaitWithDefaultTimeout()

View File

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