Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add MTA-STS support for outbound mail #3592

Merged
merged 23 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
09b9a53
feat: add support for MTA-STS for outgoing mails
jsonn Oct 21, 2023
16dc9af
Apply suggestions from code review
polarathene Oct 24, 2023
5a3c7f4
Rename setup script to kebab-case.
jsonn Oct 24, 2023
696bf35
Move documentation from Advanced to Best Practises
jsonn Oct 24, 2023
33c0c7a
Link MTA-STS in the main navigation
jsonn Oct 24, 2023
a9c8458
Update docs/mkdocs.yml
polarathene Oct 24, 2023
0faed2f
Persist mta-sts-daemon cache. Run unprivileged.
jsonn Nov 1, 2023
a9669ab
Merge two RUN commands to appease lint
jsonn Nov 1, 2023
b204093
Syntax
jsonn Nov 1, 2023
83f8788
Merge branch 'master' into mta-sts-support
georglauterbach Nov 1, 2023
8850612
Update target/supervisor/conf.d/supervisor-app.conf
jsonn Nov 7, 2023
97c75a1
Update target/mta-sts-daemon/mta-sts-daemon.yml
jsonn Nov 7, 2023
115435a
Update target/scripts/startup/setup.d/mta-sts.sh
georglauterbach Nov 14, 2023
824ea33
Merge branch 'master' into mta-sts-support
georglauterbach Nov 14, 2023
fe2ff7b
Merge branch 'docker-mailserver:master' into mta-sts-support
jsonn Jan 3, 2024
f517302
Restart mta-sts-daemon automatically if killed.
jsonn Jan 10, 2024
7e0fd83
Hook-up mta-sts-daemon into basic process handling test
jsonn Jan 10, 2024
df475f1
fix: Call python script directly
polarathene Jan 11, 2024
cef26a9
Merge branch 'master' into mta-sts-support
polarathene Jan 11, 2024
96c112c
Merge branch 'master' into mta-sts-support
polarathene Jan 12, 2024
2eb9f7d
Prepare Changelog entry
jsonn Jan 12, 2024
df6073b
Update CHANGELOG.md
polarathene Jan 13, 2024
4ee42bd
Merge branch 'master' into mta-sts-support
polarathene Jan 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/content/config/advanced/mail-mta-sts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: 'Advanced | MTA-STS'
jsonn marked this conversation as resolved.
Show resolved Hide resolved
---

MTA-STS is an optional mechanism for a domain to signal support for
STARTTLS. It can be used to prevent man-in-the-middle-attacks hiding the
feature to force mail servers to send outgoing emails as plain text.
MTA-STS is an alternative to DANE without the need of DNSSEC.

MTA-STS is supported by some of the biggest mail providers like Google Mail
and Outlook.
polarathene marked this conversation as resolved.
Show resolved Hide resolved

## Supporting MTA-STS for outgoing mails
polarathene marked this conversation as resolved.
Show resolved Hide resolved

