diff --git a/docs/tenant-alignment.md b/docs/tenant-alignment.md index 29fb950..cd03596 100644 --- a/docs/tenant-alignment.md +++ b/docs/tenant-alignment.md @@ -115,3 +115,45 @@ alone. - A supported owner access path to `flex-auth-secrets-engine` for a workstation CLI. Service DNS is not workstation connectivity, so the tenant fix above is necessary but not sufficient for activation (`FLEX-WP-0021-T05`). + +## Hazard: the Service DNS name resolves here, to the wrong host + +Probed from the workstation 2026-09-06. This is worse than "not reachable" and +is the reason the access path must be handed over explicitly rather than assumed +from a Service name. + +```text +$ getent hosts flex-auth-secrets-engine.flex-auth.svc.cluster.local +80.158.43.29 flex-auth-secrets-engine.flex-auth.svc.cluster.local.ad.binect.de + +$ getent hosts this-service-does-not-exist.flex-auth.svc.cluster.local +80.158.43.29 this-service-does-not-exist.flex-auth.svc.cluster.local.ad.binect.de + +$ getent hosts flex-auth-secrets-engine.flex-auth.svc.cluster.local. # trailing dot +(no resolution) +``` + +`/etc/resolv.conf` carries `search ad.binect.de fritz.box`, and `ad.binect.de` +answers wildcard, so **every** `*.svc.cluster.local` name resolves on this +workstation to one unrelated public address. A nonexistent service resolves +identically, which proves it is suffix expansion rather than any real record. + +Consequences if `SECRETS_ENGINE_PDP_URL` were pointed at the Service name from a +workstation: + +- The `POST /v1/check` body — subject id/type, tenant, lane/resource ids, stage, + declared field names, purpose — would go to an arbitrary internet host. It + contains no secret values, but it is a structural map of the estate's + credential lanes. +- The static Bearer token would be sent to that host. +- Decision envelopes are structurally validated but **not signed**. A responder + that knows the package name and version can return a well-formed + `effect: allow`, and this engine's checks would pass it. The fail-closed + production posture assumes the PDP is the PDP. + +This is not fixed here, because choosing the transport control is the owners' +call, not this consumer's. Recommended for `FLEX-WP-0021-T05`: hand over a +trailing-dot FQDN or an explicit address, and state the authentication of the +response channel (mTLS, or a signed envelope) rather than leaving an unsigned +allow over plaintext HTTP as the contract. This engine keeps its pin unset in +the meantime, which is why the hazard is currently theoretical. diff --git a/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md b/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md index 5123d78..bf7fd5f 100644 --- a/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md +++ b/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md @@ -52,6 +52,50 @@ priority: high state_hub_task_id: "f8069c8a-ad6b-5d0b-9a36-c2326699437d" ``` +Inbound 2026-09-06 (glas-harness `GLAS-WP-0015` production dependency handoff). +Ownership acknowledged. Answering their tenant question rather than assuming the +values lined up found a blocking defect in this repo, so the handoff earned its +keep before any activation work started. + +- **Our CheckRequest carried no `tenant` at all.** The deployed + `secrets-engine.catalog-lane.lifecycle` **v2** package reads + `request_tenant := object.get(input, "tenant", "")` against + `known_tenant := "tenant:platform"`, so an absent tenant is a `wrong_tenant` + denial rather than an ignored field. Every gated action this engine sent would + have been denied, and the omission also produced a `request_digest` matching no + correctly issued decision, because `tenant` is hashed material. Fixed in + `80eafaf`; `REQUEST_TENANT` is pinned against the vendored allow envelopes. +- **The package is v2, not the v1 glas reported.** v1 had no tenant rule and + failed open — a `rotate` under `tenant:coulomb` returned allow against the + deployed package (`decision:066e629bbf0c0924`). flex-auth superseded rather + than amended it, because a fail-open correction must be visible as a version + change. Our accepted version moved to v2 and a test refuses a v1 decision. +- **Wrong-tenant denial evidence delivered**, from flex-auth's real deny envelope + rather than asserted: `decision:7d56b7fc274ddfd6`, `matched_rule wrong_tenant`, + `binding.tenant tenant:coulomb`. Vendored with tests that we refuse it on + `effect` first and that a deny legally carries no `lifetime`. +- **Tenant mapping NOT resolved, deliberately.** `service_auth.TENANT` is + `tenant:coulomb`, which is exactly the value the package denies. Either those + are two layers sharing a namespace format or one constant is wrong. Choosing + without an owner ruling is the fail-open shape `GH-DEC-2026-008` rejected for + action vocabularies. Both constants stay as they are, not unified behind one + symbol. Recorded in `docs/tenant-alignment.md` for `KEY-WP-0013-T02` / + `APPROVAL-WP-0002-T01`. +- **Hazard found while probing the caller access path.** Every + `*.svc.cluster.local` name resolves on this workstation to one unrelated public + address via the `ad.binect.de` search suffix, including names of services that + do not exist. Pointing `SECRETS_ENGINE_PDP_URL` at the Service name would ship + CheckRequest metadata and the Bearer token to that host, and decision + envelopes are structurally validated but not signed, so a knowing responder + could return a well-formed allow. Reported to flex-auth for `FLEX-WP-0021-T05`; + not mitigated here because the transport control is the owners' call. The pin + stays unset, so the hazard is theoretical today. + +CCR-2026-0016 is explicitly treated as custody provenance, not a per-action +authorization. No unsafe-demo switch and no human runtime token will be used to +reach a prod lane. This task stays `wait`: activation is blocked on the owner +access path, not on engine work or on approval shape. + Depends on SECRETS-WP-0007-T04 and SECRETS-WP-0008-T02/T06: canonical production authorization, successful consume and scoped service authority must exist. Current production exec refuses before OpenBao because durable access-engine