Sign-in by customer: realm petrolyst, row rules, guest role (PD-21)
Keycloak import for realm petrolyst: two clients, petrolyst with MFA for Superset and Traefik, petrolyst-app password only. It lists only app.petrolyst.com and traefik.petrolyst.com, and no customer names, as this repository is public. public.v_operator shows each customer's group. Dashboards overview and site, role Petrolyst Guest, filtered by row. Superset refuses a user with no mapped role. The realm travels with the release: each release's compose files name their realm and secret file, and a rollback needs only its target's secret. Not deployed. Checks earlier on this content: tests/run.sh 109 of 109 and read-only, dashboards import twice, column drift 0, compose config on old and new trees, the secret check on a scratch folder, a rehearsal on a throwaway Keycloak 26.4. Never a real deploy or rollback; shellcheck not installed today, clean when last run. Codex inspected once; its fixes are not re-inspected, on the owner's word. Bands: persistence, Superset. Rules: 1, 2, 3, 6, 14. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ab553526f6
commit
78a498f1ce
20 files changed
+279
-78
No files matched your search
@@ -21,7 +21,7 @@ Customization and deployment are separate, as with media-insights and media-inte
|
||||
| `stacks/fuel-monitoring/` | Compose stack, Superset Dockerfile and init, fuel-db roles and migrations |
|
||||
| `deploy/` | `deploy.sh` (run on the server by a manual deploy), `rollback.sh` (manual), `lib.sh` (shared) |
|
||||
| `host/` | one-time server setup: `bootstrap.sh`, `secrets-init.sh` |
|
||||
| `keycloak/` | the `petrolyst` client for Keycloak's partial import |
|
||||
| `keycloak/` | the clients `petrolyst` and `petrolyst-app` for Keycloak's partial import |
|
||||
|
||||
No credential is in this repository. Secrets exist only on the server, under
|
||||
`/opt/petrolyst/secrets/`. A deploy needs only root SSH access to the server (`ssh petrolyst`).
|
||||
@@ -88,16 +88,23 @@ Create the private repository `Petrolyst/petrolyst-infra` and push this reposito
|
||||
it. Gitea only keeps the history: deploys are manual, so it needs no runner, no Actions and no
|
||||
deploy secrets.
|
||||
|
||||
### 2. Keycloak (realm `thedigitalbridge` on iam.thedigitalbridge.io)
|
||||
### 2. Keycloak (realm `petrolyst` on iam.thedigitalbridge.io)
|
||||
|
||||
1. Realm settings → Action → **Partial import**: choose `keycloak/petrolyst-partial-import.json`,
|
||||
1. Realm settings → Action → **Partial import**: choose `keycloak/petrolyst-realm-import.json`,
|
||||
and "Skip" if a resource exists. This creates:
|
||||
- the confidential client `petrolyst`: standard flow only, PKCE S256, redirect URIs for both
|
||||
hosts;
|
||||
- its client roles `superset-admin`, `superset-editor`, `superset-viewer`, `atoma-reader`
|
||||
and `traefik-admin`;
|
||||
- a mapper that puts those roles into the claim `petrolyst_roles`.
|
||||
- the confidential client `petrolyst`, for Superset and Traefik: standard flow only, PKCE S256,
|
||||
redirect URIs for both hosts;
|
||||
- its client roles `superset-admin`, `superset-editor`, `superset-viewer` and `traefik-admin`;
|
||||
- a mapper that puts those roles into the claim `petrolyst_roles`;
|
||||
- the confidential client `petrolyst-app`, for the Petrolyst app, with its role `reader` and two
|
||||
mappers: `petrolyst_roles`, and the person's groups with their full path in `petrolyst_groups`,
|
||||
both in the ID token. Both clients' addresses are on app.petrolyst.com, where everything moves
|
||||
(PD-16, PD-21); atoma.petrolyst.com is going away and is not in the import.
|
||||
2. Clients → petrolyst → Credentials: copy the client secret (step 3 stores it on the server).
|
||||
**Realm settings:** registration off; brute-force detection on; a password policy of at least 12
|
||||
characters, because a password is the only lock for customers' users; SSO session idle 8 hours,
|
||||
max 10 hours, because the app's session is renewed through Keycloak about hourly and the default
|
||||
idle limit of 30 minutes would ask for a password each time.
|
||||
3. **MFA**, which Exhibit B requires. A copy of the built-in `browser` flow is not enough, for two
|
||||
reasons: its Cookie step accepts an existing password-only session from another client of the
|
||||
realm, and its conditional OTP subflow skips users who have not set up OTP. Instead:
|
||||
@@ -107,14 +114,17 @@ deploy secrets.
|
||||
- Clients → petrolyst → Advanced → Authentication flow overrides → Browser flow:
|
||||
`petrolyst-mfa`.
|
||||
|
||||
Result: every sign-in to a Petrolyst app asks for password and OTP, and a user without OTP
|
||||
enrols first. Other clients of the realm are unaffected. There is no silent SSO between
|
||||
Superset and the dashboard.
|
||||
4. Give people roles: Users → (user) → Role mapping → Assign role → filter by clients →
|
||||
`petrolyst`. Group mappings work too. ATOMA's users get `atoma-reader` and nothing else:
|
||||
it opens the Petrolyst app (petrolyst-platform) and maps to no Superset role, so in
|
||||
Superset they see nothing. A realm that already has the client gets the role by hand
|
||||
(Clients → petrolyst → Roles → Create role) or by importing again with "Skip".
|
||||
Result: every sign-in to Superset or Traefik asks for password and OTP, and a user without OTP
|
||||
enrols first. There is no silent SSO between them. Client `petrolyst-app` keeps the realm's own
|
||||
flow: customers' users sign in with a password only, the owner's choice "for now" (PD-21).
|
||||
4. **A customer is a top-level group.** Groups → Create group, named as the customer's
|
||||
`keycloak_group` in fuel-db (without the slash), then Role mapping → Assign role → filter by
|
||||
clients → `petrolyst-app` `reader`. Adding a person to the group is then the only step, and they
|
||||
see that customer in the app and no other. Exactly one customer per person: two give "No access".
|
||||
They need no role on client `petrolyst`, and Superset refuses them. A person in no customer's
|
||||
group sees "No access"; that includes our own staff, who join a customer's group to see it in
|
||||
the app.
|
||||
**Administrators:** Users → (user) → Role mapping → Assign role → filter by clients → `petrolyst`.
|
||||
5. Users of the Traefik dashboard need **Email verified** switched on, because oauth2-proxy
|
||||
refuses unverified addresses.
|
||||
|
||||
@@ -158,7 +168,7 @@ becomes the first Superset admin; there is no local admin account.
|
||||
| Task | How |
|
||||
|---|---|
|
||||
| Deploy | "Manual deploy" below, from a clean `main` |
|
||||
| Re-deploy a kept release | `sudo -u deploy /opt/petrolyst/infra/deploy/deploy.sh <sha>` |
|
||||
| Re-deploy a kept release | `sudo -u deploy /opt/petrolyst/infra/deploy/deploy.sh <sha>`. Not across a change of Keycloak realm: see "Moving to realm petrolyst" |
|
||||
| Roll back | see below |
|
||||
| Traefik dashboard | https://traefik.petrolyst.com (role `traefik-admin`) |
|
||||
| fuel-db shell | `docker exec -it petrolyst-fuel-db-1 psql -U postgres -d fuel` |
|
||||
@@ -293,6 +303,23 @@ docker rm -f $(docker ps -aq --filter label=com.docker.compose.project=fuel-moni
|
||||
7. Releases from before this deploy can no longer run, as their role is gone, and `rollback.sh`
|
||||
stops on them. Remove them from `/opt/petrolyst/releases/`.
|
||||
|
||||
**Moving to realm `petrolyst`, one time.** A deploy of its own, after the deploy that removes Trino:
|
||||
releases from before that one can no longer run, so a combined deploy leaves nothing to roll back to.
|
||||
1. The realm is ready (Keycloak, above) and an admin has signed in on client `petrolyst` there. While
|
||||
Superset still answers at atoma.petrolyst.com, add that address's redirect URI to the client by
|
||||
hand, or nobody can sign in after step 3; remove it when the domain goes.
|
||||
2. As root: `host/secrets-init.sh keycloak-secret`. The value goes into the new file
|
||||
`shared/keycloak_petrolyst_realm_client_secret`, so nothing changes for anyone yet. Leave the old
|
||||
file `shared/keycloak_petrolyst_client_secret` alone.
|
||||
3. Deploy by "Manual deploy" only. That one step moves Superset and Traefik together.
|
||||
4. **Prove it by hand:** an admin signs in to Superset and to the Traefik dashboard. The smoke tests
|
||||
check redirects only, and pass with a wrong secret.
|
||||
5. **To go back:** `rollback.sh <previous sha>` returns to the old realm with the old secret. Afterwards go
|
||||
forward again by "Manual deploy" only: "Re-deploy a kept release" runs the old `lib.sh`, whose realm
|
||||
address would override the new release's.
|
||||
6. Not before 7 days of working sign-ins, and on the owner's word: retire client `petrolyst` in realm
|
||||
`thedigitalbridge` and delete the old secret file. From then on there is no way back.
|
||||
|
||||
**Adding a project**: create `stacks/<name>/compose.yaml`, attach the public service to the
|
||||
external network `proxy`, and set `traefik.*` labels with a unique router name and
|
||||
`tls.certresolver: le`. Add the DNS record first. `deploy.sh` picks the new folder up
|
||||
|
||||
Reference in new issue
Block a user