65 lines
2.7 KiB
Bash
Executable File
65 lines
2.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# Provision the restricted PostgreSQL runtime role without printing its secret.
|
|
|
|
set -euo pipefail
|
|
|
|
NAMESPACE="${NAMESPACE:-ljwx-health}"
|
|
SECRET_NAME="${SECRET_NAME:-ljwx-health-secret}"
|
|
POSTGRES_NAMESPACE="${POSTGRES_NAMESPACE:-infra}"
|
|
POSTGRES_POD="${POSTGRES_POD:-postgres-infra-postgresql-0}"
|
|
POSTGRES_DATABASE="${POSTGRES_DATABASE:-ljwx_health}"
|
|
APP_ROLE="${APP_ROLE:-ljwx_app}"
|
|
DB_HOST="${DB_HOST:-postgres-lb.infra.svc.cluster.local}"
|
|
DB_PORT="${DB_PORT:-5432}"
|
|
|
|
if [[ ! "${APP_ROLE}" =~ ^[a-z_][a-z0-9_]*$ ]]; then
|
|
echo "APP_ROLE must be a lowercase PostgreSQL identifier" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! kubectl -n "${NAMESPACE}" get secret "${SECRET_NAME}" >/dev/null 2>&1; then
|
|
echo "missing secret ${NAMESPACE}/${SECRET_NAME}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
app_password="${DB_APP_PASSWORD:-}"
|
|
if [[ -z "${app_password}" ]]; then
|
|
app_password="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
|
|
fi
|
|
if [[ ! "${app_password}" =~ ^[0-9A-Za-z._~-]{32,128}$ ]]; then
|
|
echo "DB_APP_PASSWORD must contain 32-128 URL-safe characters" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The generated alphabet needs no SQL escaping. Feed the DDL through stdin so
|
|
# the password does not appear in argv, terminal output, or shell history.
|
|
printf "ALTER ROLE %s LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS PASSWORD '%s';\n" \
|
|
"${APP_ROLE}" "${app_password}" \
|
|
| kubectl -n "${POSTGRES_NAMESPACE}" exec -i "${POSTGRES_POD}" -- \
|
|
psql -U postgres -d "${POSTGRES_DATABASE}" -v ON_ERROR_STOP=1 -f - >/dev/null
|
|
|
|
# Prove TCP password authentication and role flags before publishing the key.
|
|
actual_flags="$({ printf '%s\n' "${app_password}"; } \
|
|
| kubectl -n "${POSTGRES_NAMESPACE}" exec -i "${POSTGRES_POD}" -- sh -ec '
|
|
IFS= read -r PGPASSWORD
|
|
export PGPASSWORD
|
|
psql --host="$1" --port="$2" --username="$3" --dbname="$4" \
|
|
--no-password --tuples-only --no-align \
|
|
--command="SELECT current_user || '\''|'\'' || rolsuper || '\''|'\'' || rolbypassrls FROM pg_roles WHERE rolname = current_user"
|
|
' sh "${DB_HOST}" "${DB_PORT}" "${APP_ROLE}" "${POSTGRES_DATABASE}")"
|
|
if [[ "${actual_flags}" != "${APP_ROLE}|false|false" ]]; then
|
|
echo "restricted-role verification failed" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Preserve every existing Secret key and merge only DB_APP_PASSWORD. The patch
|
|
# travels over stdin, so neither the plaintext nor base64 value appears in argv.
|
|
DB_APP_PASSWORD_B64="$(printf '%s' "${app_password}" | base64 | tr -d '\n')"
|
|
printf '{"data":{"DB_APP_PASSWORD":"%s"}}\n' "${DB_APP_PASSWORD_B64}" \
|
|
| kubectl -n "${NAMESPACE}" patch secret "${SECRET_NAME}" \
|
|
--type=merge --patch-file=/dev/stdin >/dev/null
|
|
unset DB_APP_PASSWORD_B64
|
|
unset app_password
|
|
|
|
echo "provisioned ${APP_ROLE}: LOGIN/NOSUPERUSER/NOBYPASSRLS; secret key updated without disclosure"
|