mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00
Add filepath glob support to --security-opt unmask
Want to allow users to specify --security-opt unmask=/proc/*. This allows us to run podman within podman more securely, then specifing umask=all, also gives the user more flexibilty. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
@ -540,7 +540,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
|
||||
return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1])
|
||||
}
|
||||
case "unmask":
|
||||
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, strings.Split(con[1], ":")...)
|
||||
s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...)
|
||||
default:
|
||||
return fmt.Errorf("invalid --security-opt 2: %q", opt)
|
||||
}
|
||||
|
@ -882,8 +882,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the
|
||||
- `proc-opts=OPTIONS` : Comma-separated list of options to use for the /proc mount. More details for the
|
||||
possible mount options are specified in the **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 or made read only by default.
|
||||
- **unmask**=_ALL_ or _/path/1:/path/2_, or shell expanded paths (/proc/*): Paths to unmask separated by a colon. If set to **ALL**, it will unmask all the paths that are masked or made read only 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.** The default paths that are read only are **/proc/asound, /proc/bus, /proc/fs, /proc/irq, /proc/sys, /proc/sysrq-trigger, /sys/fs/cgroup**.
|
||||
|
||||
Note: Labeling can be disabled for all containers by setting label=false in the **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file.
|
||||
|
@ -934,8 +934,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the
|
||||
- **proc-opts**=_OPTIONS_ : Comma-separated list of options to use for the /proc mount. More details
|
||||
for the possible mount options are specified in the **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 or made read only by default.
|
||||
- **unmask**=_ALL_ or _/path/1:/path/2_, or shell expanded paths (/proc/*): Paths to unmask separated by a colon. If set to **ALL**, it will unmask all the paths that are masked or made read only 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.**. The default paths that are read only are **/proc/asound**, **/proc/bus**, **/proc/fs**, **/proc/irq**, **/proc/sys**, **/proc/sysrq-trigger**, **/sys/fs/cgroup**.
|
||||
|
||||
Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file.
|
||||
@ -1644,6 +1643,13 @@ the **mask** option.
|
||||
$ podman run --security-opt unmask=ALL fedora bash
|
||||
```
|
||||
|
||||
To unmask all the paths that start with /proc, set the **unmask** option to
|
||||
**/proc/***.
|
||||
|
||||
```
|
||||
$ podman run --security-opt unmask=/proc/* fedora bash
|
||||
```
|
||||
|
||||
```
|
||||
$ podman run --security-opt unmask=/foo/bar:/sys/firmware fedora bash
|
||||
```
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/containers/podman/v3/libpod/define"
|
||||
"github.com/containers/podman/v3/pkg/rootless"
|
||||
"github.com/containers/podman/v3/pkg/util"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-tools/generate"
|
||||
"github.com/pkg/errors"
|
||||
@ -151,30 +150,23 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask
|
||||
"/sys/dev/block",
|
||||
}
|
||||
|
||||
unmaskAll := false
|
||||
if unmask != nil && unmask[0] == "ALL" {
|
||||
unmaskAll = true
|
||||
}
|
||||
|
||||
if !privileged {
|
||||
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)
|
||||
}
|
||||
for _, mp := range defaultMaskPaths {
|
||||
// check that the path to mask is not in the list of paths to unmask
|
||||
if shouldMask(mp, unmask) {
|
||||
g.AddLinuxMaskedPaths(mp)
|
||||
}
|
||||
for _, rp := range []string{
|
||||
"/proc/asound",
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
} {
|
||||
if !util.StringInSlice(rp, unmask) {
|
||||
g.AddLinuxReadonlyPaths(rp)
|
||||
}
|
||||
}
|
||||
for _, rp := range []string{
|
||||
"/proc/asound",
|
||||
"/proc/bus",
|
||||
"/proc/fs",
|
||||
"/proc/irq",
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
} {
|
||||
if shouldMask(rp, unmask) {
|
||||
g.AddLinuxReadonlyPaths(rp)
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,3 +368,21 @@ func supportAmbientCapabilities() bool {
|
||||
err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func shouldMask(mask string, unmask []string) bool {
|
||||
for _, m := range unmask {
|
||||
if strings.ToLower(m) == "all" {
|
||||
return false
|
||||
}
|
||||
for _, m1 := range strings.Split(m, ":") {
|
||||
match, err := filepath.Match(m1, mask)
|
||||
if err != nil {
|
||||
logrus.Errorf(err.Error())
|
||||
}
|
||||
if match {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
28
pkg/specgen/generate/config_linux_test.go
Normal file
28
pkg/specgen/generate/config_linux_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShouldMask(t *testing.T) {
|
||||
tests := []struct {
|
||||
mask string
|
||||
unmask []string
|
||||
shouldMask bool
|
||||
}{
|
||||
{"/proc/foo", []string{"all"}, false},
|
||||
{"/proc/foo", []string{"ALL"}, false},
|
||||
{"/proc/foo", []string{"/proc/foo"}, false},
|
||||
{"/proc/foo", []string{"/proc/*"}, false},
|
||||
{"/proc/foo", []string{"/proc/bar", "all"}, false},
|
||||
{"/proc/foo", []string{"/proc/f*"}, false},
|
||||
{"/proc/foo", []string{"/proc/b*"}, true},
|
||||
{"/proc/foo", []string{}, true},
|
||||
}
|
||||
for _, test := range tests {
|
||||
val := shouldMask(test.mask, test.unmask)
|
||||
assert.Equal(t, val, test.shouldMask)
|
||||
}
|
||||
}
|
@ -299,9 +299,17 @@ var _ = Describe("Podman run", func() {
|
||||
session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr5", "--security-opt", "systempaths=unconfined", ALPINE, "grep", "/proc", "/proc/self/mounts"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
stdoutLines := session.OutputToStringArray()
|
||||
Expect(stdoutLines).Should(HaveLen(1))
|
||||
Expect(session.OutputToStringArray()).Should(HaveLen(1))
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "-d", "--security-opt", "unmask=/proc/*", ALPINE, "grep", "/proc", "/proc/self/mounts"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToStringArray()).Should(HaveLen(1))
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=/proc/a*", ALPINE, "ls", "/proc/acpi"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(Not(BeEmpty()))
|
||||
})
|
||||
|
||||
It("podman run security-opt unmask on /sys/fs/cgroup", func() {
|
||||
|
Reference in New Issue
Block a user