Files
omarchycn/test/shell.d/cups-hardening-test.sh
T
521f1ae9ac Resume cups-browsed on whether it is enabled, not on whether it was running
The migration recomputed whether cups-browsed was active at the start of every run, after an earlier run may already have stopped it. A run interrupted between that stop and the completion marker left the service down, and the retry that followed read it as inactive, skipped the restart, and wrote the marker anyway: printer discovery stayed off until the next reboot with no migration left to bring it back. Keying the restart to whether the unit is enabled survives the interruption, and reports not-enabled for a unit the user masked or disabled, which restarting would fail on and abort the migration short of its marker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
2026-08-27 19:47:10 +02:00

225 lines
8.7 KiB
Bash

#!/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"
# An interrupted earlier run leaves cups-browsed stopped, so the retry that
# follows finds it inactive. It must still be restarted: the retry records the
# machine-wide marker either way, so a restart skipped here would leave printer
# discovery off until the next reboot with nothing left to run.
cat >"$mock_bin/systemctl" <<'SH'
#!/bin/bash
printf 'systemctl\t%s\n' "$*" >>"$OMARCHY_CUPS_TEST_LOG"
[[ $1 == "is-active" ]] && exit 1
exit 0
SH
chmod +x "$mock_bin/systemctl"
retry_log="$test_tmp/retry.log"
retry_marker="$test_tmp/var/lib/omarchy/migrations/1787815267-retry"
OMARCHY_CUPS_TEST_LOG="$retry_log" \
PATH="$mock_bin:$PATH" \
OMARCHY_PATH="$ROOT" \
OMARCHY_CUPS_FILES_CONF="$authorization_conf" \
OMARCHY_CUPS_BROWSED_SYSUSERS_CONF="$sysusers_conf" \
OMARCHY_CUPS_MIGRATION_MARKER="$retry_marker" \
bash -euo pipefail "$ROOT/migrations/1787815267.sh"
grep -qxF $'systemctl\trestart cups-browsed.service' "$retry_log" ||
fail "the retry resumes cups-browsed after an interrupted earlier run"
pass "a run following an interrupted one still resumes printer discovery"
# A unit the user masked or disabled reports not-enabled, and restarting it
# would fail and abort the migration before it records completion.
cat >"$mock_bin/systemctl" <<'SH'
#!/bin/bash
printf 'systemctl\t%s\n' "$*" >>"$OMARCHY_CUPS_TEST_LOG"
[[ $1 == "is-active" || $1 == "is-enabled" ]] && exit 1
exit 0
SH
chmod +x "$mock_bin/systemctl"
masked_log="$test_tmp/masked.log"
masked_marker="$test_tmp/var/lib/omarchy/migrations/1787815267-masked"
OMARCHY_CUPS_TEST_LOG="$masked_log" \
PATH="$mock_bin:$PATH" \
OMARCHY_PATH="$ROOT" \
OMARCHY_CUPS_FILES_CONF="$authorization_conf" \
OMARCHY_CUPS_BROWSED_SYSUSERS_CONF="$sysusers_conf" \
OMARCHY_CUPS_MIGRATION_MARKER="$masked_marker" \
bash -euo pipefail "$ROOT/migrations/1787815267.sh"
! grep -qxF $'systemctl\trestart cups-browsed.service' "$masked_log" ||
fail "the migration leaves a masked or disabled cups-browsed alone"
[[ -f $masked_marker ]] || fail "the migration completes with cups-browsed masked"
pass "a masked or disabled cups-browsed is left alone and does not fail the migration"