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.
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:
| Line | What 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_key | Without 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: true | Off by default. Without it every log line, including the access log on a proxy, goes to the console instead of {log_directory} |
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:
idsorids_egresswithids.enabledfalse,smart_firewallwithfirewall.amygdala.enabledfalse, andfirewallwithfirewall.mode: noneeach stop the start with a named error. That is why both switches are set above.fingerprintis the one feature checked against nothing. match: { name: … }andmatch: { mac: … }must match a link on this host, or the start fails withmatches no interface on this host. So two lines above are the ones to edit before this file runs anywhere:name: eth1and themac:. Apattern:that matches nothing is only a warning, which is whylanis written that way.- With this block set there is no netfilter fallback.
firewall.mode: autofalls 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 themgmtNIC this file deliberately leaves out. require_native: truerefuses 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_uprobeandtls_offloadare 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 arefirewall,ids,ids_egress,smart_firewallandfingerprint.
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 pathcert: /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 disk | Logical name |
|---|---|
example.com.crt with example.com.key beside it | example.com |
…/live/example.com/fullchain.pem with privkey.pem beside it | example.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 theproxy: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 intrusted_proxiesis exempt from the per-client cap; it still counts towardproxy.max_downstream_connections. - The internal server is on by default, on
127.0.0.1:9180, serving/healthand/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 forproxy.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
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: tlslistener needsproxy.certificateseven 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
listenersalongsideaddress_http/address_tlslogsthe proxy will not startand resolves to an empty listener set, whichstarttreats 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: trueruns a different engine here. On Windows it starts thethalamus_ids_windowsworker rather than the XDP one, and it does not consultids.capture_mode- which is why that key, set toxdpin the shipped Windows sample, is left out above.- There is no
daemon:section here on purpose. Daemonizing is a Unix path:daemon.enabled: trueon Windows logsDaemonization is not supported on this platform, ignoring daemon configanddaemon.working_directoryis never read. The service is installed withsynapse.exe --service install, and the service manager starts it with--daemonitself. - 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_logdefaults to off, so the line above is what puts events in the Windows event log.logging.etwis 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 out | Why |
|---|---|
logging.bpf_stats and logging.tcp_fingerprint | Not 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 siblings | Same: 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.acme | Still in the schema and still ships, but it is not a supported way to get a certificate - see the ACME warning |
proxy.content_scanning | In the schema, off by default, and needs a ClamAV server; it is not part of a starting configuration |
capture.pfring | No 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
- Configuration - every main-config section, with code defaults beside the values the shipped sample sets
- Configuration Interfaces - the CLI flags and environment variables that override any of this, and which wins
- Upstreams & Routing - the whole
upstreams.yamlschema - Per-Interface Features -
network.interfacesin full - Testing - confirming a running agent is doing what the file says