Running the proxy
Applies to SynapseOS builds from 2026-10-09 (20261009-141152 and later). See What changed.
The proxy is already on the unit. Give it its own configuration before you start it.
What ships
Synapse is one binary with two modes. The agent (synapse-agent.service) runs on every
appliance; the proxy (synapse-proxy.service) is installed beside it and left disabled.
Nothing needs installing.
As shipped, synapse-proxy.service reads the same /etc/synapse/config.yaml as the agent.
One file holds one mode, one firewall block and one capture block, so a proxy started
that way re-attaches the kernel packet filter on the external interface and takes
enforcement away from the agent. Both services keep reporting healthy while that happens.
The steps below give the proxy a configuration of its own first.
Derive a configuration for the proxy
Start from the agent's file and flip only what has to differ. The agent keeps packet capture, detection and kernel enforcement; the proxy keeps everything else, so a later Synapse release that adds settings reaches both files.
python3 - <<'EOF'
import os, yaml
c = yaml.safe_load(open("/etc/synapse/config.yaml"))
def section(name):
if not isinstance(c.get(name), dict): # absent, or present but empty
c[name] = {}
return c[name]
c["mode"] = "proxy"
section("capture")["enabled"] = False # the agent owns packet capture
section("ids")["enabled"] = False # the agent owns detection
section("firewall")["mode"] = "none" # the agent owns the kernel filter
# Created with its final mode: the file carries platform.api_key.
fd = os.open("/etc/synapse/config-proxy.yaml", os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o640)
with os.fdopen(fd, "w") as f:
yaml.safe_dump(c, f, default_flow_style=False, sort_keys=False)
EOF
Rerun this after changing the agent's file, so the two do not drift apart.
Point the service at it
[Service]
ExecStart=
ExecStart=/usr/bin/synapse --daemon --daemon-pid-file /run/synapse-proxy.pid --config /etc/synapse/config-proxy.yaml
The empty ExecStart= line clears the shipped command before the new one is set; systemd
appends otherwise.
Say where traffic goes
The proxy routes by hostname, from /etc/synapse/upstreams.yaml. A minimal file with one
host, TLS terminated on the appliance:
version: 2
hosts:
"app.example.com":
tls:
terminate:
cert: "/etc/synapse/certs/default.crt"
key: "/etc/synapse/certs/default.key"
upstream: "10.0.0.5:443"
Every terminating host needs the tls.terminate block, even one only ever served over
plain HTTP. A file that does not parse leaves the proxy with no routes at all, so every
host answers 404, not only the broken one. Passthrough hosts, weighted backends, health
checks and the rest of the format are on the
Configuration page.
Provide a certificate
If /etc/synapse/certs/ holds your certificate already, skip this. Otherwise a self-signed
placeholder lets the TLS listener start; clients will not trust it, so replace it with a
real one.
mkdir -p /etc/synapse/certs
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
-keyout /etc/synapse/certs/default.key -out /etc/synapse/certs/default.crt \
-subj "/CN=default.$(hostname)"
chmod 0640 /etc/synapse/certs/default.key
Open the ports
The proxy listens on ports 80 and 443 by default (proxy.address_http and
proxy.address_tls), and the base firewall drops inbound traffic to both until told
otherwise. Add them to the services chain and reload it; see
The base firewall.
tcp dport { 80, 443 } accept comment "synapse proxy"
Start it
systemctl daemon-reload
systemctl enable --now synapse-proxy
systemctl status synapse-proxy synapse-agent
Both services should be active. The agent still owns the packet filter; the proxy serves
the hosts in upstreams.yaml, and reloads that file whenever it changes.
These are the steps Gen0Sec's own configuration tooling performs, written out for a unit you manage by hand. The upstreams format shown is the one Synapse 0.8.5 and later read.