Skip to main content

Kubernetes Ingress & Gateway API

Synapse can act as a native Kubernetes Ingress controller and Gateway API implementation. The synapse-operator watches your Ingress, IngressClass, Gateway, and HTTPRoute resources and programs the Synapse reverse proxy in real time — so Synapse becomes a drop-in replacement for Traefik / ingress-nginx while keeping its eBPF firewall, WAF, and JA4+ fingerprinting on the same data path.

It integrates with cert-manager for automatic TLS, serves many domains from one proxy via SNI, and picks up new or rotated certificates without a restart.

note

This page covers the controller. To install the Synapse stack itself, see Installation.

How it works

synapse-operator runs in ingress mode alongside the Synapse proxy. On every change to the resources it owns it re-renders Synapse's upstream configuration and triggers a hot reload — no proxy restart, no dropped connections.

  • Ingress + Gateway API are reconciled into the same routing table, so you can mix both.
  • Backends are addressed by in-cluster DNS (svc.namespace.svc.cluster.local:port); named Service ports are resolved automatically and a Service change re-renders.
  • Routing is deterministic: sources are processed in a stable order (Ingresses before HTTPRoutes, each sorted by namespace/name) with first-writer-wins on a host+path conflict, so the result is reproducible regardless of watch ordering.

Deploy the controller

