docs(oidc): pin tested issuer probe and bounded owner execution packet
Assistant: codex Assistant-Model: gpt-5.6-luna Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
parent
6f33abddcf
commit
1b9793ffed
3 changed files with 186 additions and 1 deletions
63
docs/evidence/upstream-issuer-probe.json
Normal file
63
docs/evidence/upstream-issuer-probe.json
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"observed_at": "2026-09-08T21:17:50.880337+00:00",
|
||||
"image": {
|
||||
"image": "forgejo.coulomb.social/coulomb/key-cape@sha256:0c85ed377cae7ae6ca5b5c56b1a52930e706b3cb78009747e42f551e869a22e4",
|
||||
"source_commit": "6f33abddcff6cbc348ced862057973cdcc4f78ec",
|
||||
"published_tag": "main-6f33abd",
|
||||
"packaged_command_proven": true,
|
||||
"container_uid": 65534,
|
||||
"container_network": "none",
|
||||
"container_readonly": true,
|
||||
"fixture_lifetime_seconds": 1,
|
||||
"expected_exit": 1,
|
||||
"observed_exit": 1,
|
||||
"idle_receipt": {
|
||||
"downstream_credential_issued": false,
|
||||
"failure": "probe_deadline",
|
||||
"schema": "keycape.upstream-issuer-proof.v1",
|
||||
"status": "failed",
|
||||
"tokens_retained": false
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"go_test_race_all": "passed",
|
||||
"go_vet_all": "passed",
|
||||
"probe_tests_after_cli_fixture": "passed",
|
||||
"python_renderer_and_receipt_tests": 5,
|
||||
"traefik_route": {
|
||||
"traefik": "3.6.7",
|
||||
"exact_route_cases_passed": 7,
|
||||
"ordinary_login_callbacks_preserved": true,
|
||||
"live_cluster_changed": false
|
||||
},
|
||||
"server_dry_run": {
|
||||
"accepted": true,
|
||||
"resources": [
|
||||
"Job",
|
||||
"Service",
|
||||
"IngressRoute",
|
||||
"NetworkPolicy/probe",
|
||||
"NetworkPolicy/Authelia"
|
||||
],
|
||||
"image_digest": "sha256:0c85ed377cae7ae6ca5b5c56b1a52930e706b3cb78009747e42f551e869a22e4",
|
||||
"job_owner_uid": "fixture only; must bind the actual UID at execution"
|
||||
}
|
||||
},
|
||||
"deployment_metadata": {
|
||||
"deployment": {
|
||||
"name": "keycape",
|
||||
"metadata": "99ddd83c-cb3f-4847-bcf8-35f1aa87627f 55113259 29"
|
||||
},
|
||||
"secret": {
|
||||
"name": "keycape-config",
|
||||
"metadata": "2e94519d-1550-41c7-9701-2efe47fe1fd3 51346058"
|
||||
},
|
||||
"production_image": "forgejo.coulomb.social/coulomb/key-cape:main-153258b",
|
||||
"probe_jobs_before": []
|
||||
},
|
||||
"live_upstream_issuer_proved": false,
|
||||
"production_mutated": false,
|
||||
"credential_values_extracted": false,
|
||||
"custody_activated": false,
|
||||
"required_next_return": "Deployment-owner admission for the temporary config-reading probe; attended signed issuer receipt and cleanup, then contained issuer pin and named CCR reviews"
|
||||
}
|
||||
113
docs/upstream-issuer-proof.md
Normal file
113
docs/upstream-issuer-proof.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# One-shot upstream issuer proof
|
||||
|
||||
Prepared for KEY-WP-0013-T02/T07 on 2026-09-08. This packet needs
|
||||
NetKingdom/platform deployment-owner admission before its temporary workload
|
||||
uses the existing KeyCape configuration. It does not approve CCR-2026-0017/0018.
|
||||
|
||||
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/).
|
||||
|
|
@ -219,7 +219,7 @@ T06 completion: full Go suite and vet passed; published code `dcebd46` and pulle
|
|||
|
||||
```task
|
||||
id: KEY-WP-0013-T07
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
assignee: the-custodian
|
||||
```
|
||||
|
|
@ -243,3 +243,12 @@ Source tests, image and deployment packet close this preparation task. The
|
|||
attended live receipt and issuer pin remain in T02, alongside the named CCR
|
||||
reviews and custody/rollout acceptance. Preparing the diagnostic does not
|
||||
approve CCR-2026-0017/0018 or close any live factory gate.
|
||||
|
||||
T07 preparation complete: source `6f33abd`, immutable image
|
||||
`sha256:0c85ed377cae7ae6ca5b5c56b1a52930e706b3cb78009747e42f551e869a22e4`.
|
||||
Complete Go race suite and vet passed; five renderer/collector checks, seven
|
||||
real Traefik 3.6.7 route cases and Railiance server dry-run passed. The packaged
|
||||
command ran as UID 65534 with no network and a read-only filesystem and produced
|
||||
the expected bounded idle-timeout receipt. See `docs/upstream-issuer-proof.md`
|
||||
and `docs/evidence/upstream-issuer-probe.json`. T02 retains the live proof and
|
||||
configuration-owner admission; no production resource or custody value changed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue