Skip to content

Add sshd-session/sshd-auth to default macOS ULS query - #37769

Merged
MarcelKemp merged 2 commits into
4.14.8from
fix/37748-macos-sshd-session-predicate
Jul 21, 2026
Merged

MarcelKemp merged 2 commits into
4.14.8from
fix/37748-macos-sshd-session-predicate

Conversation

@Darioortegaleyva

@Darioortegaleyva Darioortegaleyva commented Jul 20, 2026

Copy link
Copy Markdown
Member

Description

This PR fixes SSH authentication logs not being collected by the Wazuh agent on modern macOS. On macOS Sequoia/Tahoe (OpenSSH 9.8+/10), sshd was split into the privilege-separated sshd-session and sshd-auth processes, which now emit the SSH authentication verdicts (Accepted/Failed password, Invalid user). The agent's default macOS Unified Logging (ULS) <query> only matched process == "sshd", so these events were never collected and no SSH alerts were generated. The fix adds sshd-session and sshd-auth to the default predicate. No decoder/rule change is required: the sshd decoder matches program_name with the ^sshd prefix, so the new process names decode and alert identically.

Proposed Release: [v4.14.8]
Issue: #37748
Internal Reference: N/A

Proposed Changes

  • Updated etc/templates/config/darwin/localfile-extra.template.
  • Fixed the default macOS ULS query by adding the sshd-session and sshd-auth processes to the existing OR-list (kept sshd; explicit process list, no BEGINSWITH/CONTAINS, to avoid pulling in the noise-only parent sshd process).
  • Added the corresponding CHANGELOG.md entry under [v4.14.8] > Agent > Fixed.

Diff:

--- a/etc/templates/config/darwin/localfile-extra.template
+++ b/etc/templates/config/darwin/localfile-extra.template
@@ -1,5 +1,5 @@
   <localfile>
     <location>macos</location>
     <log_format>macos</log_format>
-    <query type="trace,log,activity" level="info">(process == "sudo") or ... or (process == "sshd") or (process == "tccd" and message contains "Update Access Record") or ...</query>
+    <query type="trace,log,activity" level="info">(process == "sudo") or ... or (process == "sshd") or (process == "sshd-session") or (process == "sshd-auth") or (process == "tccd" and message contains "Update Access Record") or ...</query>
   </localfile>

This is a configuration-template change. The runtime predicate is fully determined by ossec.conf's <query>, which wazuh-logcollector passes verbatim to /usr/bin/log; no agent code path is involved, so no recompilation changes behavior.

Results and Evidence

Validated on macOS 26.5.1 Tahoe (Darwin 25.5.0, arm64, OpenSSH_10.2p1) with a released Wazuh agent v4.14.6 (config patched to match this PR) and a Wazuh v4.14.6 manager. SSH login attempts (success / failed password / invalid user) were generated against the host. The chain was validated at every layer, including a full live agent → manager → alerts.json run (Test E).

1) Template selection resolves to the edited file, and the shipped default carries the bug

macOS Tahoe = Darwin 25; GetTemplate (src/init/template-select.sh) finds no darwin/25/localfile-extra.template and no localfile-extra.agent.template, so it falls back to the edited generic darwin/localfile-extra.template. The freshly installed 4.14.6 agent's default config is byte-identical:

sudo grep -c 'process == "sshd"' /Library/Ossec/etc/ossec.conf   # -> 1  (buggy default ships)
sudo grep -c 'sshd-session'      /Library/Ossec/etc/ossec.conf   # -> 0

2) Collection — exact predicate the agent passes to /usr/bin/log (before vs after)

# BEFORE (default query, process == "sshd" only)
log show --style syslog --info --last 2m --predicate '<DEFAULT QUERY>'
-- processes captured --
  16 sshd
-- SSH auth verdicts captured --
-- COUNT --
0
# AFTER (patched query, + sshd-session + sshd-auth)
log show --style syslog --info --last 2m --predicate '<PATCHED QUERY>'
-- processes captured --
  16 sshd
  20 sshd-auth
  71 sshd-session
