Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
798 lines
26 KiB
Python
798 lines
26 KiB
Python
#!/usr/bin/env python3
|
|
"""Proposed NK-WP-0035 attended custody+cadence delivery; no values in receipts.
|
|
|
|
Plan is offline. Apply requires CCR0047 exact owner approval and the SHA256 of
|
|
current complete packet, immutable compatible receiver and attended platform
|
|
admin. A failed attempt retains KV version1 and must resume explicitly.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import base64
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import secrets
|
|
import sys
|
|
import time
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
from factory_audit_custody import (
|
|
REGISTRY,
|
|
credential_module,
|
|
prepare_metadata,
|
|
receiver_check,
|
|
require,
|
|
snapshot,
|
|
)
|
|
from factory_audit_delivery import (
|
|
custody_value,
|
|
projection_matches,
|
|
)
|
|
from keycape_approval_custody import read_optional, require_denied, role_matches
|
|
from state_hub_preflight_lane import ROOT, LaneError, bao, command, data, revoke
|
|
|
|
CCR = ROOT / "credential-change-requests/CCR-2026-0047-rapp-qonto-audit-sender.yaml"
|
|
MANIFEST = ROOT / "manifests/rapp-qonto-audit-sender.yaml"
|
|
WORKSPACE = ROOT.parent
|
|
APPROVAL = WORKSPACE / "approval-engine/cadence.yaml"
|
|
QONTO = WORKSPACE / "qonto-assistant/specs/audit-emission-cadence.yaml"
|
|
PROPOSAL = (
|
|
WORKSPACE / "net-kingdom/generated/coordination-frame/approval-engine-receiver.json"
|
|
)
|
|
BUILDER = WORKSPACE / "audit-core/audit_core/cadence_registration.py"
|
|
|
|
|
|
def contracts(approved=False):
|
|
module = credential_module()
|
|
ccr, errors, _ = module.validate_ccr(CCR)
|
|
require(not errors, "invalid_credential_request")
|
|
if approved:
|
|
require(
|
|
ccr["status"] in {"approved", "applied", "verified", "active"},
|
|
"approved_ccr_required",
|
|
)
|
|
for owner in (
|
|
"platform-operator",
|
|
"audit-core-owner",
|
|
"rapp-qonto-owner",
|
|
"qonto-assistant-owner",
|
|
):
|
|
require(
|
|
any(
|
|
x.get("decision") == "approved" and owner in x.get("reviewer", "")
|
|
for x in ccr["review"]["comments"]
|
|
),
|
|
"named_owner_approval_required",
|
|
)
|
|
policy = "workload-kv-read-rapp-qonto-audit"
|
|
role = "external-secrets-rapp-qonto-audit"
|
|
payload = {
|
|
"bound_service_account_names": ["external-secrets"],
|
|
"bound_service_account_namespaces": ["external-secrets"],
|
|
"policies": policy,
|
|
"ttl": "15m",
|
|
}
|
|
o = ccr["openbao"]
|
|
require(
|
|
o["kv_path"] == "platform/workloads/rapp-qonto/audit-sender"
|
|
and o["fields"] == ["AUDIT_TOKEN", "CUSTODY_REQUEST"]
|
|
and o["policy_name"] == policy
|
|
and o["metadata_read"] is False
|
|
and o["auth"]["mount"] == "kubernetes"
|
|
and o["auth"]["role"] == role
|
|
and o["auth"]["bound_claims_confirmed"] is True
|
|
and module.auth_payload(ccr) == payload,
|
|
"exact_contract_required",
|
|
)
|
|
hcl = module.generated_policy_hcl(ccr)
|
|
require((ROOT / o["policy_file"]).read_text() == hcl, "policy_source_drift")
|
|
return {
|
|
"ccr": "CCR-2026-0047",
|
|
"name": "rapp-qonto-collector",
|
|
"namespace": "rapp-qonto",
|
|
"kv": "platform/data/workloads/rapp-qonto/audit-sender",
|
|
"metadata": "platform/metadata/workloads/rapp-qonto/audit-sender",
|
|
"policy": policy,
|
|
"hcl": hcl,
|
|
"role": role,
|
|
"role_payload": payload,
|
|
"store": "openbao-rapp-qonto-audit",
|
|
"secret": "rapp-qonto-audit-sender",
|
|
"secret_key": "bearer",
|
|
}
|
|
|
|
|
|
def reviewed_objects():
|
|
objects = list(yaml.safe_load_all(MANIFEST.read_text()))
|
|
require(len(objects) == 2, "exact_projection_objects_required")
|
|
store, external = objects
|
|
require(
|
|
store["kind"] == "ClusterSecretStore"
|
|
and store["metadata"]["name"] == "openbao-rapp-qonto-audit"
|
|
and store["spec"]["conditions"] == [{"namespaces": ["rapp-qonto"]}],
|
|
"exact_store_scope_required",
|
|
)
|
|
vault = store["spec"]["provider"]["vault"]
|
|
require(
|
|
vault
|
|
== {
|
|
"server": "http://openbao.openbao.svc:8200",
|
|
"path": "platform",
|
|
"version": "v2",
|
|
"auth": {
|
|
"kubernetes": {
|
|
"mountPath": "kubernetes",
|
|
"role": "external-secrets-rapp-qonto-audit",
|
|
"serviceAccountRef": {
|
|
"name": "external-secrets",
|
|
"namespace": "external-secrets",
|
|
},
|
|
}
|
|
},
|
|
},
|
|
"exact_store_auth_required",
|
|
)
|
|
require(
|
|
external["kind"] == "ExternalSecret"
|
|
and external["metadata"]["name"] == "rapp-qonto-audit-sender"
|
|
and external["metadata"]["namespace"] == "rapp-qonto"
|
|
and external["spec"]["secretStoreRef"]
|
|
== {"kind": "ClusterSecretStore", "name": "openbao-rapp-qonto-audit"},
|
|
"exact_projection_target_required",
|
|
)
|
|
spec = external["spec"]
|
|
require(
|
|
set(spec) == {"refreshInterval", "secretStoreRef", "target", "data"},
|
|
"unexpected_projection_fields",
|
|
)
|
|
require(
|
|
spec["data"]
|
|
== [
|
|
{
|
|
"secretKey": "bearer",
|
|
"remoteRef": {
|
|
"key": "workloads/rapp-qonto/audit-sender",
|
|
"property": "AUDIT_TOKEN",
|
|
"conversionStrategy": "Default",
|
|
"decodingStrategy": "None",
|
|
"metadataPolicy": "None",
|
|
},
|
|
}
|
|
],
|
|
"exact_projection_field_required",
|
|
)
|
|
target = spec["target"]
|
|
require(
|
|
set(target) == {"name", "creationPolicy", "deletionPolicy", "template"}
|
|
and target["name"] == "rapp-qonto-audit-sender"
|
|
and target["creationPolicy"] == "Owner"
|
|
and target["deletionPolicy"] == "Retain",
|
|
"exact_projection_ownership_required",
|
|
)
|
|
template = target["template"]
|
|
require(
|
|
set(template) == {"metadata", "engineVersion", "mergePolicy"}
|
|
and template["engineVersion"] == "v2"
|
|
and template["mergePolicy"] == "Replace"
|
|
and set(template["metadata"]) == {"annotations"},
|
|
"metadata_only_template_required",
|
|
)
|
|
return objects
|
|
|
|
|
|
def packet():
|
|
contracts()
|
|
reviewed_objects()
|
|
paths = [
|
|
CCR,
|
|
MANIFEST,
|
|
ROOT / "openbao/policies/workload-kv-read-rapp-qonto-audit.hcl",
|
|
Path(__file__),
|
|
ROOT / "scripts/factory_audit_custody.py",
|
|
ROOT / "scripts/factory_audit_delivery.py",
|
|
ROOT / "scripts/keycape_approval_custody.py",
|
|
ROOT / "scripts/state_hub_preflight_lane.py",
|
|
ROOT / "scripts/credential-change.py",
|
|
APPROVAL,
|
|
QONTO,
|
|
PROPOSAL,
|
|
BUILDER,
|
|
WORKSPACE / "audit-core/audit_core/senders.py",
|
|
WORKSPACE / "audit-core/audit_core/emission_cadence.py",
|
|
]
|
|
hashes = {
|
|
str(p.relative_to(WORKSPACE)): hashlib.sha256(p.read_bytes()).hexdigest()
|
|
for p in paths
|
|
}
|
|
digest = hashlib.sha256(json.dumps(hashes, sort_keys=True).encode()).hexdigest()
|
|
return {
|
|
"schema_version": "platform.cadence-custody-packet/v1",
|
|
"sha256": digest,
|
|
"source_sha256": hashes,
|
|
"status": "proposed",
|
|
"credential_values_emitted": False,
|
|
}
|
|
|
|
|
|
def builder():
|
|
sys.path.insert(0, str(WORKSPACE / "audit-core"))
|
|
from audit_core.cadence_registration import prepare, qonto_metadata
|
|
|
|
return prepare, qonto_metadata
|
|
|
|
|
|
def proposal_metadata():
|
|
_, qonto_metadata = builder()
|
|
raw = QONTO.read_bytes()
|
|
return qonto_metadata(yaml.safe_load(raw), hashlib.sha256(raw).hexdigest())
|
|
|
|
|
|
def preflight_custody(lane, *, resume=False):
|
|
"""Refuse existing custody/scope drift before creating any reader grant."""
|
|
_, _, rows, used = snapshot()
|
|
existing = [row for row in rows if row["name"] == lane["name"]]
|
|
old = read_optional(lane["metadata"])
|
|
if old is None:
|
|
require(not existing, "registry_sender_without_custody")
|
|
return
|
|
require(
|
|
resume
|
|
and old["current_version"] == 1
|
|
and not old["versions"]["1"].get("destroyed")
|
|
and not old["versions"]["1"].get("deletion_time"),
|
|
"reviewed_version1_resume_required",
|
|
)
|
|
raw = data(bao(["read", "-format=json", lane["kv"]]))["data"]
|
|
token = custody_value(lane, raw)
|
|
wanted = dict(proposal_metadata(), tokens=[token])
|
|
if existing:
|
|
require(existing == [wanted], "registered_sender_drift")
|
|
else:
|
|
require(token not in used, "sender_token_collision")
|
|
|
|
|
|
def merge_and_seed(lane, receipt, save, *, resume=False):
|
|
"""CAS all registry metadata once; only new exact KV uses CAS0.
|
|
|
|
All private values are in memory. A CAS conflict does not retry against a
|
|
new registry silently: operator re-preflights and resumes durable version1.
|
|
"""
|
|
version, body, rows, used = snapshot()
|
|
originals = copy.deepcopy(rows)
|
|
prepare, _ = builder()
|
|
updated, approval_receipt = prepare(
|
|
body, APPROVAL.read_bytes(), json.loads(PROPOSAL.read_text())
|
|
)
|
|
wanted = proposal_metadata()
|
|
existing = [row for row in rows if row["name"] == lane["name"]]
|
|
old = read_optional(lane["metadata"])
|
|
if old is None:
|
|
require(not existing, "registry_sender_without_custody")
|
|
token = secrets.token_urlsafe(48)
|
|
require(token not in used, "sender_token_collision")
|
|
receipt.update(phase="custody_cas0", registry_version_before=version)
|
|
save()
|
|
result = data(
|
|
bao(
|
|
["write", "-format=json", lane["kv"], "-"],
|
|
payload={
|
|
"options": {"cas": 0},
|
|
"data": {"AUDIT_TOKEN": token, "CUSTODY_REQUEST": lane["ccr"]},
|
|
},
|
|
)
|
|
)
|
|
require(result["data"]["version"] == 1, "initial_version_mismatch")
|
|
receipt.update(custody_version=1, custody_request_id=result.get("request_id"))
|
|
save()
|
|
else:
|
|
require(
|
|
resume
|
|
and old["current_version"] == 1
|
|
and not old["versions"]["1"].get("destroyed")
|
|
and not old["versions"]["1"].get("deletion_time"),
|
|
"reviewed_version1_resume_required",
|
|
)
|
|
raw = data(bao(["read", "-format=json", lane["kv"]]))["data"]
|
|
token = custody_value(lane, raw)
|
|
receipt["custody_version_reused"] = 1
|
|
wanted["tokens"] = [token]
|
|
if existing:
|
|
require(existing == [wanted], "registered_sender_drift")
|
|
else:
|
|
require(token not in used, "sender_token_collision")
|
|
merged = json.loads(updated["senders.json"])
|
|
if not existing:
|
|
merged.append(wanted)
|
|
updated["senders.json"] = json.dumps(merged, separators=(",", ":"))
|
|
# The real receiver parser validates the complete candidate before CAS.
|
|
from audit_core.senders import SenderRegistry
|
|
|
|
SenderRegistry.from_env({"AUDIT_CORE_SENDERS": updated["senders.json"]})
|
|
receipt.update(phase="registry_cas", approval=approval_receipt)
|
|
save()
|
|
if updated != body:
|
|
result = data(
|
|
bao(
|
|
["write", "-format=json", REGISTRY, "-"],
|
|
payload={"options": {"cas": version}, "data": updated},
|
|
)
|
|
)
|
|
receipt["registry_request_id"] = result.get("request_id")
|
|
after_version, after_body, after_rows, _ = snapshot()
|
|
require(after_body == updated, "registry_readback_mismatch")
|
|
require(
|
|
all(row in after_rows for row in originals if row["name"] != "approval-engine"),
|
|
"other_sender_changed",
|
|
)
|
|
before_approval = next(row for row in originals if row["name"] == "approval-engine")
|
|
after_approval = next(row for row in after_rows if row["name"] == "approval-engine")
|
|
require(
|
|
{
|
|
k: v
|
|
for k, v in before_approval.items()
|
|
if k not in ("heartbeat_classes", "emission_cadence")
|
|
}
|
|
== {
|
|
k: v
|
|
for k, v in after_approval.items()
|
|
if k not in ("heartbeat_classes", "emission_cadence")
|
|
},
|
|
"approval_credentials_or_scope_changed",
|
|
)
|
|
receipt.update(
|
|
registry_version_after=after_version,
|
|
registry_readback_matches=True,
|
|
other_senders_preserved=True,
|
|
approval_credentials_preserved=True,
|
|
)
|
|
save()
|
|
return token, updated
|
|
|
|
|
|
COMPATIBILITY = """import inspect,json,sys
|
|
from audit_core.sqlite_backend import SQLiteAuditBackend
|
|
from audit_core.postgres_backend import PostgresAuditBackend
|
|
from audit_core.senders import SenderRegistry
|
|
from audit_core.cadence_registration import qonto_metadata
|
|
entry=json.load(sys.stdin)
|
|
entry['tokens']=['fixture-only-never-native']
|
|
r=SenderRegistry.from_env({'AUDIT_CORE_SENDERS':json.dumps([entry])})
|
|
a=r.authenticate('Bearer fixture-only-never-native')
|
|
proof={'cadence_registration':callable(qonto_metadata),
|
|
'sqlite_instance_filter':'stream_instance_id' in inspect.signature(SQLiteAuditBackend.event_counts).parameters,
|
|
'postgres_instance_filter':'stream_instance_id' in inspect.signature(PostgresAuditBackend.event_counts).parameters,
|
|
'synthetic_cadence_parse':a.emission_cadence is not None and a.emission_cadence.source=='qonto-assistant',
|
|
'synthetic_heartbeat_parse':dict(a.heartbeat_classes)==entry['heartbeat_classes'],
|
|
'synthetic_exact_scope':a.sources==frozenset(entry['sources']) and a.tenants==frozenset(entry['tenants']) and a.may_write and not a.may_read}
|
|
print(json.dumps(proof))
|
|
"""
|
|
|
|
|
|
def compatible(kube, image):
|
|
result = receiver_check(kube, image)
|
|
pod = data(
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"get",
|
|
"pods",
|
|
"-l",
|
|
"app.kubernetes.io/name=audit-core,app.kubernetes.io/component=receiver",
|
|
"-o",
|
|
"json",
|
|
]
|
|
)
|
|
)["items"][0]
|
|
proof = data(
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"exec",
|
|
"-i",
|
|
pod["metadata"]["name"],
|
|
"--",
|
|
"python",
|
|
"-c",
|
|
COMPATIBILITY,
|
|
],
|
|
payload=proposal_metadata(),
|
|
)
|
|
)
|
|
require(
|
|
set(proof)
|
|
== {
|
|
"cadence_registration",
|
|
"sqlite_instance_filter",
|
|
"postgres_instance_filter",
|
|
"synthetic_cadence_parse",
|
|
"synthetic_heartbeat_parse",
|
|
"synthetic_exact_scope",
|
|
}
|
|
and all(v is True for v in proof.values()),
|
|
"receiver_cadence_instance_support_required",
|
|
)
|
|
result["cadence_instance_capabilities"] = proof
|
|
return result
|
|
|
|
|
|
def reader_checks(kube, lane, token, receipt, save):
|
|
require(
|
|
role_matches(read_optional("auth/kubernetes/role/" + lane["role"]), lane),
|
|
"reader_role_drift",
|
|
)
|
|
jwt = (
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"external-secrets",
|
|
"create",
|
|
"token",
|
|
"external-secrets",
|
|
"--duration=10m",
|
|
]
|
|
)
|
|
.stdout.decode()
|
|
.strip()
|
|
)
|
|
auth = data(
|
|
bao(
|
|
["write", "-format=json", "auth/kubernetes/login", "-"],
|
|
payload={"role": lane["role"], "jwt": jwt},
|
|
)
|
|
)["auth"]
|
|
reader = auth["client_token"]
|
|
try:
|
|
require(
|
|
set(auth["token_policies"]) == {lane["policy"], "default"}
|
|
and 0 < auth["lease_duration"] <= 900,
|
|
"reader_scope_ttl_mismatch",
|
|
)
|
|
raw = data(bao(["read", "-format=json", lane["kv"]], token=reader))["data"]
|
|
require(custody_value(lane, raw) == token, "reader_value_mismatch")
|
|
for path in (
|
|
REGISTRY,
|
|
lane["metadata"],
|
|
"platform/data/workloads/approval-engine/audit-sender",
|
|
):
|
|
require_denied(
|
|
bao(["read", "-format=json", path], token=reader, allow_failure=True)
|
|
)
|
|
require_denied(
|
|
bao(
|
|
["list", "-format=json", "platform/metadata/workloads"],
|
|
token=reader,
|
|
allow_failure=True,
|
|
)
|
|
)
|
|
finally:
|
|
revoke(reader)
|
|
require_denied(
|
|
bao(["token", "lookup", "-format=json"], token=reader, allow_failure=True)
|
|
)
|
|
receipt["reader_exact_path_negatives_and_revocation"] = True
|
|
save()
|
|
# Exercise incorrect namespace and service account without creating workloads.
|
|
for namespace, account in (
|
|
("rapp-qonto", "default"),
|
|
("external-secrets", "default"),
|
|
):
|
|
jwt = (
|
|
command(
|
|
kube + ["-n", namespace, "create", "token", account, "--duration=10m"]
|
|
)
|
|
.stdout.decode()
|
|
.strip()
|
|
)
|
|
bad = bao(
|
|
["write", "-format=json", "auth/kubernetes/login", "-"],
|
|
payload={"role": lane["role"], "jwt": jwt},
|
|
allow_failure=True,
|
|
)
|
|
if bad.returncode == 0:
|
|
revoke(data(bad)["auth"]["client_token"])
|
|
raise LaneError("wrong_service_account_or_namespace_authenticated")
|
|
require(b"403" in bad.stderr, "wrong_identity_denial_inconclusive")
|
|
child = data(
|
|
bao(
|
|
[
|
|
"token",
|
|
"create",
|
|
"-format=json",
|
|
"-policy=" + lane["policy"],
|
|
"-policy=agent-high-risk-boundary",
|
|
"-no-default-policy",
|
|
"-ttl=60s",
|
|
]
|
|
)
|
|
)["auth"]["client_token"]
|
|
try:
|
|
for path in (lane["kv"], lane["metadata"]):
|
|
require_denied(
|
|
bao(["read", "-format=json", path], token=child, allow_failure=True)
|
|
)
|
|
finally:
|
|
revoke(child)
|
|
receipt.update(wrong_identity_denied=True, coding_agent_deny_wins=True)
|
|
save()
|
|
|
|
|
|
def preflight_projection(kube, objects):
|
|
"""Exact reviewed metadata template allowed; arbitrary template data refused."""
|
|
for obj in objects:
|
|
meta = obj["metadata"]
|
|
prefix = kube + (["-n", meta["namespace"]] if "namespace" in meta else [])
|
|
result = command(
|
|
prefix + ["get", obj["kind"], meta["name"], "-o", "json"],
|
|
allow_failure=True,
|
|
)
|
|
if result.returncode:
|
|
require(b"NotFound" in result.stderr, "projection_presence_unknown")
|
|
continue
|
|
live = data(result)
|
|
require(
|
|
all(live["spec"].get(k) == v for k, v in obj["spec"].items()),
|
|
"existing_projection_drift",
|
|
)
|
|
require(not live["spec"].get("dataFrom"), "unexpected_projection_datafrom")
|
|
template = live["spec"].get("target", {}).get("template")
|
|
wanted = obj["spec"].get("target", {}).get("template")
|
|
require(template == wanted, "unexpected_projection_template")
|
|
|
|
|
|
def deliver(kube, lane, token, body, receipt, save, image):
|
|
objects = reviewed_objects()
|
|
preflight_projection(kube, objects)
|
|
command(kube + ["apply", "-f", str(MANIFEST)])
|
|
command(
|
|
kube
|
|
+ [
|
|
"wait",
|
|
"--for=condition=Ready",
|
|
"clustersecretstore/" + lane["store"],
|
|
"--timeout=45s",
|
|
]
|
|
)
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
lane["namespace"],
|
|
"wait",
|
|
"--for=condition=Ready",
|
|
"externalsecret/" + lane["secret"],
|
|
"--timeout=45s",
|
|
]
|
|
)
|
|
es = data(
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
lane["namespace"],
|
|
"get",
|
|
"externalsecret",
|
|
lane["secret"],
|
|
"-o",
|
|
"json",
|
|
]
|
|
)
|
|
)
|
|
secret = data(
|
|
command(
|
|
kube
|
|
+ ["-n", lane["namespace"], "get", "secret", lane["secret"], "-o", "json"]
|
|
)
|
|
)
|
|
projection_matches(secret, es, lane, token)
|
|
receipt["projection_matches"] = True
|
|
save()
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"annotate",
|
|
"externalsecret",
|
|
"audit-core-senders",
|
|
"force-sync=" + str(time.time_ns()),
|
|
"--overwrite",
|
|
]
|
|
)
|
|
for _ in range(25):
|
|
projected = data(
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"get",
|
|
"secret",
|
|
"audit-core-senders",
|
|
"-o",
|
|
"json",
|
|
]
|
|
)
|
|
)
|
|
if json.loads(
|
|
base64.b64decode(projected["data"]["senders.json"])
|
|
) == json.loads(body["senders.json"]):
|
|
break
|
|
time.sleep(2)
|
|
else:
|
|
raise LaneError("receiver_registry_projection_mismatch")
|
|
receipt["receiver_registry_projection_matches"] = True
|
|
save()
|
|
command(kube + ["-n", "audit-core", "rollout", "restart", "deployment/audit-core"])
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"rollout",
|
|
"status",
|
|
"deployment/audit-core",
|
|
"--timeout=50s",
|
|
]
|
|
)
|
|
receipt["receiver_after"] = compatible(kube, image)
|
|
# Environment inherited by exec must equal the private projected registry.
|
|
pod = data(
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"get",
|
|
"pods",
|
|
"-l",
|
|
"app.kubernetes.io/name=audit-core,app.kubernetes.io/component=receiver",
|
|
"-o",
|
|
"json",
|
|
]
|
|
)
|
|
)["items"][0]
|
|
expected_hash = hashlib.sha256(
|
|
json.dumps(json.loads(body["senders.json"]), sort_keys=True).encode()
|
|
).hexdigest()
|
|
probe = 'import os,json,hashlib;print(json.dumps({"sha256":hashlib.sha256(json.dumps(json.loads(os.environ["AUDIT_CORE_SENDERS"]),sort_keys=True).encode()).hexdigest()}))'
|
|
proof = data(
|
|
command(
|
|
kube
|
|
+ [
|
|
"-n",
|
|
"audit-core",
|
|
"exec",
|
|
pod["metadata"]["name"],
|
|
"--",
|
|
"python",
|
|
"-c",
|
|
probe,
|
|
]
|
|
)
|
|
)
|
|
require(proof == {"sha256": expected_hash}, "receiver_runtime_registry_mismatch")
|
|
# Do not publish a low-entropy token hash or full registry hash in receipts.
|
|
receipt.update(receiver_reloaded=True, receiver_runtime_registry_matches=True)
|
|
save()
|
|
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument("action", choices=["plan", "apply"])
|
|
p.add_argument("--kubeconfig")
|
|
p.add_argument("--server")
|
|
p.add_argument("--expected-receiver-image")
|
|
p.add_argument("--confirm-packet-sha256")
|
|
p.add_argument("--receipt", type=Path)
|
|
p.add_argument("--resume", action="store_true")
|
|
a = p.parse_args()
|
|
receipt = {
|
|
"schema_version": "platform.cadence-custody/v1",
|
|
"status": "refused",
|
|
"credential_values_emitted": False,
|
|
"started_at": datetime.now(timezone.utc).isoformat(),
|
|
}
|
|
fd = None
|
|
try:
|
|
current = packet()
|
|
if a.action == "plan":
|
|
current["qonto_sender_metadata"] = proposal_metadata()
|
|
print(json.dumps(current, indent=2))
|
|
return 0
|
|
lane = contracts(approved=True)
|
|
require(
|
|
a.confirm_packet_sha256 == current["sha256"],
|
|
"exact_reviewed_packet_required",
|
|
)
|
|
require(
|
|
a.kubeconfig and a.server and a.expected_receiver_image and a.receipt,
|
|
"explicit_runtime_inputs_required",
|
|
)
|
|
require(
|
|
Path.home().parent.name == ".warden-attended-login"
|
|
and not os.getenv("BAO_TOKEN")
|
|
and not os.getenv("VAULT_TOKEN")
|
|
and not os.getenv("OPENBAO_TOKEN"),
|
|
"attended_warden_envelope_required",
|
|
)
|
|
fd = os.open(
|
|
a.receipt, os.O_RDWR | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600
|
|
)
|
|
|
|
def save():
|
|
os.lseek(fd, 0, os.SEEK_SET)
|
|
os.ftruncate(fd, 0)
|
|
os.write(fd, (json.dumps(receipt, indent=2) + "\n").encode())
|
|
os.fsync(fd)
|
|
|
|
receipt["packet_sha256"] = current["sha256"]
|
|
save()
|
|
kube = [
|
|
"kubectl",
|
|
"--kubeconfig",
|
|
a.kubeconfig,
|
|
"--server",
|
|
a.server,
|
|
"--request-timeout=20s",
|
|
]
|
|
receipt["receiver_before"] = compatible(kube, a.expected_receiver_image)
|
|
save()
|
|
identity = data(bao(["token", "lookup", "-format=json"]))["data"]
|
|
require(
|
|
"platform-admin" in identity["policies"]
|
|
and "root" not in identity["policies"],
|
|
"attended_platform_admin_required",
|
|
)
|
|
command(kube + ["get", "namespace", "rapp-qonto", "-o", "name"])
|
|
# Validate registry, declarations, lane and projection before metadata writes.
|
|
_, body, _, _ = snapshot()
|
|
prepare, _ = builder()
|
|
prepare(body, APPROVAL.read_bytes(), json.loads(PROPOSAL.read_text()))
|
|
proposal_metadata()
|
|
preflight_projection(kube, reviewed_objects())
|
|
preflight_custody(lane, resume=a.resume)
|
|
receipt["phase"] = "metadata"
|
|
save()
|
|
prepare_metadata([lane])
|
|
token, updated = merge_and_seed(lane, receipt, save, resume=a.resume)
|
|
receipt["phase"] = "reader_verification"
|
|
save()
|
|
reader_checks(kube, lane, token, receipt, save)
|
|
receipt["phase"] = "delivery"
|
|
save()
|
|
deliver(kube, lane, token, updated, receipt, save, a.expected_receiver_image)
|
|
receipt.update(
|
|
status="delivered_pending_native_producer_and_independent_acceptance",
|
|
completed_at=datetime.now(timezone.utc).isoformat(),
|
|
)
|
|
save()
|
|
return 0
|
|
except Exception as exc:
|
|
receipt.update(
|
|
status="refused",
|
|
error=str(exc)
|
|
if isinstance(exc, LaneError)
|
|
else "contained_operation_failed",
|
|
)
|
|
if fd is not None:
|
|
save()
|
|
elif a.action == "plan":
|
|
print(json.dumps(receipt, indent=2))
|
|
return 1
|
|
finally:
|
|
if fd is not None:
|
|
os.close(fd)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|