Hi List, the worst of the June 14th attack now seems to be past us (fingers crossed). Unfortunately I guess that it'll not be the last attack the AUR will see.
Here's two points: a) aur-malware-check script Here's a link to a repo that has been quite popular during the event: https://github.com/lenucksi/aur-malware-check It started out as a collection / summary of the scripts distributed throughout the initial gists and then received contributions from others for another, fully unrelated "campaign" and the "russian spam" packages that were discussed on this list later. I then turned the initial shell scripts into Python with the intent of being as concise as possible, as standard lib-only as possible and as reviewable as possible together with the best possible grounding+connection in the original sources that are connected to a "campaign". Now it has a format that can take in additional malware "campaigns" in an easier way if they're sort-of similar to the previous ones or can be extended easy enough to fit other malware approaches. The script has been created with human review (multiple people) and LLM-assistance. So for those who hate the existence and/or the use of LLMs: Please ignore the script. For the others: Maybe it's something that could be of use when the next set of nasty people come around. More constructive human review/improvement welcome of course. b) Enforcing isolated chroot building of packages (or at least promoting what already exists) To my understanding the blast radius of the 14th of June campaign could have been limited to a good degree by enforcing the building of the PKGBUILD in a chroot/container/nspawn etc that is properly isolated against the host system it runs on. Of course, if the resulting package is infected past the build process and then does its ill work after / when being installed, this is of no help. I did find the devtools (https://gitlab.archlinux.org/archlinux/devtools) package, its pkgctl tool and of course the more recent AUR helpers such as paru have an optional --chroot parameter. However none of it is default, advertised properly or promoted as a good / best practice even though upon trying those they all worked quite well, didn't really take excessive amounts of disk space and didn't really extend the time to build and AUR package that much. I dug around the devtools package and how it is used today and added a few tweaks to it that run fine here. Basic point was: See to what degree the modern Linux capabilities can be used to isolate the build a bit better than what the current chrooted builds do. Also, isolate what the AUR builds download in terms of cached packages from the main cache. Provide an option to clean said cache after build. Since the Archlinux Gitlab is invite only, I have converted the changes to patches and attached them here. Maybe they are useful to someone. Cheers, Lenucksi --
From 3ad10915cf040956d6537cdc787bc45c9a99def2 Mon Sep 17 00:00:00 2001 From: Lenucksi <[email protected]> Date: Tue, 7 Jul 2026 15:09:39 +0200 Subject: [PATCH 0/4] arch-nspawn container boundary hardening + AUR cache isolation This series hardens the systemd-nspawn container boundary in arch-nspawn and makechrootpkg, and adds AUR cache isolation. Patches 1-2 add the security infrastructure: capability drops, seccomp filters, address family restrictions, a config override mechanism for nspawn flags, noexec on tmpfs, a deterministic UID range picker, and writable overlay cache support under user namespaces. Patch 3 adds AUR cache separation: a dedicated writable cache for AUR builds when user namespaces are disabled, and a DEVTOOLS_CACHE_CLEAR flag to control cache persistence. Patch 4 adds documentation for all new features. All changes have been tested on Arch Linux with a bespoke fortress test suite (38/38 PASS) and real-world paru builds in both user-namespace and non-user-namespace modes. Lenucksi (4): feat: harden arch-nspawn container boundary with security flags feat: add user namespace support with writable overlay cache feat: separate AUR package cache from host cache docs: document container hardening, user namespace, and AUR cache isolation Makefile | 7 ++ config/nspawn.conf.d/nspawn-security.conf | 29 +++++ doc/man/arch-nspawn.1.asciidoc | 43 +++++++ doc/man/makechrootpkg.1.asciidoc | 14 ++- src/arch-nspawn.in | 130 +++++++++++++++++++++- src/makechrootpkg.in | 6 +- 6 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 config/nspawn.conf.d/nspawn-security.conf -- 2.55.0
From 86fbd1a76e2d1a772591b0b27063350ddbfd325d Mon Sep 17 00:00:00 2001 From: Lenucksi <[email protected]> Date: Tue, 7 Jul 2026 15:06:09 +0200 Subject: [PATCH 1/4] feat: harden arch-nspawn container boundary with security flags Add a default nspawn_security_flags block to arch-nspawn.in: - Drop 7 capabilities (CAP_BPF, CAP_NET_ADMIN, CAP_SYS_PTRACE, CAP_SYS_MODULE, CAP_MKNOD, CAP_NET_RAW, CAP_SYS_CHROOT) - Restrict syscalls via seccomp (~@keyring @mount) - Restrict address families to AF_INET, AF_INET6, AF_UNIX Add a config override mechanism at /usr/share/devtools/nspawn.conf.d/nspawn-security.conf, which is sourced if present. The shipped default enables user namespace isolation via --private-users=yes --private-users-ownership=map. Mount /tmp with noexec in makechrootpkg to prevent execution of binaries placed in /tmp inside the container. CAP_SYS_ADMIN is retained in the bounding set for pacman Landlock sandbox compatibility. --- Makefile | 7 +++++++ config/nspawn.conf.d/nspawn-security.conf | 21 +++++++++++++++++++++ src/arch-nspawn.in | 11 +++++++++++ src/makechrootpkg.in | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 config/nspawn.conf.d/nspawn-security.conf diff --git a/Makefile b/Makefile index 12c9745..100f79a 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ LIBRARY = $(addprefix $(BUILDDIR)/,$(patsubst src/%,%,$(patsubst %.in,%,$(LIBRAR MAKEPKG_CONFIGS=$(wildcard config/makepkg/*) PACMAN_CONFIGS=$(wildcard config/pacman/*) GIT_CONFIGS = $(wildcard config/git/*) +NSPAWN_CONFIGS = $(wildcard config/nspawn.conf.d/*) SETARCH_ALIASES = $(wildcard config/setarch-aliases.d/*) MANS = $(addprefix $(BUILDDIR)/,$(patsubst %.asciidoc,%,$(wildcard doc/man/*.asciidoc))) DATA_FILES = $(wildcard data/*) @@ -112,6 +113,8 @@ conf: @cp -a $(PACMAN_CONFIGS) $(BUILDDIR)/pacman.conf.d @install -d $(BUILDDIR)/git.conf.d @cp -a $(GIT_CONFIGS) $(BUILDDIR)/git.conf.d + @install -d $(BUILDDIR)/nspawn.conf.d + @cp -a $(NSPAWN_CONFIGS) $(BUILDDIR)/nspawn.conf.d data: @install -d $(BUILDDIR)/data @@ -125,11 +128,13 @@ install: all install -dm0755 $(DESTDIR)$(DATADIR)/setarch-aliases.d install -dm0755 $(DESTDIR)$(DATADIR)/makepkg.conf.d install -dm0755 $(DESTDIR)$(DATADIR)/pacman.conf.d + install -dm0755 $(DESTDIR)$(DATADIR)/nspawn.conf.d install -m0755 ${BINPROGS} $(DESTDIR)$(PREFIX)/bin install -dm0755 $(DESTDIR)$(DATADIR)/lib install -dm0755 $(DESTDIR)$(DATADIR)/data cp -ra $(BUILDDIR)/lib/* $(DESTDIR)$(DATADIR)/lib cp -a $(BUILDDIR)/git.conf.d -t $(DESTDIR)$(DATADIR) + cp -a $(BUILDDIR)/nspawn.conf.d -t $(DESTDIR)$(DATADIR) cp -ra $(BUILDDIR)/makepkg.conf.d -t $(DESTDIR)$(DATADIR) cp -ra $(BUILDDIR)/data -t $(DESTDIR)$(DATADIR) for conf in $(notdir $(PACMAN_CONFIGS)); do install -Dm0644 $(BUILDDIR)/pacman.conf.d/$$conf $(DESTDIR)$(DATADIR)/pacman.conf.d/$${conf##*/}; done @@ -149,6 +154,7 @@ uninstall: for f in $(notdir $(LIBRARY)); do rm -f $(DESTDIR)$(DATADIR)/lib/$$f; done rm -rf $(DESTDIR)$(DATADIR)/lib rm -rf $(DESTDIR)$(DATADIR)/git.conf.d + rm -rf $(DESTDIR)$(DATADIR)/nspawn.conf.d rm -rf $(DESTDIR)$(DATADIR)/makepkg.conf.d rm -rf $(DESTDIR)$(DATADIR)/data for conf in $(notdir $(PACMAN_CONFIGS)); do rm -f $(DESTDIR)$(DATADIR)/pacman.conf.d/$${conf##*/}; done @@ -163,6 +169,7 @@ uninstall: rmdir --ignore-fail-on-non-empty \ $(DESTDIR)$(DATADIR)/setarch-aliases.d \ $(DESTDIR)$(DATADIR)/pacman.conf.d \ + $(DESTDIR)$(DATADIR)/nspawn.conf.d \ $(DESTDIR)$(DATADIR) tag: diff --git a/config/nspawn.conf.d/nspawn-security.conf b/config/nspawn.conf.d/nspawn-security.conf new file mode 100644 index 0000000..d664c28 --- /dev/null +++ b/config/nspawn.conf.d/nspawn-security.conf @@ -0,0 +1,21 @@ +# This file is sourced by arch-nspawn to extend nspawn_security_flags +# with user-specific security options. +# +# Flags specified here are appended to the defaults in arch-nspawn.in. +# See systemd-nspawn(1) for available options. +# +# === User namespace isolation === +# +# Enable --private-users to map the container's root (UID 0) to an +# unprivileged host UID, preventing container root from having full +# host root privileges. This is the single most impactful hardening +# measure. +# +# Replace "yes" with an explicit numeric range if you need a writable +# overlay cache inside the container, e.g.: +# --private-users=524288:65536 +# +nspawn_security_flags+=( + --private-users=yes + --private-users-ownership=map +) diff --git a/src/arch-nspawn.in b/src/arch-nspawn.in index fdafe9c..2d5b5bc 100644 --- a/src/arch-nspawn.in +++ b/src/arch-nspawn.in @@ -68,6 +68,17 @@ nspawn_args=( --timezone=off ) +nspawn_security_flags=( + --drop-capability=CAP_BPF,CAP_NET_ADMIN,CAP_SYS_PTRACE,CAP_SYS_MODULE,CAP_MKNOD,CAP_NET_RAW,CAP_SYS_CHROOT + --system-call-filter="~@keyring @mount" + --restrict-address-families="AF_INET AF_INET6 AF_UNIX" +) + +nspawn_conf="@pkgdatadir@/nspawn.conf.d/nspawn-security.conf" +[[ -f $nspawn_conf ]] && source "$nspawn_conf" + +nspawn_args+=("${nspawn_security_flags[@]}") + if (( ${#cache_dirs[@]} == 0 )); then mapfile -t cache_dirs < <(pacman-conf --config "${pac_conf:-$working_dir/etc/pacman.conf}" CacheDir) fi diff --git a/src/makechrootpkg.in b/src/makechrootpkg.in index 3574ea6..eded65d 100644 --- a/src/makechrootpkg.in +++ b/src/makechrootpkg.in @@ -34,7 +34,7 @@ clean_first=0 run_namcap=0 run_checkpkg=0 temp_chroot=0 -tmp_opts="nosuid,nodev,size=50%,nr_inodes=2m" +tmp_opts="nosuid,nodev,noexec,size=50%,nr_inodes=2m" inspect=never -- 2.55.0
From 93cff43e75d733dc68e46548a03c9e796e8009d1 Mon Sep 17 00:00:00 2001 From: Lenucksi <[email protected]> Date: Tue, 7 Jul 2026 15:07:14 +0200 Subject: [PATCH 2/4] feat: add user namespace support with writable overlay cache Add pick_uid_base(): a deterministic UID allocator that derives a private-users UID range from the hostname via md5sum. This allows multiple containers on the same host to each get a stable, non- overlapping UID range. Add --private-users=yes resolution: when the config file sets --private-users=yes, resolve it to a numeric range at runtime so that overlay mounts receive the correct UID ownership. Replace read-only bind mounts (--bind-ro) with writable overlay mounts (--overlay) when --private-users is active, pre-creating and chowning the upperdir to the mapped container root UID. Cache upperdir is cleared before and after each nspawn invocation so transient build dependencies don't accumulate between builds. Add DEVTOOLS_CACHE_CLEAR=1 (default): gates the upperdir cleanup. Set to 0 in nspawn-security.conf to preserve the overlay cache. Add ID-mapped bind mounts (:idmap) for source directories in makechrootpkg so the build user inside a user namespace can access source files with their original host UIDs. Strip :idmap from positional arguments when --private-users is not active. --- src/arch-nspawn.in | 85 +++++++++++++++++++++++++++++++++++++++++--- src/makechrootpkg.in | 4 +-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/arch-nspawn.in b/src/arch-nspawn.in index 2d5b5bc..5498311 100644 --- a/src/arch-nspawn.in +++ b/src/arch-nspawn.in @@ -13,6 +13,10 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/archroot.sh # ensure that sane default is set again umask 0022 +# When set to 0, the overlay upperdir is not cleared between builds. +# Set this in nspawn.conf.d/nspawn-security.conf to persist downloaded packages. +DEVTOOLS_CACHE_CLEAR=${DEVTOOLS_CACHE_CLEAR:-1} + working_dir='' files=() @@ -77,6 +81,50 @@ nspawn_security_flags=( nspawn_conf="@pkgdatadir@/nspawn.conf.d/nspawn-security.conf" [[ -f $nspawn_conf ]] && source "$nspawn_conf" +pick_uid_base() { + local name="$1" + local min=524288 step=65536 + local max=1878982656 + local slots=$(( (max - min) / step )) + local hex slot uid + + hex=$(echo -n "$name" | md5sum 2>/dev/null | head -c 8) + hex=${hex:-0} + slot=$(( (16#${hex}) % slots )) + uid=$(( slot * step + min )) + + echo "$uid" +} + +clear_cache_upperdir() { + (( DEVTOOLS_CACHE_CLEAR )) || return + [[ -z $private_users_uid ]] && return + local cache_dir upperdir + for cache_dir in "${cache_dirs[@]}"; do + upperdir="${cache_dir}.upper" + [[ -d $upperdir ]] && find "$upperdir" -mindepth 1 -delete + done +} + +private_users_uid= +for i in "${!nspawn_security_flags[@]}"; do + if [[ ${nspawn_security_flags[$i]} == --private-users=* ]]; then + private_users_uid=${nspawn_security_flags[$i]#*=} + private_users_uid=${private_users_uid%%:*} + if ! [[ $private_users_uid =~ ^[0-9]+$ ]]; then + uid_base=$(pick_uid_base "$(cat /proc/sys/kernel/hostname 2>/dev/null || echo devtools-container)" 2>/dev/null || true) + if [[ -n $uid_base ]]; then + private_users_uid="$uid_base" + nspawn_security_flags[$i]="--private-users=${uid_base}:65536" + else + private_users_uid= + unset 'nspawn_security_flags[$i]' + fi + fi + break + fi +done + nspawn_args+=("${nspawn_security_flags[@]}") if (( ${#cache_dirs[@]} == 0 )); then @@ -107,10 +155,23 @@ while read -r line; do done done < <(pacman-conf --config "${pac_conf:-$working_dir/etc/pacman.conf}" --repo-list) -nspawn_args+=(--bind="${cache_dirs[0]//:/\\:}") - -for cache_dir in "${cache_dirs[@]:1}"; do - nspawn_args+=(--bind-ro="${cache_dir//:/\\:}") +for cache_dir in "${cache_dirs[@]}"; do + cache_dir_escaped="${cache_dir//:/\\:}" + if [[ -n $private_users_uid ]]; then + upperdir="${cache_dir}.upper" + if [[ -d $upperdir ]]; then + current_owner=$(stat -c '%u' "$upperdir") + if [[ $current_owner != "$private_users_uid" ]]; then + chown -R "${private_users_uid}:${private_users_uid}" "$upperdir" + fi + else + mkdir -p "$upperdir" + chown "${private_users_uid}:${private_users_uid}" "$upperdir" + fi + nspawn_args+=(--overlay="${cache_dir_escaped}:${upperdir}:${cache_dir_escaped}") + else + nspawn_args+=(--bind-ro="${cache_dir_escaped}") + fi done # {{{ functions @@ -163,4 +224,18 @@ else set_arch="${CARCH}" fi -exec ${CARCH:+setarch "$set_arch"} systemd-nspawn "${nspawn_args[@]}" "$@" +clear_cache_upperdir + +if [[ -z $private_users_uid ]]; then + stripped=() + for arg in "$@"; do + stripped+=("${arg/:idmap/}") + done + set -- "${stripped[@]}" +fi + +${CARCH:+setarch "$set_arch"} systemd-nspawn "${nspawn_args[@]}" "$@" +ret=$? + +clear_cache_upperdir +exit $ret diff --git a/src/makechrootpkg.in b/src/makechrootpkg.in index eded65d..b6dc17e 100644 --- a/src/makechrootpkg.in +++ b/src/makechrootpkg.in @@ -394,8 +394,8 @@ download_sources prepare_chroot nspawn_build_args=( - --bind="${PWD//:/\\:}:/startdir" - --bind="${SRCDEST//:/\\:}:/srcdest" + --bind="${PWD//:/\\:}:/startdir:idmap" + --bind="${SRCDEST//:/\\:}:/srcdest:idmap" --tmpfs="/tmp:${tmp_opts}" "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" -- 2.55.0
From c550d701370ce1ef3e0ca604d5cb908583e0b600 Mon Sep 17 00:00:00 2001 From: Lenucksi <[email protected]> Date: Tue, 7 Jul 2026 15:08:10 +0200 Subject: [PATCH 3/4] feat: separate AUR package cache from host cache Add AUR cache isolation for builds without --private-users (non-user-namespace mode): - A dedicated writable bind mount at /var/cache/aurcache stores packages downloaded by AUR helpers, preventing AUR builds from polluting the host package cache at /var/cache/pacman/pkg. - Inject a CacheDir entry into the container's pacman.conf via awk (section-aware, placed after the last existing CacheDir in [options]). - Skip the aurcache path in the cache_dir processing loop to prevent systemd-nspawn's open_tree() from failing on feed-forward mounts. - Remove the stale sed-based CacheDir manipulation from copy_hostconf that incorrectly overwrote the container's CacheDir with host paths. - Add clear_aurcache() gated by DEVTOOLS_CACHE_CLEAR, mirroring clear_cache_upperdir() for the non-secconf case. --- src/arch-nspawn.in | 65 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/src/arch-nspawn.in b/src/arch-nspawn.in index 5498311..ed04069 100644 --- a/src/arch-nspawn.in +++ b/src/arch-nspawn.in @@ -13,7 +13,7 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/archroot.sh # ensure that sane default is set again umask 0022 -# When set to 0, the overlay upperdir is not cleared between builds. +# When set to 0, the overlay upperdir and aurcache are not cleared between builds. # Set this in nspawn.conf.d/nspawn-security.conf to persist downloaded packages. DEVTOOLS_CACHE_CLEAR=${DEVTOOLS_CACHE_CLEAR:-1} @@ -106,6 +106,12 @@ clear_cache_upperdir() { done } +clear_aurcache() { + (( DEVTOOLS_CACHE_CLEAR )) || return + [[ -n $private_users_uid ]] && return + [[ -d $aurcache_host ]] && find "$aurcache_host" -mindepth 1 -delete +} + private_users_uid= for i in "${!nspawn_security_flags[@]}"; do if [[ ${nspawn_security_flags[$i]} == --private-users=* ]]; then @@ -155,24 +161,33 @@ while read -r line; do done done < <(pacman-conf --config "${pac_conf:-$working_dir/etc/pacman.conf}" --repo-list) -for cache_dir in "${cache_dirs[@]}"; do - cache_dir_escaped="${cache_dir//:/\\:}" - if [[ -n $private_users_uid ]]; then - upperdir="${cache_dir}.upper" - if [[ -d $upperdir ]]; then - current_owner=$(stat -c '%u' "$upperdir") - if [[ $current_owner != "$private_users_uid" ]]; then - chown -R "${private_users_uid}:${private_users_uid}" "$upperdir" + aurcache_host="/var/cache/pacman/pkg/.aurcache" + aurcache_container="/var/cache/aurcache" + + for cache_dir in "${cache_dirs[@]}"; do + [[ $cache_dir == "$aurcache_container" ]] && continue + cache_dir_escaped="${cache_dir//:/\\:}" + if [[ -n $private_users_uid ]]; then + upperdir="${cache_dir}.upper" + if [[ -d $upperdir ]]; then + current_owner=$(stat -c '%u' "$upperdir") + if [[ $current_owner != "$private_users_uid" ]]; then + chown -R "${private_users_uid}:${private_users_uid}" "$upperdir" + fi + else + mkdir -p "$upperdir" + chown "${private_users_uid}:${private_users_uid}" "$upperdir" fi + nspawn_args+=(--overlay="${cache_dir_escaped}:${upperdir}:${cache_dir_escaped}") else - mkdir -p "$upperdir" - chown "${private_users_uid}:${private_users_uid}" "$upperdir" + nspawn_args+=(--bind-ro="${cache_dir_escaped}") fi - nspawn_args+=(--overlay="${cache_dir_escaped}:${upperdir}:${cache_dir_escaped}") - else - nspawn_args+=(--bind-ro="${cache_dir_escaped}") + done + + if [[ -z $private_users_uid ]]; then + mkdir -p "$aurcache_host" + nspawn_args+=(--bind="${aurcache_host//:/\\:}:${aurcache_container}") fi -done # {{{ functions copy_hostconf () { @@ -199,8 +214,6 @@ copy_hostconf () { mkdir -p "$(dirname "$working_dir$dst")" cp -T "$src" "$working_dir$dst" done - - sed -r "s|^#?\\s*CacheDir.+|CacheDir = ${cache_dirs[*]}|g" -i "$working_dir/etc/pacman.conf" } # }}} @@ -215,6 +228,22 @@ fi copy_hostconf +if [[ -z $private_users_uid ]]; then + awk -v aur="$aurcache_container" ' +/^\[options\]/ { opts = 1 } +opts && /^\[/ && $1 != "[options]" { opts = 0 } +opts && /^CacheDir = / { letzte = NR } +{ zeilen[NR] = $0 } +END { + for (i = 1; i <= NR; i++) { + print zeilen[i] + if (i == letzte && aur) print "CacheDir = " aur + } +} +' "$working_dir/etc/pacman.conf" > "$working_dir/etc/pacman.conf.tmp" \ + && mv "$working_dir/etc/pacman.conf.tmp" "$working_dir/etc/pacman.conf" +fi + eval "$(grep -a '^CARCH=' "$working_dir/etc/makepkg.conf")" [[ -z $nosetarch ]] || unset CARCH @@ -225,6 +254,7 @@ else fi clear_cache_upperdir +clear_aurcache if [[ -z $private_users_uid ]]; then stripped=() @@ -238,4 +268,5 @@ ${CARCH:+setarch "$set_arch"} systemd-nspawn "${nspawn_args[@]}" "$@" ret=$? clear_cache_upperdir +clear_aurcache exit $ret -- 2.55.0
From 3ad10915cf040956d6537cdc787bc45c9a99def2 Mon Sep 17 00:00:00 2001 From: Lenucksi <[email protected]> Date: Tue, 7 Jul 2026 15:08:38 +0200 Subject: [PATCH 4/4] docs: document container hardening, user namespace, and AUR cache isolation Add Configuration section to arch-nspawn(1) documenting the nspawn-security.conf override mechanism, user namespace isolation via --private-users, and the writable overlay cache. Add Security sections to both man pages documenting capability drops, seccomp filters, address family restrictions, noexec on tmpfs, and ID-mapped source directory mounts. Document AUR cache separation (/var/cache/aurcache) and the DEVTOOLS_CACHE_CLEAR flag in both man pages and the config file. --- config/nspawn.conf.d/nspawn-security.conf | 8 +++++ doc/man/arch-nspawn.1.asciidoc | 43 +++++++++++++++++++++++ doc/man/makechrootpkg.1.asciidoc | 14 +++++++- src/arch-nspawn.in | 1 + 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/config/nspawn.conf.d/nspawn-security.conf b/config/nspawn.conf.d/nspawn-security.conf index d664c28..d3139e6 100644 --- a/config/nspawn.conf.d/nspawn-security.conf +++ b/config/nspawn.conf.d/nspawn-security.conf @@ -19,3 +19,11 @@ nspawn_security_flags+=( --private-users=yes --private-users-ownership=map ) + +# === Cache persistence === +# +# By default the overlay upperdir and AUR package cache are cleared +# before and after each build, so every build starts with a fresh cache. +# Set this to 0 to preserve downloaded packages between builds: +# +# DEVTOOLS_CACHE_CLEAR=0 diff --git a/doc/man/arch-nspawn.1.asciidoc b/doc/man/arch-nspawn.1.asciidoc index be5adc4..2d4c8c5 100644 --- a/doc/man/arch-nspawn.1.asciidoc +++ b/doc/man/arch-nspawn.1.asciidoc @@ -39,4 +39,47 @@ Options *-h*:: Show this usage message +Configuration +------------- + +The file '@pkgdatadir@/nspawn.conf.d/nspawn-security.conf' is sourced +if present. It may extend nspawn_security_flags with additional +systemd-nspawn(1) arguments. + +The shipped default enables user namespace isolation: + + --private-users=yes + --private-users-ownership=map + +With this enabled the container root (UID 0) is mapped to an +unprivileged host UID range, and the package cache is mounted as +a writable overlay so that pacman can cache downloaded packages. +The UID range is derived from the hostname via pick_uid_base(). + +When user namespaces are disabled (no --private-users), the package +cache is mounted read-only and a separate writable cache is provided +at /var/cache/aurcache. This prevents AUR builds from polluting the +host package cache. + +The temporary build cache (overlay upperdir and AUR cache) is cleared +before and after each build by default. Set DEVTOOLS_CACHE_CLEAR=0 +in the config file to preserve cached packages between builds. + +Security +-------- + +The following hardening is applied to every container: + + *Capabilities dropped*: CAP_BPF, CAP_NET_ADMIN, CAP_SYS_PTRACE, + CAP_SYS_MODULE, CAP_MKNOD, CAP_NET_RAW, CAP_SYS_CHROOT. + CAP_SYS_ADMIN retained for pacman Landlock sandbox compatibility. + + *System call filter*: The @keyring and @mount seccomp groups are + restricted, blocking keyring manipulation and mount/pivot_root + inside the container. + + *Address families*: Only AF_INET, AF_INET6 and AF_UNIX permitted. + AF_UNIX is required for pacman-key, gpg-agent and systemd-journald + communication inside the container. + include::include/footer.asciidoc[] diff --git a/doc/man/makechrootpkg.1.asciidoc b/doc/man/makechrootpkg.1.asciidoc index ea7bc05..955397b 100644 --- a/doc/man/makechrootpkg.1.asciidoc +++ b/doc/man/makechrootpkg.1.asciidoc @@ -46,7 +46,8 @@ Options Bind directory into build chroot as read-only *-t* <dir>[:opts]:: - Mount a tmpfs at 'dir'. See the '--tmpfs' argument in systemd-nspawn(1) for more details. + Mount a tmpfs at 'dir' (default: noexec). See the '--tmpfs' argument + in systemd-nspawn(1) for more details. *-u*:: Update the working copy of the chroot before building @@ -79,6 +80,17 @@ Options *-x* <when>:: Inspect chroot after build, possible modes are 'never' (default), 'always' or 'failure' +Security +-------- + +Source directories (/startdir, /srcdest) use ID-mapped bind mounts +so the build user inside a user namespace can access source files +with their original host UIDs. The /tmp filesystem is mounted with +noexec to prevent execution of binaries placed in /tmp. + +Without user namespaces, AUR builds use a dedicated writable cache +at /var/cache/aurcache instead of writing to the host package cache. + See Also -------- diff --git a/src/arch-nspawn.in b/src/arch-nspawn.in index ed04069..934dbf3 100644 --- a/src/arch-nspawn.in +++ b/src/arch-nspawn.in @@ -214,6 +214,7 @@ copy_hostconf () { mkdir -p "$(dirname "$working_dir$dst")" cp -T "$src" "$working_dir$dst" done + } # }}} -- 2.55.0
