puppet: Add options for allow/deny rules in Smokescreen.

Fixes: #20490
This commit is contained in:
Alex Vandiver
2025-09-03 13:26:34 -04:00
committed by Tim Abbott
parent 473c8fa81a
commit 4739c4b057
4 changed files with 76 additions and 28 deletions

View File

@@ -246,15 +246,32 @@ behind reverse proxies.
## Customizing the outgoing HTTP proxy ## Customizing the outgoing HTTP proxy
To protect against [SSRF][ssrf], Zulip 4.8 and above default to To protect against [SSRF][ssrf], Zulip routes all outgoing HTTP and
routing all outgoing HTTP and HTTPS traffic through HTTPS traffic through [Smokescreen][smokescreen], an HTTP `CONNECT`
[Smokescreen][smokescreen], an HTTP `CONNECT` proxy; this includes proxy; this includes outgoing webhooks, website previews, and mobile
outgoing webhooks, website previews, and mobile push notifications. push notifications.
By default, the Camo image proxy will be automatically configured to
use a custom outgoing proxy, but does not use Smokescreen by default ### IP address rules
because Camo includes similar logic to deny access to private
subnets. You can [override][proxy.enable_for_camo] this default By default, Smokescreen denies access to all [non-public IP
configuration if desired. addresses](https://en.wikipedia.org/wiki/Private_network), including
127.0.0.1, but allows traffic to all public Internet hosts. You can
[adjust those rules][smokescreen-acls]. For instance, if you have an
outgoing webhook at `http://10.17.17.17:80/`, you would need to:
1. Add the following block to `/etc/zulip/zulip.com`, substituting
your internal host's IP address:
```ini
[http_proxy]
allow_addresses = 10.17.17.17
```
1. As root, run
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This
will reconfigure and restart Zulip.
### Using a different outgoing proxy
To use a custom outgoing proxy: To use a custom outgoing proxy:
@@ -271,29 +288,31 @@ To use a custom outgoing proxy:
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This `/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This
will reconfigure and restart Zulip. will reconfigure and restart Zulip.
If you wish to disable the outgoing proxy entirely, follow the above
steps, configuring an empty `host` value. **This is not
recommended**, as it allows attackers to leverage the Zulip server to
access internal resources.
### Routing Camo requests through an outgoing proxy
By default, the Camo image proxy will use a custom outgoing proxy if
one is configured, but will not use the default Smokescreen proxy
(because Camo includes similar logic to deny access to private
subnets). You can [override][proxy.enable_for_camo] this logic in
either direction, if desired.
### Installing Smokescreen on a separate host
If you have a deployment with multiple frontend servers, or wish to If you have a deployment with multiple frontend servers, or wish to
install Smokescreen on a separate host, you can apply the install Smokescreen on a separate host, you can apply the
`zulip::profile::smokescreen` Puppet class on that host, and follow `zulip::profile::smokescreen` Puppet class on that host, and follow
the above steps, setting the `[http_proxy]` block to point to that the above steps, setting the `[http_proxy]` block to point to that
host. 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.
[proxy.enable_for_camo]: system-configuration.md#enable_for_camo
[smokescreen]: https://github.com/stripe/smokescreen
[smokescreen-acls]: https://github.com/stripe/smokescreen#acls
[ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery [ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
[smokescreen]: https://github.com/stripe/smokescreen
[proxy.enable_for_camo]: system-configuration.md#enable_for_camo
[smokescreen-acls]: system-configuration.md#allow_addresses-allow_ranges-deny_addresses-deny_ranges
### S3 file storage requests and outgoing proxies ### S3 file storage requests and outgoing proxies

View File

@@ -423,10 +423,13 @@ not_ send any requests to Zulip which came in unencrypted.
### `[http_proxy]` ### `[http_proxy]`
See "[Customizing the outgoing HTTP
proxy](deployment.md#customizing-the-outgoing-http-proxy)" for general
instructions on the outgoing proxy.
#### `host` #### `host`
The hostname or IP address of an [outgoing HTTP `CONNECT` The hostname or IP address of an outgoing HTTP `CONNECT`. Defaults to
proxy](deployment.md#customizing-the-outgoing-http-proxy). Defaults to
`localhost` if unspecified. `localhost` if unspecified.
#### `port` #### `port`
@@ -446,6 +449,12 @@ its requests through Smokescreen is generally not necessary. Set to
true or false to override the default, which uses the proxy only if true or false to override the default, which uses the proxy only if
it is not the default of Smokescreen on a local host. it is not the default of Smokescreen on a local host.
#### `allow_addresses`, `allow_ranges`, `deny_addresses`, `deny_ranges`
Comma-separated lists of IP addresses or CIDR range rules. All
private IP addresses (e.g., 127.0.0.0/8, 192.168.0.0/16) are denied by
default; allow rules override deny rules.
### `[sentry]` ### `[sentry]`
#### `organization` #### `organization`

View File

@@ -37,6 +37,11 @@ class zulip::smokescreen {
} }
$listen_address = zulipconf('http_proxy', 'listen_address', '127.0.0.1') $listen_address = zulipconf('http_proxy', 'listen_address', '127.0.0.1')
$allow_addresses = split(zulipconf('http_proxy', 'allow_addresses', ''), ',')
$allow_ranges = split(zulipconf('http_proxy', 'allow_ranges', ''), ',')
$deny_addresses = split(zulipconf('http_proxy', 'deny_addresses', ''), ',')
$deny_ranges = split(zulipconf('http_proxy', 'deny_ranges', ''), ',')
file { "${zulip::common::supervisor_conf_dir}/smokescreen.conf": file { "${zulip::common::supervisor_conf_dir}/smokescreen.conf":
ensure => file, ensure => file,
require => [ require => [

View File

@@ -1,5 +1,20 @@
<%
acls = []
acls.concat(@allow_addresses.map {|a| "--allow-address #{a}"})
acls.concat(@allow_ranges.map {|a| "--allow-range #{a}"})
acls.concat(@deny_addresses.map {|a| "--deny-address #{a}"})
acls.concat(@deny_ranges.map {|a| "--deny-range #{a}"})
if acls.empty?
acl = ""
else
acl = " " + acls.join(" ")
end
-%>
[program:smokescreen] [program:smokescreen]
command=<%= @bin %> --listen-ip <%= @listen_address %> --expose-prometheus-metrics --prometheus-port 4760 command=<%= @bin %> --listen-ip <%= @listen_address %> --expose-prometheus-metrics --prometheus-port 4760<%= acl %>
priority=15 priority=15
autostart=true autostart=true
autorestart=true autorestart=true