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 scoring and ML classification also evaluate ingress clients on this path. See Firewall Rules, Threat Detection, and Machine Learning Models.

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.