diff --git a/.github/workflows/generic_build.yml b/.github/workflows/generic_build.yml index 0f375d45..ccef46f5 100644 --- a/.github/workflows/generic_build.yml +++ b/.github/workflows/generic_build.yml @@ -64,7 +64,7 @@ jobs: # When full, the least accessed cache upload is evicted to free up storage. # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows - name: 'Handle Docker build layer cache' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: cache-buildx-${{ steps.derive-image-cache-key.outputs.digest }} diff --git a/.github/workflows/generic_publish.yml b/.github/workflows/generic_publish.yml index 00771221..d7a791c5 100644 --- a/.github/workflows/generic_publish.yml +++ b/.github/workflows/generic_publish.yml @@ -46,7 +46,7 @@ jobs: # NOTE: Until adopting `type=gha` scoped cache exporter (in `docker/build-push-action`), # only AMD64 image is expected to be cached, ARM images will build from scratch. - name: 'Retrieve image build from build cache' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: cache-buildx-${{ inputs.cache-key }} diff --git a/.github/workflows/generic_test.yml b/.github/workflows/generic_test.yml index 5b8bac62..2c1d1045 100644 --- a/.github/workflows/generic_test.yml +++ b/.github/workflows/generic_test.yml @@ -29,7 +29,7 @@ jobs: # This should always be a cache-hit, thus `restore-keys` fallback is not used. # No new cache uploads should ever happen for this job. - name: 'Retrieve image built from build cache' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: cache-buildx-${{ inputs.cache-key }} diff --git a/.github/workflows/generic_vulnerability-scan.yml b/.github/workflows/generic_vulnerability-scan.yml index 896ee80e..b261de91 100644 --- a/.github/workflows/generic_vulnerability-scan.yml +++ b/.github/workflows/generic_vulnerability-scan.yml @@ -28,7 +28,7 @@ jobs: # This should always be a cache-hit, thus `restore-keys` fallback is not used. # No new cache uploads should ever happen for this job. - name: 'Retrieve image built from build cache' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: cache-buildx-${{ inputs.cache-key }} @@ -55,7 +55,7 @@ jobs: provenance: false - name: 'Run the Anchore Grype scan action' - uses: anchore/scan-action@v3.5.0 + uses: anchore/scan-action@v3.6.0 id: scan with: image: mailserver-testing:ci diff --git a/CHANGELOG.md b/CHANGELOG.md index 30fa1d54..d8fbe449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,13 @@ The most noteworthy change of this release is the update of the container's base - `smtpd_relay_restrictions` (relay policy) is now evaluated after `smtpd_recipient_restrictions` (spam policy). Previously it was evaluated before `smtpd_recipient_restrictions`. Mail to be relayed via DMS must now pass through the spam policy first. - The TLS fingerprint policy has changed the default from MD5 to SHA256 (_DMS does not modify this Postfix parameter, but may affect any user customizations that do_). +### Fixes + +- **Dovecot:** + - Restrict the auth mechanisms for PassDB configs we manage (oauth2, passwd-file, ldap) ([#3812](https://github.com/docker-mailserver/docker-mailserver/pull/3812)) + - Prevents misleading auth failures from attempting to authenticate against a PassDB with incompatible auth mechanisms. + - When the new OAuth2 feature was enabled, it introduced false-positives with logged auth failures which triggered Fail2Ban to ban the IP. + ## [v13.3.0](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v13.3.0) ### Features diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 53254c78..f86435f4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -814,14 +814,21 @@ Thanks goes to these wonderful people ✨ fl42 + + + nilshoell +
+ nilshoell +
+ + stigok
stigok
- - + 5ven @@ -856,15 +863,15 @@ Thanks goes to these wonderful people ✨
thomasschmit
- + + Thiritin
Thiritin
- - + tweibert @@ -899,15 +906,15 @@ Thanks goes to these wonderful people ✨
k3it
- + + Drakulix
Drakulix
- - + vilisas @@ -942,13 +949,6 @@ Thanks goes to these wonderful people ✨
allddd
- - - - nilshoell -
- nilshoell -
@@ -1022,21 +1022,28 @@ Thanks goes to these wonderful people ✨ romansey + + + norrs +
+ norrs +
+ MightySCollins
MightySCollins
- + + 501st-alpha1
501st-alpha1
- - + klamann @@ -1071,15 +1078,15 @@ Thanks goes to these wonderful people ✨
sjmudd
- + + simonsystem
simonsystem
- - + stephan-devop @@ -1114,15 +1121,15 @@ Thanks goes to these wonderful people ✨
okamidash
- + + olaf-mandel
olaf-mandel
- - + ontheair81 @@ -1157,15 +1164,15 @@ Thanks goes to these wonderful people ✨
rmlhuk
- + + rriski
rriski
- - + schnippl0r @@ -1200,15 +1207,15 @@ Thanks goes to these wonderful people ✨
strarsis
- + + tamueller
tamueller
- - + vivacarvajalito @@ -1243,15 +1250,15 @@ Thanks goes to these wonderful people ✨
arcaine2
- + + awb99
awb99
- - + brainkiller @@ -1286,15 +1293,15 @@ Thanks goes to these wonderful people ✨
eleith
- + + ghnp5
ghnp5
- - + helmutundarnold @@ -1329,15 +1336,15 @@ Thanks goes to these wonderful people ✨
ixeft
- + + jjtt
jjtt
- - + paralax @@ -1372,15 +1379,15 @@ Thanks goes to these wonderful people ✨
marios88
- + + matrixes
matrixes
- - + mchamplain @@ -1388,13 +1395,6 @@ Thanks goes to these wonderful people ✨ mchamplain - - - 0xflotus -
- 0xflotus -
- auchri @@ -1452,21 +1452,28 @@ Thanks goes to these wonderful people ✨ danielvandenberg95 + + + denisix +
+ denisix +
+ mlatorre31
mlatorre31
- + + mazzz1y
mazzz1y
- - + aydodo @@ -1501,15 +1508,15 @@ Thanks goes to these wonderful people ✨
ekkis
- + + ErikEngerd
ErikEngerd
- - + huncode @@ -1532,12 +1539,20 @@ Thanks goes to these wonderful people ✨ - - froks + + thechubbypanda
- froks + thechubbypanda
+ + + 0xflotus +
+ 0xflotus +
+ + ifokeev @@ -1551,8 +1566,7 @@ Thanks goes to these wonderful people ✨
20th
- - + 2b @@ -1580,7 +1594,8 @@ Thanks goes to these wonderful people ✨
vifino
- + + kachkaev @@ -1594,8 +1609,7 @@ Thanks goes to these wonderful people ✨
alexanderneu
- - + ch3sh1r @@ -1623,7 +1637,8 @@ Thanks goes to these wonderful people ✨
green-anger
- + + iRhonin @@ -1637,8 +1652,7 @@ Thanks goes to these wonderful people ✨
MrFreezeex
- - + arunvc @@ -1666,7 +1680,8 @@ Thanks goes to these wonderful people ✨
spock
- + + erdos4d @@ -1680,14 +1695,6 @@ Thanks goes to these wonderful people ✨
crash7
- - - - - fkefer -
- fkefer -
@@ -1716,15 +1723,15 @@ Thanks goes to these wonderful people ✨
LeoWinterDE
- + + linhandev
linhandev
- - + luke- @@ -1759,15 +1766,15 @@ Thanks goes to these wonderful people ✨
maxemann96
- + + dragetd
dragetd
- - + michaeljensen @@ -1802,15 +1809,15 @@ Thanks goes to these wonderful people ✨
MohammedNoureldin
- + + mpldr
mpldr
- - + naveensrinivasan @@ -1832,6 +1839,21 @@ Thanks goes to these wonderful people ✨ radicand + + + froks +
+ froks +
+ + + + fkefer +
+ fkefer +
+ + frugan-dev @@ -1852,8 +1874,7 @@ Thanks goes to these wonderful people ✨
glandais
- - + GiovanH @@ -1874,7 +1895,8 @@ Thanks goes to these wonderful people ✨
HeySora
- + + sirgantrithon @@ -1895,8 +1917,7 @@ Thanks goes to these wonderful people ✨
jcalfee
- - + mivek @@ -1917,7 +1938,8 @@ Thanks goes to these wonderful people ✨
Jeidnx
- + + JiLleON @@ -1938,8 +1960,7 @@ Thanks goes to these wonderful people ✨
jmccl
- - + jurekbarth @@ -1960,20 +1981,14 @@ Thanks goes to these wonderful people ✨
Kaan88
- + + akkumar
akkumar
- - - - thechubbypanda -
- thechubbypanda -
diff --git a/mailserver.env b/mailserver.env index 9b085c9f..1d131696 100644 --- a/mailserver.env +++ b/mailserver.env @@ -388,7 +388,7 @@ SPAMASSASSIN_SPAM_TO_INBOX=1 # spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required) MOVE_SPAM_TO_JUNK=1 -# spam messages wil be marked as read +# spam messages will be marked as read MARK_SPAM_AS_READ=0 # add 'spam info' headers at, or above this level diff --git a/target/dovecot/auth-ldap.conf.ext b/target/dovecot/auth-ldap.conf.ext new file mode 100644 index 00000000..222769aa --- /dev/null +++ b/target/dovecot/auth-ldap.conf.ext @@ -0,0 +1,21 @@ +# NOTE: This is effectively the same default LDAP config shipped by Dovecot +# The only difference is the addition of the passdb mechanisms field, +# which restricts what auth mechanisms are supported / expected. +# This prevents unnecessary auth failure logs triggering Fail2Ban when +# additional passdb are enabled (OAuth2). + +passdb { + driver = ldap + mechanism = plain login + + # Path for LDAP configuration file, see example-config/dovecot-ldap.conf.ext + args = /etc/dovecot/dovecot-ldap.conf.ext +} + +userdb { + driver = ldap + args = /etc/dovecot/dovecot-ldap.conf.ext + + # Default fields can be used to specify defaults that LDAP may override + #default_fields = home=/home/virtual/%u +} diff --git a/target/dovecot/auth-oauth2.conf.ext b/target/dovecot/auth-oauth2.conf.ext index 6096d1e4..99a7986b 100644 --- a/target/dovecot/auth-oauth2.conf.ext +++ b/target/dovecot/auth-oauth2.conf.ext @@ -1,5 +1,12 @@ +# Allow clients to use these additional mechanisms: auth_mechanisms = $auth_mechanisms oauthbearer xoauth2 +# Dovecot docs consider the oauth2 driver as a "success/failure" type PassDB: +# https://doc.dovecot.org/configuration_manual/authentication/password_databases_passdb/#success-failure-database +# Which implies it cannot be configured for the non-plaintext SASL mechanisms listed here: +# https://doc.dovecot.org/configuration_manual/authentication/authentication_mechanisms/#dovecot-supports-the-following-non-plaintext-mechanisms +# However that is not the case, these mechanisms are still valid to prevent trying other incompatible mechanisms (like `plain`). + passdb { driver = oauth2 mechanisms = xoauth2 oauthbearer diff --git a/target/dovecot/auth-passwdfile.inc b/target/dovecot/auth-passwdfile.inc index 6bbf8258..38be4e5f 100644 --- a/target/dovecot/auth-passwdfile.inc +++ b/target/dovecot/auth-passwdfile.inc @@ -9,6 +9,7 @@ passdb { driver = passwd-file + mechanisms = plain login args = scheme=SHA512-CRYPT username_format=%u /etc/dovecot/userdb } diff --git a/target/scripts/startup/setup-stack.sh b/target/scripts/startup/setup-stack.sh index f55cb548..c3c54cc3 100644 --- a/target/scripts/startup/setup-stack.sh +++ b/target/scripts/startup/setup-stack.sh @@ -94,6 +94,10 @@ function _setup_apply_fixes_after_configuration() { _log 'debug' 'Removing files and directories from older versions' rm -rf /var/mail-state/spool-postfix/{dev,etc,lib,pid,usr,private/auth} + + # /tmp/docker-mailserver/rspamd/dkim + _log 'debug' "Ensuring ${RSPAMD_DMS_DKIM_D} is owned by '_rspamd:_rspamd'" + chown -R _rspamd:_rspamd "${RSPAMD_DMS_DKIM_D}" } function _run_user_patches() { diff --git a/test/config/oauth2/Caddyfile b/test/config/oauth2/Caddyfile index e116aa55..f87ffc80 100644 --- a/test/config/oauth2/Caddyfile +++ b/test/config/oauth2/Caddyfile @@ -38,6 +38,9 @@ } } +# NOTE: This portion of config is only relevant for understanding what happens seamlesssly, +# DMS tests no longer use raw IMAP commands with netcat, thus none of this is relevant beyond reference for troubleshooting. +# # /imap/xoauth2 # Generate IMAP commands for authentication testing # Base64 encoded credentials can alternative be done via CLI with: diff --git a/test/files/auth/imap-oauth2-oauthbearer.txt b/test/files/auth/imap-oauth2-oauthbearer.txt deleted file mode 100644 index d85c63e8..00000000 --- a/test/files/auth/imap-oauth2-oauthbearer.txt +++ /dev/null @@ -1,4 +0,0 @@ -a0 NOOP See test/config/oauth2/Caddyfile to generate the below OAUTHBEARER string -a1 AUTHENTICATE OAUTHBEARER bixhPXVzZXIxQGxvY2FsaG9zdC5sb2NhbGRvbWFpbiwBaG9zdD1sb2NhbGhvc3QBcG9ydD0xNDMBYXV0aD1CZWFyZXIgRE1TX1lXTmpaWE56WDNSdmEyVnUBAQ== -a2 EXAMINE INBOX -a3 LOGOUT diff --git a/test/files/auth/imap-oauth2-xoauth2.txt b/test/files/auth/imap-oauth2-xoauth2.txt deleted file mode 100644 index 0371b0cf..00000000 --- a/test/files/auth/imap-oauth2-xoauth2.txt +++ /dev/null @@ -1,4 +0,0 @@ -a0 NOOP See test/config/oauth2/Caddyfile to generate the below XOAUTH2 string -a1 AUTHENTICATE XOAUTH2 dXNlcj11c2VyMUBsb2NhbGhvc3QubG9jYWxkb21haW4BYXV0aD1CZWFyZXIgRE1TX1lXTmpaWE56WDNSdmEyVnUBAQ== -a2 EXAMINE INBOX -a3 LOGOUT diff --git a/test/tests/serial/mail_with_oauth2.bats b/test/tests/serial/mail_with_oauth2.bats index 0cc34a01..9b4d12fe 100644 --- a/test/tests/serial/mail_with_oauth2.bats +++ b/test/tests/serial/mail_with_oauth2.bats @@ -58,21 +58,59 @@ function teardown_file() { docker network rm "${DMS_TEST_NETWORK}" } -@test "should authenticate with XOAUTH2 over IMAP" { - _nc_wrapper 'auth/imap-oauth2-xoauth2.txt' '-w 1 0.0.0.0 143' - __verify_successful_login 'XOAUTH2' +@test "should authenticate with XOAUTH2" { + __should_login_successfully_with 'XOAUTH2' } -@test "should authenticate with OAUTHBEARER over IMAP" { - _nc_wrapper 'auth/imap-oauth2-oauthbearer.txt' '-w 1 0.0.0.0 143' - __verify_successful_login 'OAUTHBEARER' +@test "should authenticate with OAUTHBEARER" { + __should_login_successfully_with 'OAUTHBEARER' } -function __verify_successful_login() { +function __should_login_successfully_with() { local AUTH_METHOD=${1} + # These values are the auth credentials checked against the Caddy `/userinfo` endpoint: + local USER_ACCOUNT='user1@localhost.localdomain' + local ACCESS_TOKEN='DMS_YWNjZXNzX3Rva2Vu' + __verify_auth_with_imap + __verify_auth_with_smtp +} + +# Dovecot direct auth verification via IMAP: +function __verify_auth_with_imap() { + # NOTE: Include the `--verbose` option if you're troubleshooting and want to see the protocol exchange messages + # NOTE: `--user username:password` is valid for testing `PLAIN` auth mechanism, but you should prefer swaks instead. + _run_in_container curl --silent \ + --login-options "AUTH=${AUTH_METHOD}" --oauth2-bearer "${ACCESS_TOKEN}" --user "${USER_ACCOUNT}" \ + --url 'imap://localhost:143' -X 'LOGOUT' + + __dovecot_logs_should_verify_success +} + +# Postfix delegates by default to Dovecot via SASL: +# NOTE: This won't be compatible with LDAP if `ENABLE_SASLAUTHD=1` with `ldap` SASL mechanism: +function __verify_auth_with_smtp() { + # NOTE: `--upload-file` with some mail content seems required for using curl to test OAuth2 authentication. + # TODO: Replace with swaks and early exit option when it supports XOAUTH2 + OAUTHBEARER: + _run_in_container curl --silent \ + --login-options "AUTH=${AUTH_METHOD}" --oauth2-bearer "${ACCESS_TOKEN}" --user "${USER_ACCOUNT}" \ + --url 'smtp://localhost:587' --mail-from "${USER_ACCOUNT}" --mail-rcpt "${USER_ACCOUNT}" --upload-file - <<< 'RFC 5322 content - not important' + + # Postfix specific auth logs: + _run_in_container grep 'postfix/submission/smtpd' /var/log/mail.log + assert_output --partial "sasl_method=${AUTH_METHOD}, sasl_username=${USER_ACCOUNT}" + + # Dovecot logs should still be checked as it is handling the actual auth process under the hood: + __dovecot_logs_should_verify_success +} + +function __dovecot_logs_should_verify_success() { # Inspect the relevant Dovecot logs to catch failure / success: _run_in_container grep 'dovecot:' /var/log/mail.log refute_output --partial 'oauth2 failed: Introspection failed' - assert_output --partial "dovecot: imap-login: Login: user=, method=${AUTH_METHOD}" + assert_output --partial "dovecot: imap-login: Login: user=<${USER_ACCOUNT}>, method=${AUTH_METHOD}" + + # If another PassDB is enabled, it should not have been attempted with the XOAUTH2 / OAUTHBEARER mechanisms: + # dovecot: auth: passwd-file(${USER_ACCOUNT},127.0.0.1): Password mismatch (SHA1 of given password: d390c1) - trying the next passdb + refute_output --partial 'trying the next passdb' }