mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Merge pull request #8408 from umohnani8/sec-opt
Add mask and unmask option to --security-opt
This commit is contained in:
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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.
|
||||||
|
Reference in New Issue
Block a user