89 lines
3 KiB
Python
89 lines
3 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Accept one SSH-authenticated native projection state transition."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import pathlib
|
||
|
|
import secrets
|
||
|
|
import shlex
|
||
|
|
import tempfile
|
||
|
|
import time
|
||
|
|
|
||
|
|
|
||
|
|
SCHEMA = "guanghu.native-public-projection-state/v1"
|
||
|
|
|
||
|
|
|
||
|
|
def parse_command(command: str) -> tuple[str, str, str, str, str]:
|
||
|
|
fields = shlex.split(command)
|
||
|
|
if len(fields) != 5 or fields[0] not in {"ready", "dormant"}:
|
||
|
|
raise ValueError("exact ready or dormant projection command required")
|
||
|
|
return fields[0], fields[1], fields[2], fields[3], fields[4]
|
||
|
|
|
||
|
|
|
||
|
|
def atomic_write(path: pathlib.Path, payload: dict[str, object]) -> None:
|
||
|
|
if not path.is_absolute() or not path.parent.is_dir():
|
||
|
|
raise ValueError("state file must have an existing absolute parent")
|
||
|
|
encoded = (json.dumps(payload, sort_keys=True, separators=(",", ":")) + "\n").encode()
|
||
|
|
descriptor, temporary_name = tempfile.mkstemp(
|
||
|
|
dir=path.parent,
|
||
|
|
prefix=f".{path.name}.",
|
||
|
|
)
|
||
|
|
temporary = pathlib.Path(temporary_name)
|
||
|
|
try:
|
||
|
|
os.fchmod(descriptor, 0o640)
|
||
|
|
with os.fdopen(descriptor, "wb") as output:
|
||
|
|
output.write(encoded)
|
||
|
|
output.flush()
|
||
|
|
os.fsync(output.fileno())
|
||
|
|
os.replace(temporary, path)
|
||
|
|
finally:
|
||
|
|
temporary.unlink(missing_ok=True)
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
parser = argparse.ArgumentParser()
|
||
|
|
parser.add_argument("--state-file", required=True)
|
||
|
|
parser.add_argument("--expected-node", required=True)
|
||
|
|
parser.add_argument("--expected-relay", required=True)
|
||
|
|
parser.add_argument("--expected-candidate-sha256", required=True)
|
||
|
|
parser.add_argument("--expected-source-commit", required=True)
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
try:
|
||
|
|
action, node, candidate_sha, source_commit, relay = parse_command(
|
||
|
|
os.environ.get("SSH_ORIGINAL_COMMAND", "")
|
||
|
|
)
|
||
|
|
except ValueError as error:
|
||
|
|
raise SystemExit(str(error)) from error
|
||
|
|
expected = (
|
||
|
|
args.expected_node,
|
||
|
|
args.expected_candidate_sha256,
|
||
|
|
args.expected_source_commit,
|
||
|
|
args.expected_relay,
|
||
|
|
)
|
||
|
|
if (node, candidate_sha, source_commit, relay) != expected:
|
||
|
|
raise SystemExit("projection command does not match the pinned deployment")
|
||
|
|
if len(candidate_sha) != 64 or len(source_commit) != 40:
|
||
|
|
raise SystemExit("projection hashes are malformed")
|
||
|
|
|
||
|
|
payload: dict[str, object] = {
|
||
|
|
"schema": SCHEMA,
|
||
|
|
"status": "READY_NATIVE_RESIDENT" if action == "ready" else "DORMANT",
|
||
|
|
"node_id": node,
|
||
|
|
"relay_node": relay,
|
||
|
|
"candidate_sha256": candidate_sha,
|
||
|
|
"source_commit": source_commit,
|
||
|
|
"accepted_at_epoch": time.time(),
|
||
|
|
"receipt_nonce": secrets.token_hex(16),
|
||
|
|
"transport_authentication": "SSH_FORCED_COMMAND",
|
||
|
|
}
|
||
|
|
atomic_write(pathlib.Path(args.state_file), payload)
|
||
|
|
print(json.dumps(payload, sort_keys=True, separators=(",", ":")))
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|