diff --git a/.env.dist b/.env.dist index cfa07fae..873883d7 100644 --- a/.env.dist +++ b/.env.dist @@ -22,13 +22,29 @@ ONE_DIR=0 # empty => postmaster@domain.com # => Specify the postmaster address POSTMASTER_ADDRESS= - + # Set different options for mynetworks option (can be overwrite in postfix-main.cf) # empty => localhost only # host => Add docker host (ipv4 only) # network => Add all docker containers (ipv4 only) PERMIT_DOCKER= +# empty => modern +# modern => Enables TLSv1.2 and modern ciphers only. (default) +# intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers. +# old => NOT implemented. If you really need it, then customize the TLS ciphers overriding postfix and dovecot settings +# (https://github.com/tomav/docker-mailserver/wiki/) +TLS_LEVEL= + +# Configures the handling of creating mails with forged sender addresses. +# +# empty => (not recommended, but default for backwards compatability reasons) +# Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. +# See also https://en.wikipedia.org/wiki/Email_spoofing +# 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. +# Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. +SPOOF_PROTECTION= + # 1 => Enables POP3 service # empty => disables POP3 ENABLE_POP3= @@ -85,7 +101,7 @@ POSTFIX_DAGENT= ENABLE_SPAMASSASSIN=0 # add spam info headers if at, or above that level: -SA_TAG=2.0 +SA_TAG=2.0 # add 'spam detected' headers at that level SA_TAG2=6.31 @@ -102,7 +118,7 @@ SA_SPAM_SUBJECT=***SPAM***** ENABLE_FETCHMAIL=0 -# The interval to fetch mail in seconds +# The interval to fetch mail in seconds FETCHMAIL_POLL=300 # ----------------------------------------------------------------------------------------------------------------------------- @@ -220,4 +236,4 @@ SASLAUTHD_LDAP_FILTER= # empty => No sasl_passwd will be created # string => `/etc/postfix/sasl_passwd` will be created with the string as password -SASL_PASSWD= \ No newline at end of file +SASL_PASSWD= diff --git a/Dockerfile b/Dockerfile index b94fe0e8..2ba5f502 100644 --- a/Dockerfile +++ b/Dockerfile @@ -176,7 +176,7 @@ RUN mkdir /var/run/fetchmail && chown fetchmail /var/run/fetchmail # Configures Postfix COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/ -COPY target/postfix/sender_header_filter.pcre /etc/postfix/maps/sender_header_filter.pcre +COPY target/postfix/sender_header_filter.pcre target/postfix/sender_login_maps.pcre /etc/postfix/maps/ RUN echo "" > /etc/aliases && \ openssl dhparam -out /etc/postfix/dhparams.pem 2048 && \ echo "@weekly FILE=`mktemp` ; openssl dhparam -out $FILE 2048 > /dev/null 2>&1 && mv -f $FILE /etc/postfix/dhparams.pem" > /etc/cron.d/dh2048 diff --git a/Makefile b/Makefile index 8d18e0cc..e272b2f6 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ run: -v "`pwd`/test":/tmp/docker-mailserver-test \ -v "`pwd`/test/onedir":/var/mail-state \ -e ENABLE_CLAMAV=1 \ + -e SPOOF_PROTECTION=1 \ -e ENABLE_SPAMASSASSIN=1 \ -e SA_TAG=-5.0 \ -e SA_TAG2=2.0 \ @@ -130,6 +131,7 @@ run: -e ENABLE_LDAP=1 \ -e LDAP_SERVER_HOST=ldap \ -e LDAP_START_TLS=no \ + -e SPOOF_PROTECTION=1 \ -e LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain \ -e LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain \ -e LDAP_BIND_PW=admin \ diff --git a/README.md b/README.md index 2a0aca54..a450ba9c 100644 --- a/README.md +++ b/README.md @@ -53,16 +53,16 @@ Minimum: #### Get the tools Download the docker-compose.yml, the .env and the setup.sh files: - + curl -o setup.sh https://raw.githubusercontent.com/tomav/docker-mailserver/master/setup.sh; chmod a+x ./setup.sh - + curl -o docker-compose.yml https://raw.githubusercontent.com/tomav/docker-mailserver/master/docker-compose.yml.dist - + curl -o .env https://raw.githubusercontent.com/tomav/docker-mailserver/master/.env.dist #### Create a docker-compose environment -- Edit the `.env` to your liking. Adapt this file with your FQDN. +- Edit the `.env` to your liking. Adapt this file with your FQDN. - Install [docker-compose](https://docs.docker.com/compose/) in the version `1.6` or higher. #### Create your mail accounts @@ -232,6 +232,7 @@ If you enable Fail2Ban, don't forget to add the following lines to your `docker- - NET_ADMIN Otherwise, `iptables` won't be able to ban IPs. + ##### SMTP_ONLY - **empty** => all daemons start @@ -254,6 +255,11 @@ Please read [the SSL page in the wiki](https://github.com/tomav/docker-mailserve - intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers. - old => NOT implemented. If you really need it, then customize the TLS ciphers overriding postfix and dovecot settings [ wiki](https://github.com/tomav/docker-mailserver/wiki/ +##### SPOOF_PROTECTION +Configures the handling of creating mails with forged sender addresses. + - **empty** => Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. See also [Wikipedia](https://en.wikipedia.org/wiki/Email_spoofing)(not recommended, but default for backwards compatability reasons) + - 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. + ##### PERMIT_DOCKER Set different options for mynetworks option (can be overwrite in postfix-main.cf) diff --git a/docker-compose.yml.dist b/docker-compose.yml.dist index f01e7db1..d5924466 100644 --- a/docker-compose.yml.dist +++ b/docker-compose.yml.dist @@ -26,6 +26,8 @@ services: - POSTSCREEN_ACTION=${POSTSCREEN_ACTION} - SMTP_ONLY=${SMTP_ONLY} - SSL_TYPE=${SSL_TYPE} + - TLS_LEVEL=${TLS_LEVEL} + - SPOOF_PROTECTION=${SPOOF_PROTECTION} - PERMIT_DOCKER=${PERMIT_DOCKER} - VIRUSMAILS_DELETE_DELAY=${VIRUSMAILS_DELETE_DELAY} - ENABLE_POSTFIX_VIRTUAL_TRANSPORT=${ENABLE_POSTFIX_VIRTUAL_TRANSPORT} @@ -72,4 +74,4 @@ volumes: driver: local mailstate: driver: local - + diff --git a/target/postfix/main.cf b/target/postfix/main.cf index 8e1b9fb4..10af5b65 100644 --- a/target/postfix/main.cf +++ b/target/postfix/main.cf @@ -46,7 +46,7 @@ smtpd_helo_restrictions = permit_mynetworks, reject_invalid_helo_hostname, permi smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, check_policy_service unix:private/policyd-spf, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain, reject_rbl_client zen.spamhaus.org, reject_rbl_client bl.spamcop.net smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining -smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain, reject_sender_login_mismatch +smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unknown_sender_domain disable_vrfy_command = yes # Postscreen settings to drop zombies/open relays/spam early diff --git a/target/postfix/sender_login_maps.pcre b/target/postfix/sender_login_maps.pcre new file mode 100644 index 00000000..96399fc1 --- /dev/null +++ b/target/postfix/sender_login_maps.pcre @@ -0,0 +1 @@ +/^(.*)$/ ${1} diff --git a/target/start-mailserver.sh b/target/start-mailserver.sh index a463f8df..f6e3b644 100644 --- a/target/start-mailserver.sh +++ b/target/start-mailserver.sh @@ -25,6 +25,7 @@ DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}" DEFAULT_VARS["OVERRIDE_HOSTNAME"]="${OVERRIDE_HOSTNAME}" DEFAULT_VARS["POSTMASTER_ADDRESS"]="${POSTMASTER_ADDRESS:="postmaster@domain.com"}" DEFAULT_VARS["POSTSCREEN_ACTION"]="${POSTSCREEN_ACTION:="enforce"}" +DEFAULT_VARS["SPOOF_PROTECTION"]="${SPOOF_PROTECTION:="0"}" DEFAULT_VARS["TLS_LEVEL"]="${TLS_LEVEL:="modern"}" ########################################################################## # << DEFAULT VARS @@ -118,6 +119,11 @@ function register_functions() { _register_setup_function "_setup_postfix_vhost" _register_setup_function "_setup_postfix_dhparam" _register_setup_function "_setup_postfix_postscreen" + + if [ "$SPOOF_PROTECTION" = 1 ]; then + _register_setup_function "_setup_spoof_protection" + fi + _register_setup_function "_setup_postfix_access_control" if [ ! -z "$AWS_SES_HOST" -a ! -z "$AWS_SES_USERPASS" ]; then @@ -128,7 +134,7 @@ function register_functions() { _register_setup_function "_setup_postfix_virtual_transport" fi - _register_setup_function "_setup_environment" + _register_setup_function "_setup_environment" ################### << setup funcs @@ -544,7 +550,7 @@ function _setup_ldap() { done notify 'inf' 'Starting to override configs' - for f in /etc/postfix/ldap-users.cf /etc/postfix/ldap-groups.cf /etc/postfix/ldap-aliases.cf /etc/postfix/ldap-domains.cf + for f in /etc/postfix/ldap-users.cf /etc/postfix/ldap-groups.cf /etc/postfix/ldap-aliases.cf /etc/postfix/ldap-domains.cf /etc/postfix/maps/sender_login_maps.ldap do [[ $f =~ ldap-user ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_USER}" [[ $f =~ ldap-group ]] && export LDAP_QUERY_FILTER="${LDAP_QUERY_FILTER_GROUP}" @@ -615,6 +621,14 @@ function _setup_postfix_postscreen() { -e "s/postscreen_bare_newline_action = enforce/postscreen_bare_newline_action = $POSTSCREEN_ACTION/" /etc/postfix/main.cf } +function _setup_spoof_protection () { + notify 'inf' "Configuring Spoof Protection" + sed -i 's|smtpd_sender_restrictions =|smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch,|' /etc/postfix/main.cf + [ "$ENABLE_LDAP" = 1 ] \ + && postconf -e "smtpd_sender_login_maps=ldap:/etc/postfix/ldap-users.cf ldap:/etc/postfix/ldap-aliases.cf ldap:/etc/postfix/ldap-groups.cf" \ + || postconf -e "smtpd_sender_login_maps=texthash:/etc/postfix/virtual, texthash:/etc/aliases, pcre:/etc/postfix/maps/sender_login_maps.pcre" +} + function _setup_postfix_access_control() { notify 'inf' "Configuring user access" [ -f /tmp/docker-mailserver/postfix-send-access.cf ] && sed -i 's|smtpd_sender_restrictions =|smtpd_sender_restrictions = check_sender_access texthash:/tmp/docker-mailserver/postfix-send-access.cf,|' /etc/postfix/main.cf diff --git a/test/auth/added-smtp-auth-spoofed-alias.txt b/test/auth/added-smtp-auth-spoofed-alias.txt new file mode 100644 index 00000000..48145183 --- /dev/null +++ b/test/auth/added-smtp-auth-spoofed-alias.txt @@ -0,0 +1,14 @@ +EHLO mail +AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu +bXlwYXNzd29yZA== +MAIL FROM: alias1@localhost.localdomain +RCPT TO: user1@localhost.localdomain +DATA +From: user1_alias +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message +This is a test mail. + +. +QUIT diff --git a/test/auth/added-smtp-auth-spoofed.txt b/test/auth/added-smtp-auth-spoofed.txt new file mode 100644 index 00000000..279b6c0e --- /dev/null +++ b/test/auth/added-smtp-auth-spoofed.txt @@ -0,0 +1,14 @@ +EHLO mail +AUTH LOGIN YWRkZWRAbG9jYWxob3N0LmxvY2FsZG9tYWlu +bXlwYXNzd29yZA== +MAIL FROM: user2@localhost.localdomain +RCPT TO: user1@localhost.localdomain +DATA +From: Not_My_Business +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message +This is a test mail. + +. +QUIT diff --git a/test/auth/ldap-smtp-auth-spoofed-alias.txt b/test/auth/ldap-smtp-auth-spoofed-alias.txt new file mode 100644 index 00000000..663abb53 --- /dev/null +++ b/test/auth/ldap-smtp-auth-spoofed-alias.txt @@ -0,0 +1,15 @@ +EHLO mail +AUTH LOGIN +c29tZS51c2VyQGxvY2FsaG9zdC5sb2NhbGRvbWFpbg== +c2VjcmV0 +MAIL FROM: postmaster@localhost.localdomain +RCPT TO: some.user@localhost.localdomain +DATA +From: alias_address +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message +This is a test mail. + +. +QUIT diff --git a/test/auth/ldap-smtp-auth-spoofed.txt b/test/auth/ldap-smtp-auth-spoofed.txt new file mode 100644 index 00000000..cc0b164d --- /dev/null +++ b/test/auth/ldap-smtp-auth-spoofed.txt @@ -0,0 +1,15 @@ +EHLO mail +AUTH LOGIN +c29tZS51c2VyQGxvY2FsaG9zdC5sb2NhbGRvbWFpbg== +c2VjcmV0 +MAIL FROM: ldap@localhost.localdomain +RCPT TO: user1@localhost.localdomain +DATA +From: forged_address +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message +This is a test mail. + +. +QUIT diff --git a/test/tests.bats b/test/tests.bats index e4878406..f6d27b67 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -367,8 +367,6 @@ load 'test_helper/bats-assert/load' [ "$status" -ge 0 ] } - - # # accounts # @@ -1089,7 +1087,6 @@ load 'test_helper/bats-assert/load' assert_success } - @test "checking accounts: listmailuser" { run docker exec mail /bin/sh -c "listmailuser | head -n 1" assert_success @@ -1397,11 +1394,29 @@ load 'test_helper/bats-assert/load' @test "checking dovecot: postmaster address" { run docker exec mail /bin/sh -c "grep 'postmaster_address = postmaster@domain.com' /etc/dovecot/conf.d/15-lda.conf" assert_success - + run docker exec mail_with_ldap /bin/sh -c "grep 'postmaster_address = postmaster@localhost.localdomain' /etc/dovecot/conf.d/15-lda.conf" assert_success } +@test "checking spoofing: rejects sender forging" { + # checking rejection of spoofed sender + run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed.txt | grep 'Sender address rejected: not owned by user'" + assert_success + # checking ldap + run docker exec mail_with_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed.txt | grep 'Sender address rejected: not owned by user'" + assert_success +} + +@test "checking spoofing: accepts sending as alias" { + + run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/added-smtp-auth-spoofed-alias.txt | grep 'End data with'" + assert_success + # checking ldap alias + run docker exec mail_with_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed-alias.txt | grep 'End data with'" + assert_success +} + # saslauthd @test "checking saslauthd: sasl ldap authentication works" { run docker exec mail_with_ldap bash -c "testsaslauthd -u some.user -p secret"