Skip to main content

Configuration Examples

Five files you can copy whole. Each one is complete: a key that is not in it keeps the default from Configuration, and nothing here depends on a companion snippet elsewhere on the page.

Where these come from

They are adapted from config/config.yaml and config/config_windows.yaml as shipped with 0.8.8, not written from scratch. Two kinds of edit were made: keys the shipped samples set but the schema does not accept are removed, and the values are the ones a deployment would actually choose rather than the sample's "show every option" defaults. What these leave out says exactly what went and why, because the difference is visible the moment you compare a file here with the one in the package.

1. Minimal agent​

An agent on one machine: kernel firewall on, fingerprints collected, nothing else.

# /etc/synapse/config.yaml
mode: "agent"

network:
iface: "auto"

firewall:
mode: "auto"

platform:
api_key: "YOUR_API_KEY"

logging:
level: "info"
file_logging_enabled: true
log_directory: "/var/log/synapse"

Every one of those lines could be dropped and synapse would still start:

LineWhat it does for you
mode: "agent"Already the default. Spelled out because the other value, "proxy", is what the next examples need, and a file that names its mode is one you can diff against them
network.iface: "auto"Attaches only to physical uplinks - loopback, veth*, CNI and tunnel devices are skipped, so an agent on a Kubernetes node does not land on the CNI datapath
firewall.mode: "auto"Picks the best available backend: XDP, then nftables, then iptables, then nothing
platform.api_keyWithout it synapse starts anyway and logs Gen0Sec API key not provided. Some features may not work. - threat intelligence, fleet bans and telemetry are the features in question
logging.file_logging_enabled: trueOff by default. Without it every log line, including the access log on a proxy, goes to the console instead of {log_directory}
The fingerprint stream needs nothing turned on

logging.eventbridge_log.enabled defaults to true, so {log_directory}/eventbridge.log is written by an agent and a proxy alike with no configuration at all. See Testing for what lands in it and how to read it.

2. Agent with per-interface features​

A gateway with three NICs: a WAN link that gets everything, a LAN link that only gets the firewall, and a management NIC that is deliberately named with nothing enabled so no future change can start attaching to it.

# /etc/synapse/config.yaml
mode: "agent"

network:
# `interfaces` replaces iface/ifaces - leave both of those unset.
interfaces:
wan:
match: { name: eth1 }
features: [firewall, ids, smart_firewall, fingerprint]
require_native: true
lan:
match: { pattern: "enp3s*" }
features: [firewall]
mgmt:
match: { mac: "52:54:00:12:34:56" }
# No `features`: matched, listed in the startup summary, never attached.

firewall:
mode: "auto"
amygdala:
enabled: true

ids:
enabled: true
rule_paths:
- "/etc/synapse/rules/*.rules"
address_vars:
HOME_NET: "[10.0.0.0/8,172.16.0.0/12,192.168.0.0/16]"
EXTERNAL_NET: "!$HOME_NET"
# Alert only until the ruleset has been tuned on real traffic.
enforce_block: false

platform:
api_key: "YOUR_API_KEY"

logging:
level: "info"
file_logging_enabled: true
log_directory: "/var/log/synapse"
module_levels:
synapse_idp: debug

Five things about this file that are easy to get wrong:

  • A per-interface feature still needs its global switch - and the load fails without it. The block says where a feature runs, not whether, and it will not let you say "here" for a feature that is globally off: ids or ids_egress with ids.enabled false, smart_firewall with firewall.amygdala.enabled false, and firewall with firewall.mode: none each stop the start with a named error. That is why both switches are set above. fingerprint is the one feature checked against nothing.
  • match: { name: … } and match: { mac: … } must match a link on this host, or the start fails with matches no interface on this host. So two lines above are the ones to edit before this file runs anywhere: name: eth1 and the mac:. A pattern: that matches nothing is only a warning, which is why lan is written that way.
  • With this block set there is no netfilter fallback. firewall.mode: auto falls back XDP → nftables → iptables only when the block is absent; with it, no XDP means no kernel firewall at all and an error line saying so, because the netfilter backends are host-wide and would enforce on the mgmt NIC this file deliberately leaves out.
  • require_native: true refuses the interface whose driver offers only generic (SKB) XDP, rather than attaching there and running degraded. On a NIC without native XDP that is a link synapse does not protect - which is the point, but it has to be the choice you meant.
  • ssl_uprobe and tls_offload are not valid here. They are features in the schema but not per-interface ones, and naming either fails the load. The five that belong in this block are firewall, ids, ids_egress, smart_firewall and fingerprint.

Per-Interface Features has the matching rules, the validation table and what each feature costs.

3. Proxy terminating TLS​

A reverse proxy holding its own certificates, with routing in a separate file.

# /etc/synapse/config.yaml
mode: "proxy"

network:
iface: "auto"

