#!/usr/bin/env bash # Seed all secrets for the local dev stack. # - Initializes OpenBao (if needed) and stores root token + unseal key # - Sets postgres user passwords # - Creates K8s secrets consumed by each service # - Stores all secrets in OpenBao as source of truth # # Idempotent: safe to run multiple times. set -euo pipefail CTX="--context=sunbeam" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Deterministic local-dev passwords (simple, memorable, not for production) DB_PASSWORD="localdev" S3_ACCESS_KEY="minioadmin" S3_SECRET_KEY="minioadmin" HYDRA_SYSTEM_SECRET="local-hydra-system-secret-at-least-16" HYDRA_COOKIE_SECRET="local-hydra-cookie-secret-at-least-16" HYDRA_PAIRWISE_SALT="local-hydra-pairwise-salt-value-1" LIVEKIT_API_KEY="devkey" LIVEKIT_API_SECRET="secret-placeholder" # --------------------------------------------------------------------------- # Helper # --------------------------------------------------------------------------- ensure_ns() { kubectl $CTX create namespace "$1" --dry-run=client -o yaml | kubectl $CTX apply -f - 2>/dev/null } create_secret() { local ns="$1"; shift local name="$1"; shift # remaining args are --from-literal=key=value kubectl $CTX create secret generic "$name" -n "$ns" "$@" \ --dry-run=client -o yaml | kubectl $CTX apply -f - } # --------------------------------------------------------------------------- # 1. Wait for postgres to be ready # --------------------------------------------------------------------------- echo "==> Waiting for postgres cluster..." for i in $(seq 1 60); do PHASE=$(kubectl $CTX -n data get cluster postgres -o jsonpath='{.status.phase}' 2>/dev/null || echo "") if [[ "$PHASE" == "Cluster in healthy state" ]]; then echo " Postgres is ready." break fi if [[ $i -eq 60 ]]; then echo "WARN: Postgres not ready after 5 min, continuing anyway..." fi sleep 5 done # --------------------------------------------------------------------------- # 2. Set postgres user passwords # --------------------------------------------------------------------------- echo "==> Setting postgres user passwords..." PG_POD=$(kubectl $CTX -n data get pods -l cnpg.io/cluster=postgres,role=primary -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [[ -n "$PG_POD" ]]; then for user in kratos hydra gitea hive docs meet drive messages conversations people find; do kubectl $CTX -n data exec "$PG_POD" -c postgres -- \ psql -U postgres -c "ALTER USER $user WITH PASSWORD '$DB_PASSWORD';" 2>/dev/null || true done echo " Done." else echo "WARN: No postgres primary pod found, skipping password setup." fi # --------------------------------------------------------------------------- # 3. Create K8s secrets for each service # --------------------------------------------------------------------------- echo "==> Creating K8s secrets..." # Ory namespace ensure_ns ory # Secret name must match chart release name (secret.enabled: false means chart uses release name) create_secret ory hydra \ --from-literal=dsn="postgresql://hydra:${DB_PASSWORD}@postgres-rw.data.svc.cluster.local:5432/hydra_db?sslmode=disable" \ --from-literal=secretsSystem="$HYDRA_SYSTEM_SECRET" \ --from-literal=secretsCookie="$HYDRA_COOKIE_SECRET" \ --from-literal=pairwise-salt="$HYDRA_PAIRWISE_SALT" # Kratos chart (secret.enabled: true, nameOverride: kratos-secrets) creates kratos-secrets # from Helm values — DSN is in kratos-values.yaml, random secrets generated by chart. # This create is a no-op placeholder; chart apply overwrites with Helm-generated values. # Devtools namespace ensure_ns devtools create_secret devtools gitea-db-credentials \ --from-literal=password="$DB_PASSWORD" create_secret devtools gitea-s3-credentials \ --from-literal=access-key="$S3_ACCESS_KEY" \ --from-literal=secret-key="$S3_SECRET_KEY" # Storage namespace ensure_ns storage create_secret storage seaweedfs-s3-credentials \ --from-literal=S3_ACCESS_KEY="$S3_ACCESS_KEY" \ --from-literal=S3_SECRET_KEY="$S3_SECRET_KEY" # La Suite namespace ensure_ns lasuite create_secret lasuite seaweedfs-s3-credentials \ --from-literal=S3_ACCESS_KEY="$S3_ACCESS_KEY" \ --from-literal=S3_SECRET_KEY="$S3_SECRET_KEY" create_secret lasuite hive-db-url \ --from-literal=url="postgresql://hive:${DB_PASSWORD}@postgres-rw.data.svc.cluster.local:5432/hive_db" create_secret lasuite hive-oidc \ --from-literal=client-id="hive-local" \ --from-literal=client-secret="hive-local-secret" # People (desk) create_secret lasuite people-db-credentials \ --from-literal=password="$DB_PASSWORD" create_secret lasuite people-django-secret \ --from-literal=DJANGO_SECRET_KEY="local-dev-people-django-secret-key-not-for-production" # Media namespace ensure_ns media echo " Done." # --------------------------------------------------------------------------- # 4. Initialize and unseal OpenBao (if deployed) # --------------------------------------------------------------------------- echo "==> Checking OpenBao..." OB_POD=$(kubectl $CTX -n data get pods -l app.kubernetes.io/name=openbao,component=server -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") if [[ -z "$OB_POD" ]]; then echo " OpenBao pod not found, skipping." else # Wait for pod to be running (not necessarily ready — it won't be ready until unsealed) kubectl $CTX -n data wait pod "$OB_POD" --for=jsonpath='{.status.phase}'=Running --timeout=120s 2>/dev/null || true # Check if initialized INIT_STATUS=$(kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao status -format=json 2>/dev/null | grep '"initialized"' | grep -c 'true' || echo "0") if [[ "$INIT_STATUS" != "1" ]]; then echo "==> Initializing OpenBao..." INIT_OUTPUT=$(kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao operator init -key-shares=1 -key-threshold=1 -format=json 2>/dev/null) UNSEAL_KEY=$(echo "$INIT_OUTPUT" | jq -r '.unseal_keys_b64[0]') ROOT_TOKEN=$(echo "$INIT_OUTPUT" | jq -r '.root_token') # Store keys in K8s secret create_secret data openbao-keys \ --from-literal=key="$UNSEAL_KEY" \ --from-literal=root-token="$ROOT_TOKEN" echo " Initialized. Keys stored in secret/openbao-keys." else echo " Already initialized." # Read unseal key from existing secret UNSEAL_KEY=$(kubectl $CTX -n data get secret openbao-keys -o jsonpath='{.data.key}' 2>/dev/null | base64 -d || echo "") ROOT_TOKEN=$(kubectl $CTX -n data get secret openbao-keys -o jsonpath='{.data.root-token}' 2>/dev/null | base64 -d || echo "") fi # Unseal if sealed SEALED=$(kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao status -format=json 2>/dev/null | grep '"sealed"' | grep -c 'true' || echo "0") if [[ "$SEALED" == "1" && -n "$UNSEAL_KEY" ]]; then echo "==> Unsealing OpenBao..." kubectl $CTX -n data exec "$OB_POD" -c openbao -- bao operator unseal "$UNSEAL_KEY" echo " Unsealed." fi # Seed secrets into OpenBao if [[ -n "$ROOT_TOKEN" ]]; then echo "==> Seeding secrets into OpenBao..." kubectl $CTX -n data exec "$OB_POD" -c openbao -- sh -c " export BAO_ADDR=http://127.0.0.1:8200 export BAO_TOKEN='$ROOT_TOKEN' bao secrets enable -path=secret -version=2 kv 2>/dev/null || true bao kv put secret/postgres password='$DB_PASSWORD' bao kv put secret/hydra db-password='$DB_PASSWORD' system-secret='$HYDRA_SYSTEM_SECRET' cookie-secret='$HYDRA_COOKIE_SECRET' pairwise-salt='$HYDRA_PAIRWISE_SALT' bao kv put secret/kratos db-password='$DB_PASSWORD' bao kv put secret/gitea db-password='$DB_PASSWORD' s3-access-key='$S3_ACCESS_KEY' s3-secret-key='$S3_SECRET_KEY' bao kv put secret/seaweedfs access-key='$S3_ACCESS_KEY' secret-key='$S3_SECRET_KEY' bao kv put secret/hive db-url='postgresql://hive:${DB_PASSWORD}@postgres-rw.data.svc.cluster.local:5432/hive_db' oidc-client-id='hive-local' oidc-client-secret='hive-local-secret' bao kv put secret/people db-password='$DB_PASSWORD' django-secret-key='local-dev-people-django-secret-key-not-for-production' bao kv put secret/livekit api-key='$LIVEKIT_API_KEY' api-secret='$LIVEKIT_API_SECRET' " 2>/dev/null echo " Done." fi fi echo "" echo "==> All secrets seeded."