railiance-platform/scripts/prove-backup-publisher-prefixes.py
codex d217c806ca
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Add the two publisher prefixes to the backup bucket policy.
The live policy keeps the original backup application. Each new key can abort only inside its own prefix.

Assistant: grok
Assistant-Session: 01a11cdb-f9fe-7011-947c-936e72007e54
2026-10-08 23:42:25 +02:00

98 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""Prove each new publisher can abort its own upload and cannot write elsewhere.
No completed object is left behind. The receipt contains status codes only.
"""
from __future__ import annotations
import json
import subprocess
from pathlib import Path
import boto3
from botocore.exceptions import ClientError
RECEIPT = Path("/home/worsch/.local/state/railiance/receipts/backup-publisher-prefix-proof.json")
BUCKET = "railiance-platform-pg-backup"
PROFILES = {
"coulomb-social": "platform-pg/application-archives/coulomb-social/.policy-probe",
"email-connect": "platform-pg/application-archives/email-connect/.policy-probe",
}
FOREIGN = "platform-pg/apps-pg/.policy-probe"
def kv(path):
proc = subprocess.run(["bao", "kv", "get", "-format=json", path], capture_output=True)
if proc.returncode:
raise SystemExit(81)
return json.loads(proc.stdout)["data"]["data"]
def client(values):
return boto3.client(
"s3",
region_name="nl-ams",
endpoint_url="https://s3.nl-ams.scw.cloud",
aws_access_key_id=values["AWS_ACCESS_KEY_ID"],
aws_secret_access_key=values["AWS_SECRET_ACCESS_KEY"],
)
def code(exc):
return exc.response.get("Error", {}).get("Code", "error")
def attempt(action):
try:
action()
return "ok"
except ClientError as exc:
return code(exc)
def open_or_deny(remote, key):
try:
upload = remote.create_multipart_upload(Bucket=BUCKET, Key=key)
except ClientError as exc:
return code(exc)
remote.abort_multipart_upload(Bucket=BUCKET, Key=key, UploadId=upload["UploadId"])
return "opened_then_aborted"
def main():
rows = []
for name, own_key in PROFILES.items():
remote = client(kv(f"platform/workloads/{name}/backup-primary"))
own = "error"
try:
upload = remote.create_multipart_upload(Bucket=BUCKET, Key=own_key)
remote.abort_multipart_upload(
Bucket=BUCKET, Key=own_key, UploadId=upload["UploadId"]
)
own = "aborted"
except ClientError as exc:
own = code(exc)
sibling_key = next(key for other, key in PROFILES.items() if other != name)
foreign = open_or_deny(remote, FOREIGN)
sibling = open_or_deny(remote, sibling_key)
delete = attempt(lambda key=own_key: remote.delete_object(Bucket=BUCKET, Key=key))
rows.append({
"application": name,
"own_multipart": own,
"foreign_multipart": foreign,
"sibling_multipart": sibling,
"delete_object": delete,
})
RECEIPT.parent.mkdir(mode=0o700, exist_ok=True)
RECEIPT.write_text(json.dumps({"proofs": rows}, indent=2) + "\n")
RECEIPT.chmod(0o600)
if __name__ == "__main__":
try:
main()
except SystemExit:
raise
except Exception:
raise SystemExit(82) from None