ops-warden/src/warden/caller_identity.py
tegwick 0a331413a2
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Send a caller identity to flex-auth so policy.enabled can flip
flex-auth's flex-auth-ops-warden pin (FLEX-WP-0016) TokenReviews the caller and
binds resource.system: ops-warden to system:serviceaccount:ops-warden:ops-warden.
policy.py posted /v1/check with no Authorization header, so the pin logs
"caller authentication warning" and can only run callerAuth.mode: warn — which,
under ADHOC-2026-08-17-T01, is exactly what blocks policy.enabled: true.

- policy.caller_auth (none | file | env | command) + src/warden/caller_identity.py:
  token resolved per call, never cached, written, or logged (ADR-0002)
- both check_sign_policy and check_fetch_policy attach the bearer header; an
  unobtainable token fails closed rather than retrying anonymously
- scripts/check_policy_caller_identity.py: read-only gate, prints length and a
  truncated fingerprint only, distinguishes 401 (audience/binding) from 403
- example config: caller_auth block, and flex_auth_url corrected — it pointed at
  flex-auth.flex-auth.svc, a Service that does not exist
- WARDEN-WP-0031, PolicyGatedSigning caller-identity section and flip sequence

Default stays mode: none, so behaviour is unchanged until an operator opts in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 15:08:34 +02:00

92 lines
3.5 KiB
Python

"""Caller identity for ops-warden's outbound flex-auth policy calls.
flex-auth's `flex-auth-ops-warden` pin (FLEX-WP-0016) authenticates the *caller*
before it evaluates the request: `Authorization: Bearer <token>` is passed to a
Kubernetes TokenReview, and `resource.system: ops-warden` is bound to the
principal `system:serviceaccount:ops-warden:ops-warden`. Until ops-warden sends
that header, the pin logs `caller authentication warning` and can only run in
`warn` mode — which is why `policy.enabled` cannot flip.
This module resolves the token at call time and hands it straight to the request.
Nothing is cached to disk, logged, or echoed: ops-warden carries the value, it
does not hold it (ADR-0002).
"""
from __future__ import annotations
import os
import subprocess
from warden.config import CallerAuthConfig
class CallerIdentityError(Exception):
"""Raised when a caller token was configured but could not be obtained."""
def resolve_caller_token(cfg: CallerAuthConfig) -> str | None:
"""Return the bearer token for flex-auth, or None when mode is ``none``.
Raises CallerIdentityError when a token was configured but is unavailable.
The token itself never appears in an exception message.
"""
mode = cfg.mode
if mode == "none":
return None
if mode == "file":
if cfg.token_path is None:
raise CallerIdentityError("caller_auth mode 'file' has no token_path")
try:
token = cfg.token_path.read_text()
except OSError as e:
raise CallerIdentityError(
f"caller token file unreadable: {cfg.token_path} ({e.strerror})"
) from e
elif mode == "env":
token = os.environ.get(cfg.token_env, "")
if not token.strip():
raise CallerIdentityError(
f"caller token env {cfg.token_env} is unset or empty"
)
elif mode == "command":
if not cfg.command:
raise CallerIdentityError("caller_auth mode 'command' has no command")
try:
result = subprocess.run(
cfg.command,
capture_output=True,
text=True,
timeout=30,
check=False,
)
except FileNotFoundError as e:
raise CallerIdentityError(
f"caller token command not found: {cfg.command[0]}"
) from e
except subprocess.TimeoutExpired as e:
raise CallerIdentityError("caller token command timed out") from e
if result.returncode != 0:
stderr = (result.stderr or "").strip().splitlines()
detail = stderr[-1] if stderr else f"exit {result.returncode}"
raise CallerIdentityError(f"caller token command failed: {detail}")
token = result.stdout
else:
raise CallerIdentityError(f"unsupported caller_auth mode {mode!r}")
token = token.strip()
if not token:
raise CallerIdentityError(f"caller_auth mode {mode!r} produced an empty token")
if any(ch.isspace() for ch in token):
# flex-auth rejects a bearer token containing whitespace outright.
raise CallerIdentityError(
f"caller_auth mode {mode!r} produced a token containing whitespace"
)
return token
def caller_auth_headers(cfg: CallerAuthConfig) -> dict[str, str]:
"""Headers to attach to a flex-auth /v1/check call ({} when unauthenticated)."""
token = resolve_caller_token(cfg)
if token is None:
return {}
return {"Authorization": f"Bearer {token}"}