net-kingdom/sso-mfa/k8s/keycape/openbao-negative-node-route.md
tegwick 7ceac4372d
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Publish reviewed bounded JWT ready handoff and retained owner history
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 18:31:04 +02:00

13 KiB

KEY39 node-local OpenBao route prerequisite

Source-only successor KEY-WP-0039-T02/attended-lifecycle-v9-ready-input of frozen v5. No native preflight, port-forward, login, Secret access or execution has run. Previous v5 source and every review remain unchanged. The new executor and health endpoint need fresh exact source-owner acknowledgments and a founder native decision; previous v5 agreement is historical compatibility only.

The child runs on railiance01 and uses the explicit node k3s kubeconfig. The environment orientation's port 18200 is a workstation address. The installed openbao-ui-railiance01 routing metadata is direction: local, local port 18200, node destination 10.43.5.152:8080. OpsBridge's supported local forward builds -L 127.0.0.1:18200:10.43.5.152:8080; its listener belongs to the machine invoking Bridge. It does not establish a node-local listener. No current node DNS evidence establishes that openbao.openbao.svc resolves there. Do not infer node reachability from workstation role observations or Bridge status.

OpsBridge supports named up, down, status, check, and local/reverse configurations. There is no existing named KEY39 node-local reverse route in the inspected routing metadata. Do not invent bridge forward, silently modify the shared workstation tunnel, repurpose its port, or request a new SSH certificate for this preparation. A new Bridge-managed route needs its own owner-reviewed configuration and authority; this source does not create one.

Proposed founder-held route on the child node

The Platform source publishes Service openbao/openbao-ui-gateway, port 8080, which proxies the OpenBao API. The proposed attended route uses this existing service and node loopback; it requires no pod DNS or hardcoded ClusterIP:

# Proposed only: run in a dedicated founder terminal ON railiance01, after exact
# route/session authority and current cluster/service identity are confirmed.
# Lifetime is capped at 25 minutes, including preparation/action/cleanup.
timeout --foreground 1500s kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml --request-timeout=20s -n openbao port-forward --address=127.0.0.1 --pod-running-timeout=20s service/openbao-ui-gateway 18200:8080 >/dev/null 2>&1

Keep that exact foreground process alive through issuer restoration, removed client refusal and Secret absence verification. Stop only the new process with Ctrl-C when those obligations are verified; timeout also stops it. Do not kill an arbitrary existing listener, run bridge down against shared tunnels, or restart a shared gateway. Port conflict, unavailable service, route loss or non-ready OpenBao means stop and retain any pending custody for separately approved recovery. Recovery requires re-establishing the same admitted node route under its own founder recovery decision; no automatic tunnel is opened. The lifetime estimate is a finite preparation ceiling, not an authority grant.

Public preflight and exact envelope pin

The separate diagnostic command reads only the exact unauthenticated health endpoint, never Kubernetes, Secrets, a token producer or a credential. Run from the same founder-owned node execution context as the intended child:

python3 /OWNER-PINNED-PATH/openbao-negative-session.py route-preflight --bao-addr http://127.0.0.1:18200 --receipt /OWNER-PRIVATE-PATH/key39-node-route.json

The receipt is exclusive and mode 0600. It records only booleans and SHA256 of public cluster_id; probe_location=current_process_only expressly avoids claiming that an arbitrary workstation run proves node readiness. It accepts only HTTP 200 with strictly boolean initialized=true, sealed=false, standby=false, performance_standby=false when supplied, and a nonempty bounded cluster identity. The official endpoint defines these health states: OpenBao health API.

Then run the existing founder preflight with the explicit same Bao origin:

python3 /OWNER-PINNED-PATH/openbao-negative-session.py founder-preflight --confirm 'FOUNDER ATTENDED KEY39 PREFLIGHT' --bao-addr http://127.0.0.1:18200 --receipt /OWNER-PRIVATE-PATH/key39-preflight.json

This retains all previous runtime/registry/headroom/quiet-window obligations. Copy its bao_health_identity_sha256 into the exact reviewed execute/recovery envelope. The attending founder/Platform owner must verify the route target and provider identity against their authoritative facts. Public health is not cryptographic service authentication, role/policy proof or execution admission; its hash detects accidental provider changes and does not replace that review.

Execute probes this pinned health identity before protected token input/native object reads, then again after dry-runs immediately before the first temporary Secret create. Recovery probes it before native object/Secret reads. A missing pin, changed identity, redirect, unreachable/oversized/malformed response or inactive provider refuses. The existing no-proxy/no-redirect/byte/total-elapsed bounds and exception abort obligations apply to this new GET too. No local helper opens or repairs the route. Loss after the first mutation still invokes the existing bounded owner cleanup path; unproved cleanup stays pending.

The admitted human/coding-agent private stdin producer and all Platform/Mason/ Secrets Engine native acceptance prerequisites remain unresolved separately. This preflight never fetches or produces those tokens and closes no owner task.

V7 actual entrypoint sequencing correction

V6's statement that health preceded private token input was too broad: its main read private stdin before calling execute, though health still preceded all native reads/mutations. Platform identified that exact source/documentation mismatch. V6 and its reviews remain immutable history.

V7 main verifies the exact approval/source envelope, starts the bounded action window and checks the approved public health identity before reading private stdin. On route failure, stdin is never read and no native access occurs. After input arrives, execute revalidates source/authority and expiry, preserving the original pre-input monotonic deadline rather than restarting it; its existing health checks before native access and immediately before mutation remain. Recovery never reads private stdin and still checks the same public health pin before native Secret/config/deployment reads. Public route-preflight likewise consumes no private input. These are separately tested through the actual main entrypoint, with synthetic data and zero native/network calls.

V8 bounded private JSON receive correction

