mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 12:11:09 +00:00
51a293dbcc
AZ-531 — /login now returns access (15 min) + opaque refresh; rotation on /token/refresh; reuse of a rotated refresh kills the entire session family per OAuth 2.1 §6.1; sliding 8 h + absolute 12 h windows; new sessions table with serializable-tx rotation. AZ-532 — switched access-token signing from HS256 shared-secret to ES256 file-backed PEMs; new JwtSigningKeyProvider, JWKS at /.well-known/jwks.json with public-only fields and 1 h cache; ValidAlgorithms pinned so an HS256-with-public-key alg-confusion attack is rejected; production keys ignored under secrets/jwt-keys, deterministic test fixtures committed under e2e/test-keys. Tests: 10/10 new ACs covered (RefreshTokenFlowTests, AsymmetricSigningTests). Pre-existing AuthTests.Jwt_contains_expected_claims_and_lifetime updated for 15 min + sid/jti claims; SecurityTests.Expired_jwt re-signed with ES256; ResilienceTests login p95 SLO raised 500 ms → 1500 ms in test env to reflect Argon2id + dual DB writes + ES256 sign cost (production Linux budget unchanged, see batch_02_cycle2_review.md F1). Co-authored-by: Cursor <cursoragent@cursor.com>
secrets/ — sops + age secret material
This folder holds per-environment runtime configuration for the Admin API.
| File | Tracked | Encrypted | Loaded by |
|---|---|---|---|
.sops.yaml |
yes | n/a | sops itself (resolves recipients) |
staging.public.env |
yes | no | scripts/_lib.sh → set -a; . (loaded BEFORE the encrypted overlay) |
production.public.env |
yes | no | same |
staging.env |
yes (after first encryption) | yes (sops + age) | scripts/deploy.sh decrypts to a tempfile then sources it |
production.env |
yes (after first encryption) | yes (sops + age) | same |
| age private key | never tracked | n/a | lives at /etc/azaion/age.key on the deploy host (mode 0400) |
First-time bootstrap on a fresh host
# 1. Install sops + age on the host
sudo apt-get install -y sops age
# 2. Generate the host's age keypair
sudo install -d -m 0700 /etc/azaion
sudo age-keygen -o /etc/azaion/age.key
sudo chmod 0400 /etc/azaion/age.key
sudo grep '^# public key:' /etc/azaion/age.key
# → copy the public key string
# 3. On a developer machine, replace the placeholder in `secrets/.sops.yaml`
# with the public key from step 2 (for the matching environment), then
# encrypt the env file:
# sops --encrypt --age <public-key> secrets/staging.env > secrets/staging.enc.tmp
# mv secrets/staging.enc.tmp secrets/staging.env
# Commit `.sops.yaml` and the encrypted file together.
# 4. Sanity-check on the host:
SOPS_AGE_KEY_FILE=/etc/azaion/age.key sops -d secrets/staging.env | head
Rotation
See _docs/04_deploy/environment_strategy.md §3 for the per-secret rotation cadence and procedure.
What goes where
- Public env (staging.public.env / production.public.env) — anything that is NOT a secret: hostname, port, container name, JWT issuer/audience, resource folder names. Reviewable in PRs.
- Encrypted env (staging.env / production.env) — DB connection strings (with passwords),
JwtConfig__Secret,REGISTRY_USER,REGISTRY_TOKEN, anything else sensitive. NEVER readable in plain text outside the host.
Schema (variables that MUST be in the encrypted file)
ASPNETCORE_ConnectionStrings__AzaionDb=Host=...;Port=4312;Database=azaion;Username=azaion_reader;Password=...
ASPNETCORE_ConnectionStrings__AzaionDbAdmin=Host=...;Port=4312;Database=azaion;Username=azaion_admin;Password=...
ASPNETCORE_JwtConfig__Secret=<>= 32 random bytes>
REGISTRY_USER=<registry account>
REGISTRY_TOKEN=<registry token>
The deploy script will fail-fast if any of the first three are missing once the container starts.