From 5845fc56943646b6f2fa15b7d42ab275a421e641 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Sep 2026 12:46:33 -0400 Subject: [PATCH] ci: deploy Firestore rules with a service account, not login:ci (#51) The deploy-firestore-rules job has failed on every push because FIREBASE_TOKEN was never set, so rule changes (e.g. aircraft/vessels for node-26#9) never reached prod. Switch to a dedicated least-privilege service account whose JSON key lives in FIREBASE_SA_KEY; login:ci tokens are deprecated and carry their minter's full access. Key is written to RUNNER_TEMP at 0600 and removed on exit. Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/deploy.yml | 28 +++++++++++++++------------- infra/firestore/firestore.rules | 12 +++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index b865299..93ab700 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -307,28 +307,30 @@ jobs: - name: Deploy firestore rules and indexes env: - FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + FIREBASE_SA_KEY: ${{ secrets.FIREBASE_SA_KEY }} run: | set -e # server-26#51: this used to run over SSH on the deploy VM, gated # on the VM having firebase-tools installed. It never did, so it # silently warned-and-skipped on every single deploy for weeks. - # Running it here instead means the only prerequisite is a secret - # -- FIREBASE_TOKEN, from `firebase login:ci` -- rather than - # something installed by hand on a machine this pipeline doesn't - # otherwise touch. A missing token now fails this job LOUDLY - # (picked up by notify-failure) instead of a buried warning line - # nobody reads in the app deploy's logs. - if [ -z "$FIREBASE_TOKEN" ]; then - echo "FIREBASE_TOKEN secret is not set -- cannot deploy Firestore rules/indexes." >&2 - echo "Generate one with 'firebase login:ci' and add it as a Gitea Actions secret." >&2 + # Auth is a dedicated service account (drb-ci-firestore-deploy, + # roles: Firebase Rules Admin, Cloud Datastore Index Admin, + # Service Usage Consumer), its JSON key stored as the + # FIREBASE_SA_KEY secret. Not `firebase login:ci`: those tokens are + # deprecated and carry the full permissions of whoever minted them. + # A missing key fails this job LOUDLY (picked up by notify-failure). + if [ -z "$FIREBASE_SA_KEY" ]; then + echo "FIREBASE_SA_KEY secret is not set -- cannot deploy Firestore rules/indexes." >&2 + echo "Add the drb-ci-firestore-deploy service account's JSON key as a Gitea Actions secret." >&2 exit 1 fi + export GOOGLE_APPLICATION_CREDENTIALS="$RUNNER_TEMP/firebase-sa.json" + trap 'rm -f "$GOOGLE_APPLICATION_CREDENTIALS"' EXIT + ( umask 077 && printf '%s' "$FIREBASE_SA_KEY" > "$GOOGLE_APPLICATION_CREDENTIALS" ) npm install -g firebase-tools cd infra/firestore firebase deploy --only firestore:rules,firestore:indexes \ - --project ${{ secrets.FIREBASE_PROJECT_ID }} \ - --token "$FIREBASE_TOKEN" --non-interactive + --project ${{ secrets.FIREBASE_PROJECT_ID }} --non-interactive notify-failure: name: Report a failed deploy @@ -371,7 +373,7 @@ jobs: # failed before any deploy was attempted" text even when the app # deployed fine and only the Firestore rules/indexes push failed. if deploy_result != "failure" and rules_result == "failure": - detail = "App deploy succeeded; Firestore rules/indexes deploy FAILED (server-26#51). Rules may be stale — check FIREBASE_TOKEN and the job log." + detail = "App deploy succeeded; Firestore rules/indexes deploy FAILED (server-26#51). Rules may be stale — check the FIREBASE_SA_KEY secret and the job log." # server-26#65: the old text here unconditionally claimed # "production is still running the previous build" -- true only diff --git a/infra/firestore/firestore.rules b/infra/firestore/firestore.rules index bf21883..64bcbb0 100644 --- a/infra/firestore/firestore.rules +++ b/infra/firestore/firestore.rules @@ -8,13 +8,11 @@ // hand-set in the Firebase console: unversioned, unreviewed, unknown. See // SAAS_PLAN.md B1. // -// DEPLOY IS A MANUAL, OUT-OF-BAND STEP — nothing in CI or this codebase -// pushes these rules to Firebase: -// firebase deploy --only firestore:rules --project -// (from this directory, or point --config at infra/firestore/firebase.json -// from the repo root). Do this before or immediately after the code that -// starts stamping org_id ships — until these rules are live, the -// console-configured rules are still what's actually enforced. +// DEPLOYED BY CI on every push to main (.gitea/workflows/deploy.yml, job +// deploy-firestore-rules, service-account auth via the FIREBASE_SA_KEY +// secret — server-26#51). That job is separate from the app deploy, so a +// green app deploy does NOT mean these rules are live: check that job too. +// Editing rules in the Firebase console is overwritten by the next push. // // MODEL: c2-core (firebase-admin SDK, server-side) bypasses these rules // entirely and is the sole writer for every collection below — that was