Elastic Certified SIEM Analyst study materials
Elastic Certified SIEM Analyst study resources

CIDR rule fires on Windows but never on firewall data

Elastic Certified SIEM Analyst · Put your knowledge to work in context.

All case studies

CIDR rule fires on Windows but never on firewall data

Your Fleet deployment manages 1,800 agents. Ingest includes logs-windows.security-* from domain controllers, logs-endpoint.events.process-*, and a custom TCP integration receiving roughly 400 M events/day of PAN-OS traffic plus 90 M/day of Fortinet traffic. You wrote a rule intended to catch internal-to-internal admin access: source.ip : 10.20.0.0/16 and destination.port >= 8080. It produces hits from Windows hosts but has never matched a single firewall event. In Discover, firewall events show source.ip as text with a .keyword subfield, and destination.port as keyword too. Separately, only about 60% of PAN events appear in Timeline when you filter on event.category: network.

1. Diagnose why the rule produces nothing from the firewalls and explain what the port clause is actually doing.

Read the worked answer

The rule is not erroring; it is being applied to incompatible types.

source.ip : 10.20.0.0/16 is a CIDR query, which only matches fields mapped as ip. On the firewall streams source.ip is text/keyword, so those indices return zero documents while the Windows indices match normally. Because the rule queries several indices in one search, you get silent partial under-detection rather than a visible failure.

destination.port >= 8080 on a keyword field compares strings lexicographically. 443 < 8080 because 4 sorts before 8, so HTTPS is excluded. 9999 > 8080, so an arbitrary high port is included. The clause therefore yields false negatives across most real ports plus unpredictable hits. Never range-query a keyword field.

Fixes, in order of preference:

  1. Correct the mapping in a component/index template: source.ip as ip, destination.port and source.port as long. This is the only fix that makes rules, aggregations and entity analytics all correct.
  2. Ingest pipeline: rename/set into ECS names plus convert for numeric fields. A pipeline cannot change a field's type, so pair it with the template.
  3. Runtime field typed as ip/long in the index template's runtime section, referenced by the rule. Workable, but costs query time and must be defined in every template serving those streams.

Do not switch the rule to source.ip.keyword: a keyword subfield cannot do CIDR matching.

2. Why are 40% of PAN events missing from `event.category: network` queries, and where should the fix live?

Read the worked answer

event.category is an ECS field that integrations populate. A raw syslog feed has no such concept unless something classifies the message, so those documents are invisible to prebuilt rules, Timeline filters and your own hunts that anchor on ECS categorization.

Fix it at ingest with a set processor in the integration pipeline, driven by the PAN-OS/Fortinet log type field:

  • event.category: ['network'], event.type (allowed, denied, connection), event.outcome
  • network.direction, rule.name, observer.*
  • source.ip, destination.ip, destination.port as properly typed values

Deterministic, cheap, and visible to every consumer, including detection rules.

Do not fix this with a Discover runtime field. Ad-hoc runtime fields created in the UI are not available to the detection engine, and the rule's own filters reference event.category directly. A runtime field declared in the index template is a valid stopgap but must be declared identically in every template for those streams.

Validate with the _simulate API against a captured sample document before enabling, then re-run the count comparison (event.category: network versus total PAN documents) to confirm the 60/40 split closes. Silently dropping 40% of firewall telemetry is worse than having no rule, because the absence of alerts reads as absence of activity.

3. You decide to correct the mappings. Walk through doing it safely for existing data, including what happens to the last 30 days.

Read the worked answer

You cannot change the type of an existing field in an existing index.

  1. Update the component template so new backing indices declare source.ip as ip and ports as long.
  2. Roll over the data stream so a fresh backing index is created with the new mapping. New data is now correct.
  3. The previous backing indices keep the old mapping. Until they age out, queries spanning the boundary are inconsistent. Options:
    • Add a runtime field in the template so old and new indices answer consistently at query time (query-time cost, but no storage).
    • Reindex the last 30 days into a new data stream with the corrected template. This doubles storage for the migrated window, competes with live ingest, and rewrites event.ingested, which invalidates any timestamp-override rules relying on it.
    • Scope hunts on @timestamp to after the rollover and accept a gap in coverage, documented.
  4. Compare before/after document counts per data stream to prove nothing was dropped.

Watch the other direction as well: if another integration already writes source.ip as ip into the same index (same data stream, not merely a similarly named one), a keyword write will be rejected with mapper_parsing_exception and the whole document is dropped unless ignore_malformed is set. A dropped document is worse than a mistyped field, so prefer fixing the ingest over blanket ignore_malformed.