-- SSH auth verdicts captured --
2026-07-20 05:38:38  sshd-session[3178]: Accepted password for vagrant from 192.168.64.1 port 50301 ssh2
2026-07-20 05:36:57  sshd-session[3143]: Failed password for vagrant from 127.0.0.1 port 49165 ssh2
2026-07-20 05:39:24  sshd-session[3214]: Invalid user ghostuser from 127.0.0.1 port 49170
2026-07-20 05:39:49  sshd-session[3214]: Connection closed by invalid user ghostuser 127.0.0.1 port 49170 [preauth]
-- COUNT --
5

3) Agent-level — real wazuh-logcollector (debug) with the patched config

sudo grep "Monitoring macOS logs with" /Library/Ossec/logs/ossec.log
sudo grep -c "Reading macOS message.*sshd-\(session\|auth\)" /Library/Ossec/logs/ossec.log
macos_log.c:419 at w_macos_create_log_stream_env(): INFO: (1604): Monitoring macOS logs with: /usr/bin/log stream --style syslog --type activity --type log --type trace --level info --predicate (process == "sudo") or ... or (process == "sshd") or (process == "sshd-session") or (process == "sshd-auth") or ...
read_macos.c:296 at w_macos_log_getlog(): DEBUG: Reading macOS message: '2026-07-20 05:44:26.147981-0700  localhost sshd-session[5007]: ('...
read_macos.c:296 at w_macos_log_getlog(): DEBUG: Reading macOS message: '2026-07-20 05:44:26.153033-0700  localhost sshd-auth[5009]: (lib'...

# sshd-session + sshd-auth messages ingested per login:
#   patched config: 26
#   default config:  0   (only 4 parent-sshd noise lines)

4) Manager — wazuh-logtest (Wazuh v4.14.6): events decode and alert

sudo /var/ossec/bin/wazuh-logtest < captured_sshd_session_lines.txt
**Phase 1: Completed pre-decoding.
    program_name: 'sshd-session'
**Phase 2: Completed decoding.
    name: 'sshd'
    parent: 'sshd'
    dstuser: 'vagrant'
    srcip: '192.168.64.1'
**Phase 3: Completed filtering (rules).
    id: '5715'
    level: '3'
    description: 'sshd: authentication success.'
**Alert to be generated.
Input (process) program_name decoder rule level description
Accepted password (sshd-session) sshd-session sshd 5715 3 sshd: authentication success
Failed password (sshd-session) sshd-session sshd 5760 5 sshd: authentication failed
Invalid user (sshd-session) sshd-session sshd 5710 5 sshd: non-existent user
control — Failed password (sshd) sshd sshd 5710 5 sshd: non-existent user

sshd-session/sshd-auth decode identically to classic sshd (via the ^sshd prefix) and generate the correct SSH alerts — confirming no ruleset change is needed.

5) Full live end-to-end — real enrolled agent → manager → alerts.json

