secrets: ship encrypted SA key, switch install to git-clone + decrypt
- secrets/bookkeeping-sa.json.enc: team service-account key, encrypted with AES-256-CBC + PBKDF2(100k iter) using a 48-char random passphrase. Safe to commit to a public repo; the passphrase lives in the team password manager. - scripts/decrypt-key.sh: one-liner that decrypts to ~/.config/gcp/ (mode 600) and prints the service-account email so users know which address to share their Sheet with. - secrets/README.md: explains the crypto, decrypt flow, and rotation procedures (passphrase rotation vs underlying GCP key rotation). - README + DEPLOY.md + setup.md: install flow updated. Users no longer wait for the admin to send a JSON; they git clone, run decrypt-key.sh with the passphrase from the team password manager, and continue. Cuts one out-of-band file transfer from the user experience. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
81
secrets/README.md
Normal file
81
secrets/README.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# secrets/
|
||||
|
||||
This directory holds the team's shared Google Cloud service-account key,
|
||||
encrypted with **AES-256-CBC + PBKDF2 (100k iterations)** using a single
|
||||
team passphrase.
|
||||
|
||||
| File | What it is |
|
||||
|---|---|
|
||||
| `bookkeeping-sa.json.enc` | The SA private key, encrypted. Safe to commit. |
|
||||
|
||||
## To decrypt (end users)
|
||||
|
||||
```bash
|
||||
bash scripts/decrypt-key.sh
|
||||
```
|
||||
|
||||
You'll be prompted for the team passphrase. Ask your admin if you don't have
|
||||
it — it's stored in the team password manager (1Password / Bitwarden), never
|
||||
in this repo or in chat history.
|
||||
|
||||
On success the decrypted JSON lands at `~/.config/gcp/bookkeeping-sa.json`
|
||||
with mode 600, and the script prints the service-account email you need to
|
||||
share your Sheet with.
|
||||
|
||||
## To rotate the passphrase (admin)
|
||||
|
||||
```bash
|
||||
# Decrypt with old passphrase
|
||||
openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -d \
|
||||
-in secrets/bookkeeping-sa.json.enc \
|
||||
-out /tmp/sa.json
|
||||
|
||||
# Re-encrypt with new passphrase
|
||||
openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt \
|
||||
-in /tmp/sa.json \
|
||||
-out secrets/bookkeeping-sa.json.enc
|
||||
|
||||
shred -u /tmp/sa.json # or: rm -P on macOS
|
||||
|
||||
git add secrets/bookkeeping-sa.json.enc
|
||||
git commit -m "secrets: rotate passphrase"
|
||||
git push
|
||||
```
|
||||
|
||||
Then update the passphrase in the team password manager and notify users.
|
||||
|
||||
## To rotate the SA key itself (admin)
|
||||
|
||||
When the underlying GCP key needs replacement (key leak, periodic rotation,
|
||||
team member departure):
|
||||
|
||||
1. In GCP Console → Service Accounts → `bookkeeping@autoacct...` → Keys → **Add Key** → JSON → download.
|
||||
2. **Delete the old key** in the same panel.
|
||||
3. Re-encrypt:
|
||||
```bash
|
||||
openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -salt \
|
||||
-in ~/Downloads/<new-key>.json \
|
||||
-out secrets/bookkeeping-sa.json.enc
|
||||
```
|
||||
4. `git add` + `commit` + `push`.
|
||||
5. Tell users to `git pull` and re-run `bash scripts/decrypt-key.sh`.
|
||||
|
||||
## Why this works
|
||||
|
||||
- AES-256 + PBKDF2 with 100k iterations makes brute-forcing infeasible for a
|
||||
strong (>32 char) random passphrase, even with the ciphertext public.
|
||||
- Encrypted blob in git history means anyone with the passphrase can install
|
||||
with just `git clone` + run the decrypt script — no out-of-band file
|
||||
transfer needed.
|
||||
- Passphrase distribution is the only remaining out-of-band step; that
|
||||
belongs in a password manager.
|
||||
|
||||
## Threats this does NOT protect against
|
||||
|
||||
- Anyone with the passphrase can read/write **all team sheets** shared with
|
||||
the service account. The passphrase is the team's collective trust boundary.
|
||||
- If the passphrase leaks publicly: rotate both passphrase **and** the
|
||||
underlying GCP key (see above). Don't just rotate the passphrase.
|
||||
- A user who has previously decrypted the key has a plaintext copy on their
|
||||
machine. Removing them from the team requires rotating the GCP key (the
|
||||
passphrase rotation is not enough — they already have a decrypted JSON).
|
||||
BIN
secrets/bookkeeping-sa.json.enc
Normal file
BIN
secrets/bookkeeping-sa.json.enc
Normal file
Binary file not shown.
Reference in New Issue
Block a user