Commit Graph

481 Commits

Author SHA1 Message Date
Laura Hausmann
5edcc209c3 nginx: Send SNI for proxied S3 requests.
Some S3 backends (e.g. garage or minio behind caddy) are unable to
respond to TLS requests that only have the Host header set. This makes
sure those configurations are supported going forward.
2024-05-28 16:30:44 -04:00
Alex Vandiver
549f4fe00b nginx: Strip off request headers which might affect S3's behaviour.
Clients making requests to Zulip with a `Authorization: Basic ...` for
an upload in S3 pass along all of their request headers to the S3
backend -- causing errors of the form:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>InvalidArgument</Code>
  <Message>Only one auth mechanism allowed; only the X-Amz-Algorithm
  query parameter, Signature query string parameter or the
  Authorization header should be specified</Message>
  <ArgumentName>Authorization</ArgumentName>
  <ArgumentValue>Basic ...</ArgumentValue>
  <RequestId>...</RequestId>
  <HostId>...</HostId>
</Error>
```

Strip off all request headers which AWS reports that S3 may read[^1].

Fixes: #30180.

[^1]: https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonRequestHeaders.html
2024-05-28 12:04:19 -07:00
Alex Vandiver
f246b82f67 puppet: Factor out pattern of writing a nagios state file atomically. 2024-05-24 11:31:25 -07:00
Alex Vandiver
230040caa9 puppet: Remove check_postgresql_backup.
We have replaced this monitoring with the black-box wal-g monitoring,
which is more accurate.
2024-05-24 11:27:59 -07:00
Alex Vandiver
2218de0399 management: Disable Sentry for management commands run interactively.
This adds `--automated` and `--no-automated` flags to all Zulip
management commands, whose default is based on if STDIN is a TTY.
This enables cron jobs and supervisor commands to continue to report
to Sentry, and manually-run commands (when reporting to Sentry does
not provide value, since the user can see them) to not.

Note that this only applies to Zulip commands -- core Django
commands (e.g. `./manage.py`) do not grow support for `--automated`
and will always report exceptions to Sentry.

`manage.py` subcommands in the `upgrade` and `restart-server` paths
are marked as `--automated`, since those may be run semi-unattended,
and they are useful to log to Sentry.
2024-05-24 10:30:16 -07:00
Alex Vandiver
cf24d2c25e check_send_receive_time: Use time.perf_counter() for duration timing. 2024-05-08 15:51:20 -07:00
Alex Vandiver
f42153f670 check_send_receive_time: Use machine.deploy_type to check "staging".
This brings it in line with other locations (e.g. Sentry) and is less
likely to have accidental false-positives.
2024-05-08 15:51:20 -07:00
Alex Vandiver
8bdf1e4a10 check_send_receive_time: Move "states" to inside where they are used. 2024-05-08 15:51:20 -07:00
Alex Vandiver
04e21044b9 check_send_receive_time: Default --site usefully.
This saves us the time of shelling out to a new python process,
loading all of Django, and printing one value we could just have read
in-process.  It is unclear why we ever did it this way.
2024-05-08 15:51:20 -07:00
Alex Vandiver
cbc9065ed2 check_send_receive_time: Remove no-longer-used "config" option.
This become unused in 927660a7b6.
2024-05-08 15:51:20 -07:00
Alex Vandiver
2bd60e8562 check_send_receive_time: Print no output on success. 2024-05-08 15:51:20 -07:00
Alex Vandiver
2df91c70ef puppet: Move rabbitmq monitoring into kandra/, where it is used from. 2024-04-24 14:40:28 -07:00
Alex Vandiver
6e981c18d5 puppet: Factor out cron job creation. 2024-04-24 14:40:28 -07:00
Alex Vandiver
503bddab9c cron: Remove stale comment.
122d0bca83 removed the hour-specific exception.
2024-04-24 14:40:28 -07:00
Alex Vandiver
b298d08fda wal-g: Support parameterizing the bucket which is fetched. 2024-04-24 09:04:16 -07:00
Alex Vandiver
f7bc881ca3 puppet: Update dependencies. 2024-04-12 15:06:42 -07:00
Alex Vandiver
30f71639f0 pg_backup_and_purge: Properly preserve needed base backups.
Without `FIND_FULL`, `wal_g delete before ...` will fail, rather than
delete a base backup which is needed by the delta backups after it.
By passing `FIND_FULL`[^1], we tell it explicitly that we're OK
preserving files before the specified one, as long as they are
necessary for the delta chain.

[^1]: https://github.com/wal-g/wal-g/blob/master/docs/README.md#delete
2024-04-12 11:39:54 -07:00
Prakhar Pratyush
30273403ea hooks: Add a send_zulip_update_announcements post deploy hook.
This commit adds a post upgrade hook to run the
'send_zulip_update_announcements' management command.

The aim is to improve UX for self hosters by sending
zulip updates as soon as the upgrade completes instead
of waiting for the cron to run.
2024-04-11 16:13:42 -07:00
Prakhar Pratyush
3add31496f puppet: Add a daily cron-job to send zulip update announcements.
A daily cron job is configured to run the
'send_zulip_update_announcements' management command.
2024-03-27 11:43:08 -07:00
Alex Vandiver
c129b1779f wal-g: Add support for incremental backups.
This only defaults to on for local-disk backups, since they are more
disk-size-sensitive, and local accesses are quite cheap compared to
loading multiple incremental backups from S3.
2024-03-19 09:58:58 -07:00
Alex Vandiver
44ff1c24df wal-g: Provide a to-local-disk backup option. 2024-03-19 09:58:58 -07:00
Anders Kaseorg
570f3dd447 python: Reformat with Ruff formatter.
https://docs.astral.sh/ruff/formatter/

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-02-29 17:07:16 -08:00
Alex Vandiver
927660a7b6 kandra: Remove munin. 2024-02-06 21:34:56 -08:00
Anders Kaseorg
cd96193768 models: Extract zerver.models.realms.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-12-16 22:08:44 -08:00
Anders Kaseorg
45bb8d2580 models: Extract zerver.models.users.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-12-16 22:08:44 -08:00
Alex Vandiver
4989221b9e nginx: Limit the methods that we proxy to Tornado.
While the Tornado server supports POST requests, those are only used
by internal endpoints.  We only support OPTIONS, GET, and DELETE
methods from clients, so filter everything else out at the nginx
level.

We set `Accepts` header on both `OPTIONS` requests and 405 responses,
and the CORS headers on `OPTIONS` requests.
2023-12-08 09:23:30 -08:00
Tim Abbott
b59e90d100 puppet: Fix buggy media-src Content-Security-Policy.
The colon is invalid syntax. Verified the updated policy using an
online CSP checker.
2023-11-06 14:45:05 -05:00
Aman Agrawal
f3ab45a152 uploads-internal: Mark self as a valid source of loading media.
Without this, browser refused to play the video. To reproduce press `open`
on an uploaded video on CZO. Chrome gives us the following error
in console:

Refused to load media from '<source>' because it violates the
following Content Security Policy directive: "default-src 'none'".
Note that 'media-src' was not explicitly set, so 'default-src' is
used as a fallback.
2023-10-12 09:57:21 -07:00
Anders Kaseorg
2665a3ce2b python: Elide unnecessary list wrappers.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-13 12:41:23 -07:00
Alex Vandiver
e8c8544028 nginx: Do not forward X-amz-cf-id header to S3.
All `X-amz-*` headers must be included in the signed request to S3;
since Django did not take those headers into account (it constructed a
request from scratch, while nginx's request inherits them from the
end-user's request), the proxied request fails to be signed correctly.

Strip off the `X-amz-cf-id` header added by CloudFront.  While we
would ideally strip off all `X-amz-*` headers, this requires a
third-party module[^1].

[^1]: https://github.com/openresty/headers-more-nginx-module#more_clear_input_headers
2023-08-28 12:30:14 -07:00
Anders Kaseorg
c43629a222 ruff: Fix PLW1510 subprocess.run without explicit check argument.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-08-17 17:05:34 -07:00
Anders Kaseorg
0b95d83f09 ruff: Fix PERF402 Use list or list.copy to create a copy of a list.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-08-07 17:23:55 -07:00
Alex Vandiver
60ce5e1955 wal-g: Use "start_time" field, not "time" which is S3 modified-at.
The `time` field is based on the file metadata in S3, which means that
touching the file contents in S3 can move backups around in the list.

Switch to using `start_time` as the sort key, which is based on the
contents of the JSON file stored as part of the backup, so is not
affected by changes in S3 metadata.
2023-07-19 14:57:51 -07:00
Alex Vandiver
5a26237b54 wal-g: Support alternate S3 storage classes. 2023-07-19 10:55:18 -07:00
Alex Vandiver
52eacd30c5 wal-g: Set WALG_S3_PREFIX, instead of WALE_S3_PREFIX.
The `WALE_` prefix was only used for backwards compatibility.  Switch
to the canonical variable name.
2023-07-19 10:55:18 -07:00
Alex Vandiver
149bea8309 puppet: Configure smokescreen for 14 days of logs, via logrotate.
supervisord's log rotation is only "every x bytes" which is not a good
enough policy for tracking auditing logs.  The default is also 10 logs
of 50MB, which is very much not enough for active instances.

Switch to tracking 14 days of daily logs.
2023-07-13 11:47:34 -07:00
Alex Vandiver
8a77cca341 middleware: Detect reverse proxy misconfigurations.
Combine nginx and Django middlware to stop putting misleading warnings
about `CSRF_TRUSTED_ORIGINS` when the issue is untrusted proxies.
This attempts to, in the error logs, diagnose and suggest next steps
to fix common proxy misconfigurations.

See also #24599 and zulip/docker-zulip#403.
2023-07-02 16:20:21 -07:00
Alex Vandiver
edfc911649 hooks: Tell Sentry the explicit commit range.
This is necessary if one has different deployments (with different
commit ranges) using the same projects.

See https://docs.sentry.io/product/releases/associate-commits/#using-the-cli
for the API of the `sentry-cli` tool.
2023-06-19 13:43:56 -07:00
Alex Vandiver
bd217ad31b puppet: Read resolver from /etc/resolv.conf.
04cf68b45e make nginx responsible for downloading (and caching)
files from S3.  As noted in that commit, nginx implements its own
non-blocking DNS resolver, since the base syscall is blocking, so
requires an explicit nameserver configuration.  That commit used
127.0.0.53, which is provided by systemd-resolved, as the resolver.

However, that service may not always be enabled and running, and may
in fact not even be installed (e.g. on Docker).  Switch to parsing
`/etc/resolv.conf` and using the first-provided nameserver.  In many
deployments, this will still be `127.0.0.53`, but for others it will
provide a working DNS server which is external to the host.

In the event that a server is misconfigured and has no resolvers in
`/etc/resolv.conf`, it will error out:
```console
Error: Evaluation Error: Error while evaluating a Function Call, No nameservers found in /etc/resolv.conf!  Configure one by setting application_server.nameserver in /etc/zulip/zulip.conf (file: /home/zulip/deployments/current/puppet/zulip/manifests/app_frontend_base.pp, line: 76, column: 70) on node example.zulipdev.org
```
2023-06-12 20:18:28 +00:00
Anders Kaseorg
9797de52a0 ruff: Fix RUF010 Use conversion in f-string.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-26 22:09:18 -07:00
Alex Vandiver
0935d388f0 nginx: Set X-Forwarded-Proto based on trust from requesting source.
Django has a `SECURE_PROXY_SSL_HEADER` setting[^1] which controls if
it examines a header, usually provided by upstream proxies, to allow
it to treat requests as "secure" even if the proximal HTTP connection
was not encrypted.  This header is usually the `X-Forwarded-Proto`
header, and the Django configuration has large warnings about ensuring
that this setting is not enabled unless `X-Forwarded-Proto` is
explicitly controlled by the proxy, and cannot be supplied by the
end-user.

In the absence of this setting, Django checks the `wsgi.url_scheme`
property of the WSGI environment[^2].

Zulip did not control the value of the `X-Forwarded-Proto` header,
because it did not set the `SECURE_PROXY_SSL_HEADER` setting (though
see below).  However, uwsgi has undocumented code which silently
overrides the `wsgi.url_scheme` property based on the
`HTTP_X_FORWARDED_PROTO` property[^3] (and hence the
`X-Forwarded-Proto` header), thus doing the same as enabling the
Django `SECURE_PROXY_SSL_HEADER` setting, but in a way that cannot be
disabled.  It also sets `wsgi.url_scheme` to `https` if the
`X-Forwarded-SSL` header is set to `on` or `1`[^4], providing an
alternate route to deceive to Django.

These combine to make Zulip always trust `X-Forwarded-Proto` or
``X-Forwarded-SSL` headers from external sources, and thus able to
trick Django into thinking a request is "secure" when it is not.
However, Zulip is not accessible via unencrypted channels, since it
redirects all `http` requests to `https` at the nginx level; this
mitigates the vulnerability.

