Skip to content

Commit

Permalink
Add support for --security-opt mask and unmask
Browse files Browse the repository at this point in the history
Fixes: containers#5881

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed Dec 18, 2024
1 parent 247c786 commit 8428d2a
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 3 deletions.
4 changes: 4 additions & 0 deletions define/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type CommonBuildOptions struct {
// LabelOpts is a slice of the fields of an SELinux context, given in "field:pair" format, or "disable".
// Recognized field names are "role", "type", and "level".
LabelOpts []string
// Paths to mask
Masks []string
// MemorySwap limits the amount of memory and swap together.
MemorySwap int64
// NoHostname tells the builder not to create /etc/hostname content when running
Expand Down Expand Up @@ -109,6 +111,8 @@ type CommonBuildOptions struct {
SSHSources []string
// OCIHooksDir is the location of OCI hooks for the build containers
OCIHooksDir []string
// Paths to unmask
Unmasks []string
}

// BuildOptions can be used to alter how an image is built.
Expand Down
6 changes: 6 additions & 0 deletions docs/buildah-build.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -935,11 +935,17 @@ Security Options
"label=type:TYPE" : Set the label type for the container
"label=level:LEVEL" : Set the label level for the container
"label=disable" : Turn off label confinement 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 : JSON configuration for a seccomp filter

"unmask=_ALL_ or _/path/1:/path/2_, or shell expanded paths (/proc/*): Paths to unmask separated by a colon. If set to **ALL**, it unmasks 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**, **/sys/devices/virtual/powercap**. The default paths that are read-only are **/proc/asound**, **/proc/bus**, **/proc/fs**, **/proc/irq**, **/proc/sys**, **/proc/sysrq-trigger**, **/sys/fs/cgroup**.

**--shm-size**=""

Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`.
Expand Down
Binary file modified internal/mkcw/embed/entrypoint_amd64.gz
Binary file not shown.
12 changes: 12 additions & 0 deletions pkg/parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ func parseSecurityOpts(securityOpts []string, commonOpts *define.CommonBuildOpti
commonOpts.ApparmorProfile = con[1]
case "seccomp":
commonOpts.SeccompProfilePath = con[1]
case "mask":
commonOpts.Masks = append(commonOpts.Masks, strings.Split(con[1], ":")...)
case "unmask":
unmasks := strings.Split(con[1], ":")
for _, unmask := range unmasks {
matches, _ := filepath.Glob(unmask)
if len(matches) > 0 {
commonOpts.Unmasks = append(commonOpts.Unmasks, matches...)
continue
}
commonOpts.Unmasks = append(commonOpts.Unmasks, unmask)
}
default:
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
Expand Down
12 changes: 9 additions & 3 deletions run_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
}
}

setupMaskedPaths(g)
setupMaskedPaths(g, b.CommonBuildOpts)
setupReadOnlyPaths(g)

setupTerminal(g, options.Terminal, options.TerminalSize)
Expand Down Expand Up @@ -1199,8 +1199,14 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
return mounts, nil
}

func setupMaskedPaths(g *generate.Generator) {
for _, mp := range config.DefaultMaskedPaths {
func setupMaskedPaths(g *generate.Generator, opts *define.CommonBuildOptions) {
if slices.Contains(opts.Unmasks, "all") {
return
}
for _, mp := range append(config.DefaultMaskedPaths, opts.Masks...) {
if slices.Contains(opts.Unmasks, mp) {
continue
}
g.AddLinuxMaskedPaths(mp)
}
}
Expand Down
13 changes: 13 additions & 0 deletions tests/bud.bats
Original file line number Diff line number Diff line change
Expand Up @@ -7123,3 +7123,16 @@ EOF
echo RUN --mount=type=tmpfs,target=tmpfssubdir test '`stat -f -c %i .`' '!=' '`stat -f -c %i tmpfssubdir`' >> ${TEST_SCRATCH_DIR}/Containerfile
run_buildah build --security-opt label=disable ${TEST_SCRATCH_DIR}
}

@test "build-security-opt-mask" {
base=busybox
_prefetch $base
run_buildah build bud/masks
expect_output --substring "masked" "Everything should be masked"
run_buildah build --security-opt unmask=all bud/masks
expect_output --substring "unmasked" "Everything should be masked"
run_buildah build --security-opt unmask=/proc/* bud/masks
expect_output --substring "unmasked" "Everything should be masked"
run_buildah build --security-opt unmask=/proc/acpi bud/masks
expect_output --substring "unmasked" "Everything should be masked"
}
4 changes: 4 additions & 0 deletions tests/bud/masks/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM alpine
COPY --chmod=700 test.sh /
RUN /test.sh

15 changes: 15 additions & 0 deletions tests/bud/masks/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# !/bin/sh

function output {
for mask in /proc/acpi /proc/kcore /proc/keys /proc/latency_stats /proc/sched_debug /proc/scsi /proc/timer_list /proc/timer_stats /sys/dev/block /sys/devices/virtual/powercap /sys/firmware /sys/fs/selinux; do
test -e $mask || continue
test -f $mask && cat $mask 2> /dev/null
test -d $mask && ls $mask
done
}
output=$(output | wc -c )
if [ $output == 0 ]; then
echo "masked"
else
echo "unmasked"
fi

0 comments on commit 8428d2a

Please sign in to comment.