firewall:
mode: "auto"

ids:
enabled: true
rule_paths:
- "/etc/synapse/rules/*.rules"
enforce_block: false
post_tls:
# Inspect decrypted HTTP inside the proxy process.
mode: inline

platform:
api_key: "YOUR_API_KEY"

proxy:
address_http: "0.0.0.0:80"
address_tls: "0.0.0.0:443"

# Directory scanned for certificates, and the logical name served when no
# loaded certificate matches the requested hostname.
certificates: "/etc/synapse/certs"
default_certificate: "example.com"
tls_grade: "medium"

upstream:
conf: "/etc/synapse/upstreams.yaml"
healthcheck:
method: "HEAD"
interval: 2

# Per-client connection cap, counted per client address (per /64 for IPv6).
# It is ON by default at 512, so this line is documentation rather than a
# change: `0` turns it off, and a number raises or lowers it.
max_downstream_connections_per_client: 512

logging:
level: "info"
file_logging_enabled: true
log_directory: "/var/log/synapse"
# /etc/synapse/upstreams.yaml
version: 2

proxy:
fingerprints:
forward: true
header_names: g0s

headers:
response:
- "Strict-Transport-Security: max-age=31536000; includeSubDomains"

hosts:
example.com:
tls:
terminate:
# A logical NAME, not a path: the file is /etc/synapse/certs/example.com.crt
# with its key beside it as example.com.key.
cert: example.com
paths:
"/":
upstream: "10.0.0.10:8080"
force_https: true
"/api/":
upstream: "10.0.0.11:8080"
timeouts:
connect: 5
read: 60
cert: takes a name, not a path

cert: /etc/synapse/certs/example.com.crt looks right and is not: the value is matched against the logical names of the certificates found in proxy.certificates, so a path matches nothing and the host silently falls back to the default certificate with a warning. A logical name comes from one of two layouts:

On diskLogical name
example.com.crt with example.com.key beside itexample.com
…/live/example.com/fullchain.pem with privkey.pem beside itexample.com

Anything else in the directory is skipped, not reported as an error - including a lone example.com.pem, because the flat layout is matched on .crt and its sibling .key. Upstreams & Routing has the rest of the certificate behaviour.

Three notes on the main config:

  • mode: "proxy" is load-bearing. The schema default is "agent", whatever the comment in the shipped sample says, and an agent binds no listener however complete the proxy: section is.
  • Behind a load balancer, set proxy.trusted_proxies. The per-client cap keys on the socket peer address, so every connection arriving from one load balancer shares a single key and the 512 becomes a cap on the whole site. A peer listed in trusted_proxies is exempt from the per-client cap; it still counts toward proxy.max_downstream_connections.
  • The internal server is on by default, on 127.0.0.1:9180, serving /health and /metrics. It is also reachable through the public listeners, for the reason in Testing. Certificates are not obtained by synapse here - see the ACME warning before reaching for proxy.acme.

4. TLS passthrough for a database​

Postgres on 5432, passed through without terminating, with the real client address handed to the backend. The proxy also serves HTTP on 80 so a redirect or an ACME HTTP-01 solver has somewhere to live.

# /etc/synapse/config.yaml
mode: "proxy"

firewall:
mode: "auto"

platform:
api_key: "YOUR_API_KEY"

proxy:
# Named listeners, instead of address_http / address_tls. Setting both forms
# is refused: there is no precedence between them, so rather than dropping
# one, the proxy logs the refusal and binds nothing.
listeners:
- name: web
address: "0.0.0.0:80"
mode: http
- name: pg
address: "0.0.0.0:5432"
mode: tls

# Required by any `mode: tls` listener, passthrough included. See below.
certificates: "/etc/synapse/certs"

upstream:
conf: "/etc/synapse/upstreams.yaml"

logging:
level: "info"
file_logging_enabled: true
log_directory: "/var/log/synapse"
# /etc/synapse/upstreams.yaml
version: 2

hosts:
db.example.com:
# Only this listener serves the host, so it is never answered on :80.
listeners: [pg]
passthrough:
tls:
upstreams:
- { addr: "10.0.0.20:5432", weight: 2 }
- { addr: "10.0.0.21:5432", weight: 1 }
lb:
# `round_robin` ignores `weight:` entirely. Weighted entries need
# this method, or the weights are config that does nothing.
method: weighted_round_robin
health_check:
type: tcp
# Seconds a tunnel may carry nothing before both sockets are closed.
# 0 means no bound. This is not `timeouts.idle`, which is the upstream
# connection pool's keepalive and would tear down live sessions.
idle_timeout: 3600
max_connections: 500
# Prepend a PROXY protocol v2 header so the database sees the real client.
proxy_protocol_v2: true
A passthrough host is matched by SNI, one label deep

