Skip to content

fix(linux): derive final operation outcomes and login peer IPs - #2663

Closed
kryonsx wants to merge 4 commits into
utmstack:v11from
kryonsx:codex/data-engine-linux-20260923
Closed

kryonsx wants to merge 4 commits into
utmstack:v11from
kryonsx:codex/data-engine-linux-20260923

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Linux currently copies vendor job and syscall outcomes into actionResult, misses native final-login results, and can reject an event when a negative syscall exit is moved into unsigned statusCode.

This change derives canonical success, failure, and denied outcomes from the native audit collector and systemd job contracts. Canceled/skipped jobs, earlier authentication checks, repeated login-record arrays, unknown values, and nonblocking connections still in progress remain unset. Failed permission checks become denied. Vendor outcome and errno fields are retained, and only valid unsigned values populate statusCode.

Final USER_LOGIN records also expose valid IPv4/IPv6 peer addresses as origin.ip, preserving log.userlogin.addr. Invalid and unspecified addresses remain vendor detail. This makes final login outcome and peer identity available together to correlation.

Validation uses fabricated raw collector/journal JSON, the existing ordered extraction model, and the pinned SDK v1.1.33. The model does not run the actual EventProcessor or prove TI membership, live deployment, or alert creation. Read-only raw/normalized samples and current deployed configuration were checked privately; customer records and identifiers are not included here.

Authoritative contracts: pinned audit collector, pinned audit result decoding, systemd job completion, connect completion and errors, audit user address, and SDK standard event schema (wiki revision c18b54bd5ea5a34abb0e690458d73f89835edd29).

Regression results: 91 fabricated raw outcome/IP cases and 14 existing-rule controls pass with SDK v1.1.33. No rule YAML migration is required. Separate isolated pipeline replay validated 98 resulting events and three expected rule alerts; its dependency versions differ from the reviewed snapshot, so this does not establish deployed equivalence.

Companion dependency: the shared TI outcome consumer also needs its compatibility correction before relying on canonical failure or unknown-outcome suppression. That tested change is now available as a separate draft.

Final alerts-module suite: 2,352 passed test events/subtests, 11 documented skips, zero failures.

GitHub CI: dependency and prompt-discovery checks passed. The three AI-review jobs could not start because the workflow requires the missing THREATWINDS_API_KEY secret; the aggregate check therefore failed. This is separate from the local regression results above.

kryonsx and others added 2 commits September 23, 2026 09:58
The parser removes underscores from the names it writes, so journald
JOB_RESULT is stored as JOBRESULT and auditd subj_user as subjuser. The
JOB_RESULT halves of the job outcome conditions could never match; keep
only JOBRESULT (same results). The rename of log.subj_user to origin.user
never ran; remove it rather than switch it on, because it would put the
SELinux label (for example unconfined_u) into origin.user, which is the
account used for attribution, grouping and deduplication.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kryonsx

kryonsx commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Change pushed (75bfef15): field names the parser never produces are removed

What changed (filters/linux/linux.yml)

  • The job outcome conditions now read only log.JOBRESULT. The log.JOB_RESULT halves are gone.
  • The rename of log.subj_user to origin.user is removed.

Why

  • The EventProcessor parser keeps only letters, digits and dots in the field names it writes (utils.SanitizeField). The journald key JOB_RESULT is stored as JOBRESULT, and the audit key subj_user as subjuser. The underscored names never match anything.
  • The subj_user rename has never run, not even in v11. Pointing it at subjuser would switch it on for about 40% of real audit records, and it would put the SELinux label (for example unconfined_u) into origin.user. That field is the account used for attribution, grouping and deduplication, so removing the step keeps today's behavior and avoids wrong user attribution.
  • Until the EventProcessor handles these characters, filters and rules should use only letters, digits and dots in field names.

Validation

  • Local EventProcessor playground on 220 real Linux records from 5 production instances, comparing this draft before and after the change: output is identical (same outcomes, alerts and rule matches). Job outcomes still come out as done → success and failed → failure through JOBRESULT.
  • go test ./... in plugins/alerts passes. The Linux filter has no field names with _ or other special characters left.

Not changed: five rules under rules/linux/rhel_family/ (OpenShift, RPM database, Secure Boot, SELinux and YUM/DNF) read fields that no Linux filter produces, such as log.audit_verb, log.kernel_message, log.gpg_verification and log.efi_variable. Renaming them would not make them work, so they need a redesign against the fields this filter really writes.

