Files
bongo/contrib/testing/agent-privilege-check.sh
T
2026-07-22 18:31:28 +02:00

156 lines
4.8 KiB
Bash
Executable File

#!/bin/sh
set -eu
SERVICE=${BONGO_TEST_SERVICE:-bongo.service}
EXPECTED=${BONGO_TEST_AGENTS:-"bongostore bongoqueue bongoantispam bongoavirus bongorules bongosieve bongocollector bongoworker bongosmtp bongosmtpc bongoimap bongopop3 bongo-web"}
if [ "${BONGO_ALLOW_LIVE_PRIVILEGE_TEST:-}" != 1 ]; then
echo "set BONGO_ALLOW_LIVE_PRIVILEGE_TEST=1 for the disposable host" >&2
exit 2
fi
manager=$(sudo -n /usr/bin/systemctl show "$SERVICE" -p MainPID --value)
bongo_uid=$(/usr/bin/id -u bongo)
bongo_gid=$(/usr/bin/id -g bongo)
allowed_caps=" cap_chown cap_dac_override cap_fowner cap_setgid cap_setuid cap_net_bind_service "
if [ -z "$manager" ] || [ "$manager" -le 1 ] ||
! sudo -n /usr/bin/systemctl is-active "$SERVICE" >/dev/null; then
echo "$SERVICE is not active" >&2
exit 1
fi
bounding=$(sudo -n /usr/bin/systemctl show "$SERVICE" \
-p CapabilityBoundingSet --value)
if [ -z "$bounding" ]; then
echo "$SERVICE has an empty or unavailable capability boundary" >&2
exit 1
fi
for capability in $bounding; do
case "$allowed_caps" in
*" $capability "*) ;;
*)
echo "$SERVICE permits unexpected capability $capability" >&2
exit 1
;;
esac
done
status_field()
{
field=$1
pid=$2
sed -n "s/^${field}:[[:space:]]*//p" "/proc/$pid/status"
}
assert_zero_caps()
{
pid=$1
name=$2
for field in CapInh CapPrm CapEff CapAmb; do
value=$(status_field "$field" "$pid")
if [ "$value" != 0000000000000000 ]; then
echo "$name ($pid) retained $field=$value" >&2
exit 1
fi
done
}
assert_no_groups()
{
pid=$1
name=$2
groups=$(status_field Groups "$pid")
if [ -n "$groups" ]; then
echo "$name ($pid) retained supplementary groups: $groups" >&2
exit 1
fi
}
assert_permanent_drop()
{
pid=$1
name=$2
set -- $(status_field Uid "$pid")
if [ "$#" -ne 4 ] || [ "$1" != "$bongo_uid" ] ||
[ "$2" != "$bongo_uid" ] || [ "$3" != "$bongo_uid" ] ||
[ "$4" != "$bongo_uid" ]; then
echo "$name ($pid) did not permanently drop UID: $*" >&2
exit 1
fi
set -- $(status_field Gid "$pid")
if [ "$#" -ne 4 ] || [ "$1" != "$bongo_gid" ] ||
[ "$2" != "$bongo_gid" ] || [ "$3" != "$bongo_gid" ] ||
[ "$4" != "$bongo_gid" ]; then
echo "$name ($pid) did not permanently drop GID: $*" >&2
exit 1
fi
assert_no_groups "$pid" "$name"
assert_zero_caps "$pid" "$name"
}
find_child()
{
wanted=$1
for pid in $(/usr/bin/pgrep -P "$manager" 2>/dev/null || true); do
name=$(status_field Name "$pid")
if [ "$name" = "$wanted" ]; then
printf '%s\n' "$pid"
return 0
fi
done
return 1
}
# The supervisor must regain root only while forking a replacement which has
# to bind a privileged listener. It must otherwise run as bongo with no active
# or ambient capability and no supplementary group.
set -- $(status_field Uid "$manager")
if [ "$#" -ne 4 ] || [ "$1" != 0 ] || [ "$2" != "$bongo_uid" ] ||
[ "$4" != "$bongo_uid" ]; then
echo "manager has unexpected UID tuple: $*" >&2
exit 1
fi
assert_no_groups "$manager" bongo-manager
for field in CapInh CapEff CapAmb; do
value=$(status_field "$field" "$manager")
if [ "$value" != 0000000000000000 ]; then
echo "manager retained active $field=$value" >&2
exit 1
fi
done
for name in $EXPECTED; do
pid=$(find_child "$name") || {
echo "configured agent $name is not a child of manager $manager" >&2
exit 1
}
if [ "$name" = bongoworker ]; then
# ACME certificate deployment is the one scheduled operation which
# currently needs a short, explicit seteuid(0) window. The idle worker
# must still have bongo as effective/filesystem UID and no active or
# ambient capability. This exception is removed when deployment is
# split into a dedicated privileged helper.
set -- $(status_field Uid "$pid")
if [ "$#" -ne 4 ] || [ "$1" != 0 ] ||
[ "$2" != "$bongo_uid" ] || [ "$4" != "$bongo_uid" ]; then
echo "$name ($pid) has unexpected UID tuple: $*" >&2
exit 1
fi
assert_no_groups "$pid" "$name"
for field in CapInh CapEff CapAmb; do
value=$(status_field "$field" "$pid")
if [ "$value" != 0000000000000000 ]; then
echo "$name ($pid) retained active $field=$value" >&2
exit 1
fi
done
else
assert_permanent_drop "$pid" "$name"
fi
echo "PASS $name PID $pid privilege state"
done
echo "PASS all configured agents are manager children with bounded privilege"
echo "PASS service capability boundary: $bounding"