Run the operator in ingress mode (it co-locates with the proxy and writes the proxy's upstreams + certificates):

--ingress-mode
--gateway-api # also reconcile Gateway API (optional)
--ingress-class=synapse # spec.ingressClassName this controller serves
--certs-out=/certs # project referenced TLS Secrets here (optional)
--status-leader-election # see "Scaling" below (multi-replica)

Create the IngressClass:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: synapse
# Optional: make Synapse the default for class-less Ingresses
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: gen0sec.com/synapse

The controller needs RBAC to get/list/watch ingresses, ingressclasses, services, secrets (and the Gateway API resources when --gateway-api is set), plus update on their /status, create/patch on events, and coordination.k8s.io/leases when leader election is enabled.

Routing with Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
spec:
ingressClassName: synapse
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80

Class selection follows Kubernetes precedence: an explicit spec.ingressClassName wins; otherwise the legacy kubernetes.io/ingress.class annotation; otherwise the default IngressClass (when annotated is-default-class: "true").

note

Exact path types and HTTPRoute header/method/query matches are approximated or ignored (Synapse routes on host + longest-prefix path); the controller logs a warning and emits a Kubernetes event so the behaviour is never silent.

Upstream settings via annotations

Annotations on the Ingress (or HTTPRoute) tune the upstream. The synapse.gen0sec.com/ prefix is canonical; a small nginx.ingress.kubernetes.io/ compatibility subset is also accepted.

AnnotationEffect
backend-protocol: HTTP|HTTPSTLS to the upstream
http2: "true"HTTP/2 to the upstream
force-https / ssl-redirect: "true"redirect HTTP→HTTPS
connect-timeout / read-timeout / write-timeout / idle-timeoutseconds
healthcheck: "true"upstream health checking
disable-access-log: "true"drop access logs for this route
request-headers / response-headerscomma-separated Name: value injection
sticky-sessions: "true"sticky upstream selection

Routing with Gateway API

Create a GatewayClass whose controllerName is gen0sec.com/synapse, a Gateway, and HTTPRoutes bound to it:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app
spec:
parentRefs:
- name: synapse-gw
hostnames: ["app.example.com"]
rules:
- matches:
- path: { type: PathPrefix, value: / }
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set: [{ name: X-From, value: gateway }]
backendRefs:
- { name: app-v1, port: 80, weight: 90 }
- { name: app-v2, port: 80, weight: 10 }
  • Weighted backendRefs are honored, including Gateway API semantics where weight: 0 means receive no traffic (the backend is excluded).
  • RequestHeaderModifier / ResponseHeaderModifier filters are translated to header injection.
  • The controller maintains GatewayClass, Gateway, and HTTPRoute status conditions (Accepted, Programmed, ResolvedRefs).

Smart firewall, WAF & IDS on ingress traffic

Because Synapse is the proxy, its eBPF smart firewall, WAF, and signature IDS/IPS run on the very same data path as the Ingress/Gateway traffic — no sidecar, no second hop. These are configured on the Synapse side (config file + rules), not via Ingress annotations (the controller annotations above only tune the upstream); they then apply to every route the controller programs.

Smart firewall (eBPF)

Set in the Synapse config:

firewall:
mode: "auto" # auto | xdp | nftables | iptables | none
enabled: true # false → pass all traffic, rules not evaluated
default_action: "allow" # action when no rule matches: allow | drop

Kernel firewall rules are evaluated in ascending order, first match wins (action: allow/drop), matching on src_ip/dst_ip (+src_ip_prefix/dst_ip_prefix), src_*_port/dst_*_port, ip_version, ip_protocol. The JA4/JA4T/JA4H smart-firewall plus threat- and anomaly-detection scoring also evaluate ingress clients on this path. See Firewall Rules, Threat Detection, and Anomaly Detection.

WAF

The WAF inspects ingress HTTP requests using Wirefilter-compatible expression rules (Cloudflare Ruleset-Engine style) with OWASP Top 10 coverage — e.g. matching on http.request.method, http.request.host, http.request.path, http.request.uri, http.request.user_agent, with and/or/not, contains, matches, in, etc., and an action on match. Rules are managed centrally (per organization), not per-Ingress. See WAF and Access Rules.

Intrusion detection (IDS / IPS)

Synapse runs a signature IDS/IPS engine (thalamus) that inspects decrypted, post-TLS HTTP on the same path as ingress traffic — so it sees real requests even for HTTPS that Synapse terminates. Configured under ids: in the Synapse config:

ids:
enabled: true
rule_paths: # signature rule files (Suricata / Emerging Threats compatible)
- /etc/synapse/rules/emerging-all.rules
address_vars: { HOME_NET: "10.0.0.0/8" } # rule $VARS
port_vars: { HTTP_PORTS: "80,8080" }
enforce_block: false # false = IDS (detect/alert); true = IPS (drop on match)
post_tls:
mode: inline # inline | offload | off
socket: /run/synapse/ids.sock # offload: per-node consumer UDS
# flow/capture tuning: snaplen, poll_timeout_ms, flow_timeout_secs,
# max_flows, cleanup_interval_secs, stats_log_interval_secs
  • enabled turns the engine on. rule_paths point at signature files; address_vars/port_vars populate rule variables (e.g. $HOME_NET, $HTTP_PORTS).
  • enforce_block: false = detect & alert (IDS); true = block matched requests (IPS).
  • post_tls.mode: inline runs the engine in every proxy process (simple, but a horizontally-scaled proxy holds the ruleset in every replica — O(replicas)); offload keeps the proxy light and taps decrypted HTTP to an out-of-process per-node consumer over post_tls.socket (ruleset cost O(nodes)); off disables post-TLS inspection. With >1 replica, prefer offload.
  • Advanced: post_tls.enforce (default off) additionally bans the proxy-observed source IP in the agent firewall on a block detection — guarded against shared/NAT IPs; distinct from enforce_block.

See Architecture overview and Data flow for where IDS/IPS sits, and Threat Feeds API to feed blocklists.

Rate limiting

The Synapse upstreams routing config (which the controller renders) supports a global_rate_limit and per-host / per-path rate_limit; these bound request rates on the programmed ingress routes. See Configuration.

note

Firewall mode, firewall/WAF/IDS rules, and rate limits are Synapse configuration — they are not expressed as Ingress annotations. The controller renders routing/upstreams; the firewall, WAF, and IDS apply globally to the proxy and therefore to all ingress traffic.

TLS with cert-manager

Automatic certificates (HTTP-01)

Annotate the Ingress for cert-manager and declare spec.tls. The HTTP-01 challenge is served straight through Synapse:

metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: synapse
tls:
- hosts: [app.example.com]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- { path: /, pathType: Prefix, backend: { service: { name: app, port: { number: 80 } } } }

Multi-domain TLS via SNI, no restart

When --certs-out is set, the controller projects every referenced TLS Secret (Ingress spec.tls, Gateway listener certificateRefs) into Synapse's certificate directory. Synapse then selects the right certificate per SNI hostname, so a single proxy serves many domains. Adding, rotating (cert-manager renewal), or removing a certificate is picked up live — no pod restart; an unreferenced certificate is pruned and that hostname falls back to the default certificate.

tip

This works per-pod and is never gated by leader election, so every replica serves correct certificates immediately.

Scaling (multiple replicas)

Every proxy replica renders its own local config and reloads its own Synapse — that is never gated. To avoid replicas racing on shared cluster status, enable --status-leader-election: only the lease holder writes Gateway/HTTPRoute/Ingress status, and leadership fails over automatically if the holder dies.

Observability

  • Kubernetes events on the Ingress/HTTPRoute: Programmed, RouteConflict, BackendUnresolved, UnsupportedMatch.
  • Prometheus metrics on the metrics endpoint: renders, changes, reload signals, route conflicts, unresolved backends, projected certs, hosts/routes gauges, and a readiness gauge.
  • Readiness is gated on the first successful render, so the proxy is not advertised ready before it has correct upstreams.

Migrating from Traefik

  1. Deploy synapse-operator in ingress mode and create the synapse IngressClass.
  2. Repoint Ingresses by setting spec.ingressClassName: synapse (or make Synapse the default class).
  3. Move Traefik-specific annotations to the synapse.gen0sec.com/ equivalents above.
  4. cert-manager Certificate/Issuer resources are unchanged — HTTP-01 via the ingress solver works as-is.
  5. Remove Traefik once traffic is verified on Synapse.