Ricardo Valdes and others added 2 commits September 24, 2026 15:13
go-sdk v1.1.35 and later keep "_" in the field names parsers write
(EventProcessor 8a3ade7; v11 d2479c1 pins v1.1.36). Journald JOB_RESULT is
stored as log.JOB_RESULT on current engines and as log.JOBRESULT on older
ones, so the job outcome conditions read both again, as before 75bfef1.

The log.subj_user rename stays removed. On current engines it would run and
put the SELinux user (for example unconfined_u) into origin.user, the
account used for attribution, grouping and deduplication.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parsers on go-sdk v1.1.35 or later keep "_" in the key names they write
(EventProcessor 8a3ade7; v11 d2479c1 pins v1.1.36); older ones remove it.
Journald and audit keys such as _HOSTNAME, _COMM, _CMDLINE, _SYSTEMD_UNIT,
SYSLOG_IDENTIFIER and __REALTIME_TIMESTAMP therefore arrive as log._HOSTNAME
on current engines, and the renames that read only log.HOSTNAME no longer
ran. On 220 real Linux records, v11 on the 8a3ade7 engine lost origin.host on
134 events, and four rules lost matches (crontab persistence 6 to 0, reverse
shell 7 to 0, systemd timer persistence 2 to 0, modified SSH binaries 2 to 1).

The 45 renames of these keys now read both spellings. Rename targets are
unchanged, so stored field names stay the same on both engines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kryonsx

kryonsx commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Changes pushed (3a6ee9ee, 1837e4f8): read journald keys under both spellings for the latest engine

What changed (filters/linux/linux.yml)

  • The job outcome conditions read log.JOB_RESULT again, next to log.JOBRESULT. This puts back what my earlier commit 75bfef15 removed.
  • The 45 renames of journald and audit keys now read both spellings. For example, log.HOSTNAME or log._HOSTNAME goes to origin.host, log.COMM or log._COMM to origin.process, and log.CMDLINE or log._CMDLINE to origin.command. The rename targets are unchanged, so stored field names stay the same.
  • The log.subj_user rename stays removed.

Why

  • Since go-sdk v1.1.35, parsers keep _ in the key names they write. EventProcessor main (8a3ade7) links v1.1.36, and v11 (d2479c1a) pins v1.1.36. Journald keys such as _HOSTNAME, _COMM, SYSLOG_IDENTIFIER and JOB_RESULT now arrive with their underscores, so renames that read only the old cleaned names stop running. My earlier commit was written for the older behavior.
  • The subj_user removal matters more now. On current engines that rename would run and put the SELinux user (for example unconfined_u) into origin.user.
  • Current v11 has the same problem on the new engine, so merging this draft also fixes it there.

Validation
Local EventProcessor playground built from 8a3ade7 (every plugin on go-sdk v1.1.36), with this draft merged into v11 d2479c1a, on 220 real Linux records from production instances:

  • v11 alone on 8a3ade7, compared with the old engine: origin.host is lost on 134 events and the job outcome on 13. The rules also lose matches: crontab persistence 6 to 0, reverse shell 7 to 0, systemd timer persistence 2 to 0 and modified SSH binaries 2 to 1.
  • This draft before the change on 8a3ade7: the same losses.
  • After the change: all 220 normalized events match what this draft produced on the old engine, and the four rules match again (6, 7, 2 and 2).
  • go test ./... in plugins/alerts passes on the merged tree with v1.1.36.

@kryonsx

kryonsx commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

The five RHEL-family rules (SELinux, Secure Boot, RPM database, YUM/DNF repository, OpenShift) read fields that no Linux producer writes. They are rebuilt in #2750, which is stacked on this branch because they read log.syslogIdentifier and log.message, which only this filter renames on current engines. This draft is unchanged.

@osmontero

Copy link
Copy Markdown
Member

Superseded by #2750 — byte-identical subset (identical filters/linux/linux.yml blob 71d5eb09c and identical linux_action_result.json testdata blob f986d082b at both heads). #2750 = #2663's 4 filter commits + the 5 RHEL-family rule rebuilds. Closing to avoid duplicate-merge confusion.

@osmontero osmontero closed this Sep 25, 2026
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.

2 participants