V7 health preceded private stdin but stdin.read() had no byte or elapsed bound. That issuer-owner finding remains history; exporter write bounds do not bound a consumer read. V8 reads binary chunks of at most 4096 bytes, at most 65536 total bytes, and requires actual EOF before decoding the exact two-token JSON object. Each token is capped at 16384 UTF-8 bytes. The entire receive has a Linux main- thread total timer capped at 15 seconds and clipped to the original remaining wall-clock/monotonic action window; waiting never renews that window. Expiry is checked before receiving, after EOF, and after decoding. Empty, oversized, incomplete, invalid, stalled or late input cannot reach native execution. Receipts contain only fixed failure codes/booleans, never bytes or parser errors.

This child-side read gate still does not establish producer-side readiness. Before ANY token bytes enter a pipe/SSH transport, a separately reviewed trusted launcher/exporter must receive an exact-child source/approval/public-health ready acknowledgment bound to that pipe session and window. The current producer v1 has no such protocol; the producer consumer review requests it. V8 supplies no handshake, launcher admission or native readiness and must not be piped to the old exporter as though its health-before-read check prevents early transmission.

V9 private ready protocol and concrete node wrapper

The old producer v1 refusal remains history: a child checking health before reading stdin cannot prevent an exporter from buffering or transmitting tokens earlier. V9 execute therefore requires --ready-fd, --session-nonce and --ready-envelope-sha256 before it will read private input. The ready FD is a locally inherited writable FIFO >=3 whose inode/device differs from every stdio endpoint; stdout/stderr aliases, regular files and missing FDs refuse.

After source/approval validation, original-window start and approved public health, the child emits one <=4096-byte newline JSON marker to that private FD and closes it. Emission has a total two-second cap clipped to the original action window. Child stdout and stderr remain quiet. Exact marker fields are:

{
  "schema": "key39-private-input-ready/v1",
  "session_nonce": "<fresh 64 lowercase hex characters from trusted launcher>",
  "proposal_id": "KEY-WP-0039-T02/attended-lifecycle-v9-ready-input",
  "target": "sso/keycape",
  "executor_sha256": "<exact reviewed child source hash>",
  "envelope_sha256": "<exact approved raw envelope byte hash>",
  "bao_health_identity_sha256": "<exact owner provider identity pin>",
  "valid_until_epoch": 0,
  "receive_budget_seconds": 0
}

Zeros/placeholders illustrate keys only and are never a valid marker. Epoch is the exact finite nonboolean approved envelope expiry, still future; receive budget is finite positive <=15 and clipped at emission to the original action window. The exporter clips its already-running deadline to the advertised budget and current approval remainder. It must not reset a deadline or demand that a budget measured at emission equal the smaller remainder after transport.

openbao-negative-node-wrapper.py is the concrete node-local relay. The trusted launcher supplies nonsecret exact child/envelope paths and SHA256 pins, a new nonce and exclusive receipt destination. Wrapper envelope must be owner UID, mode0600, single-link regular file, stable bounded bytes; its child/wrapper source paths must be included in the exact envelope's native source hash map. This is reconstruction of the caller's review context, not authenticated human approval: assertions in JSON cannot create a grant. The founder must separately admit the exact source/profile, target and SSH peer under the applicable lane.

The wrapper creates an ordinary local pipe on the node, starts the pinned Python child with that write endpoint passed locally, and reads its bounded marker plus EOF. The child's private stdin is inherited directly from the SSH input stream: wrapper never reads, copies, logs or writes token bytes. No extra file descriptor is forwarded by SSH or OpsBridge. Within one five-second total preparation deadline, the wrapper validates all marker keys/nonce/pins/window and forwards exactly one value-free marker line on its own stdout control pipe. The admitted workstation launcher must own/capture that SSH stdout separately from its private stdin data pipe, never display/tee/log it, validate the exact marker and then allow the KeyCape adapter to load/consume caller files or write ANY token bytes. The control stream stays open while the child runs; the exporter stops marker reception at newline, not SSH EOF. Missing, partial, wrong-nonce, stale, oversized or pin-mismatched markers mean zero token file reads/writes and retained caller files. The old v1 adapter is incompatible.

This wrapper emits control stdout and therefore cannot run inside the warden attended command lane that rejects all child output. It requires a separately admitted founder-owned direct SSH profile with no PTY, captured stdin/stdout/stderr, pinned peer/host identity, exact wrapper/source/receipt paths and no agent credential access. No such runtime admission is supplied here. A bare marker, caller boolean or downloaded ready file cannot authenticate a peer, source execution or founder decision. A trusted reviewed launcher owning that transport and verifying the context is a prerequisite.

The wrapper waits only through the original approval remainder plus 300 seconds cleanup. Any child stdout/stderr is captured with bounded reads and refused, never forwarded. Failure terminates the exact launched child with bounded five- second TERM then two-second kill/wait fallback; capture closes have 20 ms caps. No retry or replay occurs. Interruption, partial private delivery, emitted ready followed by child death, unconfirmed revoke or interrupted native cleanup remains unknown/pending owner reconciliation using the separately approved recovery path. Wrapper exit zero is not owner/native acceptance; the child receipt still requires actual owner acceptance. No persistent file/copy on the node is added for token material, and Unix/Python memory cleanup is not secure zeroization.

Human browser issuance and admitted coding-agent custody remain distinct owner choices. Any new files must be produced by the pinned verified owner CLI under continuous exclusive tmpfs custody after the separately admitted route preflight. They must be prepared before launching this short-lived private receive window; the child cannot wait for a five-minute browser login after emitting ready. Fresh registered-client/MFA/custody evidence, exact independent KeyCape/Platform reviews and the founder's complete route/transport/private-file/native decision remain required. No native handshake, SSH, credential or route was exercised.