Skip to main content

Security Event Export

Synapse decides things at several layers — a packet dropped in the kernel, a request blocked by the WAF, a source flagged by threat intelligence, an IDS signature matched. Each of those is a security event, and each is emitted through one unified stream.

That matters because the alternative is correlating seven log formats to answer "why was this client blocked?".

One event model, whatever decided

Every layer that makes a decision emits the same structured event:

LayerDecides
Access rulesAddress blocked in the kernel
Smart firewallFingerprint-driven kernel block
IDSSignature matched
WAFHTTP request blocked or challenged
Threat intelSource reputation acted on
CAPTCHAChallenge issued or passed
Rate limitClient throttled

Each event carries the layer that decided and the action taken, so a single query answers "everything we blocked, and why" without joining across sources.

Where it can go

DestinationFormatReach for it when
Local event logHuman-readable linesYou are on the box, tailing
JSON Lines fileOne self-contained record per lineA shipper — Fluent Bit, Vector, an OTel Collector filelog receiver — is already running
OTLP over HTTPBatched OpenTelemetry log records and metricsYou have a collector and want push, not scrape
CEFArcSight Common Event FormatYour SIEM is ArcSight, QRadar, or anything that ingests CEF natively
syslogStandard syslog, with facility and identifierCentral syslog is the collection point

On Windows, events can additionally go to ETW and the Windows Event Log.

OTLP carries two signals, not one: the event stream as log records, and a set of counters, gauges and a histogram as metrics — so the same collector gets both "what was blocked" and "how much is being blocked" without a second exporter. See OpenTelemetry.

CEF carries three streams, not just one: security decisions, IDS alerts, and access logs — so a SIEM gets the whole picture in the format it already parses, rather than a decision feed it has to reconcile with logs from elsewhere.

Try it

What a decision looks like

Every layer emits the same record, so one line tells you who enforced, what fired it, and against whom:

{
"ts": "2026-09-08T11:04:22.481Z",
"layer": "smart_firewall",
"action": "drop",
"source": "fingerprint_rule",
"signals": ["fingerprint_rule", "threat_intel"],
"rule_id": "sf-scanner-ja4t-01",
"rule_msg": "known scanner TCP fingerprint",
"hits": 47,
"ban_secs": 3600,
"src_ip": "203.0.113.42",
"src_port": 54321,
"dst_ip": "10.0.0.5",
"dst_port": 443,
"src_country": "NL",
"fingerprints": {
"ja4t": "t64320_2_1-3-8-nop_nop,sackOK,ts,nop,ws,eol_",
"ja4": "t13d311200_e8f1e7e78f70_d339722ba4af"
}
}

Three fields carry most of the meaning, and they are not interchangeable:

FieldAnswersValues
layerWho enforcedaccess_rules, smart_firewall, ids, waf
actionWhat was doneblock, drop, allow, log, notice, ratelimit, captcha
sourceWhat decidedthreat_intel, classifier, traffic_classifier, ids, waf, fingerprint_rule, geoip, cidr, behavioral, microseg, access_rules

signals is the one people miss. A rule that fuses two families — an IDS score and a threat-intel verdict — lists every contributing signal, with source being the first. A scalar source cannot represent fusion, so if you are attributing blocks to a cause, read signals, not source alone.

ban_secs is the duration declared at install, not a countdown. Two events for the same address both report the full duration.

Answer "why was this blocked?"

# Everything we dropped in the last run, newest first
jq -c 'select(.action == "drop") | {ts, layer, source, src_ip, rule_msg}' \
/var/log/synapse/events.log | tail -20

# One address, across every layer that touched it
jq -c 'select(.src_ip == "203.0.113.42")' /var/log/synapse/events.log

# Which sources are driving enforcement
jq -r '.source // "unknown"' /var/log/synapse/events.log | sort | uniq -c | sort -rn

That last one is the useful daily query — a sudden shift in which source dominates is usually a feed change or a misfiring rule, not an attack.

Ship it to a SIEM as CEF

CEF is configured under logging.cef, and carries three independent streams. Only the first is on by default, which is deliberate: block events are per decision, IDS alerts per signature match, and access logs per request, so the third is by far the largest.

# /etc/synapse/config.yaml
logging:
cef:
enabled: true
file: "/var/log/synapse/cef.log"
max_size: 104857600 # rotate at 100 MB
keep: 10
streams:
block_events: true # security decisions — the primary stream
ids_alerts: true # including alert-only signatures that never block
access_logs: false # one record per proxied request; leave off unless metered
# dvchost on every record, so a SIEM can attribute events to an agent.
# Defaults to the system hostname.
device_host: ""

Leave file empty to disable the file sink and deliver over syslog only.

access_logs is a volume decision, not a detail

Block events fire per decision; access logs fire per request. On a busy proxy that is orders of magnitude more records, and most SIEM licensing is metered by ingest. Turn it on deliberately.

Structured fingerprints, separately

Decisions and observations are different streams. The unified fingerprint log carries every JA4+ and BPF event, whether or not anything was enforced:

logging:
fingerprint_log:
enabled: true
# file: /var/log/synapse/fingerprints.log
The six per-type sections are deprecated

bpf_stats, tcp_fingerprint, ssh_fingerprint, latency_fingerprint, tls_fingerprint and http_fingerprint are superseded by fingerprint_log, ship disabled, and will be removed. If you are still enabling them individually, move over.

Why both a readable log and a structured one

They serve different readers, and neither substitutes for the other.

The readable log is for a person debugging at 3am who wants to see what just happened. The structured stream is for machines — stable field names, one record per line, no parsing heuristics. Both are fed from the same event, so they cannot disagree about what occurred.

Limits worth knowing

  • Export is push or file, not a query API. Synapse emits; it does not store a searchable history. Retention and search are your collector's job.
  • OTLP batches. Events arrive at the collector in batches rather than one at a time, so there is a small delay between decision and visibility.
  • A shipper is still your responsibility. The JSON Lines sink writes a file; something has to move it.

Use cases

  • Answer "why was this blocked?" from one query, instead of correlating kernel, proxy and IDS logs by timestamp.
  • Feed an existing SIEM without a translation layer, by emitting CEF it already understands.
  • Alert on the decision rate — a sudden spike in blocks is a signal in its own right.
  • Keep an audit trail of enforcement actions, with the layer and reason attached to each.

See also

  • Fleet Bans — the same decisions, aggregated across the deployment

  • OpenTelemetry — OTLP export of both logs and metrics, and the config precedence

  • Axiom and Honeycomb — worked examples for specific backends

  • Configuration — sinks, paths, endpoints and levels

  • Threat Detection — where the reputation half of these events comes from

  • WAF — where the HTTP half comes from