3"""Authenticate a mutating role payload against fleet and wrapper authority."""
5from __future__
import annotations
10from pathlib
import Path
11from unittest
import mock
13sys.path.insert(0, str(Path(__file__).resolve().parent))
15import bench_lock_capability
as blc
16import fleet_model
as fm
19 "dev_box":
"dev_box_hil_runner_",
20 "hil_bench":
"hil_bench_",
24class TransactionError(ValueError):
25 """A role payload or controller capability was not fleet-authenticated."""
28def expected_payload(host_name: str, role: str) -> dict[str, object]:
29 """Return the exact fleet-derived policy values owned by one role."""
31 problems = fm.validate(data)
33 raise TransactionError(
"fleet declaration is invalid: " +
"; ".join(problems))
34 host = data.get(
"hosts", {}).get(host_name)
35 if not isinstance(host, dict)
or host.get(
"class") != role:
36 msg = f
"{host_name!r} is not the declared {role} host"
37 raise TransactionError(msg)
38 prefix = ROLE_PREFIXES[role]
41 for key, value
in fm.role_vars(data, host_name, host).items()
42 if key.startswith(prefix)
45 msg = f
"fleet declaration produced no {role} policy payload"
46 raise TransactionError(msg)
54 capability: blc.Capability,
56 """Require exact fleet policy plus the live controller wrapper process."""
58 actual = json.loads(raw_payload)
59 except json.JSONDecodeError
as exc:
60 msg =
"role payload is not valid JSON"
61 raise TransactionError(msg)
from exc
62 expected = expected_payload(host_name, role)
63 if type(actual)
is not dict
or actual != expected:
64 msg =
"role payload differs from the immutable fleet declaration"
65 raise TransactionError(msg)
67 blc.authenticate(capability, fm.REPO_ROOT)
68 except blc.CapabilityError
as exc:
69 msg = f
"controller bench capability failed: {exc}"
70 raise TransactionError(msg)
from exc
73def run_selftest() -> list[str]:
74 """Prove each role authority class and the controller capability fail closed."""
76 "dev_box_hil_runner_service":
"ra8-hil-runner.service",
77 "dev_box_hil_runner_root":
"/opt/ra8-hil-runner",
78 "dev_box_hil_runner_url":
"https://example.invalid/runner.tgz",
79 "dev_box_hil_runner_sha256":
"a" * 64,
80 "dev_box_hil_runner_bench_alias":
"star",
82 capability = blc.Capability(
"0123456789abcdef", 41, 1000,
"star.local")
83 failures: list[str] = []
84 module = sys.modules[__name__]
86 mock.patch.object(module,
"expected_payload", return_value=expected),
87 mock.patch.object(blc,
"authenticate", return_value=
None),
89 authenticate(
"dev",
"dev_box", json.dumps(expected), capability)
91 changed = dict(expected)
92 changed[key] =
"attacker-controlled"
94 authenticate(
"dev",
"dev_box", json.dumps(changed), capability)
95 except TransactionError:
98 failures.append(f
"role payload override escaped: {key}")
100 mock.patch.object(module,
"expected_payload", return_value=expected),
101 mock.patch.object(blc,
"authenticate", side_effect=blc.CapabilityError(
"forged")),
104 authenticate(
"dev",
"dev_box", json.dumps(expected), capability)
105 except TransactionError:
108 failures.append(
"direct role payload passed without controller capability")
113 """Parse one Ansible-local authentication request."""
114 if sys.argv[1:] == [
"--selftest"]:
115 failures = run_selftest()
116 for failure
in failures:
117 print(f
"fleet-transaction-auth selftest: {failure}", file=sys.stderr)
118 return 1
if failures
else 0
119 parser = argparse.ArgumentParser(description=__doc__)
120 parser.add_argument(
"host")
121 parser.add_argument(
"role", choices=tuple(ROLE_PREFIXES))
122 parser.add_argument(
"payload_json")
123 parser.add_argument(
"lock_id")
124 parser.add_argument(
"holder_pid", type=int)
125 parser.add_argument(
"holder_start_ticks", type=int)
126 parser.add_argument(
"holder_target")
127 args = parser.parse_args()
128 capability = blc.Capability(
131 args.holder_start_ticks,
135 authenticate(args.host, args.role, args.payload_json, capability)
136 except (OSError, TransactionError)
as exc:
137 print(f
"fleet-transaction-auth: {exc}", file=sys.stderr)
139 print(
"fleet-transaction-auth: authenticated")
143if __name__ ==
"__main__":
144 raise SystemExit(
main())
void main(void)
The application entry point Reset_Handler hands control to.