The test lab has no direct L3 path (macOS behind an Apple-vmnet NAT; manager on a VirtualBox host-only network). This was bridged with an SSH reverse tunnel launched from the Windows host (which reaches both the macOS SSH port and the manager's host-only IP), forwarding the agent's 127.0.0.1:1514/1515 to the manager:

# on Windows (reaches manager 10.2.0.13 on the VirtualBox host-only net):
ssh -i tunnelkey -N -R 1514:10.2.0.13:1514 -R 1515:10.2.0.13:1515 -p 43229 vagrant@10.10.0.250

The released 4.14.6 agent (patched <query>, server 127.0.0.1, TCP) enrolled through the tunnel and reached Active; SSH attempts produced real alerts on the manager:

# manager: /var/ossec/bin/agent_control -l
   ID: 004, Name: idr-5604-macos-26-arm64-4062, IP: any, Active

One full, unmodified entry from the manager's alerts.json:

{
  "timestamp": "2026-07-21T05:40:30.869+0000",
  "rule": { "level": 3, "description": "sshd: authentication success.", "id": "5715",
            "groups": ["syslog", "sshd", "authentication_success"] },
  "agent": { "id": "004", "name": "idr-5604-macos-26-arm64-4062", "ip": "127.0.0.1" },
  "manager": { "name": "ubuntumanager" },
  "predecoder": { "program_name": "sshd-session", "timestamp": "2026-07-20 22:40:27.838188-0700" },
  "decoder": { "parent": "sshd", "name": "sshd" },
  "data": { "srcip": "192.168.64.1", "srcport": "50885", "dstuser": "vagrant" },
  "location": "macos",
  "full_log": "2026-07-20 22:40:27.838188-0700  localhost sshd-session[20552]: Accepted password for vagrant from 192.168.64.1 port 50885 ssh2"
}

Alert tally for agent 004 (alerts.json): sshd: authentication success (5715) ×3, sshd: Attempt to login using a non-existent user (5710) ×1. The live wrong-password attempts were logged by macOS as Connection closed by authenticating user ... [preauth] (Tahoe/OpenSSH 10 timing variance), so rule 5760 was not captured live this run; it is proven deterministically in Test 4.

Artifacts Affected

  • etc/templates/config/darwin/localfile-extra.template
  • CHANGELOG.md

Configuration Changes

Default macOS agent ossec.conf (generated from the template) now includes sshd-session and sshd-auth in the ULS <query>.

Deployment note: on upgrade, Wazuh does not overwrite an existing ossec.conf, so already-deployed macOS agents keep the old <query> and will keep missing sshd-session/sshd-auth until their ULS predicate is updated. Fresh installs / clean config regeneration pick up the fix automatically. This is worth a callout in the release notes / upgrade guide so operators update the predicate on existing macOS agents.

Documentation Updates

N/A (recommend a release-note / upgrade-guide callout per the deployment note above).

Tests Introduced

  • Scope: Manual validation — macOS ULS collection, agent-level wazuh-logcollector, manager ruleset validation via wazuh-logtest, and a full live agent→manager→alerts.json run.
  • Details: Generated success/failure/invalid-user SSH attempts on macOS Tahoe; confirmed the default predicate collects 0 auth verdicts while the patched predicate collects them under sshd-session/sshd-auth; confirmed the real wazuh-logcollector launches log stream with the patched predicate and ingests the events; confirmed the manager decodes and alerts on them (rules 5715/5760/5710); and confirmed a real enrolled agent produces genuine alerts in the manager's alerts.json (location: "macos", program_name: "sshd-session"). This is a config-template change, so no unit tests apply.

Review Checklist

Manual tests with their corresponding evidence:

  • Compilation without warnings on every supported platform (N/A — configuration/template change, no code compiled)

    • Linux
    • Windows
    • MAC OS X
  • Log syntax and correct language review

  • Memory tests for Linux (N/A — no code change)

    • Coverity
    • Valgrind (memcheck and descriptor leaks check)
    • AddressSanitizer

General Checklist:

  • Code changes reviewed
  • Relevant evidence provided
  • Tests cover the new functionality (manual validation; no code path added)
  • Configuration changes documented
  • Developer documentation reflects the changes
  • Meets requirements and/or definition of done
  • No unresolved dependencies with other issues

@Darioortegaleyva Darioortegaleyva self-assigned this Jul 20, 2026
@Darioortegaleyva
Darioortegaleyva force-pushed the fix/37748-macos-sshd-session-predicate branch from d9c9c5d to ac54922 Compare July 20, 2026 13:14
@Darioortegaleyva Darioortegaleyva linked an issue Jul 20, 2026 that may be closed by this pull request
6 tasks
@Darioortegaleyva
Darioortegaleyva marked this pull request as ready for review July 21, 2026 05:54
@Darioortegaleyva Darioortegaleyva changed the title Collect macOS SSH logs from sshd-session and sshd-… Add sshd-session/sshd-auth to default macOS ULS query Jul 21, 2026
@MarcelKemp
MarcelKemp merged commit 93a7cb1 into 4.14.8 Jul 21, 2026
9 checks passed
@MarcelKemp
MarcelKemp deleted the fix/37748-macos-sshd-session-predicate branch July 21, 2026 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS: SSH logs missing for sshd-session/sshd-auth processes

2 participants