merge groente's changes with the old docs authored by anarcat's avatar anarcat
......@@ -419,6 +419,7 @@ Those services also provide ways to validate SPF records:
* [pyspf](https://pypi.org/project/pyspf/): a Python library and binary to test SPF records
* [learndmarc.com](https://www.learndmarc.com/): has a Matrix (the movie) vibe and more
explanations
* [vamsoft's SPF checker](https://vamsoft.com/support/tools/spf-policy-tester)
### Testing the submission server
......@@ -514,9 +515,60 @@ and use their personal address instead of user@torproject.org.
# Reference
## Mail servers
## Installation
The new mail server setup is fully Puppetized. See the [design and
architecture section](#design-and-architecture) for more information about the various
components and associated Puppet classes in use.
### Submission server
To setup a new submission mail server, create a machine with the
`email::submission` role in Puppet. Ideally, it should be on a network
with a good IP reputation.
In `letsencrypt.git`, add an entry for that host's specific TLS
certificate. For example, the `submit-01.torproject.org` server has a
line like this:
submit-01.torproject.org submit.torproject.org
Those domains are glued together in DNS with:
submission IN CNAME submit-01
_submission._tcp IN SRV 0 1 465 submission
This implies there is only *one* `submission.torproject.org`, because
one cannot have multiple `CNAME` records, of course. But it should
make replacing the server transparent for end-users.
The latter SRV record is actually specified in [RFC6186](https://datatracker.ietf.org/doc/html/rfc6186), but may
not be sufficient for all automatic configuration. We do *not* go
deeper into auto-discovery, because that typically implies IMAP
servers and so on. But if we would, we could consider using [this
software which tries to support all of them](https://github.com/Monogramm/autodiscover-email-settings) (e.g. [Microsoft](https://docs.microsoft.com/en-us/exchange/architecture/client-access/autodiscover?view=exchserver-2019),
[Mozilla](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo), [Apple](https://developer.apple.com/library/archive/featuredarticles/iPhoneConfigurationProfileRef/index.html)). For now, we'll only stick with the SRV
record.
### Mailman server
See the [mailman documentation](service/lists#installation).
## Upgrades
### Design & architecture
Upgrades should generally be covered by the normal Debian package
workflow.
## SLA
There is no SLA specific to this service, but mail delivery is
generally considered to be high priority. Complaints about delivery
failure should be filed as [issues in our ticket tracker](#issues) and
addressed.
## Design and architecture
### Mail servers
Our main mail servers (mx-dal-01, srs-dal-01, mta-dal-01, and submit-01) try to fit into the picture presented in TPA-RFC-44:
......@@ -752,6 +804,27 @@ Examples of independent mailers are: lists-01.torproject.org, crm-int-01.torproj
## DMARC
[DMARC](https://en.wikipedia.org/wiki/DMARC) records glue together SPF and DKIM to tell which *policy*
to apply once the rules defined above check out (or not). It is
defined in [RFC7489](https://www.rfc-editor.org/rfc/rfc7489.html) and has a [friendly homepage](https://dmarc.org/) with a [good
introduction](https://dmarc.org/overview/). Note that DMARC usage has been growing steadily
since 2018 and more steeply since 2021, see the [usage stats](https://dmarc.org/stats/dmarc/). See
also the [Alex top site usage](https://dmarc.org/stats/alexa-top-sites/).
Our current DMARC policy is:
_dmarc IN TXT "v=DMARC1;p=none;pct=100;rua=mailto:postmaster@torproject.org"
That is a "soft" policy (`p=` is `none` instead of `quarantine` or
`reject`) that applies to all email (`pct=100`) and sends reports to
the `postmaster@` address.
Note that this applies to *all* subdomains by default, to change the
subdomain policy, the `sp=` mechanism would be used (same syntax as
`p=`, e.g. `sp=quarantine` would apply the `quarantine` policy to
subdomains, independently of the top domain policy). See [RFC 7489
section 6.6.3](https://www.rfc-editor.org/rfc/rfc7489.html#section-6.6.3) for more details on discovery.
We currently have DMARC policy set to none, but this should be changed.
DKIM signing and verification is done by rspamd. The profile::rspamd::dkimdomain can be set to ensure all mail from those domains are signed. The profile automatically ensures the appropriate DNS record is created.
......@@ -864,460 +937,6 @@ or
/usr/bin/rspamc -h localhost learn_ham
```
## Monitoring
By default, all servers with profile::postfix::independent set to true are monitored by Prometheus.
This only checks that the SMTP port (or optionally whatever you set in profile::postfix::monitor_ports or monitor_tls_ports) is open. We do
not have end to end delivery monitoring just yet, that is part of the
[improve mail services milestone](https://gitlab.torproject.org/groups/tpo/tpa/-/milestones/4), specifically [issue 40494](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40494).
All servers that have `profile::postfix::mtail_monitor` enabled (which is the default) have the `mtail` exporter
(`profile::prometheus::postfix_mtail_exporter`). The [Grafana
dashboard](https://grafana.torproject.org/d/Ds5BxBYGk/postfix-mtail?orgId=1&var-node=submit-01.torproject.org) should provide shiny graphs.
## High availability
We currently have no high availability/redundancy.
Since SMTP conveniently has failover mechanisms built in, it would be easy to add redundancy for our MX, SRS, and MTA servers by simply deploying copies of them.
If we do host our own IMAP servers eventually, we would like them to
be highly available, without human intervention. That means having an
"active-active" mirror setup where the failure of one host doesn't
affect users at all and doesn't require human intervention to restore
services.
We already know quite well how to do an active/passive setup: DRBD
allows us to replicate entire disks between machines. It *might* be
possible to do the same with active/active setups in DRBD, in theory,
but in practice this quickly runs into filesystem limitations, as
(e.g.) ext4 is *not* designed to be accessed by multiple machines
simultaneously.
Dovecot has a [replication system called dsync](https://doc.dovecot.org/configuration_manual/replication/) that replicates
mailboxes over a pipe. There are examples for TCP, TLS and SSH. [This
blog post](http://blog.dovecot.org/2012/02/dovecot-clustering-with-dsync-based.html) explains the design as well. A pair of [director](https://doc.dovecot.org/admin_manual/director/dovecotdirector/)
processes could be used to direct users to the right server. [This
tutorial](http://web.archive.org/web/20201111212844/https://blog.le-vert.net/?p=160) seems to have been useful for people.
Dovecot also shows a [HAProxy configuration](https://doc.dovecot.org/configuration_manual/haproxy/). A script called
[poolmon](https://github.com/brandond/poolmon/tree/master) seems to be used by some folks to remove/re-add backends
to the director when the go unhealthy. Dovecot now ships a
[dovemon](https://doc.dovecot.org/configuration_manual/dovemon/) program that works similarly, but it's only available in
the non-free "Pro" version.
There's also a [ceph plugin](https://github.com/ceph-dovecot/dovecot-ceph-plugin) to store emails in a Ceph backend.
It also seems possible to store mailbox and index objects in an
[object storage backend](https://doc.dovecot.org/admin_manual/dovecot_backend/), a configuration documented in the
[Dovecot Cluster Architecture](https://doc.dovecot.org/admin_manual/dovecot_cluster_architecture/). It seems that, unfortunately, this
is part of the "Pro" version of Dovecot, not usable in the free
version (see [mailbox formats](https://doc.dovecot.org/admin_manual/mailbox_formats/)). There's also someone who
implemented a [syncthing backend](https://github.com/fragtion/dovecot-core).
## Future plans
We went through a number of proposals to improve mail services over
time:
* [TPA-RFC-15: Email services](policy/tpa-rfc-15-email-services) (rejected, replaced with
TPA-RFC-31)
* [TPA-RFC-31: outsource email services](policy/tpa-rfc-31-outsource-email) (rejected as well, in
favor of TPA-RFC-44 and following)
* [TPA-RFC-44: Email emergency recovery](policy/tpa-rfc-44-email-emergency-recovery) (DKIM, SPF, DMARC
records, long term plan postponed)
* TPA-RFC-45: Mail architecture (long term plans from TPA-RFC-44)
* TPA-RFC-71: Emergency email deployments, phase B (implementation of SRS, done)
* [Enabling local delivery for regular nodes](https://gitlab.torproject.org/tpo/tpa/team/-/issues/42024)
* [Email deliverability monitoring](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40494)
## Known issues
* [LDAP is a SPOF for MX and SRS](https://gitlab.torproject.org/tpo/tpa/team/-/issues/41888)
* [We're susceptible to spoofing](https://gitlab.torproject.org/tpo/tpa/team/-/issues/41889)
## Installation
### DKIM configuration
Hosts which generate outbound mail should be configured to add `DKIM-Signature`
headers. This is done by including the `profile::postfix::dkim` class on Puppet
nodes which handle mail and configuring Postfix's `main.cf` to use it as a
milter. When the node's `profile::postfix::mail_processing` flag is
set to true, this is done automatically.
This class will install the `opendkim` and `opendkim-tools` packages, manage
the `/etc/opendkim.conf` configuration file, generate a private key under
`/etc/opendkim/keys` and set up what is needed for OpenDKIM and Postfix to
communicate.
By default, the class configures OpenDKIM to only sign email where the sender
domain part is identical to the node's FQDN. However, this is not often the
case and the OpenDKIM `Domain` parameter must be specified.
To do this, add a `profile::opendkim::domain` key in Hiera, ideally in the
yaml file that provide data for the node's role, eg.
`hiera/roles/email::submission.yaml` for the submission sever. The value must
be specified as an array containing one or more email sender domains. For
example, if a node sends mail as `gettor@torproject.org`, the
`profile::opendkim::domain` must be an array that contains `torproject.org`.
Once this configuration is applied on the node, at least one new DNS entry must
be created for external hosts to be able to validate DKIM signatures. For
example, on the `submit-01` host, a `/etc/opendkim/keys/2022-submit-01.txt`
file is automatically generated containing a DNS entry appropriate for
validating mails signed for the `@torproject.org` sender domain:
2022-submit-01._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6iJa25pvx5qatYV4u44zolHLMiqkWZBvF6UJcX8hrSNES/aw/k4zXiliChs3eUtGHjf5aSNC6TrOndfQqRxMxNn+XhSEsUa6zFhZeeAOIi0A3TPRd5qL8aWfHH2NtHZAnZ5lodkA6HjJ/HpyrJvFuyuJ94yNL/bjvRWu+bMwixBIYr6znDoJYGTPC5YHZt48bJgvg3lAb3vIwD"
"bkBw+bMkZCbRjSQuOM52pg6uQLSBiBeQHqWkSd03vp4A906jWaMLDHMfVZDDrXLg+QG2nAOoJmZ0l5argoIRiEG/8GO72FI2dEKJaXgXYqpSXGCtzZJNIr8schHFZBirZBLljbEwIDAQAB" ) ; ----- DKIM key 2022-submit-01 for torproject.org
If a subdomain prefix is used (e.g. `crm.torproject.org`) make sure the
`_domainkey` is under that prefix:
2022-crm-int-01._domainkey.crm IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtSNGCjHmZnGrnBb9nCsPUc6MjZd5QueGKV+iXwcRNfU0LapFZMi5t7WE/kTPJsRWIF8AMHymNqLA5835m5LwaBBXZdu1utNARKSXDzGsEjxuDiAnSqD0Rb1px1JA+Eex0RC3thYZuyIIAxK31pXxJt2mowXtrhIkuKFB2YpE0yUudKuDZIZZ3YNH025czK/jFLD6TH+5xD9Cej"
"H0MB6tE4O41rCjZUjSZ7Ar7BjVID6foCmlbr/3EG7dbzQv6YqH19OX6YgL0UMfG2RhvhWEUNYghS6K88vTelnHx/ShUzIeu05jd6mi9OLCA/Hl2bFRsa0f1ttHKpnzuC+ecn0sWwIDAQAB" ) ; ----- DKIM key 2022 for crm.torproject.org
The key may now be tested with the command below. Make sure to use the correct
sender domain for the `-d` command line argument:
opendkim-testkey -d torproject.org -s 2022-$(hostname) -vv
This should show, once DNS propagated:
root@submit-01:/etc/opendkim/keys# opendkim-testkey -d torproject.org -s 2022-$(hostname) -vv
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: key loaded from /etc/opendkim/keys/2022-submit-01.private
opendkim-testkey: checking key '2022-submit-01._domainkey.torproject.org'
opendkim-testkey: key secure
If you see `record not found`, it's because the DNS record wasn't found. See if
DNS has propagated properly, maybe flush negative responses with
`unbound-control flush-negative`.
The `keys not secure` message means you are not using DNSSEC, which should
*not* happen in our configuration. Investigate if you do see the warning.
At this point it's a good idea to [test outgoing mail](#testing-outgoing-mail):
echo "this is a test email" | mail -r postmaster@torproject.org -s 'test email from anarcat' -- check-auth@verifier.port25.com
#### DKIM signing on the mail relay
On some hosts it's not practical to configure OpenDKIM because it generates
some mail but otherwise doesn't process mail, so its `mail_processing` flag is
not enabled. Usually such hosts route their outbound email though `mta-dal-01`, so
we can use it to sign email on behalf of other hosts.
#### Manual DKIM configuration (deprecated)
This was a rushed OpenDKIM deployment procedure that was used in
[tpo/tpa/team#40981][] and [tpo/tpa/team#40988][] (eugeni and
submit-01). It has been added to Puppet in [tpo/tpa/team#40989][].
This procedure is DEPRECATED. Hosts MUST be configured with Puppet
(above) instead. The procedure is kept only for historical reference.
1. install OpenDKIM:
apt install opendkim opendkim-tools
2. ensure you have those lines in `/etc/opendkim.conf`:
LogWhy yes
Mode s # sign only, use sv to also check incoming
Domain torproject.org
Selector 2022-submit-01 # 2022-submit-01._domainkey.torproject.org
Keyfile /etc/opendkim/keys/2022-submit-01.private
Socket local:/var/spool/postfix/opendkim/opendkim.sock
Note that the `Selector` and `Domain` fields are prone to change
if the server is sending mail from more than one domain. For
example, on `crm-int-01`, we also put `crm-int-01.torproject.org`
and `crm.torproject.org` in there. The selector is also based on
the year of creation (`2022` in this case) and the short hostname
of the server the key belongs to (`submit-01`) so that we don't
have to copy that private key around.
3. generate the keys and directories:
mkdir -p /etc/opendkim/keys &&
mkdir /var/spool/postfix/opendkim &&
chown opendkim /var/spool/postfix/opendkim &&
opendkim-genkey --directory=/etc/opendkim/keys/ --selector=2022-submit-01 --domain=torproject.org --verbose
4. grant Postfix access to the OpenDKIM socket:
adduser postfix opendkim
5. restart the server:
service opendkim restart
6. add the keys from `/etc/opendkim/keys/2022-submit-01.txt` into
DNS, e.g.:
2022-submit-01._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6iJa25pvx5qatYV4u44zolHLMiqkWZBvF6UJcX8hrSNES/aw/k4zXiliChs3eUtGHjf5aSNC6TrOndfQqRxMxNn+XhSEsUa6zFhZeeAOIi0A3TPRd5qL8aWfHH2NtHZAnZ5lodkA6HjJ/HpyrJvFuyuJ94yNL/bjvRWu+bMwixBIYr6znDoJYGTPC5YHZt48bJgvg3lAb3vIwD"
"bkBw+bMkZCbRjSQuOM52pg6uQLSBiBeQHqWkSd03vp4A906jWaMLDHMfVZDDrXLg+QG2nAOoJmZ0l5argoIRiEG/8GO72FI2dEKJaXgXYqpSXGCtzZJNIr8schHFZBirZBLljbEwIDAQAB" ) ; ----- DKIM key 2022-submit-01 for torproject.org
if you had a subdomain prefix (e.g. in [tpo/tpa/team#40981][] we
used `crm.tpo`), make sure the `_domainkey` is under that prefix:
2022._domainkey.crm IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtSNGCjHmZnGrnBb9nCsPUc6MjZd5QueGKV+iXwcRNfU0LapFZMi5t7WE/kTPJsRWIF8AMHymNqLA5835m5LwaBBXZdu1utNARKSXDzGsEjxuDiAnSqD0Rb1px1JA+Eex0RC3thYZuyIIAxK31pXxJt2mowXtrhIkuKFB2YpE0yUudKuDZIZZ3YNH025czK/jFLD6TH+5xD9Cej"
"H0MB6tE4O41rCjZUjSZ7Ar7BjVID6foCmlbr/3EG7dbzQv6YqH19OX6YgL0UMfG2RhvhWEUNYghS6K88vTelnHx/ShUzIeu05jd6mi9OLCA/Hl2bFRsa0f1ttHKpnzuC+ecn0sWwIDAQAB" ) ; ----- DKIM key 2022 for crm.torproject.org
7. then test the key with:
opendkim-testkey -d torproject.org -s 2022-submit-01 -vv
... which should show, once DNS propagated:
root@submit-01:/etc/opendkim/keys# opendkim-testkey -d torproject.org -s 2022-submit-01 -vv
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: key loaded from /etc/opendkim/keys/2022-submit-01.private
opendkim-testkey: checking key '2022-submit-01._domainkey.torproject.org'
opendkim-testkey: key secure
If you see `record not found`, it's because the DNS record wasn't
found. See if DNS has propagated properly, maybe flush negative
responses with `unbound-control flush-negative`.
The `keys not secure` message means you are not using DNSSEC,
which should *not* happen in our configuration. Investigate if you
do see the warning.
8. stop puppet to keep it from messing with the Puppet config
puppet agent --disable 'rush opendkim deployment by hand, tpo/tpa/team#40988'
8. hook into postfix:
postconf -e milter_default_action=accept &&
postconf -e smtpd_milters=local:opendkim/opendkim.sock &&
postconf -e non_smtpd_milters=local:opendkim/opendkim.sock
9. reload postfix (warning: this will retry the queue)
service postfix reload
10. [test outgoing mail](#testing-outgoing-mail):
```
echo "this is a test email" | mail -r postmaster@torproject.org -s 'test email from anarcat' -- check-auth@verifier.port25.com
```
[tpo/tpa/team#40981]: https://gitlab.torproject.org/tpo/tpa/team/-/issues/40981
[tpo/tpa/team#40988]: https://gitlab.torproject.org/tpo/tpa/team/-/issues/40988
[tpo/tpa/team#40989]: https://gitlab.torproject.org/tpo/tpa/team/-/issues/40989
If you get double OpenDKIM signatures, consider adding
`receive_override_options=no_milters` to the server in `master.cf`, if
already does some content filtering.
It's possible that some mail doesn't get signed when injected from
Mailman, consider changing the `InternalHosts` to:
InternalHosts lists-01.torproject.org,lists.torproject.org,127.0.0.1,::1,localhost # cargo-culted, to remove?
If the signatures come from another server and cause problems (for
example with Mailman), you can strip those with:
# strip incoming sigs, typically from submit-01 or other servers on mailman
RemoveOldSignatures yes
### SPF records
In [tpo/tpa/team#40990](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40990), anarcat deployed "soft" [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework) records
for all outgoing mail servers under `torproject.org`. The full
specification of SPF is in [RFC7208](https://www.rfc-editor.org/rfc/rfc7208), here's a condensed
interpretation of our (current, 2025) policy:
#### torproject.org
```
@ IN TXT "v=spf1 a:crm-int-01.torproject.org a:submit-01.torproject.org a:rdsys-frontend-01.torproject.org a:polyanthum.torproject.org a:srs-dal-01.torproject.org a:mta-dal-01.torproject.org ~all"
```
This is a "soft" (`~all`) record that will tell servers to downgrade
the reputation of mail send with a `From: *@torproject.org` header
when it doesn't match any of the preceding mechanisms.
We use the `a:` mechanism to point at 6 servers that normally should
be sending mail as `torproject.org`:
* `crm-int-01`, the [CRM](service/crm) server
* `submit-01`, the submission mail server
* `rdsys-frontend-01`, the rdsys server
* `polyanthum`, the bridges server
* `srs-dal-01`, the sender-rewriting server
* `mta-dal-01`, our MTA
The `a` mechanism tells SPF-compatible servers to check the `A` and
`AAAA` records of the given server to see if it matches with the
connecting server.
We use the `a:` mechanism instead of the (somewhat more common) `ip4:`
mechanism because we do not want to add both the IPv4 and IPv6
records.
#### db.torproject.org: a
Some servers have a record like that:
```
db IN A 49.12.57.132 ; alberti
IN AAAA 2a01:4f8:fff0:4f:266:37ff:fea1:4d3 ; alberti
IN MX 0 alberti
IN TXT "v=spf1 a ~all"
```
This is also a "soft" record that tells servers to check the `A` or
`AAAA` records (`a`) to see if it matches the connecting server. It
will match only if the connecting server has an IP matching the `A` or
`AAAA` record for `db.torproject.org`.
#### lists.torproject.org: mx
```
lists IN TXT "v=spf1 mx ~all"
```
This is also a "soft" record that tells servers to check the Mail
Exchanger record (`MX`) to see if it matches the connecting server.
It might be better to use a `a:` record here to avoid a DNS lookup,
but it might actually be possible that the MX for lists is in a
different location than the web interface, for example.
#### gitlab: a and CNAME
Some mail servers have a record like:
```
gitlab IN CNAME gitlab-02
gitlab-02 IN TXT "v=spf1 a ~all" ; no one else than gitlab can send for gitlab
```
This is similar to the `db` record in that it uses a `a` mechanism,
but the actual name is behind a `CNAME` record.
In other words, only that server can send email as
`gitlab-02.torproject.org`. But since there's also a `CNAME` from
`gitlab` to `gitlab-02`, this policy actually also applies to
`gitlab.torproject.org`:
$ dig -t txt gitlab.torproject.org +nostats +nocomments +nocmd +noquestion
gitlab.torproject.org. 1415 IN CNAME gitlab-02.torproject.org.
gitlab-02.torproject.org. 3496 IN TXT "v=spf1 a ~all"
#### CRM: hard record
Finally, one last example is the CiviCRM records:
```
crm IN A 116.202.120.186 ; crm-int-01
IN AAAA 2a01:4f8:fff0:4f:266:37ff:fe4d:f883
IN TXT "v=spf1 a -all"
IN MX 0 crm
```
Those are similar to the `db.torproject.org` records except they are
"hard" (`-all`) which should, in theory, make other servers completely
reject attempts from servers not in the `A` or `AAAA` record of
`crm.torproject.org`.
#### Debugging SPF
[vamsoft's SPF checker](https://vamsoft.com/support/tools/spf-policy-tester) is good to quickly diagnose issues.
### DMARC records
[DMARC](https://en.wikipedia.org/wiki/DMARC) records glue together SPF and DKIM to tell which *policy*
to apply once the rules defined above check out (or not). It is
defined in [RFC7489](https://www.rfc-editor.org/rfc/rfc7489.html) and has a [friendly homepage](https://dmarc.org/) with a [good
introduction](https://dmarc.org/overview/). Note that DMARC usage has been growing steadily
since 2018 and more steeply since 2021, see the [usage stats](https://dmarc.org/stats/dmarc/). See
also the [Alex top site usage](https://dmarc.org/stats/alexa-top-sites/).
Our current DMARC policy is:
_dmarc IN TXT "v=DMARC1;p=none;pct=100;rua=mailto:postmaster@torproject.org"
That is a "soft" policy (`p=` is `none` instead of `quarantine` or
`reject`) that applies to all email (`pct=100`) and sends reports to
the `postmaster@` address.
Note that this applies to *all* subdomains by default, to change the
subdomain policy, the `sp=` mechanism would be used (same syntax as
`p=`, e.g. `sp=quarantine` would apply the `quarantine` policy to
subdomains, independently of the top domain policy). See [RFC 7489
section 6.6.3](https://www.rfc-editor.org/rfc/rfc7489.html#section-6.6.3) for more details on discovery.
### Submission server
To setup a new submission mail server, create a machine with the
`email::submission` role in Puppet. Ideally, it should be on a network
with a good IP reputation.
In `letsencrypt.git`, add an entry for that host's specific TLS
certificate. For example, the `submit-01.torproject.org` server has a
line like this:
submit-01.torproject.org submit.torproject.org
Those domains are glued together in DNS with:
submission IN CNAME submit-01
_submission._tcp IN SRV 0 1 465 submission
This implies there is only *one* `submission.torproject.org`, because
one cannot have multiple `CNAME` records, of course. But it should
make replacing the server transparent for end-users.
The latter SRV record is actually specified in [RFC6186](https://datatracker.ietf.org/doc/html/rfc6186), but may
not be sufficient for all automatic configuration. We do *not* go
deeper into auto-discovery, because that typically implies IMAP
servers and so on. But if we would, we could consider using [this
software which tries to support all of them](https://github.com/Monogramm/autodiscover-email-settings) (e.g. [Microsoft](https://docs.microsoft.com/en-us/exchange/architecture/client-access/autodiscover?view=exchserver-2019),
[Mozilla](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo), [Apple](https://developer.apple.com/library/archive/featuredarticles/iPhoneConfigurationProfileRef/index.html)). For now, we'll only stick with the SRV
record.
### Mailman server
See the [mailman documentation](service/lists#installation).
## Upgrades
Upgrades should generally be covered by the normal Debian package
workflow.
## SLA
There is no SLA specific to this service, but mail delivery is
generally considered to be high priority. Complaints about delivery
failure should be filed as [issues in our ticket tracker](#issues) and
addressed.
## Design and architecture
The submission email service allows users to submit mail as if they
were on a `torproject.org` machine. Concretely, it is a Postfix server
which relays email to anywhere once [SASL authentication](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer) is
passed.
Most of the code is glue code in Puppet, along with a small set of
patches to ud-ldap which were sent (and mostly accepted) upstream.
### Email password sync flow
This horrid diagram describes the way email passwords are set from
LDAP to the submission server:
![](email/submit-mail-password-sync-diagram.png)
## Services
The "submission" port (587) was previously used in the documentation
......@@ -1393,6 +1012,11 @@ The emergency changes to the infrastructure (including DKIM, DMARC,
and SPF records) were done as part of [TPA-RFC-44](policy/tpa-rfc-44-email-emergency-recovery)
([tpo/tpa/team#40981](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40981)).
### Known issues
* [LDAP is a SPOF for MX and SRS](https://gitlab.torproject.org/tpo/tpa/team/-/issues/41888)
* [We're susceptible to spoofing](https://gitlab.torproject.org/tpo/tpa/team/-/issues/41889)
## Maintainer
This service is mostly written as a set of Puppet manifests. It was
......@@ -1421,18 +1045,20 @@ The [Dovecot mail server](https://dovecot.org/) was written by [Timo Sirainen](h
one of the most widely used IMAP servers out there. It is an active
upstream as well.
[OpenDKIM](https://github.com/trusteddomainproject/OpenDKIM) is not in such good shape: it hasn't had a commit or
release in over 4 years (as of late 2022).
[OpenDKIM](https://github.com/trusteddomainproject/OpenDKIM) is not in such good shape: it hasn't had a commit
orrelease in over 4 years (as of late 2022). We have stopped using
OpenDKIM and instead use rspamd for DKIM signing and verification.
TODO: document rspamd upstream.
## Monitoring and metrics
The Postfix server is monitored by Prometheus, as with all servers in the
`publicmail` group. This only checks that the SMTP port is open. We do
By default, all servers with `profile::postfix::independent` set to true are monitored by Prometheus.
This only checks that the SMTP port (or optionally whatever you set in `profile::postfix::monitor_ports` or `monitor_tls_ports`) is open. We do
not have end to end delivery monitoring just yet, that is part of the
[improve mail services milestone](https://gitlab.torproject.org/groups/tpo/tpa/-/milestones/4), specifically [issue 40494](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40494).
The submission server is monitored like other mail servers that have
`profile::postfix::mtail_monitor` enabled, which is that it has the `mtail` exporter
All servers that have `profile::postfix::mtail_monitor` enabled (which is the default) have the `mtail` exporter
(`profile::prometheus::postfix_mtail_exporter`). The [Grafana
dashboard](https://grafana.torproject.org/d/Ds5BxBYGk/postfix-mtail?orgId=1&var-node=submit-01.torproject.org) should provide shiny graphs.
......@@ -1483,10 +1109,11 @@ This service was setup following some or all of those documents:
# Discussion
The mail services at Tor have been rather neglected,
traditionally. No effort was done to adopt modern standards (SPF,
DKIM, DMARC) which led to significant deliverability problems in late
2022.
The mail services at Tor have been rather neglected, traditionally. No
effort was done to adopt modern standards (SPF, DKIM, DMARC) which led
to significant deliverability problems in late 2022. This has improved
significantly since then, with those standards being mostly adopted in
2025, although with a "soft" SPF fail policy.
## Overview
......@@ -1508,8 +1135,8 @@ adopted, in effect it means that anyone can easily impersonate
We have heard regular reports of phishing attempts against our users
as well ([tpo/tpa/team#40596](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40596)), sometimes coming from our own
domain. Inbound mail filters are expected to improve that situation
([tpo/tpa/team#40539](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40539)).
domain. Inbound mail filters improved that situation significantly in
2025 ([tpo/tpa/team#40539](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40539)).
## Technical debt and next steps
......@@ -1518,20 +1145,13 @@ the long term plan from TPA-RFC-44 (TPA-RFC-45, [issue
tpo/tpa/team#41009](https://gitlab.torproject.org/tpo/tpa/team/-/issues/41009)). This will mean either outsourcing mail
services or building a proper mail hosting service.
## Proposed Solutions
We went through a number of proposals to improve mail services over
time:
### High availability
* [TPA-RFC-15: Email services](policy/tpa-rfc-15-email-services) (rejected, replaced with
TPA-RFC-31)
* [TPA-RFC-31: outsource email services](policy/tpa-rfc-31-outsource-email) (rejected as well, in
favor of TPA-RFC-44 and following)
* [TPA-RFC-44: Email emergency recovery](policy/tpa-rfc-44-email-emergency-recovery) (DKIM, SPF, DMARC
records, long term plan postponed)
* TPA-RFC-45: Mail architecture (long term plans from TPA-RFC-44)
We currently have no high availability/redundancy.
## High availability notes
Since SMTP conveniently has failover mechanisms built in, it would be
easy to add redundancy for our MX, SRS, and MTA servers by simply
deploying copies of them.
If we do host our own IMAP servers eventually, we would like them to
be highly available, without human intervention. That means having an
......@@ -1567,6 +1187,22 @@ is part of the "Pro" version of Dovecot, not usable in the free
version (see [mailbox formats](https://doc.dovecot.org/admin_manual/mailbox_formats/)). There's also someone who
implemented a [syncthing backend](https://github.com/fragtion/dovecot-core).
## Proposed Solutions
We went through a number of proposals to improve mail services over
time:
* [TPA-RFC-15: Email services](policy/tpa-rfc-15-email-services) (rejected, replaced with
TPA-RFC-31)
* [TPA-RFC-31: outsource email services](policy/tpa-rfc-31-outsource-email) (rejected as well, in
favor of TPA-RFC-44 and following)
* [TPA-RFC-44: Email emergency recovery](policy/tpa-rfc-44-email-emergency-recovery) (DKIM, SPF, DMARC
records, long term plan postponed)
* TPA-RFC-45: Mail architecture (long term plans from TPA-RFC-44)
* TPA-RFC-71: Emergency email deployments, phase B (implementation of SRS, done)
* [Enabling local delivery for regular nodes](https://gitlab.torproject.org/tpo/tpa/team/-/issues/42024)
* [Email deliverability monitoring](https://gitlab.torproject.org/tpo/tpa/team/-/issues/40494)
## Submission server proposal
Note: this proposal was discussed inline in the old
......@@ -1846,67 +1482,3 @@ Details of the chosen alternative (SMTP authentication):
See also internal Nextcloud document.
No benchmark considered necessary.
## New mail setup as per 2024-11-25
Our new MX (mx-dal-01.torproject.org) and SRS (srs-dal-01.torproject.org) servers try to fit into the picture presented in TPA-RFC-44:
![](../policy/tpa-rfc-44-email-emergency-recovery/architecture-post.png)
SRS would classify as 'other TPA mail server' in this picture. It notably *does* send mail to internet non-TPO mail hosts.
### Routing
Our main domain name is `torproject.org`. There are numerous subdomains and domain variants (e.g., `nevii.torproject.org`, `torproject.net`, etc.). These are all alias domains, meaning all addresses will be aliased to their `torproject.org` counterpart.
Lacking mailboxes, a `torproject.org` e-mail address can either be defined as an alias or as a forward.
Aliases are defined in Hiera.
Domain aliases are defined in Hiera and through puppet exported resources.
Forwards are defined in Hiera and in LDAP.
The MX resolves all aliases. It does *not* resolve forwards, but transports them to the SRS server(s). It does *not* deliver mail to internet non-TPO mail servers.
The SRS server resolves all forwards, applies sender rewriting when necessary, and sends the mail out into the world.
### DNS
MX servers need to be part of the `torproject.org` MX record.
SRS servers need to be part of the `srs.torproject.org` MX record.
SRS servers need to be added to the `torproject.org` SPF record.
SRS servers need to have their DKIM keys added to `torproject.org` DNS.
All mail servers need reverse DNS records.
All mail servers need a TLSA record.
### TLS
All mail servers need a valid public CA (Let's Encrypt) certificate.
All mail servers implement DANE (both in terms of publishing a TLSA record and checking for TLSA records when delivering mail).
### DMARC
DMARC checks are performed on the MX servers and Authentication-Results are added as header. DMARC is not strictly enforced, but failure to pass results in penalties to the spam score.
The SRS servers add DKIM signatures for all mail that has a From: header with a `torproject.org` address (header `From`, not envelope From). It does so using rspamd.
### Spam and virus checks
The MX server checks incoming mail for spam and virus using rspamd and clamav.
Viruses and very obvious spam get rejected straight away.
Suspicion of possible spam results in gray listing, with spam results added as headers when the mail does go through.
### SRS
SRS servers perform sender rewriting, changing the envelope `From` to a `torproject.org` address where needed.
The MX servers translate rewritten `torproject.org` addresses back to their original to process bounce mails.