Worker unit mirrors decnet-webhook.service shape: simple type, runs as the decnet user/group, append-style log file, full security hardening (NoNewPrivileges/ProtectSystem/ProtectHome/PrivateTmp/ LockPersonality + the rest). Added /var/lib/decnet to ReadWritePaths because the API process persists operator-uploaded canary blobs there. CAP_NET_BIND_SERVICE granted (ambient + bounded) so an operator who overrides DECNET_CANARY_DNS_PORT to 53 or HTTP_PORT to 80/443 in .env.local doesn't need to fight systemd. The defaults stay unprivileged (5353 / 8088). Added decnet-canary.service to decnet.target so 'systemctl start decnet.target' brings it up alongside the rest of the workers. decnet init auto-discovers deploy/decnet-*.service.j2 files (per decnet/cli/init.py:_install_units) so no further wiring needed — running 'decnet init' on a fresh host installs the new unit. Static tests confirm the unit references decnet canary, depends on the bus, carries the standard security directives, and is listed in the master target.
45 lines
1.5 KiB
Python
45 lines
1.5 KiB
Python
"""Sanity check on the decnet-canary.service unit + decnet.target.
|
|
|
|
Tests are deliberately static (no rendering, no systemd) — they just
|
|
confirm the unit file exists, references the canary CLI command, is
|
|
included in the master target, and follows the same security
|
|
hardening posture as decnet-webhook.service.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
DEPLOY = Path(__file__).resolve().parents[2] / "deploy"
|
|
|
|
|
|
def test_canary_unit_exists() -> None:
|
|
assert (DEPLOY / "decnet-canary.service.j2").exists()
|
|
|
|
|
|
def test_canary_unit_runs_decnet_canary() -> None:
|
|
body = (DEPLOY / "decnet-canary.service.j2").read_text()
|
|
assert "{{ venv_dir }}/bin/decnet canary" in body
|
|
assert "After=" in body and "decnet-bus.service" in body
|
|
|
|
|
|
def test_canary_unit_has_security_hardening() -> None:
|
|
"""Canary handles attacker traffic — must mirror webhook's hardening."""
|
|
body = (DEPLOY / "decnet-canary.service.j2").read_text()
|
|
for required in (
|
|
"NoNewPrivileges=yes",
|
|
"ProtectSystem=full",
|
|
"ProtectHome=read-only",
|
|
"PrivateTmp=yes",
|
|
"ProtectKernelTunables=yes",
|
|
"ProtectKernelModules=yes",
|
|
"ProtectControlGroups=yes",
|
|
"RestrictSUIDSGID=yes",
|
|
"LockPersonality=yes",
|
|
):
|
|
assert required in body, f"missing hardening directive: {required}"
|
|
|
|
|
|
def test_canary_listed_in_master_target() -> None:
|
|
body = (DEPLOY / "decnet.target").read_text()
|
|
assert "decnet-canary.service" in body
|