railiance-platform/scripts/application_backup_credentials.py
codex 1900e24f80
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Accept both backup readers and deliver the email-connect pair.
Record founder acceptance on CCR-2026-0036 and CCR-2026-0037, install the reader roles, and admit only the email-connect node delivery. Treat a missing OpenBao token period as non-periodic.

Assistant: grok
Assistant-Session: 01a11cdb-f9fe-7011-947c-936e72007e54
2026-10-09 00:05:35 +02:00

444 lines
16 KiB
Python

#!/usr/bin/env python3
"""Preview or deliver one admitted backup reader's credentials on railiance01.
Stdlib only. No ambient OpenBao credential, Secret read, subprocess secret argv,
provider call, rotation, backup capture or service activation.
"""
from __future__ import annotations
import argparse
import base64
import datetime as dt
import fcntl
import hashlib
import ipaddress
import json
import os
import re
import signal
import stat
import subprocess
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path
CLUSTER_UID = "a553c742-0115-43d4-99a4-a5ca56fe0786"
FIELDS = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
SA = "backup-primary-delivery"
PROFILES = {
"coulomb-social": {"ccr": "CCR-2026-0036", "directory": "/etc/coulomb-social-backup"},
"email-connect": {"ccr": "CCR-2026-0037", "directory": "/etc/email-connect-backup"},
}
class DeliveryError(Exception):
"""Only fixed diagnostics cross the containment boundary."""
def guard(condition, code):
if not condition:
raise DeliveryError(code)
def expected_openbao(application):
policy = f"workload-kv-read-{application}-backup-primary"
return {
"mount": "platform",
"kv_path": f"platform/workloads/{application}/backup-primary",
"fields": FIELDS,
"policy_name": policy,
"policy_file": f"openbao/policies/{policy}.hcl",
"metadata_read": False,
"token_self_lifecycle": True,
"auth": {
"method": "kubernetes",
"mount": "kubernetes",
"role": f"{application}-backup-primary-workload-kv-read",
"audience": "openbao",
"ttl": "5m",
"token_max_ttl": "5m",
"token_explicit_max_ttl": "5m",
"token_no_default_policy": True,
"token_num_uses": 4,
"token_type": "service",
"policies": [policy],
"bound_claims": {
"service_account_names": [SA],
"service_account_namespaces": [application],
},
},
}
def contract(raw, application, execute=False, expected_hash=None):
guard(len(raw) <= 65536, "contract_too_large")
ccr = json.loads(raw)
expected = expected_openbao(application)
actual = dict(ccr.get("openbao", {}))
auth = dict(actual.get("auth", {}))
confirmed = auth.pop("bound_claims_confirmed", None)
actual["auth"] = auth
guard(
actual == expected
and ccr.get("id") == PROFILES[application]["ccr"]
and ccr.get("request_type") == "workload-kv-read",
"exact_contract_required",
)
delivery = ccr.get("delivery", {})
guard(
delivery.get("target") == PROFILES[application]["directory"] + "/primary.env"
and delivery.get("cluster_uid") == CLUSTER_UID
and delivery.get("owner") == "root:root"
and delivery.get("mode") == "0600"
and delivery.get("directory_mode") == "0700",
"exact_delivery_required",
)
digest = hashlib.sha256(raw).hexdigest()
if execute:
guard(expected_hash == digest, "reviewed_source_hash_required")
guard(
ccr.get("status") in {"applied", "verified", "active"} and confirmed is True,
"applied_confirmed_reader_required",
)
reviews = ccr.get("review", {}).get("comments", [])
for owner in ("platform-owner", f"{application}-owner"):
guard(
any(
r.get("reviewer") == f"User ({owner})"
and r.get("decision") == "approved"
and r.get("source_ref")
for r in reviews
),
"owner_review_required",
)
admission = delivery.get("admission", {})
guard(
all(
admission.get(key) is True
for key in (
"node_executor_approved",
"provider_scope_verified",
"reader_boundary_verified",
)
)
and isinstance(admission.get("source_ref"), str)
and admission["source_ref"],
"delivery_admission_required",
)
return ccr, digest
def command(argv):
try:
result = subprocess.run(
argv,
capture_output=True,
check=False,
timeout=30,
env={
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"KUBECONFIG": "/etc/rancher/k3s/k3s.yaml",
},
)
guard(result.returncode == 0 and len(result.stdout) <= 1048576, "kubernetes_command_failed")
return result.stdout
except (OSError, subprocess.SubprocessError):
raise DeliveryError("kubernetes_command_failed") from None
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
return None
class Native:
def __init__(self):
self.address = None
self.opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect())
def identity(self, application):
kube = ["kubectl", "--request-timeout=20s"]
ns = json.loads(command(kube + ["get", "namespace", "kube-system", "-o", "json"]))
guard(ns.get("metadata", {}).get("uid") == CLUSTER_UID, "cluster_identity_mismatch")
svc = json.loads(
command(kube + ["-n", "openbao", "get", "service", "openbao", "-o", "json"])
)
addr = ipaddress.ip_address(svc.get("spec", {}).get("clusterIP", ""))
guard(
addr in ipaddress.ip_network("10.43.0.0/16")
and any(
p.get("port") == 8200 and p.get("protocol", "TCP") == "TCP"
for p in svc["spec"].get("ports", [])
),
"openbao_service_mismatch",
)
self.address = f"http://{addr}:8200/v1/"
sa = json.loads(
command(kube + ["-n", application, "get", "serviceaccount", SA, "-o", "json"])
)
guard(
sa.get("automountServiceAccountToken") is False, "non_mounting_service_account_required"
)
jwt = (
command(
kube
+ ["-n", application, "create", "token", SA, "--audience=openbao", "--duration=10m"]
)
.decode()
.strip()
)
pieces = jwt.split(".")
guard(len(pieces) == 3 and len(jwt) < 16384, "bounded_jwt_required")
claims = json.loads(base64.urlsafe_b64decode(pieces[1] + "=" * (-len(pieces[1]) % 4)))
bound = claims.get("kubernetes.io", {}).get("serviceaccount", {})
guard(
claims.get("sub") == f"system:serviceaccount:{application}:{SA}"
and claims.get("aud") == ["openbao"]
and bound.get("uid") == sa.get("metadata", {}).get("uid")
and bound.get("uid")
and 0 < claims.get("exp", 0) - time.time() <= 660,
"jwt_binding_mismatch",
)
return jwt
def request(self, method, path, payload=None, token=None):
headers = {"Content-Type": "application/json"}
if token is not None:
headers["X-Vault-Token"] = token
body = None if payload is None else json.dumps(payload).encode()
request = urllib.request.Request(
self.address + path, data=body, headers=headers, method=method
)
try:
with self.opener.open(request, timeout=20) as response:
raw = response.read(1048577)
guard(len(raw) <= 1048576, "openbao_response_too_large")
return {} if not raw else json.loads(raw)
except (urllib.error.URLError, OSError):
raise DeliveryError("openbao_request_failed") from None
def retrieve(native, application, expected_version):
role = expected_openbao(application)["auth"]["role"]
policy = expected_openbao(application)["policy_name"]
jwt = native.identity(application)
response = native.request("POST", "auth/kubernetes/login", {"role": role, "jwt": jwt})
auth = response.get("auth", {})
token = auth.get("client_token")
guard(isinstance(token, str) and token, "scoped_token_required")
try:
guard(
auth.get("policies") == [policy] and 0 < auth.get("lease_duration", 0) <= 300,
"login_scope_mismatch",
)
info = native.request("GET", "auth/token/lookup-self", token=token).get("data", {})
guard(
info.get("policies") == [policy]
and not info.get("identity_policies")
and 0 < info.get("ttl", 0) <= 300
and (info.get("period") or 0) == 0
and 0 < info.get("explicit_max_ttl", 0) <= 300
and 0 < info.get("num_uses", 0) <= 4,
"reader_scope_mismatch",
)
record = native.request(
"GET", f"platform/data/workloads/{application}/backup-primary", token=token
).get("data", {})
metadata = record.get("metadata", {})
guard(
type(expected_version) is int
and expected_version > 0
and metadata.get("version") == expected_version
and metadata.get("destroyed") is False
and not metadata.get("deletion_time"),
"custody_version_mismatch",
)
values = record.get("data", {})
guard(set(values) == set(FIELDS), "credential_fields_mismatch")
# Scaleway/AWS key characters; prevents EnvironmentFile or shell injection.
for name in FIELDS:
guard(
isinstance(values[name], str)
and re.fullmatch(r"[A-Za-z0-9/+=_.-]{8,256}", values[name]),
"credential_encoding_rejected",
)
return "".join(f"{name}={values[name]}\n" for name in FIELDS).encode()
finally:
# Revoke BEFORE publishing; failed revocation never admits a delivery.
try:
native.request("POST", "auth/token/revoke-self", {}, token=token)
except Exception: # noqa: BLE001 - contain all backend failures without disclosing values
raise DeliveryError("token_revocation_unconfirmed") from None
def private_directory(path, uid=0):
# O_NOFOLLOW checks every ancestor, including /etc and the private leaf.
fd = os.open("/", os.O_RDONLY | os.O_DIRECTORY)
try:
for part in Path(path).parts[1:]:
new = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fd)
os.close(fd)
fd = new
info = os.fstat(fd)
guard(
info.st_uid == uid and info.st_gid == uid and stat.S_IMODE(info.st_mode) == 0o700,
"private_directory_required",
)
return fd
except BaseException:
os.close(fd)
raise
def private_read(directory_fd, name, uid=0):
fd = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory_fd)
with os.fdopen(fd, "rb") as stream:
info = os.fstat(stream.fileno())
guard(
stat.S_ISREG(info.st_mode)
and info.st_uid == uid
and info.st_gid == uid
and stat.S_IMODE(info.st_mode) == 0o600
and info.st_nlink == 1,
"private_file_required",
)
body = stream.read(65537)
guard(len(body) <= 65536, "private_file_too_large")
return body
def publish(directory_fd, name, body, uid=0):
try:
existing = private_read(directory_fd, name, uid)
except FileNotFoundError:
existing = None
if existing is not None:
guard(existing == body, "existing_delivery_differs_owner_rotation_required")
return "unchanged"
temporary = f".{name}.{os.getpid()}.{os.urandom(8).hex()}"
fd = os.open(
temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600, dir_fd=directory_fd
)
try:
with os.fdopen(fd, "wb") as stream:
os.fchmod(stream.fileno(), 0o600)
stream.write(body)
stream.flush()
os.fsync(stream.fileno())
os.link(
temporary, name, src_dir_fd=directory_fd, dst_dir_fd=directory_fd, follow_symlinks=False
)
finally:
os.unlink(temporary, dir_fd=directory_fd)
os.fsync(directory_fd)
guard(private_read(directory_fd, name, uid) == body, "delivery_readback_failed")
return "created"
def deliver(ccr, digest, application, version, native, directory_fd, uid=0):
guard(type(version) is int and version > 0, "explicit_custody_version_required")
lock = os.open(
"delivery.lock",
os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW | os.O_NONBLOCK,
0o600,
dir_fd=directory_fd,
)
try:
info = os.fstat(lock)
guard(
stat.S_ISREG(info.st_mode)
and info.st_uid == uid
and info.st_gid == uid
and info.st_nlink == 1
and stat.S_IMODE(info.st_mode) == 0o600,
"private_lock_required",
)
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
payload = retrieve(native, application, version)
outcome = publish(directory_fd, "primary.env", payload, uid)
receipt = {
"schema": "platform.application-backup-credential-delivery.v1",
"status": "delivered",
"application": application,
"ccr": ccr["id"],
"ccr_sha256": digest,
"cluster_uid": CLUSTER_UID,
"kv_path": ccr["openbao"]["kv_path"],
"kv_version": version,
"target": ccr["delivery"]["target"],
"owner": "root:root",
"mode": "0600",
"directory_mode": "0700",
"token_revoked": True,
"readback_equal": True,
"outcome": outcome,
"delivered_at": dt.datetime.now(dt.timezone.utc).isoformat(),
}
# One immutable metadata receipt per run. Crash after file publication is
# recoverable by exact equality on a fresh scoped read, never overwrite.
name = f"delivery-v{version}-{time.time_ns()}.json"
publish(directory_fd, name, (json.dumps(receipt, sort_keys=True) + "\n").encode(), uid)
return receipt
finally:
os.close(lock)
def terminate(signum, frame):
raise DeliveryError("delivery_interrupted")
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--application", choices=PROFILES, required=True)
parser.add_argument("--ccr", type=Path, required=True)
parser.add_argument("--expected-ccr-sha256")
parser.add_argument("--expected-kv-version", type=int)
parser.add_argument("--execute", action="store_true")
args = parser.parse_args(argv)
signal.signal(signal.SIGTERM, terminate)
try:
ccr, digest = contract(
args.ccr.read_bytes(), args.application, args.execute, args.expected_ccr_sha256
)
if not args.execute:
print(
json.dumps(
{
"status": "preview",
"ccr": ccr["id"],
"ccr_sha256": digest,
"application": args.application,
"target": ccr["delivery"]["target"],
"effects": [],
},
sort_keys=True,
)
)
return 0
guard(os.geteuid() == 0, "admitted_node_root_required")
guard(
type(args.expected_kv_version) is int and args.expected_kv_version > 0,
"explicit_custody_version_required",
)
directory = private_directory(PROFILES[args.application]["directory"])
try:
receipt = deliver(
ccr, digest, args.application, args.expected_kv_version, Native(), directory
)
finally:
os.close(directory)
print(json.dumps(receipt, sort_keys=True))
return 0
except DeliveryError as exc:
print(json.dumps({"status": "failed", "code": str(exc)}), file=sys.stderr)
except Exception: # noqa: BLE001 - CLI is the final secret-containment boundary
# Never stringify backend exceptions, response bodies or private paths.
print('{"status":"failed","code":"contained_delivery_failure"}', file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())