This is enabled by setting `ENABLE_MTA_STS=1` [](../environment.md#enable_mta_sts)
in the environment.
polarathene marked this conversation as resolved.
Show resolved Hide resolved

!!! warning

MTA-STS will by default override DANE if both are in used by a domain.
This can be partially addressed by configuring a dane-only policy resolver
before the MTA-STS entry in smtp_tls_policy_maps. See [the postfix-mta-sts-resolver documentation](https://github.com/Snawoot/postfix-mta-sts-resolver#warning-mta-sts-policy-overrides-dane-tls-authentication)
for further details.
polarathene marked this conversation as resolved.
Show resolved Hide resolved

## Supporting MTA-STS for incoming mails

A good introduction can be found on [dmarcian.com](https://dmarcian.com/mta-sts/).
polarathene marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions docs/content/config/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ This enables DNS block lists in _Postscreen_. If you want to know which lists we
- **0** => DNS block lists are disabled
- 1 => DNS block lists are enabled

##### ENABLE_MTA_STS

Enables MTA-STS for outgoing mails.
polarathene marked this conversation as resolved.
Show resolved Hide resolved

- **0** => Disabled
- 1 => Enabled

See [MTA-STS](advanced/mail-mta-sts.md) for further explanation.
jsonn marked this conversation as resolved.
Show resolved Hide resolved

##### ENABLE_OPENDKIM

Enables the OpenDKIM service.
Expand Down
7 changes: 7 additions & 0 deletions mailserver.env
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,13 @@ POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0
# Note: More details at http://www.postfix.org/postconf.5.html#inet_protocols
POSTFIX_INET_PROTOCOLS=all

# If enabled, STARTTLS support is enforced for outgoing mails to domains
# with MTA-STS records like Google Mail.
# This can prevent man-in-the-middle that hide the STARTTLS feature.
polarathene marked this conversation as resolved.
Show resolved Hide resolved
# - **0** ==> MTA-STS disabled
# - 1 => MTA-STS enabled
ENABLE_MTA_STS=0
jsonn marked this conversation as resolved.
Show resolved Hide resolved

# Choose TCP/IP protocols for dovecot to use
# **all** => Listen on all interfaces
# ipv4 => Listen only on IPv4 interfaces. Most likely you want this behind Docker.
Expand Down
2 changes: 1 addition & 1 deletion target/scripts/build/packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function _install_packages() {
)

POSTFIX_PACKAGES=(
pflogsumm postgrey postfix-ldap
pflogsumm postgrey postfix-ldap postfix-mta-sts-resolver
postfix-pcre postfix-policyd-spf-python postsrsd
)

Expand Down
5 changes: 5 additions & 0 deletions target/scripts/start-mailserver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ function _register_functions() {
_register_setup_function '_setup_apply_fixes_after_configuration'
_register_setup_function '_environment_variables_export'

if [[ ${ENABLE_MTA_STS} -eq 1 ]]; then
_register_setup_function '_setup_mta_sts'
_register_start_daemon '_start_daemon_mta_sts_daemon'
fi

# ? >> Daemons

_register_start_daemon '_start_daemon_cron'
Expand Down
1 change: 1 addition & 0 deletions target/scripts/startup/daemons-stack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function _start_daemon_opendkim { _default_start_daemon 'opendkim' ;
function _start_daemon_opendmarc { _default_start_daemon 'opendmarc' ; }
function _start_daemon_postgrey { _default_start_daemon 'postgrey' ; }
function _start_daemon_postsrsd { _default_start_daemon 'postsrsd' ; }
function _start_daemon_mta_sts_daemon { _default_start_daemon 'mta-sts-daemon' ; }
function _start_daemon_rspamd { _default_start_daemon 'rspamd' ; }
function _start_daemon_rspamd_redis { _default_start_daemon 'rspamd-redis' ; }
function _start_daemon_rsyslog { _default_start_daemon 'rsyslog' ; }
Expand Down
8 changes: 8 additions & 0 deletions target/scripts/startup/setup.d/mta_sts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
jsonn marked this conversation as resolved.
Show resolved Hide resolved

# Set up MTA-STS

function _setup_mta_sts() {
_log 'trace' 'Adding MTA-STS lookup to the Postfix TLS policy map'
polarathene marked this conversation as resolved.
Show resolved Hide resolved
postconf 'smtp_tls_policy_maps = socketmap:inet:127.0.0.1:8461:postfix'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your feature request issue noted that a separate container was "messy" to support?


What is the messy part requiring this in DMS?

I'm assuming it's container to container connection as unlike the port publishing from the endorsed example, you'd not have the port mapped to an interface reachable outside of the container? (IIRC, I think I've run into that networking problem in the past)

If so then the messy part is just providing your own config that adjusts the daemon config for host?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, we could use a unix socket here instead with the daemons config if we were to accept it. The only benefit of the TCP socket is using the defaults (EDIT: it seems there may be some ownership issues with a unix socket 🤷‍♂️ ).

For reference the socketmap table config is using :postfix in the name parameter which I assume is expected by the daemon service.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's messy as it requires inter-dependencies between containers. For something that I would consider an important security property, this problematic. I don't have a strong preference for communication locally via TCP oder UDS.

I'm still looking into the best option for testing. It seems to be a pity that there currently is no easy test domain in the wild and I'm trying to reach out to folks in the hope of something being willing to host one.

Copy link
Member

@polarathene polarathene Oct 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not too difficult to do this all locally, we don't need an external test domain.

It's just something I've been putting off until the test suite is refactored more to switch to docker compose as I can more easily manage flexible configurations.


If you'd like to implement it, I'll allow that.

  • Use a Caddy container to host the policy file. We can use our existing local testing certs, Caddy needs to be provided these along with a little config.
  • Use a CoreDNS container with a BIND file zone to handle the DNS records.
  • Ensure that DMS has the CA root cert in the trust store for verifying trust against the Caddy container.
  • I'm not sure if submitting mail to be sent to a domain the DMS container manages itself will work for the logs, it might optimize that delivery if it recognizes that it loops back into itself. That may require another DMS instance, but I don't think we're setup for that supporting that in tests yet 🤔(maybe)

I have familiarity with all of the above, so could probably contribute the Caddy + CoreDNS setup via a separate PR, then you could rebase to leverage that. I'll let you know if I get that handled, if you know of a better way I'm open to ideas :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason I would like to see a test domain exist is to give the user a way to test the correct behavior on their own. E.g. for DKIM there are validation services and the report functions. Without this testing, #3593 might have gone undetected for a while. I was just trying to resist the setup so far :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a working local offline setup with caddy + coredns to resolve the lookup via mta-sts-query from the package. Uses the test CA + cert I linked for TLS.

A user could setup the same locally with two DMS instances to test setup is correct, then after that is successful use real public DNS + certs 🤷‍♂️

I'll put a test together tomorrow to handle the sending and logs verification.


Our test suite is slowly getting better, once it adopts the local DNS setup more broadly it can have better test coverage to catch issues like you've linked, assuming contributors have time to write the tests 😅

The main reason I would like to see a test domain exist is to give the user a way to test the correct behavior on their own.
E.g. for DKIM there are validation services and the report functions.

I can't assist much with public validation concern, but I think being able to have an offline setup is a useful tool for getting confidence in getting everything working for new users :)

polarathene marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 8 additions & 0 deletions target/supervisor/conf.d/supervisor-app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,11 @@ autostart=false
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
command=/bin/bash -l -c /usr/local/bin/update-check.sh

jsonn marked this conversation as resolved.
Show resolved Hide resolved
[program:mta-sts-daemon]
startsecs=0
stopwaitsecs=55
autostart=false
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
command=/usr/bin/python3 /usr/bin/mta-sts-daemon --config /etc/mta-sts-daemon.yml
polarathene marked this conversation as resolved.
Show resolved Hide resolved