Harden CUPS printer discovery

Run cups-browsed as a locked service account with a dedicated cache and a focused systemd sandbox. Restrict automatic queues to driverless IPP printers, remove wheel from passwordless CUPS administration, replace cups-pdf with Polkit-backed setup, and migrate existing systems safely.

Reported-By: Erik Hunstad (Bad Sector Labs)

Co-Authored-By: Daybreak Blue <noreply@openai.com>
This commit is contained in:
Mehmet Ince
2026-08-27 18:00:12 +01:00
co-authored by Daybreak Blue
parent 946704f309
commit 5c336885d2
9 changed files with 375 additions and 6 deletions
+169
View File
@@ -0,0 +1,169 @@
#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
packages="$ROOT/install/omarchy-base.packages"
cups_browsed_conf="$ROOT/etc/cups/cups-browsed.conf"
sysusers_conf="$ROOT/etc/sysusers.d/omarchy-cups-browsed.conf"
service_dropin="$ROOT/etc/systemd/system/cups-browsed.service.d/10-omarchy.conf"
grep -qxF cups-browsed "$packages" || fail "cups-browsed remains in the base package set"
grep -qxF cups-pk-helper "$packages" || fail "Polkit printer administration is installed"
! grep -qxF cups-pdf "$packages" || fail "the root CUPS-PDF backend is removed"
pass "the base install keeps discovery and replaces CUPS-PDF with Polkit administration"
grep -qxF 'CacheDir /var/cache/cups-browsed' "$cups_browsed_conf" ||
fail "cups-browsed keeps state outside the print-filter cache"
grep -qxF 'CreateIPPPrinterQueues Driverless' "$cups_browsed_conf" ||
fail "automatic queues are limited to driverless IPP printers"
grep -qxF 'CreateRemoteCUPSPrinterQueues No' "$cups_browsed_conf" ||
fail "remote CUPS queues are not created automatically"
! grep -q 'CreateRemotePrinters' "$cups_browsed_conf" ||
fail "the unsupported CreateRemotePrinters directive is gone"
pass "cups-browsed uses explicit supported discovery policy and an isolated cache"
grep -qxF 'u cups-browsed - "CUPS printer discovery" / -' "$sysusers_conf" ||
fail "a locked cups-browsed system account is declared"
for setting in \
'User=cups-browsed' \
'Group=cups-browsed' \
'CacheDirectory=cups-browsed' \
'CacheDirectoryMode=0750' \
'UMask=0027' \
'NoNewPrivileges=yes' \
'ProtectSystem=strict' \
'ProtectHome=yes' \
'PrivateTmp=yes' \
'RestrictSUIDSGID=yes'; do
grep -qxF "$setting" "$service_dropin" ||
fail "cups-browsed service hardening includes $setting"
done
! grep -q '^\(Ambient\|CapabilityBoundingSet\).*CAP_NET_BIND_SERVICE' "$service_dropin" ||
fail "cups-browsed is not granted an unverified network capability"
pass "cups-browsed runs as its confined service account without added capabilities"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
mock_bin="$test_tmp/bin"
mkdir -p "$mock_bin" "$test_tmp/etc/cups" "$test_tmp/var/lib/omarchy/migrations"
cat >"$mock_bin/systemd-sysusers" <<'SH'
#!/bin/bash
printf 'sysusers\t%s\n' "$*" >>"$OMARCHY_CUPS_TEST_LOG"
SH
cat >"$mock_bin/chown" <<'SH'
#!/bin/bash
printf 'chown\t%s\n' "$*" >>"$OMARCHY_CUPS_TEST_LOG"
SH
cat >"$mock_bin/omarchy-pkg-present" <<'SH'
#!/bin/bash
[[ $1 == "cups" || $1 == "cups-browsed" ]]
SH
for command in omarchy-pkg-add omarchy-pkg-drop; do
cat >"$mock_bin/$command" <<'SH'
#!/bin/bash
printf '%s\t%s\n' "${0##*/}" "$*" >>"$OMARCHY_CUPS_TEST_LOG"
SH
done
cat >"$mock_bin/systemctl" <<'SH'
#!/bin/bash
printf 'systemctl\t%s\n' "$*" >>"$OMARCHY_CUPS_TEST_LOG"
exit 0
SH
cat >"$mock_bin/sudo" <<'SH'
#!/bin/bash
printf 'sudo\t%s\n' "$*" >>"$OMARCHY_CUPS_TEST_LOG"
exec "$@"
SH
chmod +x "$mock_bin"/*
authorization_conf="$test_tmp/etc/cups/cups-files.conf"
cat >"$authorization_conf" <<'CONF'
# Keep this custom preamble.
SystemGroup sys root wheel custom-admin wheel # Keep this inline comment.
SystemGroup wheel print-operators # Keep this second inline comment.
PeerCred off # Keep this PeerCred comment.
PeerCred off # Keep this second PeerCred comment.
CONF
log="$test_tmp/actions.log"
export OMARCHY_CUPS_TEST_LOG="$log"
run_printing_setup() {
PATH="$mock_bin:$PATH" \
OMARCHY_CUPS_FILES_CONF="$authorization_conf" \
OMARCHY_CUPS_BROWSED_SYSUSERS_CONF="$sysusers_conf" \
bash -euo pipefail "$ROOT/install/config/printing.sh"
}
run_printing_setup
grep -qxF 'SystemGroup sys root custom-admin print-operators cups-browsed # Keep this inline comment.' "$authorization_conf" ||
fail "printing setup reserves CUPS administration for the service account"
grep -qxF '# Keep this second inline comment.' "$authorization_conf" ||
fail "printing setup preserves comments from consolidated SystemGroup directives"
grep -qxF 'PeerCred on # Keep this PeerCred comment.' "$authorization_conf" ||
fail "printing setup enables peer credentials for the service account"
grep -qxF '# Keep this second PeerCred comment.' "$authorization_conf" ||
fail "printing setup preserves comments from duplicate PeerCred directives"
grep -qxF '# Keep this custom preamble.' "$authorization_conf" ||
fail "printing setup preserves unrelated CUPS configuration"
[[ $(grep -c '^SystemGroup ' "$authorization_conf") == 1 ]] ||
fail "printing setup emits one SystemGroup directive"
cp "$authorization_conf" "$test_tmp/first-run.conf"
run_printing_setup
cmp -s "$authorization_conf" "$test_tmp/first-run.conf" ||
fail "printing setup is idempotent"
pass "printing setup narrows CUPS authorization without clobbering other configuration"
ln -s "$authorization_conf" "$test_tmp/etc/cups/symlinked.conf"
if PATH="$mock_bin:$PATH" \
OMARCHY_CUPS_FILES_CONF="$test_tmp/etc/cups/symlinked.conf" \
OMARCHY_CUPS_BROWSED_SYSUSERS_CONF="$sysusers_conf" \
bash -euo pipefail "$ROOT/install/config/printing.sh" 2>/dev/null; then
fail "printing setup refuses a symlinked authorization file"
fi
pass "printing setup refuses to rewrite a symlinked privileged configuration"
marker="$test_tmp/var/lib/omarchy/migrations/1787815267"
PATH="$mock_bin:$PATH" \
OMARCHY_PATH="$ROOT" \
OMARCHY_CUPS_FILES_CONF="$authorization_conf" \
OMARCHY_CUPS_BROWSED_SYSUSERS_CONF="$sysusers_conf" \
OMARCHY_CUPS_MIGRATION_MARKER="$marker" \
bash -euo pipefail "$ROOT/migrations/1787815267.sh"
grep -qxF $'omarchy-pkg-drop\tcups-pdf' "$log" ||
fail "the migration removes CUPS-PDF"
grep -qxF $'omarchy-pkg-add\tcups-pk-helper' "$log" ||
fail "the migration installs authenticated printer administration"
grep -qxF $'systemctl\tstop cups-browsed.service' "$log" ||
fail "the migration stops the root cups-browsed process before reconfiguration"
grep -qxF $'systemctl\tdaemon-reload' "$log" ||
fail "the migration reloads the hardened service"
grep -qxF $'systemctl\ttry-reload-or-restart cups.service' "$log" ||
fail "the migration applies narrowed CUPS authorization"
grep -qxF $'systemctl\trestart cups-browsed.service' "$log" ||
fail "the migration resumes an active cups-browsed service"
[[ -f $marker ]] || fail "the migration records machine-wide completion"
actions_after_first_run=$(wc -l <"$log")
PATH="$mock_bin:$PATH" \
OMARCHY_PATH="$ROOT" \
OMARCHY_CUPS_MIGRATION_MARKER="$marker" \
bash -euo pipefail "$ROOT/migrations/1787815267.sh"
[[ $(wc -l <"$log") == "$actions_after_first_run" ]] ||
fail "the machine-wide migration repeats privileged work"
pass "the migration safely converts an active existing installation once"