diff --git a/Dockerfile b/Dockerfile index 228f6e7..e3f9021 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ ADD ${NV_MODULES_TARBALL_URL} /nvidia-modules.tar.gz # reviewed digest bumps - see Dockerfile.buildenv and buildenv.yml. Dependabot # keeps the pin current, with buildenv-diff.yml summarizing the package # changes in each bump PR. -FROM --platform=$BUILDPLATFORM ghcr.io/edera-dev/kernel-buildenv:latest@sha256:5f1111cde2487436b4102ae876c1810c304760a3accbb4ba51ff3f725b7374eb AS buildenv +FROM --platform=$BUILDPLATFORM ghcr.io/edera-dev/kernel-buildenv:latest@sha256:00c0e67a93f8d3e6a1d71b03548c4b1157554e28ff117f2d12a8929335aad938 AS buildenv COPY --chown=build:build . /build USER build WORKDIR /build diff --git a/configs/aarch64/zone.config b/configs/aarch64/zone.config index 96aefcd..011e4a4 100644 --- a/configs/aarch64/zone.config +++ b/configs/aarch64/zone.config @@ -1379,7 +1379,7 @@ CONFIG_SECURITY_LANDLOCK=y CONFIG_INTEGRITY=y CONFIG_INTEGRITY_AUDIT=y CONFIG_DEFAULT_SECURITY_DAC=y -CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity" +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor" CONFIG_INIT_STACK_ALL_ZERO=y diff --git a/configs/apparmor/edera-default b/configs/apparmor/edera-default new file mode 100644 index 0000000..56af3fd --- /dev/null +++ b/configs/apparmor/edera-default @@ -0,0 +1,51 @@ +# Edera RuntimeDefault AppArmor profile. +# +# Source: containerd's default container AppArmor profile, from +# contrib/apparmor/template.go at tag v2.3.3 (latest release) +# https://github.com/containerd/containerd/blob/v2.3.3/contrib/apparmor/template.go +# +# This is the policy template that the Edera runtime applies in-zone by default, when instructed +# to load and use the `RuntimeDefault` profile. The AppArmor profile is a binary, built against +# the zone kernel's ABI, compiled from this policy template by the apparmor tooling. +# We ship the default profile with the zone kernel, where it is available to be used within the zone +# for confining processes. + +abi , +#include + +profile edera-default flags=(attach_disconnected,mediate_deleted) { + #include + + network, + capability, + file, + umount, + + # The unconfined supervisor may signal workloads under this profile. + signal (receive) peer=unconfined, + # Processes running under this profile may signal each other (only). + signal (send,receive) peer=edera-default, + + deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) + # deny write to files not in /proc//** or /proc/sys/** + deny @{PROC}/{[^1-9/],[^1-9/][^0-9/],[^1-9s/][^0-9y/][^0-9s/],[^1-9/][^0-9/][^0-9/][^0-9/]*}/** w, + deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) + deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ + deny @{PROC}/sysrq-trigger rwklx, + deny @{PROC}/kcore rwklx, + + deny mount, + + deny /sys/[^f]*/** wklx, + deny /sys/f[^s]*/** wklx, + deny /sys/fs/[^c]*/** wklx, + deny /sys/fs/c[^g]*/** wklx, + deny /sys/fs/cg[^r]*/** wklx, + deny /sys/firmware/** rwklx, + deny /sys/devices/virtual/powercap/** rwklx, + deny /sys/kernel/security/** rwklx, + + # allow processes within the container to trace each other, + # provided all other LSM and similar settings allow it. + ptrace (trace,tracedby,read,readby) peer=edera-default, +} diff --git a/configs/x86_64/zone.config b/configs/x86_64/zone.config index 734acff..5379f75 100644 --- a/configs/x86_64/zone.config +++ b/configs/x86_64/zone.config @@ -1752,7 +1752,7 @@ CONFIG_SECURITY_LANDLOCK=y CONFIG_INTEGRITY=y CONFIG_INTEGRITY_AUDIT=y CONFIG_DEFAULT_SECURITY_DAC=y -CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity" +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor" CONFIG_INIT_STACK_ALL_ZERO=y diff --git a/hack/build/apparmor.sh b/hack/build/apparmor.sh new file mode 100644 index 0000000..eea2677 --- /dev/null +++ b/hack/build/apparmor.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Compiles the Edera 'RuntimeDefault' AppArmor profile against the zone kernel's ABI, +# and stages it in the addon overlay at apparmor/. +# +# Only zone kernels run workloads; the host kernel has no use for the profile, +# so it is skipped there. +# +# Requires apparmor_parser in the build environment. + +case "${KERNEL_FLAVOR}" in +zone*) + APPARMOR_OUTPUT_PATH="${ADDONS_OUTPUT_PATH}/apparmor" + PROFILE_SRC="${KERNEL_DIR}/configs/apparmor/edera-default" + + mkdir -p "${APPARMOR_OUTPUT_PATH}" + + # Compile to a loadable binary policy without attempting to load it into the current kernel. + # The cache file apparmor_parser writes is what we load into the guest kernel via its `.load` interface. + APPARMOR_CACHE_DIR="$(mktemp -d)" + apparmor_parser --skip-kernel-load -M /etc/apparmor.d/abi/3.0 --write-cache \ + --cache-loc="${APPARMOR_CACHE_DIR}" "${PROFILE_SRC}" + + # Drop the compiled policy under // + # alongside a shared `.features` file. + COMPILED="$(find "${APPARMOR_CACHE_DIR}" -type f ! -name '.features' | head -n1)" + if [ -z "${COMPILED}" ]; then + echo "ERROR: apparmor_parser produced no compiled policy for ${PROFILE_SRC}" >&2 + exit 1 + fi + cp "${COMPILED}" "${APPARMOR_OUTPUT_PATH}/edera-default" + rm -rf "${APPARMOR_CACHE_DIR}" + ;; +esac diff --git a/hack/build/build.sh b/hack/build/build.sh index 04c8449..dc9f0cc 100755 --- a/hack/build/build.sh +++ b/hack/build/build.sh @@ -30,6 +30,9 @@ mv "${MODULES_INSTALL_PATH}/lib/modules/${KERNEL_MODULES_VER}" "${MODULES_OUTPUT rm -rf "${MODULES_INSTALL_PATH}" [ -L "${MODULES_OUTPUT_PATH}/build" ] && unlink "${MODULES_OUTPUT_PATH}/build" +# shellcheck source-path=SCRIPTDIR source=apparmor.sh +. "${KERNEL_DIR}/hack/build/apparmor.sh" + mksquashfs "${ADDONS_OUTPUT_PATH}" "${ADDONS_SQUASHFS_PATH}" -all-root SQUASH_SIZE=$(stat -c %s "${ADDONS_SQUASHFS_PATH}")