Skip to content

fix(fortigate): one VPN brute force alert per source address - #2790

Merged
kryonsx merged 1 commit into
utmstack:v11from
kryonsx:codex/fortigate-alert-noise-20260929
Oct 1, 2026
Merged

kryonsx merged 1 commit into
utmstack:v11from
kryonsx:codex/fortigate-alert-noise-20260929

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Why

"FortiGate VPN Authentication Brute Force" grouped by source address and user name. Once a source crosses the threshold (10 SSL-VPN login failures within 15 minutes), a password spraying run opens a new top-level alert for every user name it tries and stores a child alert for every repeat. On one v11 deployment a spraying run from two addresses produced 10 alerts in one hour on 2026-09-28, the first day the filter's vpnAuthFailure marker (which the history search counts) was deployed.

Noise is reduced here only by de-duplication. The condition and the history threshold are unchanged.

What changes

Before After
Alerts groupBy adversary.ip, adversary.user, lastEvent.log.devid, lastEvent.log.vd deduplicateBy dataSource, lastEvent.log.devid, lastEvent.log.vd, adversary.ip: one alert per firewall, VDOM and source address, repeats dropped for seven days

The user names a source tried are in the alert's events and in the firewall logs.

Expected volume

Replaying 30 days of real SSL-VPN login failures from the eight v11 deployments that send FortiGate logs with the unchanged 10-in-15-minutes history:

Failures Sources over the threshold Alerts in 30 days Worst hour Worst day
Shipped grouping (every failure after the threshold) 14,706 26 5,007 551 2,868
This change 14,706 26 26 5 15

All 26 sources over the threshold were on one deployment (10,637 of the failures); the failures on the other deployments stayed below it.

Tests

  • plugins/alerts/fortigate_alert_volume_test.go (new) runs fabricated SSL-VPN failure lines through the step-by-step FortiGate filter model and the pinned go-sdk v1.1.36 CEL. It pins the de-duplication keys, checks that they resolve, and checks that two user names from one address produce the same key.
  • The FortiGate contract test now also checks deduplicateBy keys; TestFortiGateSDKHistory still pins the 10-in-15-minutes history.
  • go test ./... in plugins/alerts passes on this branch, which is based on current v11 (89cd26c4).
  • Local EventProcessor lab (engine 8a3ade7, go-sdk v1.1.36, the production events and alerts plugins, OpenSearch 2.19.1, this branch's FortiGate filter from current v11) with fabricated SSL-VPN failure lines in three runs: 10 failures each from two spraying addresses (one per user name) and 1 from a third address; then a new user name from the first address and a second one from the third; then another user name from the first address and the first attempt from the second spraying address. Every run processed all of its events, and each alert carried its 10 earlier failures from the history search.
    • With this branch: 3 alerts produced and 2 stored (one per spraying address); the first address's next user name was dropped as a duplicate, and the address below the threshold produced none.
    • With the rule as shipped: 3 alerts produced and 3 top-level alerts stored, one per user name.

🤖 Generated with Claude Code

"FortiGate VPN Authentication Brute Force" grouped by source address and
user name, so a password spraying run opened a new top-level alert for
every user name it tried after the source crossed the threshold (10
failures within 15 minutes), and stored a child for every repeat.

The rule now raises one alert per firewall, VDOM and source address and
drops repeats for seven days (deduplicateBy dataSource, devid, vd,
adversary.ip). Its condition and history threshold are unchanged.

fortigate_alert_volume_test.go pins the keys and that two user names from
one address share them; the contract test now also checks
deduplicateBy keys.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@kryonsx
kryonsx marked this pull request as ready for review October 1, 2026 02:07
@kryonsx
kryonsx requested a review from a team October 1, 2026 02:07
@kryonsx
kryonsx merged commit 117d733 into utmstack:v11 Oct 1, 2026
3 of 7 checks passed
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.

1 participant