Files
Superminaren d81a49175b
CI / check (push) Canceled after 0s
CI / integration-e2e (push) Canceled after 0s
Deploy to production / deploy (push) Canceled after 0s
Updated
2026-10-14 23:30:02 +02:00

86 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# generate-env.sh: copy .env.example to .env and fill in every secret.
#
# Fills POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY,
# SECRET_KEY_BASE, SECRETS_ENCRYPTION_KEY and CRON_SECRET. ANON_KEY and
# SERVICE_ROLE_KEY are HS256 JWTs signed with the generated JWT_SECRET.
#
# Usage: scripts/generate-env.sh [--output FILE] [--yes]
# --output FILE write here instead of .env
# --yes, --force skip the confirmation prompt (required when stdin is not a
# terminal)
#
# Always asks before writing; the default answer is N.
#
# The default target is .env (git-ignored), not .env.example, because
# .env.example is committed and must never hold real secrets.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TEMPLATE="$ROOT/.env.example"
OUT="$ROOT/.env"
FORCE=0
while [[ $# -gt 0 ]]; do
case "$1" in
--output) OUT="$2"; shift 2 ;;
--yes|--force) FORCE=1; shift ;;
-h|--help) sed -n '2,18p' "${BASH_SOURCE[0]}"; exit 0 ;;
*) echo "error: unknown argument: $1" >&2; exit 1 ;;
esac
done
command -v openssl >/dev/null || { echo "error: openssl is required" >&2; exit 1; }
[[ -f "$TEMPLATE" ]] || { echo "error: $TEMPLATE not found" >&2; exit 1; }
if [[ $FORCE -ne 1 ]]; then
if [[ ! -t 0 ]]; then
echo "error: not a terminal; pass --yes to run non-interactively" >&2
exit 1
fi
if [[ -e "$OUT" ]]; then
echo "$OUT already exists. Continuing replaces it and rotates every secret."
else
echo "This will create $OUT with newly generated secrets."
fi
read -r -p "Continue? [y/N] " answer
case "$answer" in
y|Y|yes|YES) ;;
*) echo "aborted"; exit 1 ;;
esac
fi
b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }
# sign_jwt ROLE SECRET -> HS256 JWT valid for 10 years
sign_jwt() {
local role="$1" secret="$2" iat header payload sig
iat="$(date +%s)"
header="$(printf '{"alg":"HS256","typ":"JWT"}' | b64url)"
payload="$(printf '{"role":"%s","iss":"supabase","iat":%d,"exp":%d}' \
"$role" "$iat" "$((iat + 315360000))" | b64url)"
sig="$(printf '%s.%s' "$header" "$payload" \
| openssl dgst -sha256 -hmac "$secret" -binary | b64url)"
printf '%s.%s.%s' "$header" "$payload" "$sig"
}
POSTGRES_PASSWORD="$(openssl rand -hex 24)"
JWT_SECRET="$(openssl rand -hex 32)"
ANON_KEY="$(sign_jwt anon "$JWT_SECRET")"
SERVICE_ROLE_KEY="$(sign_jwt service_role "$JWT_SECRET")"
SECRET_KEY_BASE="$(openssl rand -hex 32)"
SECRETS_ENCRYPTION_KEY="$(openssl rand -hex 32)"
CRON_SECRET="$(openssl rand -hex 32)"
umask 077
cp "$TEMPLATE" "$OUT"
for name in POSTGRES_PASSWORD JWT_SECRET ANON_KEY SERVICE_ROLE_KEY \
SECRET_KEY_BASE SECRETS_ENCRYPTION_KEY CRON_SECRET; do
# Values are hex or base64url, so they contain no sed-special characters.
sed -i "s|^${name}=.*|${name}=${!name}|" "$OUT"
done
chmod 600 "$OUT"
echo "wrote $OUT"