security(Postfix): Protect against "SMTP Smuggling" attack#3727
security(Postfix): Protect against "SMTP Smuggling" attack#3727polarathene merged 6 commits intomasterfrom
Conversation
|
Oh great, this messes with our test suite commands for sending and receiving e-mails, I think... I do not have time now to fix this though... |
There was a problem hiding this comment.
Change overview (see review feedback for insights):
- Add
smtpd_data_restrictionstomain.cf, remove the client and recipient restriction forreject_unauth_pipelining.- This defers rejection to a later stage (assuming a permit rule didn't skip restrictions prior), where any detected pipelining in the exchange would trigger the restriction, not just within
DATAcommand.
- This defers rejection to a later stage (assuming a permit rule didn't skip restrictions prior), where any detected pipelining in the exchange would trigger the restriction, not just within
- Keep
smtpd_data_restrictionsin Amavis config. - For a v14 target,
smtpd_data_restrictionsis not necessary to workaround SMTP smuggling vulnerability.- We would have Postfix 3.7.6, this makes
smtpd_forbid_unauth_pipelining = yesavailable. It should be adopted instead (but thesmtpd_data_restrictionschecklist above is valid (perhaps as a separate PR). - Both short-term workarounds also reference enabling
smtpd_discard_ehlo_keywords = chunking. This conflicts with the same existing parameter already configured, prepending/appending to that should be fine, but itshould be assessed that the feature doesn't have any logic that would accidentally remove(EDIT: Doesn't look like a concern 👍 ).chunking, the feature tests may not be sufficient for that, but should be obvious looking over the PR diff
- We would have Postfix 3.7.6, this makes
- Additional context in comments should be added regarding the long-term fix:
- It replaces the short-term fix parameters as workarounds.
- It's unclear when Debian Bookworm will update Postfix package with the available fix, but we can mention the min version needed to toggle the long-term fix.
EDIT: smtpd_data_restrictions review feedback has been deferred to a separate PR, with tracking issue: #3728
Overview for smtpd_data_restrictions:
smtpd_data_restrictions = reject_unauth_pipeliningshould probably remain on Amavis config, even though it'd be technically redundant (since it would now inherit frommain.cf). Reasoning is for maintenance, where this is an explicit Amavis config (official docs), and DMSmain.cfcould change again as review feedback implies. Better for maintenance visibility to retain it.smtpd_data_restrictions = reject_unauth_pipeliningonmain.cfstill may make sense, regardless of Amavis integration.- Presently we have this check at earlier restriction phases (client and recipient), but those seem unnecessary and can be dropped in favor of deferring to the data restriction which Postfix docs seem to indicate would be sufficient. Only difference would be rejecting due to the restriction slightly earlier (at the
RCPT TOSMTP command). - Potentially breaking, but only in the sense that a client is not respecting waiting their turn at the
DATAcommand, but for some reason was respectful earlier 🤷♂️
- Presently we have this check at earlier restriction phases (client and recipient), but those seem unnecessary and can be dropped in favor of deferring to the data restriction which Postfix docs seem to indicate would be sufficient. Only difference would be rejecting due to the restriction slightly earlier (at the
| smtpd_sender_restrictions = $dms_smtpd_sender_restrictions | ||
| smtpd_discard_ehlo_keywords = silent-discard, dsn | ||
| disable_vrfy_command = yes | ||
| smtpd_data_restrictions = reject_unauth_pipelining |
There was a problem hiding this comment.
Note that this exists already on smtpd_client_restrictions and smtpd_recipient_restrictions, introduced 7 years ago in #165
Both of those restrictions occur before smtpd_data_restrictions (section describes order), and probably should be removed in favor of smtpd_data_restrictions (based on information that follows below)
The PR that introduced reject_unauth_pipelining to the those two restrictions has a dead link, and while it does seem common enough to see in configs online, I'm not sure if they're correct. This comment also advises moving the restriction to smtpd_data_restrictions.
Amavis config for Postfix clearly communicates this configuration requirement for smtpd_data_restrictions:
- https://www.ijs.si/software/amavisd/README.postfix.txt
- https://wiki.archlinux.org/title/Amavis#Quick_start
- https://help.ubuntu.com/community/PostfixAmavisNew#Postfix_integration
The Postfix docs note that it is applicable during any SMTP command context, but also mentions:
With Postfix 2.6 and later, the SMTP server sets a per-session flag whenever it detects illegal pipelining, including pipelined
HELOorEHLOcommands.
Thereject_unauth_pipeliningfeature simply tests whether the flag was set at any point in time during the session.
Since smtpd_data_restrictions is pretty much at the end of the session, I think that is why earlier reject_unauth_pipelining restrictions aren't likely to provide any benefit? 🤷♂️ (other than rejecting at an earlier stage, which should technically only be RCPT TO due to default smtpd_delay_reject=yes, which we for some reason set explicitly)
Also related I think is the postscreen Command pipelining test (Postfix docs for postscreen):
Unlike the Postfix SMTP server,
postscreen(8)does not announce support for ESMTP command pipelining.
Therefore, clients are not allowed to send multiple commands.With
postscreen_pipelining_enable = yes,postscreen(8)detects zombies that send multiple commands, instead of sending one command and waiting for the server to reply.
This test is opportunistically enabled when
postscreen(8)has to use the built-in SMTP engine anyway. This is to makepostscreen(8)logging more informative.The
postscreen_pipelining_actionparameter specifies the action that is taken next.
There was a problem hiding this comment.
Might be worth adding some context of the restriction.
| smtpd_data_restrictions = reject_unauth_pipelining | |
| # Block clients that speak too early (don't wait their turn). | |
| smtpd_data_restrictions = reject_unauth_pipelining |
Keeping it in main.cf should be fine AFAIK, Amavis will inherit it and it may still make sense when Amavis is not used? (although I kinda prefer to keep it explicit on the Amavis snippet, instead of implicitly inferring it from main.cf)
The documented short-term workaround for the SMTP smuggling vulnerability is:
| smtpd_data_restrictions = reject_unauth_pipelining | |
| smtpd_data_restrictions = reject_unauth_pipelining | |
| # This will conflict with existing line: | |
| # smtpd_discard_ehlo_keywords = silent-discard, dsn | |
| smtpd_discard_ehlo_keywords = chunking | |
| # Should replace original (and add contextual comment to PR for the dsn config) | |
| # Also requires checking that the logic for that PR isn't broken from any assumptions that prepending/appending 'chunking' here may break: | |
| # smtpd_discard_ehlo_keywords = chunking, silent-discard, dsn |
but once we have v14 of DMS we'll have Postfix 3.7.6 and can use the original short-term workaround:
| smtpd_data_restrictions = reject_unauth_pipelining | |
| # Block clients that speak too early (don't wait their turn). | |
| # Amavis config expects this (implicitly inherits from main.cf) | |
| smtpd_data_restrictions = reject_unauth_pipelining | |
| # Security - Prevent "SMTP Smuggling" exploit | |
| # https://github.com/docker-mailserver/docker-mailserver/issues/3719 | |
| smtpd_forbid_unauth_pipelining = yes | |
| # See prior suggestion for concerns | |
| smtpd_discard_ehlo_keywords = chunking |
Only benefit then is to get this in for 13.1, but you've got it marked for v14? In which case the Postfix 3.7.6 fix is more appropriate.
| # TODO enable when possible, see https://github.com/docker-mailserver/docker-mailserver/issues/3719#issuecomment-1868287208 | ||
| #smtpd_forbid_bare_newline = yes |
There was a problem hiding this comment.
"When possible" could better clarify that the blocker is waiting on Debian to provide an updated package of Postfix (3.8.4, 3.7.9, 3.6.13 and 3.5.23). For Bookworm that'd be 3.7.9.
The comment also only refers to enabling the parameter, despite obsoleting the short-term workarounds.
The longterm-fix also references an optional 2nd parameter to configure:
smtpd_forbid_bare_newline = yes
smtpd_forbid_bare_newline_exclusions = $mynetworks
We shouldn't configure it by default in the image though (since Docker with IPv6 hosts but IPv4 only containers with default user-proxy: true daemon config) can be part of the trusted docker subnet (depending on our PERMIT_DOCKER ENV). Those that need it (like our test-suite) could enable it, and we can document that in our docs somewhere.
Technically an improvement 😝 We skipped it in the past by having Amavis disabled in the test suite. Now that you've lifted it up into default
Should be fixed if we migrate away from An easier workaround of course is to just use our own override support with EDIT: There is an alternative short-term workaround for SMTP smuggling that you can use instead (as per my review checklist), that'll avoid the test failure issue. EDIT: The |
|
Nice feedback, I will take care of all mentions over the course of the next days. This is marked for v14 still, so we do not need to fiddle with v13.1.0; makes my life easier. |
|
fyi: I just noticed, that the debian11 postfix package was bumped to version 3.5.23 and introduced this new default values:
However, the new version is not listed yet. |
|
Update: we are currently working on reviewing
which are both high-priority by now. Hence, a few breaking changes are going to be introduced with v14.0.0. |
|
Since the current base image now has an updated |
There was a problem hiding this comment.
Updated to use the long-term fix discussed in prior review feedback.
Prior review feedback on the smtpd_data_restrictions = reject_unauth_pipelining is no longer required as part of this PR. I've reverted that, but we can have a separate PR handle that (add to main.cf, keep duplicate in postfix-amavis.cf).
Technically if we make a v13.1.1 release, while it's a fix against the attack, it may be breaking for clients unintentionally? Which is why the default is no for existing releases.
- Release as v13.1.1?
- Release as v13.2?
- Release with feature disabled, but add a
postfix-main.cfoverride suggestion in changelog? - Delay until v14 release?
UPDATE: I've had over 5 runs fail now with the Fail2Ban multiple failed logins test-case. Although failure is due to timeout:
Failing test
not ok 52 [Fail2Ban] ban ip on multiple failed login in 11659ms
# (from function `assert_success' in file test/test_helper/bats-assert/src/assert_success.bash, line 42,
# in test file test/tests/parallel/set1/spam_virus/fail2ban.bats, line 82)
# `assert_success' failed
#
# -- command failed --
# status : 1
# output : Timed out on command: _exec_in_container /bin/bash -c fail2ban-client status postfix-sasl | grep -F '172.17.0.5'
# --
docker-mailserver/test/tests/parallel/set1/spam_virus/fail2ban.bats
Lines 73 to 83 in 0889b0f
I can only assume this is due to smtpd_forbid_bare_newline = yes, as the expectation is for CR LF not LF, and I believe the PR that added .gitattributes normalized all our nc input files to LF... yet this is the only test-case failing 🤷♂️
Reply with
Error: bare <LF> receivedand disconnect when a remote SMTP client sends a line ending in<LF>, violating the RFC 5321 requirement that lines must end in<CR><LF>.
The failed login attempts were presumably rejected before they'd log anything that Fail2Ban monitors to ban the IP. A workaround in the test would be to leverage PERMIT_DOCKER=network to be excluded from the restriction.
#3732 should be compatible, and could be part of a v13.2 release?
| # https://www.postfix.org/smtp-smuggling.html#long | ||
| smtpd_forbid_bare_newline = yes | ||
| # It is possible to exclude clients on trusted networks from this restriction: | ||
| # smtpd_forbid_bare_newline_exclusions = $mynetworks |
There was a problem hiding this comment.
The default is actually $mynetworks already, perhaps we want to disable that? Or rely on the default of PERMIT_DOCKER=none making that unnecessary?
Only concern is due to Docker and IPv6 capable hosts mistakenly routing through to an IPv4 only DMS container (due to Docker daemon user-proxy: true default config), where it'll be trusted in $mynetworks that cover the Docker network gateway address in a subnet (eg: x.y.z.1).
|
Resolved. TODO added. CI cache prevented package updatesTests presently fail due to the new parameter not being recognized. The image was not built with newer packages as our cache fallback provided all earlier layers of the Failing test snippetLine 204 in 0889b0f I'll purge the CI cache to fix. |
See my recent review update. Fail2Ban is failing consistently from this change.
We can get v14 out quickly, so I don't mind. It may benefit users to stage this out as v13.2 before the base image update, but hopefully neither change will cause any new issues to troubleshoot 👍 |
|
I like this route:
|
Description
Prevent (currently known) attack vectors of SMTP smuggling, see #3719.
Fixes #3719
Type of change
Checklist:
docs/)CHANGELOG.md