Regardless, we harden Zulip against this vulnerability provided by the
undocumented uwsgi feature, by stripping off `X-Forwarded-SSL` headers
before they reach uwsgi, and setting `X-Forwarded-Proto` only if the
request was received directly from a trusted proxy.

Tornado, because it does not use uwsgi, is an entirely separate
codepath.  It uses the `proxy_set_header` values from
`puppet/zulip/files/nginx/zulip-include-common/proxy`, which set
`X-Forwarded-Proto` to the scheme that nginx received the request
over.  As such, `SECURE_PROXY_SSL_HEADER` was set in Tornado, and only
Tornado; since the header was always set in nginx, this was safe.
However, it was also _incorrect_ in cases where nginx did not do SSL
termination, but an upstream proxy did -- it would mark those requests
as insecure when they were actually secure.  We adjust the
`proxy_set_header X-Forwarded-Proto` used to talk to Tornado to
respect the proxy if it is trusted, or the local scheme if not.

[^1]: https://docs.djangoproject.com/en/4.2/ref/settings/#secure-proxy-ssl-header
[^2]: https://wsgi.readthedocs.io/en/latest/definitions.html#envvar-wsgi.url_scheme
[^3]: 73efb013e9/core/protocol.c (L558-L561)
[^4]: 73efb013e9/core/protocol.c (L531-L534)
2023-05-22 16:50:29 -07:00
Alex Vandiver
a9f51a0c02 static: Add Timing-Allow-Origin: * to allow sentry data timing.
This is required for the browser to provide detailed timing
information about resource fetches from other domains[^1].

