Skip to content

0.3.4 — Security: accept sshd-session (OpenSSH 9.8+ on Ubuntu 26.04 / Fedora 41+)

Choose a tag to compare

@anhtuank7c anhtuank7c released this 03 May 16:26
· 57 commits to main since this release

Security release. Fixes a real production defect: pamsignal silently dropped every sshd auth event on Ubuntu 26.04, Fedora 41+, and Debian Trixie (any host running OpenSSH 9.8+). Surfaced by an end-to-end scenario test on Ubuntu 26.04 / OpenSSH 9.10p2 / OpenSSL 3.5.5 / systemd 259 — a stack CI doesn't currently exercise. Bundles repository-hygiene additions (CONTRIBUTING.md, issue templates, PR template) that accumulated under ## Unreleased since v0.3.3, plus the tests/scenario.sh script that caught the sshd-session bug, plus forward-compat hardening of the CI mock webhook for OpenSSL 3.5+.

Security

  • _EXE allowlist now accepts sshd-session. OpenSSH 9.8 (released 2024-07) split the server into a privilege-separated listener (sshd) and a per-connection auth process (sshd-session). Failed-password / accepted-password journal entries on Ubuntu 26.04, Fedora 41+, and Debian Trixie come from sshd-session, not sshd. Pamsignal's anti-spoofing _EXE allowlist accepted only the latter, so every sshd auth event on those distributions was dropped at src/journal_watch.c's pre-parse filter — the daemon ran fine, journald accumulated events fine, the daemon just didn't see them. Fix: extend the basename allowlist with sshd-session (refactored into a testable ps_is_trusted_exe helper). Eight new CMocka test cases cover the canonical paths (/usr/sbin/sshd, /usr/bin/sudo, etc.), the new sshd-session paths (/usr/sbin/sshd-session, /usr/lib/openssh/sshd-session), the alternate system-prefix paths (/sbin, /lib, /lib64, /opt), and the spoofing-rejection paths (logger, /tmp/, /home/, basename-imposter binaries, NULL/empty input, paths without a slash). test_journal_watch grows from 19 → 27 tests.

Tooling

  • tests/scenario.sh — full-stack local end-to-end scenario test that mirrors release-packages.yml's test-deb job and adds two pieces of coverage that don't exist in CI: explicit Type=notify activation verification (systemctl reports active only after sd_notify(READY=1) actually fires), and the v0.3.0 sudo brute-force scenario with testpamuser temporarily added to /etc/sudoers.d/ so PAM auth is actually invoked. Twelve phases with pass/fail output, an EXIT trap that always cleans up (sshd_config restored from backup, testpamuser deleted, /etc/pamsignal removed, mock webhook stopped, test CA removed from system trust store, sudoers.d entry removed). The script's confirm() reads from /dev/tty directly so it works under wrappers that capture stdout. Caught the sshd-session bug above on its first non-CI run.
  • CI mock webhook hardened for OpenSSL 3.5+ peers. The Python BaseHTTPRequestHandler defaults to HTTP/1.0 + no Content-Length + no explicit TLS close_notify, which OpenSSL 3.5+ (Ubuntu 26.04, Fedora 41+) reports as error:0A000126:SSL routines::unexpected eof while reading, breaking curl with exit 56 even though the HTTP response was successfully received. Forward-port the connection.unwrap() + Content-Length + Connection: close + protocol_version='HTTP/1.1' fixes from tests/scenario.sh into release-packages.yml's test-deb mock so the workflow keeps passing when GitHub Actions promotes the runner image from ubuntu-24.04 (OpenSSL 3.0) to ubuntu-26.04 (OpenSSL 3.5+). Also adds a READY-poll startup check with bounded retries so a Python-side bind error fails loudly with the captured stderr instead of silently hanging.

Repository hygiene

  • CONTRIBUTING.md at the repo root documents the contributor workflow: clone+build quickstart, branch-per-change with Conventional Commits (with the feat:/fix:/security:/refactor:/refactor!:/docs:/chore:/test:/perf: type table the project actually uses), coding standards (cross-referenced to .clang-format / .clang-tidy), the test requirement (every new function or changed behavior gets a CMocka test), the full pre-commit checklist (clang-format, clang-tidy, meson compile, meson test, optional sanitizer build), CHANGELOG conventions, code-review focus areas (threat-model alignment, test adequacy, API stability, style), and a packaging-changes section explicitly calling out the systemd-analyze CI gate at threshold 20 plus the el9-rpm %changelog-parsing trap.
  • Issue templates rewritten as Linux-daemon reports, not generic open-source-project forms. .github/ISSUE_TEMPLATE/bug_report.yml is a structured capture of the system state we'd ask the operator to gather anyway — a component dropdown (parser / brute-force tracker / alert dispatch / unit / packaging / journald integration / config parsing), distribution + version + kernel + systemd version + MAC-policy enforcement state, the systemctl status pamsignal output, the journalctl -u pamsignal excerpt, the source-of-truth auth-event excerpt with the right per-service-unit filters, the systemd-delta + drop-in-overrides listing (frequent root cause for "directive X isn't behaving" reports), the systemd-analyze security score (CI-gated at 20; a higher score on the operator's host suggests a drop-in or repackaging loosened a directive), the redacted config, and concrete operational reproduction steps (what auth events were generated, with what config values, in what order). The template includes the exact commands to run for each capture so an operator who doesn't know the daemon internals can still file a report we can act on. feature_request.yml rewritten around the project's operator-facing surface area (config keys, journal fields, chat-text alert shape, ECS JSON webhook fields, unit directives, CLI flags, alert-channel additions) with a mandatory compatibility-commitment dropdown (additive / default-on-overridable / default-off opt-in / deprecation-cycle / breaking) referencing the PAMSIGNAL_* retirement precedent, and a threat-model-alignment field that distinguishes "strengthens an in-scope attack" from "argues for moving NS1–NS10 into scope" (the latter being the highest bar). config.yml unchanged — disables blank issues, points security reports at GitHub Security Advisories, routes question traffic to question-labeled issues.
  • Pull-request template (.github/pull_request_template.md) auto-populates new PRs with a summary / linked-issue / test-plan / threat-model-alignment structure plus the eight-item pre-commit checklist that mirrors CONTRIBUTING.md. Reviewers can see at a glance whether the contributor ran the local checks.