86 lines
2.8 KiB
Bash
Executable File
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"
|