Fix contained OpenBao login handoff
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
This commit is contained in:
tegwick 2026-09-01 00:13:26 +02:00
parent 4e267179db
commit b4c1d3900a
3 changed files with 81 additions and 6 deletions

View file

@ -12,8 +12,9 @@ intact. Three guardrails are enforced here in code:
the tool with inherited stdout/stderr so the value never enters warden's memory; the tool with inherited stdout/stderr so the value never enters warden's memory;
sanctioned exec/file transports hold it only for their bounded handoff. The sanctioned exec/file transports hold it only for their bounded handoff. The
high-risk attended-login lane is stricter: it captures every client byte inside high-risk attended-login lane is stricter: it captures every client byte inside
an isolated helper session, permits no output, self-revokes, and cleans up. Audit an isolated helper session, never returns that output, requires successful
records are metadata only. persistence to a private token helper, self-revokes, and cleans up. Audit records
are metadata only.
* **G3 policy gate before fetch.** The CLI runs ``check_fetch_policy`` before * **G3 policy gate before fetch.** The CLI runs ``check_fetch_policy`` before
calling anything here; this module refuses to run an unresolved command template. calling anything here; this module refuses to run an unresolved command template.
@ -380,9 +381,11 @@ def proxy_attended_login_exec(
"""Run an attended login and one silent child inside a private helper home. """Run an attended login and one silent child inside a private helper home.
The default home is proven writable before the OIDC client starts. Login, The default home is proven writable before the OIDC client starts. Login,
child, and revocation output are captured and discarded. Any non-empty output, child, and revocation output are captured and discarded. A successful login
persistence defect, or non-zero result fails closed; any possibly issued token may return client output only after the private helper has been populated;
is revoked before the isolated helper directory is removed. persistence defects and non-zero results fail closed. The reviewed child must
remain silent. Any possibly issued token is revoked before the isolated helper
directory is removed.
""" """
if not child_argv: if not child_argv:
raise ProxyError( raise ProxyError(
@ -428,7 +431,7 @@ def proxy_attended_login_exec(
except (OSError, ProxyError): except (OSError, ProxyError):
helper_valid = False helper_valid = False
if login.returncode != 0 or login_output.strip() or not helper_valid: if login.returncode != 0 or not helper_valid:
revoked = _revoke_contained( revoked = _revoke_contained(
resolved.argv[0], env=env, possible_output=login_output resolved.argv[0], env=env, possible_output=login_output
) )

View file

@ -391,6 +391,42 @@ def test_attended_login_unexpected_output_is_contained_revoked_and_cleaned(
assert not (tmp_path / ".warden-attended-login").exists() assert not (tmp_path / ".warden-attended-login").exists()
def test_attended_login_contained_success_output_never_escapes(monkeypatch, tmp_path, capsys):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
sentinel = "hvs.NONPRODUCTION_CONTAINED_LOGIN"
child_ran = False
def fake_run(argv, **kw):
nonlocal child_ran
helper = Path(kw["env"]["HOME"]) / ".vault-token"
if argv[:2] == ["bao", "login"]:
helper.write_text(sentinel)
helper.chmod(0o600)
return subprocess.CompletedProcess(
argv,
0,
stdout=json.dumps({"auth": {"client_token": sentinel}}).encode(),
stderr=b"",
)
if argv == ["reviewed-child"]:
child_ran = True
return subprocess.CompletedProcess(argv, 0, stdout=b"", stderr=b"")
if argv[:3] == ["bao", "token", "revoke"]:
return subprocess.CompletedProcess(argv, 0, stdout=b"", stderr=b"")
raise AssertionError(argv)
monkeypatch.setattr("warden.proxy.subprocess.run", fake_run)
assert proxy_attended_login_exec(
ResolvedFetch(argv=["bao", "login", "-no-print"]),
child_argv=["reviewed-child"],
) == 0
captured = capsys.readouterr()
assert child_ran is True
assert sentinel not in captured.out
assert sentinel not in captured.err
assert not (tmp_path / ".warden-attended-login").exists()
def test_real_catalog_login_entry_is_login_lane(): def test_real_catalog_login_entry_is_login_lane():
from warden.routing import load_catalog from warden.routing import load_catalog
e = load_catalog(_repo_catalog()).get("key-cape-oidc-login") e = load_catalog(_repo_catalog()).get("key-cape-oidc-login")

View file

@ -0,0 +1,36 @@
---
id: WARDEN-WP-0034
type: workplan
title: "Accept contained OpenBao login output only after helper persistence"
domain: infotech
repo: ops-warden
status: active
owner: codex
topic_slug: attended-login-openbao-output
created: "2026-09-01"
updated: "2026-09-01"
---
## Repair attended-login handoff
```task
id: WARDEN-WP-0034-T01
status: done
priority: high
```
Allow a successful OpenBao login to proceed when its output is fully contained
and the private mode-0600 token helper is populated. Continue failing closed on
non-zero login, missing persistence, child output, revocation failure, or cleanup
failure.
## Verify live contained operation
```task
id: WARDEN-WP-0034-T02
status: progress
priority: high
```
Run the proxy regression suite, reinstall the CLI, and complete one governed
OpenBao platform-admin operation with deterministic self-revocation.