#!/usr/bin/env python3 """Fail closed unless the live router reads the operator's revocation file. Run this on the Docker host before minting or revoking a Fleet Ops demo token. It reads Docker mount metadata and file metadata only; it never reads a token, signing key, or revocation-file contents. """ from __future__ import annotations import argparse import json import os import stat import subprocess import sys from pathlib import Path from typing import Any ROUTER_DIRECTORY = "/etc/itworx" ROUTER_DESTINATION = ROUTER_DIRECTORY + "/demo-token-revocations.json" def validate_mount(mounts: list[dict[str, Any]], expected_host_file: Path) -> dict[str, object]: if not expected_host_file.is_absolute(): raise ValueError("expected host file must be an absolute path") matching = [mount for mount in mounts if mount.get("Destination") == ROUTER_DIRECTORY] if len(matching) != 1: raise ValueError("router must have exactly one demo-token revocation mount") source = Path(str(matching[0].get("Source", ""))) if source != expected_host_file.parent: raise ValueError("router revocation directory mount does not match the operator host file") try: metadata = expected_host_file.stat() except OSError as exc: raise ValueError("operator revocation file is missing") from exc if not stat.S_ISREG(metadata.st_mode): raise ValueError("operator revocation file is not a regular file") mode = stat.S_IMODE(metadata.st_mode) # The operator command runs on Tower (Linux). Windows does not expose # NTFS ACLs through POSIX mode bits, so local unit tests cannot make this # assertion meaningful there. if os.name != "nt" and mode != 0o600: raise ValueError("operator revocation file mode must be exactly 0600") if matching[0].get("RW") is not False: raise ValueError("router revocation mount must be read-only") return { "status": "pass", "containerDestination": ROUTER_DESTINATION, "hostFile": str(expected_host_file), "mode": f"{mode:04o}", "readOnly": True, } def inspect_mounts(router_container: str) -> list[dict[str, Any]]: result = subprocess.run( # noqa: S603 ["docker", "inspect", router_container, "--format", "{{json .Mounts}}"], # noqa: S607 capture_output=True, text=True, check=False, ) if result.returncode != 0: raise ValueError("could not inspect router container") try: mounts = json.loads(result.stdout) except json.JSONDecodeError as exc: raise ValueError("router mount metadata is invalid") from exc if not isinstance(mounts, list) or not all(isinstance(item, dict) for item in mounts): raise ValueError("router mount metadata is invalid") return mounts def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--router-container", required=True) parser.add_argument("--expected-host-file", type=Path, required=True) args = parser.parse_args() try: report = validate_mount(inspect_mounts(args.router_container), args.expected_host_file) except ValueError as exc: print(json.dumps({"status": "fail", "reason": str(exc)}, sort_keys=True)) return 1 print(json.dumps(report, sort_keys=True)) return 0 if __name__ == "__main__": sys.exit(main())