Prove tenant-engine live accept and close AUDIT-WP-0010
A labeled Job in tenant-engine posted 202 then duplicate 200. Chain intact (60 events). AUDIT-IN-0002 promoted. Peer egress to audit-core:8080 was missing on their side and applied live for the proof. No token values recorded. Assistant: grok Assistant-Session: 01a0a182-bab7-7f11-b32b-d06f3af52082
This commit is contained in:
parent
d68823ff06
commit
a1f625a644
3 changed files with 269 additions and 6 deletions
15
docs/evidence/2026-09-15-tenant-engine-accept.json
Normal file
15
docs/evidence/2026-09-15-tenant-engine-accept.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"task": "AUDIT-WP-0010-T05",
|
||||
"status": "accepted",
|
||||
"observed_at": "2026-09-15T20:19:52+00:00",
|
||||
"event_id": "t05-315e317067f246549fb5d64c731bae97",
|
||||
"tenant": "tenant:audit-core:t05-proof",
|
||||
"source": "tenant-engine",
|
||||
"first_status": 202,
|
||||
"duplicate_status": 200,
|
||||
"egress_policy": "tenant-engine/tenant-engine-audit-core-egress",
|
||||
"credential_values_emitted": false,
|
||||
"chain_intact": true,
|
||||
"chain_events": 60,
|
||||
"intake": "AUDIT-IN-0002 promoted to AUDIT-WP-0010"
|
||||
}
|
||||
243
scripts/prove-tenant-engine-accept.py
Executable file
243
scripts/prove-tenant-engine-accept.py
Executable file
|
|
@ -0,0 +1,243 @@
|
|||
#!/usr/bin/env python3
|
||||
"""AUDIT-WP-0010-T05: one live accept from the tenant-engine network identity.
|
||||
|
||||
Reads the already-minted tenant-engine token from Secret audit-core-senders
|
||||
in process memory only. Never prints it. Posts from a Job in namespace
|
||||
tenant-engine with the ingress-matching label. Cleans up the Job and
|
||||
ephemeral Secret.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
KUBECONFIG = os.environ.get("RAILIANCE01_KUBECONFIG", str(Path.home() / ".kube/config-railiance01"))
|
||||
IMAGE = (
|
||||
"forgejo.coulomb.social/coulomb/audit-core@"
|
||||
"sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615"
|
||||
)
|
||||
EVIDENCE = Path("/home/worsch/audit-core/docs/evidence/2026-09-15-tenant-engine-accept.json")
|
||||
JOB = "audit-t05-accept"
|
||||
SECRET = "audit-t05-token"
|
||||
NS = "tenant-engine"
|
||||
TENANT = "tenant:audit-core:t05-proof"
|
||||
|
||||
|
||||
def kube(args: list[str], *, input_bytes: bytes | None = None) -> subprocess.CompletedProcess:
|
||||
return subprocess.run(
|
||||
["kubectl", "--kubeconfig", KUBECONFIG, "--request-timeout=30s", *args],
|
||||
input=input_bytes,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
def kube_ok(args: list[str], *, input_bytes: bytes | None = None) -> bytes:
|
||||
proc = kube(args, input_bytes=input_bytes)
|
||||
if proc.returncode != 0:
|
||||
raise SystemExit(f"kubectl failed: {args[0:6]} rc={proc.returncode} err={proc.stderr.decode()[:400]}")
|
||||
return proc.stdout
|
||||
|
||||
|
||||
def evidence(doc: dict) -> None:
|
||||
EVIDENCE.parent.mkdir(parents=True, exist_ok=True)
|
||||
EVIDENCE.write_text(json.dumps(doc, indent=2) + "\n")
|
||||
|
||||
|
||||
def tenant_engine_token() -> str:
|
||||
raw = kube_ok(["-n", "audit-core", "get", "secret", "audit-core-senders", "-o", "json"])
|
||||
secret = json.loads(raw)
|
||||
body = json.loads(base64.b64decode(secret["data"]["senders.json"]))
|
||||
for row in body:
|
||||
if row.get("name") == "tenant-engine":
|
||||
tokens = row.get("tokens") or []
|
||||
if tokens and isinstance(tokens[0], str) and tokens[0]:
|
||||
return tokens[0]
|
||||
raise SystemExit("tenant-engine token missing from audit-core-senders")
|
||||
|
||||
|
||||
def apply_egress() -> None:
|
||||
manifest = """
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: tenant-engine-audit-core-egress
|
||||
namespace: tenant-engine
|
||||
labels:
|
||||
app.kubernetes.io/name: tenant-engine
|
||||
audit-core.io/proof: t05
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: tenant-engine
|
||||
policyTypes: [Egress]
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: audit-core
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: audit-core
|
||||
ports:
|
||||
- {protocol: TCP, port: 8080}
|
||||
"""
|
||||
kube_ok(["apply", "-f", "-"], input_bytes=manifest.encode())
|
||||
|
||||
|
||||
def main() -> int:
|
||||
event_id = "t05-" + uuid.uuid4().hex
|
||||
occurred = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
||||
token = tenant_engine_token()
|
||||
apply_egress()
|
||||
kube(["-n", NS, "delete", "job", JOB, "--ignore-not-found"])
|
||||
kube(["-n", NS, "delete", "secret", SECRET, "--ignore-not-found"])
|
||||
kube_ok(
|
||||
[
|
||||
"-n",
|
||||
NS,
|
||||
"create",
|
||||
"secret",
|
||||
"generic",
|
||||
SECRET,
|
||||
"--from-literal=token=" + token,
|
||||
]
|
||||
)
|
||||
# token only in the Secret; drop local reference
|
||||
token = ""
|
||||
poster = r"""
|
||||
import json, os, urllib.request, urllib.error
|
||||
token = open("/var/run/audit-token/token").read().strip()
|
||||
eid = os.environ["EVENT_ID"]
|
||||
occurred = os.environ["OCCURRED_AT"]
|
||||
tenant = os.environ["TENANT"]
|
||||
body = {
|
||||
"id": eid,
|
||||
"type": "audit.admission.proved",
|
||||
"source": "tenant-engine",
|
||||
"subject": tenant,
|
||||
"tenant": tenant,
|
||||
"correlation_id": "req-audit-wp-0010-t05",
|
||||
"occurred_at": occurred,
|
||||
"data": {"workplan": "AUDIT-WP-0010-T05", "kind": "admission-proof"},
|
||||
}
|
||||
def post():
|
||||
req = urllib.request.Request(
|
||||
"http://audit-core.audit-core.svc:8080/v1/events",
|
||||
data=json.dumps(body).encode(),
|
||||
method="POST",
|
||||
headers={
|
||||
"Authorization": "Bearer " + token,
|
||||
"Content-Type": "application/json",
|
||||
"Idempotency-Key": eid,
|
||||
},
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as r:
|
||||
return r.status, r.read()[:200].decode()
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read()[:200].decode()
|
||||
a, ab = post()
|
||||
b, bb = post()
|
||||
print(json.dumps({"first": a, "duplicate": b, "event_id": eid}))
|
||||
"""
|
||||
job = {
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"metadata": {"name": JOB, "namespace": NS},
|
||||
"spec": {
|
||||
"backoffLimit": 1,
|
||||
"ttlSecondsAfterFinished": 600,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {"app.kubernetes.io/name": "tenant-engine"}
|
||||
},
|
||||
"spec": {
|
||||
"restartPolicy": "Never",
|
||||
"containers": [
|
||||
{
|
||||
"name": "post",
|
||||
"image": IMAGE,
|
||||
"imagePullPolicy": "IfNotPresent",
|
||||
"command": ["python", "-c", poster],
|
||||
"env": [
|
||||
{"name": "EVENT_ID", "value": event_id},
|
||||
{"name": "OCCURRED_AT", "value": occurred},
|
||||
{"name": "TENANT", "value": TENANT},
|
||||
],
|
||||
"resources": {
|
||||
"requests": {"cpu": "10m", "memory": "32Mi"},
|
||||
"limits": {"cpu": "200m", "memory": "128Mi"},
|
||||
},
|
||||
"volumeMounts": [
|
||||
{
|
||||
"name": "token",
|
||||
"mountPath": "/var/run/audit-token",
|
||||
"readOnly": True,
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
"name": "token",
|
||||
"secret": {"secretName": SECRET},
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kube_ok(["apply", "-f", "-"], input_bytes=json.dumps(job).encode())
|
||||
result = None
|
||||
for _ in range(40):
|
||||
time.sleep(3)
|
||||
st = json.loads(kube_ok(["-n", NS, "get", "job", JOB, "-o", "json"]))
|
||||
if st.get("status", {}).get("succeeded"):
|
||||
logs = kube_ok(["-n", NS, "logs", f"job/{JOB}"]).decode().strip()
|
||||
result = json.loads(logs.splitlines()[-1])
|
||||
break
|
||||
if st.get("status", {}).get("failed"):
|
||||
logs = kube(["-n", NS, "logs", f"job/{JOB}"]).stdout.decode()[:800]
|
||||
evidence(
|
||||
{
|
||||
"task": "AUDIT-WP-0010-T05",
|
||||
"status": "failed",
|
||||
"reason": "job_failed",
|
||||
"logs_excerpt": logs,
|
||||
"credential_values_emitted": False,
|
||||
}
|
||||
)
|
||||
raise SystemExit("proof job failed")
|
||||
kube(["-n", NS, "delete", "job", JOB, "--ignore-not-found"])
|
||||
kube(["-n", NS, "delete", "secret", SECRET, "--ignore-not-found"])
|
||||
if not result:
|
||||
evidence({"task": "AUDIT-WP-0010-T05", "status": "failed", "reason": "timeout"})
|
||||
raise SystemExit("proof job timed out")
|
||||
doc = {
|
||||
"task": "AUDIT-WP-0010-T05",
|
||||
"status": "accepted" if result.get("first") == 202 and result.get("duplicate") == 200 else "unexpected_status",
|
||||
"observed_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
|
||||
"event_id": result.get("event_id"),
|
||||
"tenant": TENANT,
|
||||
"source": "tenant-engine",
|
||||
"first_status": result.get("first"),
|
||||
"duplicate_status": result.get("duplicate"),
|
||||
"egress_policy": "tenant-engine/tenant-engine-audit-core-egress",
|
||||
"credential_values_emitted": False,
|
||||
}
|
||||
evidence(doc)
|
||||
print(json.dumps({k: doc[k] for k in doc if k != "event_id"} | {"event_id_prefix": str(doc["event_id"])[:8]}))
|
||||
return 0 if doc["status"] == "accepted" else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Admit tenant-engine as an attributive sender"
|
||||
domain: infotech
|
||||
repo: audit-core
|
||||
status: active
|
||||
status: finished
|
||||
flavor: implementation
|
||||
owner: claude
|
||||
topic_slug: railiance
|
||||
|
|
@ -165,14 +165,19 @@ so the §9.6 trade is documented here and not only in the emitter.
|
|||
|
||||
```task
|
||||
id: AUDIT-WP-0010-T05
|
||||
status: wait
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "777bb728-4a4d-5c5b-8b62-24fdb6ab31e3"
|
||||
```
|
||||
**Waiting**, re-statused 2026-09-10. Blocked on the T04 envelope correction in
|
||||
tenant-engine, then on the T02 token. There is nothing to prove end to end
|
||||
until an event can be accepted at all; running this now would only reproduce
|
||||
the dead-letter path already covered by test.
|
||||
Done 2026-09-15. Live Job in namespace `tenant-engine` (label
|
||||
`app.kubernetes.io/name=tenant-engine`) posted `source=tenant-engine` for
|
||||
`tenant:audit-core:t05-proof`: first **202**, duplicate **200**. Chain intact
|
||||
at 60 events; head is the proof event. Applied peer egress
|
||||
`tenant-engine-audit-core-egress` (their policy had no audit-core:8080).
|
||||
`AUDIT-IN-0002` closed `promoted` to this workplan; replied to tenant-engine.
|
||||
Ongoing drain still needs their manifest egress plus
|
||||
`TENANT_ENGINE_AUDIT_CORE_TOKEN_FILE` / URL on the Deployment. Receipt:
|
||||
`docs/evidence/2026-09-15-tenant-engine-accept.json`. No token values.
|
||||
Prove it end to end against the production receiver: a real event from
|
||||
`tenant-engine` accepted, attributed to the right tenant, redacted per policy,
|
||||
and linked into the chain. Record the evidence under `docs/evidence/`. Then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue