Add support for --security-opt mask and unmask

Fixes: https://github.com/containers/buildah/issues/5881

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh 2024-12-18 10:47:40 -05:00
parent 996c49d8ec
commit adf54cde0e
No known key found for this signature in database
GPG Key ID: A2DF901DABE2C028
8 changed files with 63 additions and 3 deletions

View File

@ -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
@ -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.

View File

@ -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`.

View File

@ -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)
}

View File

@ -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)
@ -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)
}
}

View File

@ -7125,3 +7125,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"
}

View File

@ -0,0 +1,4 @@
FROM alpine
COPY --chmod=700 test.sh /
RUN /test.sh

15
tests/bud/masks/test.sh Normal file
View File

@ -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