diff --git a/docs/production/reverse-proxies.md b/docs/production/reverse-proxies.md index 45b8b65f08..cc91190da3 100644 --- a/docs/production/reverse-proxies.md +++ b/docs/production/reverse-proxies.md @@ -244,6 +244,11 @@ things you need to be careful about when configuring it: connection mechanism from your proxy to Zulip. Note that the proxies _must_ set the header, overriding any existing values, not add a new header. + If your proxy _cannot_ set the `X-Forwarded-Proto` header, you can opt to do + all HTTP-to-HTTPS redirection at the load-balancer level, and set + [`loadbalancer.rejects_http_requests` in `zulip.conf`][no-proto-header]; but + note the important security caveats for that in its documentation. + 1. Configure your proxy to pass along the `Host:` header as was sent from the client, not the internal hostname as seen by the proxy. If this is not possible, you can set `USE_X_FORWARDED_HOST = True` @@ -272,4 +277,5 @@ things you need to be careful about when configuring it: with multiple IPs for your Zulip machine; sometimes this happens with IPv6 configuration). +[no-proto-header]: system-configuration.md#rejects_http_requests [nginx-proxy-longpolling-config]: https://github.com/zulip/zulip/blob/main/puppet/zulip/files/nginx/zulip-include-common/proxy_longpolling diff --git a/docs/production/system-configuration.md b/docs/production/system-configuration.md index 95c13f52c6..98a1f3ab1f 100644 --- a/docs/production/system-configuration.md +++ b/docs/production/system-configuration.md @@ -370,6 +370,16 @@ Comma-separated list of IP addresses or netmasks of external load balancers whose `X-Forwarded-For` and `X-Forwarded-Proto` should be respected. These can be individual IP addresses, or CIDR IP address ranges. +#### `rejects_http_requests` + +Set to a true value if incoming requests from load loadbalancer's IP addresses +which do not contain an `X-Forwarded-Proto` should be assumed to have come into +them over HTTPS. This setting _is a security vulnerability_ unless the load +balancer unilaterally rejects unencrypted HTTP connections, or responds to them +with 301 status codes. Note that Zulip's HSTS headers are not sufficient +protection here, since API clients do not respect them; the load balancer _must +not_ send any requests to Zulip which came in unencrypted. + ### `[http_proxy]` #### `host` diff --git a/puppet/zulip/manifests/nginx.pp b/puppet/zulip/manifests/nginx.pp index 48d4d56a46..5d3d2d062e 100644 --- a/puppet/zulip/manifests/nginx.pp +++ b/puppet/zulip/manifests/nginx.pp @@ -56,6 +56,7 @@ class zulip::nginx { } $loadbalancers = split(zulipconf('loadbalancer', 'ips', ''), ',') + $lb_rejects_http_requests = zulipconf('loadbalancer', 'rejects_http_requests', false) file { '/etc/nginx/zulip-include/trusted-proto': ensure => file, require => Package[$zulip::common::nginx], diff --git a/puppet/zulip/templates/nginx/trusted-proto.template.erb b/puppet/zulip/templates/nginx/trusted-proto.template.erb index 0c8d249383..87b27d7f38 100644 --- a/puppet/zulip/templates/nginx/trusted-proto.template.erb +++ b/puppet/zulip/templates/nginx/trusted-proto.template.erb @@ -31,14 +31,19 @@ geo $remote_addr $is_from_proxy { # We set $trusted_x_forwarded_proto in two steps because `geo` does # not support variable interpolation in the value, but does support # CIDR notation, which the loadbalancer list may use. -map $is_x_forwarded_proto_trusted $trusted_x_forwarded_proto { - 0 $scheme; - 1 $http_x_forwarded_proto; +map "$is_x_forwarded_proto_trusted:$http_x_forwarded_proto" $trusted_x_forwarded_proto { + "~^0:" $scheme; +<%- if @lb_rejects_http_requests -%> + "~^1:$" "https"; +<% end -%> + "~^1:" $http_x_forwarded_proto; } map "$is_from_proxy:$is_x_forwarded_proto_trusted:$http_x_forwarded_proto" $x_proxy_misconfiguration { "~^0:0:" "Incorrect reverse proxy IPs set in Zulip (try $remote_addr?); see https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html"; +<%- if not @lb_rejects_http_requests -%> "~^0:1:$" "No X-Forwarded-Proto header sent from trusted proxy $realip_remote_addr; see example configurations in https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html"; +<% end -%> default ""; } <% end %>