[^1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin
2023-05-09 13:16:28 -07:00
Alex Vandiver
2f4775ba68 wal-g: Write out a logfile.
Otherwise, this output goes into `/var/spool/mail/postgres`, which is
not terribly helpful.  We do not write to `/var/log/zulip` because the
backup runs as the `postgres` user, and `/var/log/zulip` is owned by
zulip and chmod 750.
2023-04-27 12:19:43 -07:00
Alex Vandiver
3aba2789d3 prometheus: Add an exporter for wal-g backup properties.
Since backups may now taken on arbitrary hosts, we need a blackbox
monitor that _some_ backup was produced.

Add a Prometheus exporter which calls `wal-g backup-list` and reports
statistics about the backups.

This could be extended to include `wal-g wal-verify`, but that
requires a connection to the PostgreSQL server.
2023-04-26 15:41:39 -07:00
Alex Vandiver
b8a6de95d2 pg_backup_and_purge: Allow adjusting the backup concurrency.
SSDs are good at parallel random reads.
2023-04-26 10:54:51 -07:00
Alex Vandiver
19a11c9556 pg_backup_and_purge: Take backups on replicas, if present.
Taking backups on the database primary adds additional disk load,
which can impact the performance of the application.

Switch to taking backups on replicas, if they exist.  Some deployments
may have multiple replicas, and taking backups on all of them is
wasteful and potentially confusing; add a flag to inhibit taking
nightly snapshots on the host.

If the deployment is a single instance of PostgreSQL, with no
replicas, it takes backups as before, modulo the extra flag to allow
skipping taking them.
2023-04-26 10:54:51 -07:00
Alex Vandiver
4b35211ca1 pg_backup_and_purge: Remove unnecessary explicit types. 2023-04-26 10:54:51 -07:00
Alex Vandiver
e72e83793d pg_backup_and_purge: Just use subprocess directly.
dry_run was never passed into run(); switch to using subprocess directly.
2023-04-26 10:54:51 -07:00
Alex Vandiver
775c7ca4ea hooks: Give a bit better Zulip deploy message. 2023-04-19 09:32:39 -07:00
Alex Vandiver
7c023042cf puppet: Rotate access log files every day, not at 500M.
Since logrotate runs in a daily cron, this practically means "daily,
but only if it's larger than 500M."  For large installs with large
traffic, this is effectively daily for 10 days; for small installs, it
is an unknown amount of time.

Switch to daily logfiles, defaulting to 14 days to match nginx; this
can be overridden using a zulip.conf setting.  This makes it easier to
ensure that access logs are only kept for a bounded period of time.
2023-04-06 14:31:16 -04:00