diff --git a/docs/overview/changelog.md b/docs/overview/changelog.md index ffc34d02a9..b5bb930cec 100644 --- a/docs/overview/changelog.md +++ b/docs/overview/changelog.md @@ -307,7 +307,7 @@ log][commit-log] for an up-to-date list of raw changes. major release. [docker-zulip-manual]: https://github.com/zulip/docker-zulip#manual-configuration -[smokescreen]: ../production/deployment.html#using-an-outgoing-http-proxy +[smokescreen]: ../production/deployment.html#customizing-the-outgoing-http-proxy [update-settings-docs]: ../production/upgrade-or-modify.html#updating-settings-py-inline-documentation #### Full feature changelog diff --git a/docs/production/deployment.md b/docs/production/deployment.md index de53b29842..0016266c3e 100644 --- a/docs/production/deployment.md +++ b/docs/production/deployment.md @@ -223,28 +223,14 @@ behind reverse proxies. [using-http]: ../production/deployment.html#configuring-zulip-to-allow-http -## Using an outgoing HTTP proxy +## Customizing the outgoing HTTP proxy -Zulip supports routing all of its outgoing HTTP and HTTPS traffic -through an HTTP `CONNECT` proxy, such as [Smokescreen][smokescreen]; -this includes outgoing webhooks, image and website previews, and -mobile push notifications. You may wish to enable this feature to -provide a consistent egress point, or enforce access control on URLs -to prevent [SSRF][ssrf] against internal resources. +To protect against [SSRF][ssrf], Zulip 4.8 and above default to +routing all outgoing HTTP and HTTPS traffic through +[Smokescreen][smokescreen], an HTTP `CONNECT` proxy; this includes +outgoing webhooks, website previews, and mobile push notifications. -To use Smokescreen: - -1. Add `, zulip::profile::smokescreen` to the list of `puppet_classes` - in `/etc/zulip/zulip.conf`. A typical value after this change is: - - ```ini - puppet_classes = zulip::profile::standalone, zulip::profile::smokescreen - ``` - -1. Optionally, configure the [smokescreen ACLs][smokescreen-acls]. By - default, Smokescreen denies access to all [non-public IP - addresses](https://en.wikipedia.org/wiki/Private_network), including - 127.0.0.1. +To use a custom outgoing proxy: 1. Add the following block to `/etc/zulip/zulip.conf`, substituting in your proxy's hostname/IP and port: @@ -255,19 +241,28 @@ To use Smokescreen: port = 4750 ``` -1. If you intend to also make the Smokescreen install available to - other hosts, set `listen_address` in the same block. Note that you - must control access to the Smokescreen port if you do this, as - failing to do so opens a public HTTP proxy! - 1. As root, run `/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This - will compile and install Smokescreen, reconfigure services to use - it, and restart Zulip. + will reconfigure and restart Zulip. -If you would like to use an already-installed HTTP proxy, omit the -first step, and adjust the IP address and port in the second step -accordingly. +If you have a deployment with multiple frontend servers, or wish to +install Smokescreen on a separate host, you can apply the +`zulip::profile::smokescreen` Puppet class on that host, and follow +the above steps, setting the `[http_proxy]` block to point to that +host. + +If you wish to disable the outgoing proxy entirely, follow the above +steps, configuring an empty `host` value. + +Optionally, you can also configure the [Smokescreen ACL +list][smokescreen-acls]. By default, Smokescreen denies access to all +[non-public IP +addresses](https://en.wikipedia.org/wiki/Private_network), including +127.0.0.1, but allows traffic to all public Internet hosts. + +In Zulip 4.7 and older, to enable SSRF protection via Smokescreen, you +will need to explicitly add the `zulip::profile::smokescreen` Puppet +class, and configure the `[http_proxy]` block as above. [smokescreen]: https://github.com/stripe/smokescreen [smokescreen-acls]: https://github.com/stripe/smokescreen#acls @@ -651,11 +646,13 @@ load balancers whose `X-Forwarded-For` should be respected. #### `host` The hostname or IP address of an [outgoing HTTP `CONNECT` -proxy](#using-an-outgoing-http-proxy). +proxy](#customizing-the-outgoing-http-proxy). Defaults to `localhost` +if unspecified. #### `port` The TCP port of the HTTP `CONNECT` proxy on the host specified above. +Defaults to `4750` if unspecified. #### `listen_address` diff --git a/docs/production/mobile-push-notifications.md b/docs/production/mobile-push-notifications.md index b2a73c0c74..797eee6c8c 100644 --- a/docs/production/mobile-push-notifications.md +++ b/docs/production/mobile-push-notifications.md @@ -18,7 +18,7 @@ support forwarding push notifications to a central push notification forwarding service. Accessing this service requires outgoing HTTPS access to the public Internet; if that is restricted by a proxy, you will need to [configure Zulip to use your outgoing HTTP -proxy](../production/deployment.html#using-an-outgoing-http-proxy) +proxy](../production/deployment.html#customizing-the-outgoing-http-proxy) first. You can enable this for your Zulip server as follows: diff --git a/docs/production/requirements.md b/docs/production/requirements.md index 743e267f66..2abfef45b5 100644 --- a/docs/production/requirements.md +++ b/docs/production/requirements.md @@ -96,13 +96,14 @@ on hardware requirements for larger organizations. address as its external hostname (though we don't recommend that configuration). - Zulip supports [running behind a reverse proxy][reverse-proxy]. -- Zulip servers running inside a private network should configure the - [Smokescreen integration][smokescreen-proxy] to protect against - [SSRF attacks][ssrf], where users could make the Zulip server make - requests to private resources. +- Zulip configures [Smokescreen, and outgoing HTTP + proxy][smokescreen-proxy], to protect against [SSRF attacks][ssrf], + which prevents user from making the Zulip server make requests to + private resources. If your network has its own outgoing HTTP proxy, + Zulip supports using that instead. [ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery -[smokescreen-proxy]: ../production/deployment.html#using-an-outgoing-http-proxy +[smokescreen-proxy]: ../production/deployment.html#customizing-the-outgoing-http-proxy [reverse-proxy]: ../production/deployment.html#putting-the-zulip-application-behind-a-reverse-proxy [email-mirror-code]: https://github.com/zulip/zulip/blob/main/zerver/management/commands/email_mirror.py diff --git a/docs/production/security-model.md b/docs/production/security-model.md index 08924f10a3..86aa2483b5 100644 --- a/docs/production/security-model.md +++ b/docs/production/security-model.md @@ -259,15 +259,15 @@ strength allowed is controlled by two settings in - Mobile push notifications (must be configured to be enabled) - Notably, these first 3 features give end users (limited) control to cause - the Zulip server to make HTTP requests on their behalf. As a result, - Zulip supports routing all outgoing outgoing HTTP requests [through + the Zulip server to make HTTP requests on their behalf. Because of this, + Zulip routes all outgoing outgoing HTTP requests [through Smokescreen][smokescreen-setup] to ensure that Zulip cannot be used to execute [SSRF attacks][ssrf] against other systems on an internal corporate network. The default Smokescreen configuration denies access to all non-public IP addresses, including 127.0.0.1. [ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery -[smokescreen-setup]: ../production/deployment.html#using-an-outgoing-http-proxy +[smokescreen-setup]: ../production/deployment.html#customizing-the-outgoing-http-proxy ## Final notes and security response diff --git a/puppet/zulip/manifests/app_frontend_base.pp b/puppet/zulip/manifests/app_frontend_base.pp index 204015d167..7517e3e82c 100644 --- a/puppet/zulip/manifests/app_frontend_base.pp +++ b/puppet/zulip/manifests/app_frontend_base.pp @@ -96,8 +96,14 @@ class zulip::app_frontend_base { $uwsgi_default_processes = 4 } $tornado_ports = $zulip::tornado_sharding::tornado_ports - $proxy_host = zulipconf('http_proxy', 'host', '') - $proxy_port = zulipconf('http_proxy', 'port', '') + + $proxy_host = zulipconf('http_proxy', 'host', 'localhost') + $proxy_port = zulipconf('http_proxy', 'port', '4750') + + if ($proxy_host in ['localhost', '127.0.0.1', '::1']) and ($proxy_port == '4750') { + include zulip::smokescreen + } + if $proxy_host != '' and $proxy_port != '' { $proxy = "http://${proxy_host}:${proxy_port}" } else { diff --git a/puppet/zulip/manifests/app_frontend_once.pp b/puppet/zulip/manifests/app_frontend_once.pp index 2a423b749c..fc46b49706 100644 --- a/puppet/zulip/manifests/app_frontend_once.pp +++ b/puppet/zulip/manifests/app_frontend_once.pp @@ -2,8 +2,8 @@ # in a cluster. class zulip::app_frontend_once { - $proxy_host = zulipconf('http_proxy', 'host', '') - $proxy_port = zulipconf('http_proxy', 'port', '') + $proxy_host = zulipconf('http_proxy', 'host', 'localhost') + $proxy_port = zulipconf('http_proxy', 'port', '4750') if $proxy_host != '' and $proxy_port != '' { $proxy = "http://${proxy_host}:${proxy_port}" } else {