fix: the RDS master user is no longer a PostgreSQL superuser (#687)

This commit is contained in:
Julian Sommer
2026-08-04 10:59:45 +10:00
committed by GitHub
parent 175b8b93d9
commit faa0519cf1
8 changed files with 117 additions and 12 deletions
+35 -5
View File
@@ -49,6 +49,10 @@ BOOTSTRAP_DIR="${RDS_BOOTSTRAP_DIR:-/run/rds-init}"
MOUNTS_FILE="${RDS_MOUNTS_FILE:-/proc/mounts}"
# The group role the master's administrative privileges hang off. Named as on
# AWS, so a customer's `GRANT rds_superuser TO ...` works here too.
MASTER_GROUP_ROLE="rds_superuser"
log() { echo "[rds-init] $*"; }
fail() { echo "[rds-init] ERROR: $*" >&2; exit 1; }
@@ -85,6 +89,17 @@ load_handoff() {
RDS_PORT="${RDS_PORT:-5432}"
[ -n "${RDS_MASTER_USERNAME}" ] || fail "bootstrap config has no RDS_MASTER_USERNAME"
# The master role is created NOSUPERUSER, so a master named after the
# bootstrap superuser would leave the cluster without one and strip rds-agent
# of the privileged SQL it reaches over peer auth — unrepairable, since the
# datadir bootstraps once. The control plane reserves both names already.
case "${RDS_MASTER_USERNAME}" in
"${PG_USER}" | "${MASTER_GROUP_ROLE}")
fail "RDS_MASTER_USERNAME ${RDS_MASTER_USERNAME} is reserved by the platform; the control plane should have rejected it"
;;
esac
case "${RDS_PORT}" in
'' | *[!0-9]*) fail "bootstrap config has a non-numeric RDS_PORT: ${RDS_PORT}" ;;
esac
@@ -264,6 +279,7 @@ psql_bootstrap() {
RDS_MASTER_USERNAME="${RDS_MASTER_USERNAME}" \
RDS_MASTER_PASSWORD="${RDS_MASTER_PASSWORD}" \
RDS_DB_NAME="${RDS_DB_NAME}" \
RDS_GROUP_ROLE="${MASTER_GROUP_ROLE}" \
"${PG_BIN}/psql" --no-psqlrc --quiet --no-align --tuples-only \
-v ON_ERROR_STOP=1 \
-h "${BOOTSTRAP_DIR}" -p "${RDS_PORT}" -U "${PG_USER}" -d postgres -f -
@@ -291,18 +307,32 @@ bootstrap_master() {
fi
}
# initdb may already have made it the bootstrap superuser, when the master
# username is "postgres".
# The master role is administrative but deliberately not a PostgreSQL SUPERUSER,
# as on AWS: a superuser reaches outside the database entirely — COPY FROM
# PROGRAM, pg_read_file/pg_write_file, untrusted languages — so master
# credentials would be a shell in the DB VM rather than a database privilege.
# The cluster's own superuser stays the bootstrap role, which only rds-init and
# rds-agent reach, over the unix socket under peer auth.
apply_master_role() {
log "setting the master password for role ${RDS_MASTER_USERNAME}"
log "creating the master role ${RDS_MASTER_USERNAME}"
# PG16 hardened CREATEROLE: it can neither grant itself SUPERUSER nor take
# membership in a predefined role it does not administer, so CREATEROLE here
# is not a route back to the privileges above.
psql_bootstrap <<'SQL'
\getenv master RDS_MASTER_USERNAME
\getenv password RDS_MASTER_PASSWORD
\getenv grp RDS_GROUP_ROLE
SELECT NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = :'grp') AS create_group \gset
\if :create_group
CREATE ROLE :"grp" NOLOGIN;
\endif
GRANT pg_monitor, pg_signal_backend, pg_checkpoint TO :"grp";
SELECT NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = :'master') AS create_role \gset
\if :create_role
CREATE ROLE :"master" WITH LOGIN SUPERUSER;
CREATE ROLE :"master" WITH LOGIN NOSUPERUSER CREATEDB CREATEROLE;
\endif
ALTER ROLE :"master" WITH LOGIN SUPERUSER PASSWORD :'password';
ALTER ROLE :"master" WITH LOGIN NOSUPERUSER CREATEDB CREATEROLE PASSWORD :'password';
GRANT :"grp" TO :"master" WITH ADMIN OPTION;
SQL
}
+39 -4
View File
@@ -111,7 +111,7 @@ EOF
cat > "${STUBBIN}/psql" <<'EOF'
#!/bin/sh
{
echo "--- psql $* [master=${RDS_MASTER_USERNAME:-} password=${RDS_MASTER_PASSWORD:-} db=${RDS_DB_NAME:-}]"
echo "--- psql $* [master=${RDS_MASTER_USERNAME:-} password=${RDS_MASTER_PASSWORD:-} db=${RDS_DB_NAME:-} grp=${RDS_GROUP_ROLE:-}]"
cat
} >> "${PSQL_CALLS}"
# ON_ERROR_STOP makes a real psql exit non-zero on any SQL failure; the stub
@@ -140,11 +140,12 @@ export INITDB_CALLS PGCTL_CALLS PSQL_CALLS
# write_handoff <mode> <password> <dbname>: lay down the bootstrap.env rds-agent
# would have written. An empty password omits the field, as `attach` does.
# MASTER_USER overrides the master role name, for the reserved-name cases.
write_handoff() {
mkdir -p "${HANDOFF}"
{
echo "RDS_MODE=$1"
echo "RDS_MASTER_USERNAME=mulgamaster"
echo "RDS_MASTER_USERNAME=${MASTER_USER:-mulgamaster}"
[ -n "$2" ] && echo "RDS_MASTER_PASSWORD=$2"
[ -n "$3" ] && echo "RDS_DB_NAME=$3"
echo "RDS_PORT=6543"
@@ -171,7 +172,7 @@ reset_state() {
: > "${INITDB_CALLS}"
: > "${PGCTL_CALLS}"
: > "${PSQL_CALLS}"
unset INITDB_FAIL PG_HBA_AS_DIR PSQL_FAIL LS_FAIL RDS_ALLOW_LOCAL_DATADIR || true
unset INITDB_FAIL PG_HBA_AS_DIR PSQL_FAIL LS_FAIL RDS_ALLOW_LOCAL_DATADIR MASTER_USER || true
}
# run: invoke rds-init with every path knob pointed into the temp dir.
@@ -213,12 +214,31 @@ if run_ok "initialize"; then
&& pass "initialize: ssl enabled" || fail "initialize: ssl not enabled"
grep -q 'shared_buffers = 128MB' "${PGDATA}/conf.d/10-rds-parameters.conf" \
&& pass "initialize: resolved parameters installed" || fail "initialize: no parameter include"
grep -q 'ALTER ROLE :"master" WITH LOGIN SUPERUSER PASSWORD' "${PSQL_CALLS}" \
grep -q 'ALTER ROLE :"master" WITH LOGIN NOSUPERUSER CREATEDB CREATEROLE PASSWORD' "${PSQL_CALLS}" \
&& pass "initialize: master password applied" || fail "initialize: no ALTER ROLE"
grep -q 'password=s3cr3t' "${PSQL_CALLS}" \
&& pass "initialize: password passed through the environment" || fail "initialize: password not in psql env"
grep -q 'CREATE DATABASE' "${PSQL_CALLS}" \
&& pass "initialize: initial database created" || fail "initialize: no CREATE DATABASE"
# The master role is administrative but not a PostgreSQL superuser: a
# superuser reaches outside the database (COPY FROM PROGRAM, pg_read_file,
# untrusted languages), so master credentials would be a shell in the DB VM.
grep -qE '(^|[^O])SUPERUSER' "${PSQL_CALLS}" \
&& fail "initialize: bootstrap SQL still grants SUPERUSER" \
|| pass "initialize: master role is not a superuser"
grep -q 'GRANT :"grp" TO :"master" WITH ADMIN OPTION' "${PSQL_CALLS}" \
&& pass "initialize: master holds the administrative group role" \
|| fail "initialize: master never granted the group role"
grep -q 'grp=rds_superuser' "${PSQL_CALLS}" \
&& pass "initialize: the group role is named as on AWS" || fail "initialize: group role name not delivered"
grep -q 'GRANT pg_monitor, pg_signal_backend, pg_checkpoint TO :"grp"' "${PSQL_CALLS}" \
&& pass "initialize: monitoring/signal/checkpoint granted" || fail "initialize: group role has no privileges"
# The three predefined roles that hand back exactly what SUPERUSER gave away.
grep -qE 'pg_(read|write)_server_files|pg_execute_server_program' "${PSQL_CALLS}" \
&& fail "initialize: granted a role that restores file or program access" \
|| pass "initialize: no server-file or program-execution roles granted"
grep -q "listen_addresses=''" "${PGCTL_CALLS}" \
&& pass "initialize: bootstrap server is socket-only" || fail "initialize: bootstrap server listened on TCP"
grep -q 'stop' "${PGCTL_CALLS}" \
@@ -400,6 +420,21 @@ reset_state
rm -rf "${HANDOFF}"
run_fails "no-handoff"
# --- Case 9: a master username the platform reserves ---
# The master role is created NOSUPERUSER, so one named after the bootstrap
# superuser would leave the cluster without a superuser at all and strip
# rds-agent of the privileged SQL it needs — on a datadir that bootstraps once.
for reserved in postgres rds_superuser; do
reset_state
MASTER_USER="${reserved}"
write_handoff initialize 's3cr3t' appdb
run_fails "reserved-master-${reserved}"
[ -s "${INITDB_CALLS}" ] \
&& fail "reserved-master-${reserved}: initdb ran before the name was refused" \
|| pass "reserved-master-${reserved}: refused before initdb"
unset MASTER_USER
done
if [ "${FAILS}" -eq 0 ]; then
echo "PASS: all rds-init cases"
exit 0