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:
| Layer | Decides |
|---|---|
| Access rules | Address blocked in the kernel |
| Smart firewall | Fingerprint-driven kernel block |
| IDS | Signature matched |
| WAF | HTTP request blocked or challenged |
| Threat intel | Source reputation acted on |
| CAPTCHA | Challenge issued or passed |
| Rate limit | Client 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
| Destination | Format | Reach for it when |
|---|---|---|
| Local event log | Human-readable lines | You are on the box, tailing |
| JSON Lines file | One self-contained record per line | A shipper — Fluent Bit, Vector, an OTel Collector filelog receiver — is already running |
| OTLP over HTTP | Batched OpenTelemetry log records and metrics | You have a collector and want push, not scrape |
| CEF | ArcSight Common Event Format | Your SIEM is ArcSight, QRadar, or anything that ingests CEF natively |
| syslog | Standard syslog, with facility and identifier | Central 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:
| Field | Answers | Values |
|---|---|---|
layer | Who enforced | access_rules, smart_firewall, ids, waf |
action | What was done | block, drop, allow, log, notice, ratelimit, captcha |
source | What decided | threat_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 detailBlock 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
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
-
Configuration — sinks, paths, endpoints and levels
-
Threat Detection — where the reputation half of these events comes from
-
WAF — where the HTTP half comes from