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
444 lines
16 KiB
Python
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())
|