diff --git a/CHANGELOG.md b/CHANGELOG.md index 74acdc61..ffb96636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file. The format > **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes. +### 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/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/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' }