On a tls listener the host is taken from the ClientHello SNI, so a client that sends no SNI never reaches this host. A wildcard key matches one label on the passthrough router where the HTTP table matches at any depth - *.example.com covers db.example.com and not a.b.example.com. Upstreams & Routing has the matching ladder.

Two things about this file that are not obvious from it:

  • A mode: tls listener needs proxy.certificates even when nothing terminates. Passthrough never reads a certificate, but the TLS acceptor is built before the router knows that, and with the key unset the proxy task panics - leaving a live process that has bound neither port and logs nothing pointing at the cause. A directory that is missing or holds no PEM only warns, so the value above is enough for a pure-passthrough proxy. It is also the line to resist deleting.
  • "Refused" here means the proxy does not start, not that synapse exits. Setting listeners alongside address_http/address_tls logs the proxy will not start and resolves to an empty listener set, which start treats as "proxy disabled". The process loads, stays up and serves nothing - so a unit file watching the exit code sees success.

5. Windows agent​

Windows builds carry no proxy and no BPF, so this is agent-only: NDIS capture, the Windows IDS engine, and logs in ProgramData.

# C:\ProgramData\Gen0Sec\Synapse\config.yaml
mode: "agent"

network:
iface: "auto"

capture:
enabled: true
# On Windows `auto` resolves to NDIS, with a line in the log saying so.
mode: auto

firewall:
mode: "auto"

ids:
enabled: true
rule_paths:
- "C:\\ProgramData\\Gen0Sec\\Synapse\\rules\\*.rules"
enforce_block: false

platform:
api_key: "YOUR_API_KEY"
threat:
enabled: true
# Without `url` the database is never downloaded or refreshed: the refresh
# worker is registered only for a non-empty url, and the initial load from
# `path` then warns and leaves the subsystem empty.
url: "https://download.gen0sec.com/v1"
path: "C:\\ProgramData\\Gen0Sec\\Synapse"
geoip:
enabled: true
country:
url: "https://github.com/gen0sec/geoip-databases/raw/download/ipinfo_lite.mmdb"
path: "C:\\ProgramData\\Gen0Sec\\Synapse\\ipinfo_lite.mmdb"

logging:
level: "info"
file_logging_enabled: true
log_directory: "C:\\ProgramData\\Gen0Sec\\Synapse\\logs"
windows_event_log:
enabled: true
source: "Synapse"
  • ids.enabled: true runs a different engine here. On Windows it starts the thalamus_ids_windows worker rather than the XDP one, and it does not consult ids.capture_mode - which is why that key, set to xdp in the shipped Windows sample, is left out above.
  • There is no daemon: section here on purpose. Daemonizing is a Unix path: daemon.enabled: true on Windows logs Daemonization is not supported on this platform, ignoring daemon config and daemon.working_directory is never read. The service is installed with synapse.exe --service install, and the service manager starts it with --daemon itself.
  • Backslashes need doubling inside double quotes in YAML. "C:\ProgramData\…" is a parse error on \P; single quotes ('C:\ProgramData\…') would also work.
  • logging.windows_event_log defaults to off, so the line above is what puts events in the Windows event log. logging.etw is separate and also off by default.

What these leave out, and why​

The shipped samples set keys the schema does not accept. They are not harmless: each one lands in the startup warning and nothing else happens.

Left outWhy
logging.bpf_stats and logging.tcp_fingerprintNot in the schema at 0.8.8. The statistics they look like they control are produced anyway - see Testing
logging.{ssh,latency,tls,http}_fingerprint and logging.fingerprint_log's per-type siblingsSame: six per-type sections, none of them read. The one that is real is logging.eventbridge_log, which fingerprint_log is an accepted alias for
proxy.acmeStill in the schema and still ships, but it is not a supported way to get a certificate - see the ACME warning
proxy.content_scanningIn the schema, off by default, and needs a ClamAV server; it is not part of a starting configuration
capture.pfringNo published package carries the pfring-capture build feature, so the block cannot take effect

Checking a file before you ship it​

There is no validate-and-exit flag at 0.8.8. The check is the first start:

sudo /usr/bin/synapse --config /etc/synapse/config.yaml 2>&1 |
grep -E 'Unused config options|Deprecated|not provided'

Unused config options in <path>: … is the line that catches a typo, a key at the wrong nesting level, or a section that was removed from the schema. It names the subtree once rather than every key beneath it, so ml in that list means the whole ml: section was ignored - on a build without the classifier, that is expected rather than a mistake.

A key that is spelled correctly but does nothing will not appear there, because it parsed. Those are the ones to watch for, and they are called out where they exist: proxy.allow_connect_method_proxying, which is forced back to false with an error line; a v2 internal: list, which is parsed, collision-checked and then dropped; lb.method on a terminating host, where it only takes effect on passthrough; and a weight: under round_robin, as above.

Next Steps​