Assistant: codex Assistant-Model: gpt-5.6-luna Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
115 lines
7.1 KiB
Markdown
115 lines
7.1 KiB
Markdown
# One-shot upstream issuer proof
|
|
|
|
Executed with explicit user admission on 2026-09-08: the verified signed issuer
|
|
is **`https://auth.coulomb.social`**. The Job exited successfully and all temporary
|
|
resources were removed. [Live receipt](evidence/2026-09-08-upstream-issuer-live-proof.json).
|
|
This completed run did not approve CCR-2026-0017/0018 or change the normal config.
|
|
The procedure below is retained for an admitted recheck after relevant changes.
|
|
|
|
The missing evidence is the actual signed Authelia ID token produced by the
|
|
existing confidential `keycape` registration and server-side token exchange.
|
|
The old production KeyCape image cannot emit that evidence. A downstream
|
|
OpenBao login or public discovery cannot substitute for it.
|
|
|
|
The diagnostic uses the same configuration and registered callback URI but a
|
|
fresh random state, browser cookie and OIDC nonce. A temporary Traefik route
|
|
matches only its start path or `/authorize/callback` with that exact state.
|
|
Ordinary callbacks continue to use the existing KeyCape service. The probe
|
|
verifies the signature against the configured upstream's keys, client audience,
|
|
validity window, nonce and a finite reviewed issuer set. Only the resulting
|
|
issuer and verification booleans leave the adapter; no subject, token or code
|
|
is returned, logged or retained. No downstream application credential is issued.
|
|
|
|
## Concrete deployment boundary
|
|
|
|
`scripts/render-upstream-issuer-probe.py` renders five temporary resources in
|
|
`sso`: one Job, one Service, one IngressRoute and two NetworkPolicies. The Job
|
|
has a ten-minute active deadline, no retry, and a five-minute completed-Job TTL.
|
|
Every child must have an owner reference to the **actual created Job UID** so
|
|
TTL collection removes the route, service and policies as well as the pod.
|
|
|
|
The probe projects only `keycape-config/config.yaml`; it does not mount the
|
|
signing key or a Kubernetes API token. The existing YAML nevertheless contains
|
|
credential data for other adapters. The diagnostic decodes only `authelia` and
|
|
never starts those adapters. Admission must explicitly cover this temporary
|
|
read of the existing configuration; the local agent never extracts its values.
|
|
The Job runs as UID/GID 65534, drops capabilities and has a read-only filesystem.
|
|
Its unique pod labels cannot join the production KeyCape Service selector.
|
|
|
|
The temporary policies permit Traefik-to-probe on TCP 8081 and
|
|
probe-to-Authelia on TCP 9091, plus DNS. Existing namespace-wide DNS allowances
|
|
remain additive. The narrow Authelia ingress allowance names only the probe's
|
|
unique selector. The route reuses `kc-tls`, rate limiting and HSTS; its access
|
|
logs and tracing are disabled so callback parameters are not added to telemetry.
|
|
|
|
The issuer allowlist is the observed public and split-horizon candidate set:
|
|
`https://auth.coulomb.social`, `http://auth.coulomb.social`,
|
|
`http://authelia.sso.svc.cluster.local:9091`. It does not supply key-fetch URLs:
|
|
keys still come from the deployment's configured Authelia/JWKS path. A parsed
|
|
issuer merely selects a reviewed candidate; full independent verification must
|
|
pass before the issuer is emitted. An issuer outside that set fails without
|
|
printing its value. This diagnostic does not change the production verifier or
|
|
make these three alternatives permanent accepted production issuers.
|
|
|
|
## Attended execution and cleanup
|
|
|
|
Use the immutable diagnostic image in `docs/evidence/upstream-issuer-probe.json`.
|
|
The approval-client rollout candidate remains its independently pinned image.
|
|
Run from the published KeyCape source checkout. Keep each rendered packet in a
|
|
new private directory outside Git. No step below requests a credential value.
|
|
|
|
1. Recheck the live KeyCape image/Deployment generation, the config Secret's UID
|
|
and resourceVersion (metadata only), the existing Ingress/backend and the
|
|
Authelia/Traefik selectors. Confirm no other issuer probe is active. Render a
|
|
fresh packet with `--image <digest> --output <new-directory>` and run a server
|
|
dry-run of its `job.json` and `children.json`. Do not create unbound children.
|
|
2. After the deployment owner's admission, create **only** `job.json` with
|
|
`kubectl create -f`. Read that Job's UID. Re-render into a second new directory
|
|
with the same `--image` and `--state` from `packet.json`, plus `--job-uid`.
|
|
Create the second packet's `children.json`. Do not recreate its Job. Record
|
|
object UIDs and image/config metadata, without Secret data.
|
|
3. Wait for the probe Pod to become Ready; do not open the start URL before the
|
|
exact route has converged. Open `packet.json`'s `start_url` in the attended
|
|
browser, using the approved browser launcher. Complete the normal upstream
|
|
login. The resulting page reports only completion or failure. This proof
|
|
does not complete downstream KeyCape MFA or an application login.
|
|
4. Immediately collect the Job's bounded output through
|
|
`scripts/collect-upstream-issuer-proof.py`, never by printing raw logs:
|
|
|
|
```bash
|
|
kubectl -n sso logs job/<exact-job-name> --tail=4 --limit-bytes=8192 | \
|
|
python3 scripts/collect-upstream-issuer-proof.py
|
|
```
|
|
|
|
A successful receipt must also match a successful Job/container exit and the
|
|
exact image/config metadata. The collector refuses extra fields, unknown
|
|
issuers, missing verification checks and arbitrary log text.
|
|
5. Delete only this exact Job and its four owned children; verify all five and
|
|
the Pod are absent. On any failure, remove these same temporary resources.
|
|
TTL ownership is the unattended fallback, not a reason to skip attended
|
|
cleanup. Do not delete a pre-existing object after a name-collision refusal.
|
|
No normal Deployment, Service, Ingress or Secret needs a rollback.
|
|
6. Return the signed issuer receipt and cleanup metadata to KEY-WP-0013-T02,
|
|
RPF-WP-0035-T05 and HFACT-WP-0001-T03. Pin the one verified issuer through the
|
|
configuration owner's contained change path. Recheck existing human login
|
|
before and after the separately reviewed client/configuration rollout.
|
|
|
|
The native founder-attended OpenBao lane remains the authority for subsequent
|
|
custody writes. This Job neither reads OpenBao data nor creates policy/role/KV
|
|
records. Named CCR reviews, verifier/client-side custody, existing-human-login
|
|
regression and the compatible KeyCape rollout remain distinct acceptance steps.
|
|
|
|
## Validation
|
|
|
|
Complete Go suite with race detection and `go vet ./...` passed. The probe tests
|
|
cover genuine public/internal issuer alternatives, forged signatures, wrong
|
|
issuer/audience/nonce, expiry, key and token endpoint failures, oversized bodies,
|
|
state/cookie binding, replay, idle timeout and output nondisclosure. Five Python
|
|
checks cover immutable images, exact callback routes, owner references, projected
|
|
files and receipt validation.
|
|
|
|
A local Traefik **3.6.7** check exercised seven real routing cases: ordinary
|
|
callbacks remain on their normal backend; only the exact proof start/callback
|
|
reaches the probe. Railiance's API accepted the five resource shapes in a server
|
|
dry-run. These are preparation checks, not live signed-token issuer evidence.
|
|
The exact state matcher follows [Traefik's routing reference](https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/).
|