Helm: Using your own Ingress solution
The Helm chart does not require any particular Ingress solution. The
Ingress resource it can create (ingress.enabled, disabled by
default) is a convenience, not a requirement; any Ingress controller,
Gateway API implementation, service mesh, or cloud load balancer
works, as long as it fulfills the small contract below.
What Zulip needs from the proxy in front of it
Whatever routes traffic to Zulip must:
Route HTTP to the chart’s Service. By default, the Service listens on port 80 and the Zulip pod serves unencrypted HTTP; see Helm: Configuring TLS.
Terminate TLS, and set
X-Forwarded-Proto. Zulip requires HTTPS in production. The proxy must set theX-Forwarded-Protoheader tohttps, overriding any client-supplied value; Django uses it for CSRF checks and secure cookies.Set
X-Forwarded-For, and be listed inLOADBALANCER_IPS. Zulip only trustsX-Forwarded-*headers from source addresses listed inLOADBALANCER_IPS, so that clients cannot spoof their IP addresses to evade rate limiting and audit logging. See Helm: Configuring LOADBALANCER_IPS.Pass the client’s
Host:header through unchanged, and route the hostname inSETTING_EXTERNAL_HOST(and any realm subdomains of it) to the Service.Permit long-lived requests. Zulip delivers real-time events over HTTP long-polling, so idle/read timeouts on the route must be significantly longer than 60 seconds (
proxy_read_timeoutin nginx terms), and response buffering should be disabled if your proxy buffers by default.
These are the same requirements as for any reverse proxy in front of a standalone Zulip server; see Reverse proxies.
What stays Zulip configuration
Two settings remain with Zulip no matter which Ingress solution you choose, because they are application configuration, not proxy configuration:
SETTING_EXTERNAL_HOSTis the canonical hostname users reach the server at. Zulip uses it to build absolute URLs in contexts where there is no request to derive a hostname from – invitation and notification emails, and API and mobile-app server URLs – and to validate theHost:header of incoming requests (Django’sALLOWED_HOSTS), which protects against Host-header attacks such as password-reset link poisoning. It cannot safely be derived from the request.LOADBALANCER_IPStells Zulip which source addresses are your proxy layer, and hence when to believeX-Forwarded-*headers.
Both are statements of fact about the network the administrator built, which Zulip cannot discover on its own; beyond them, DNS, TLS, routing, and traffic policy are entirely up to your Ingress layer.