atlas-iac/testing/tests/test_hermes_node_account_privilege_audit.py
Hermes Agent 7a55b259bf hermes: add the fenced three-node distributed execution pool
Three fenced worker Pods claim Hermes Kanban runs through a coordinator that
owns every state transition, with per-ordinal HMAC authority, a mediated
broker-only SCM path, and durable per-ordinal workspaces.

Content is the reviewed head of PR #18 (689bcb6e) with PR 16's and PR 19's
contributions removed: they were merged in only to validate co-existence and are
not prerequisites, so this branch no longer carries them as ancestors. Only PR 14
and PR 15 remain, because the broker boundary and the cli_lane_* decomposition
are load-bearing for two of the fixed P0 boundaries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 16:31:15 +00:00

201 lines
6.8 KiB
Python

"""Host privilege-policy audits for the Hermes node SSH identity."""
from __future__ import annotations
import shutil
import subprocess
from pathlib import Path
import pytest
from testing.tests.test_hermes_node_account_support import _fixture
@pytest.mark.parametrize(
"database,record",
[
("group", "disk:x:6:atlas,hermes-agent\n"),
("gshadow", "disk:!:hermes-agent:atlas\n"),
("gshadow", "disk:!::atlas,hermes-agent\n"),
],
)
def test_unexpected_group_or_gshadow_membership_fails_before_writes(
tmp_path: Path, monkeypatch, database: str, record: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
path = module.HOST_ETC / database
path.write_text(
"\n".join(
line
for line in originals[database].splitlines()
if not line.startswith("disk:")
)
+ "\n"
+ record,
encoding="utf-8",
)
before = {name: (module.HOST_ETC / name).read_bytes() for name in originals}
with pytest.raises(module.HardeningError, match="group access"):
module.reconcile(public_key)
assert {name: (module.HOST_ETC / name).read_bytes() for name in originals} == before
@pytest.mark.parametrize(
"relative,value",
[
("sudoers.d/hermes", "hermes-agent ALL=(ALL:ALL) NOPASSWD: ALL\n"),
("sudoers", "ALL ALL=(ALL:ALL) ALL\n"),
(
"polkit-1/rules.d/90-root.rules",
'if (subject.user == "hermes-agent") return polkit.Result.YES;\n',
),
(
"polkit-1/rules.d/90-root.rules",
"unix-user:* org.freedesktop.policykit.exec polkit.Result.YES\n",
),
],
)
def test_sudo_and_root_equivalent_polkit_authority_fail_closed(
tmp_path: Path, monkeypatch, relative: str, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
policy = module.HOST_ETC / relative
policy.parent.mkdir(parents=True, exist_ok=True)
policy.write_text(value, encoding="utf-8")
with pytest.raises(module.HardeningError, match="sudo|polkit"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
def test_external_group_authority_source_is_rejected(tmp_path: Path, monkeypatch):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
(module.HOST_ETC / "nsswitch.conf").write_text("passwd: files\ngroup: files ldap\n")
with pytest.raises(module.HardeningError, match="external group"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
@pytest.mark.parametrize("database", ["passwd", "group", "initgroups", "shadow"])
def test_external_identity_authority_sources_fail_closed(
tmp_path: Path, monkeypatch, database: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
(module.HOST_ETC / "nsswitch.conf").write_text(
f"passwd: files\n{database}: files ldap\n", encoding="utf-8"
)
with pytest.raises(module.HardeningError, match="external group/account"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
@pytest.mark.parametrize(
"value",
[
"#1200 ALL=(ALL:ALL) NOPASSWD: ALL\n",
"User_Alias HERMES_IDS = #1200\nHERMES_IDS ALL=(ALL:ALL) NOPASSWD: ALL\n",
"User_Alias HERMES_GROUP = %#1200\nHERMES_GROUP ALL=(ALL:ALL) NOPASSWD: ALL\n",
],
)
def test_visudo_valid_numeric_and_alias_grants_fail_closed(
tmp_path: Path, monkeypatch, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
monkeypatch.setattr(module, "ACCOUNT_UID", 1200)
policy = module.HOST_ETC / "sudoers"
policy.write_text(value, encoding="utf-8")
visudo = shutil.which("visudo")
if visudo is None:
pytest.skip("visudo is not installed in this test environment")
validation = subprocess.run(
[visudo, "-c", "-f", str(policy)],
check=False,
capture_output=True,
text=True,
)
assert validation.returncode == 0, validation.stderr
with pytest.raises(module.HardeningError, match="sudo authority"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
def test_sudo_include_is_bounded_to_the_audited_standard_directory(
tmp_path: Path, monkeypatch
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
sudoers = module.HOST_ETC / "sudoers"
sudoers.write_text("#includedir /opt/external-sudoers\n", encoding="utf-8")
with pytest.raises(module.HardeningError, match="unaudited authority source"):
module.reconcile(public_key)
sudoers.write_text("#includedir /etc/sudoers.d\n", encoding="utf-8")
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text().startswith(original)
for name, original in originals.items()
)
@pytest.mark.parametrize(
"relative,value",
[
(
"polkit-1/rules.d/90-hermes.rules",
"polkit.addRule(function(action, subject) {\n"
" if (subject.uid == 1200) return polkit.Result.YES;\n"
"});\n",
),
(
"polkit-1/localauthority/50-local.d/hermes.pkla",
"[Hermes]\nIdentity=unix-user:1200\n"
"Action=org.freedesktop.policykit.exec\nResultActive=yes\n",
),
],
)
def test_numeric_polkit_grants_fail_closed(
tmp_path: Path, monkeypatch, relative: str, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
monkeypatch.setattr(module, "ACCOUNT_UID", 1200)
policy = module.HOST_ETC / relative
policy.parent.mkdir(parents=True, exist_ok=True)
policy.write_text(value, encoding="utf-8")
with pytest.raises(module.HardeningError, match="polkit authority"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
def test_writable_privilege_policy_is_rejected(tmp_path: Path, monkeypatch):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
sudoers = module.HOST_ETC / "sudoers"
sudoers.write_text("root ALL=(ALL:ALL) ALL\n")
sudoers.chmod(0o666)
with pytest.raises(module.HardeningError, match="unsafe mutation"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)