Skip to content

0.3.3 — Sandbox tightening, threat model, sanitizer CI

Choose a tag to compare

@anhtuank7c anhtuank7c released this 03 May 15:05
· 63 commits to main since this release

Hardening release. Three independent improvements: the systemd unit's exposure score drops from 22 to 13 ("OK" band, near the achievable floor for a daemon with the daemon's network and journal-access constraints) via nine new directives; a canonical docs/threat-model.md documents what pamsignal defends against and what it deliberately doesn't (cross-referenced to source-line mitigations); ASAN + UBSAN run on every push and PR via a new .github/workflows/ci.yml workflow. No source-code behavior change visible to operators — the daemon's API surface (config keys, journal fields, webhook payload) is unchanged. Upgrade is apt upgrade pamsignal / dnf upgrade pamsignal; the systemd unit is replaced atomically and daemon-reload is auto-fired by the maintainer scripts.

Security

  • systemd unit hardening: exposure score 22 → 13 (displayed 2.2 → 1.3, "OK" band, near the achievable floor for a daemon that needs network egress + journal access). Nine new directives added to pamsignal.service.in, each justified against the daemon's actual behavior (no defensive copy-paste): UMask=0077, ProtectClock=yes, ProtectHostname=yes, ProtectProc=invisible, ProcSubset=pid, SystemCallArchitectures=native, RemoveIPC=yes, RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 (allowlists exactly the four families the daemon and its curl child need — drops AF_PACKET, AF_BLUETOOTH, AF_VSOCK, etc.), and DevicePolicy=closed (overrides the implicit RTC-read grant from PrivateDevices=yes). The CI regression-gate threshold drops from 30 to 20 to lock in the new baseline. Directives deliberately not added because they would break the daemon: PrivateNetwork=yes (curl needs outgoing HTTPS), PrivateUsers=yes (would interact badly with SupplementaryGroups=systemd-journal for journal-read access), IPAddressDeny=any (Telegram/Slack/Discord/etc. backend IPs change too frequently to allowlist), RootDirectory=/RootImage= (would require portable-service repackaging, out of scope). Documented in the directive comments so future readers know what the trade-offs were.

CI

  • AddressSanitizer + UndefinedBehaviorSanitizer on every push. New .github/workflows/ci.yml runs meson setup -Db_sanitize=address,undefined -Db_lundef=false --buildtype=debugoptimized, builds, and runs the full CMocka suite under both sanitizers on every push to main and every PR targeting it. ASAN catches use-after-free, heap/stack OOB, double-free, and leaks; UBSAN catches signed-integer overflow, misaligned pointer access, null deref, narrowing conversions, etc. — the dynamic-runtime evidence behind the threat-model claim that memory-safety bugs in src/ are an in-scope defense (attack #8). Initial run found zero issues on the existing test surface. Stack traces in failure reports include symbolized backtraces (print_stacktrace=1); halt_on_error=1 + abort_on_error=1 ensure the first finding stops the run rather than continuing in an undefined state. Concurrency group set to cancel older runs when newer commits land on the same ref.

Documentation

  • docs/threat-model.md documents what pamsignal defends against, what it deliberately does not, and the design rationale behind the split. Sections cover: assets in priority order (alert integrity, alert credentials, the pamsignal user's privilege envelope, journal entries pamsignal writes), adversary classes with explicit capabilities (external remote, local unprivileged, in-pamsignal-group, compromised daemon, network attacker on alert path), nine in-scope attacks each cross-referenced to the source-line of its mitigation (_EXE allowlist, brute-force tracker semantics, memfd credential isolation, clearenv() + absolute-path execv, TLS-only --proto =https, sanitize_string + json_escape, the systemd hardening directives, compiler hardening + libFuzzer, per-IP cooldown), ten explicit out-of-scope non-goals (root-on-host, in-pamsignal-group, compromised journald/libsystemd/curl, compromised alert provider, durable delivery, multi-host correlation, authenticated alert delivery, admin misconfiguration, input-flood DoS), the trust-boundary table, and the deliberate design limitations. SECURITY.md's scope section now references the threat model rather than duplicating the breakdown; the pamsignal(8) man page's SEE ALSO points readers there before reporting suspected vulnerabilities; the README's documentation index links it. Designed as the canonical reference for "should this contribution land?" — a feature that strengthens an in-scope mitigation is welcome; a feature that pulls work into the daemon from an out-of-scope area gets pointed at this document.