Setup

The MTA handles the transmission of email to and from the system, while the MDA handles delivery of email on that system.

In this configuration, postfix is the MTA and dovecot is the MDA.

See Email Docker and Documentation.

Ports

Files

Note

Documentation assumes the following DNS preferences:

  • mail.{DOMAIN} resolves to the mail server IP, MX records (e.g. mail.example.com).

  • Mail accounts are {USER}@{DOMAIN}.

Docker Creation

Warning

Create the compose file but do not start the docker container. Pre-existing configurations can be started normally.

Initial configuration must be done before launching the conatiner so that all auto-generated setup for domains, accounts, aliases and SPF/DKIM/SSL are generated and configured correctly.

See Configuration.

Delete configuration files to reset if started without configuration.

  • Mail containers should be placed on a separate isolated network as the is exposed to the world.

  • See Docker options for detailed docker environment setup. Explicit options set even if default to remain consistent if any changes do occur.

  • Dovecot manages SASL authentication, so SASLAUTHD is disabled.

Docker Compose
mail:
  image: tvial/docker-mailserver:latest
  hostname: mail
  domainname: {DOMAIN}
  stop_grace_period: 1m
  restart: unless-stopped
  ports:
    - "25:25"
    - "587:587"
    - "993:993"
  environment:
    - DEFAULT_RELAY_HOST=''
    - DMS_DEBUG=0
    - DOVECOT_MAILBOX_FORMAT=maildir
    - ENABLE_CLAMAV=1
    - ENABLE_ELK_FORWARDER=0
    - ENABLE_FAIL2BAN=0
    - ENABLE_FETCHMAIL=0
    - ENABLE_LDAP=''
    - ENABLE_MANAGESIEVE=1
    - ENABLE_POP3=0
    - ENABLE_POSTFIX_VIRTUAL_TRANSPORT=''
    - ENABLE_POSTGREY=1
    - ENABLE_SASLAUTHD=0
    - ENABLE_SPAMASSASSIN=1
    - ENABLE_SRS=1
    - LOGROTATE_INTERVAL=weekly
    - LOGWATCH_INTERVAL=weekly
    - ONE_DIR=1
    - PERMIT_DOCKER=host
    - PFLOGSUMM_TRIGGER=logrotate
    - POSTFIX_DAGENT=''
    - POSTFIX_MAILBOX_SIZE_LIMIT=0
    - POSTFIX_MESSAGE_SIZE_LIMIT=10480000
    - POSTGREY_AUTO_WHITELIST_CLIENTS=0
    - POSTGREY_DELAY=300
    - POSTGREY_MAX_AGE=35
    - POSTGREY_TEXT=Delayed by postgrey
    - POSTMASTER_ADDRESS=postmaster@{DOMAIN}
    - POSTSCREEN_ACTION=enforce
    - RELAY_HOST=''
    - SA_KILL=6.31
    - SA_SPAM_SUBJECT=***SPAM***
    - SA_TAG2=6.31
    - SA_TAG=3.0
    - SASL_PASSWD=''
    - SASLAUTHD_MECH_OPTIONS=''
    - SASLAUTHD_MECHANISMS=''
    - SMTP_ONLY=''
    - SPOOF_PROTECTION=1
    - SRS_EXCLUDE_DOMAINS=''
    - SRS_SENDER_CLASSES=envelope_sender,header_sender
    - SSL_TYPE=letsencrypt
    - TLS_LEVEL=modern
    - TZ=America/Los_Angeles
    - VIRUSMAILS_DELETE_DELAY=7
  volumes:
    - /data/mail:/var/mail
    - /data/mail/server/state:/var/mail-state
    - /var/log/mail:/var/log/mail
    - /data/mail/server/config:/tmp/docker-mailserver
    - /data/letsencrypt:/etc/letsencrypt:ro
    - /etc/localtime:/etc/localtime:ro

Note

NET_ADMIN capability is required if fail2ban is run within the container. Alternatively, the mail logs can be mounted externally and fail2ban run in an isolated container. See fail2ban for System.

SYS_PTRACE capability is required for debugging as well as detecting a hung procress; disabling might allow container processes to die and not restart.

The security vulnerability with SYS_PTRACE is patched for all 4.8+ Linux kernels.

fail2ban Setup

Enable fail2ban for MTA and MDA services.

Use fail2ban for System for the base fail2ban service setup.

Add read-only mail logs to Docker Compose (f2b-system)
f2b-system:
  volumes:
    - /var/log/mail:/var/log/mail:ro

Custom filters generated by extracting them from the mail docker image and adding as additional rules for the separate fail2ban container.

0644 root root /data/filter.d/mail-dovecot.conf
[INCLUDES]

before = common.conf

[Definition]

_daemon = (auth|dovecot(-auth)?|auth-worker)

failregex = ^%(__prefix_line)s(pam_unix(\(dovecot:auth\))?:)?\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(\s+user=\S*)?\s*$
            ^%(__prefix_line)s(pop3|imap)-login: (Info: )?(Aborted login|Disconnected)(: Inactivity)? \(((no auth attempts|auth failed, \d+ attempts)( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<\S*>,)?( method=\S+,)? rip=<HOST>, lip=(\d{1,3}\.){3}\d{1,3}(, session=<\w+>)?(, TLS( handshaking)?(: Disconnected)?)?\s*$
            ^%(__prefix_line)s(Info|dovecot: auth\(default\)): pam\(\S+,<HOST>\): pam_authenticate\(\) failed: (User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\))\s*$
            ^\s.*passwd-file\(\S*,<HOST>\): unknown user.*$
            (?: pop3-login|imap-login): .*(?:Authentication failure|Aborted login \(auth failed|Aborted login \(tried to use disabled|Disconnected \(auth failed).*rip=(<HOST>),.*

## ^%(__prefix_line)spasswd-file\(\S*,<HOST>\): unknown user.*$
ignoreregex =

Note

Dovecot filter needs to be adjusted to use the <HOST> as the default filter will fail by default.

Patched, pending release: https://github.com/tomav/docker-mailserver/pull/1388

0644 root root /data/filter.d/mail-postfix.conf
# Fail2Ban filter for selected Postfix SMTP rejections
#
#

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf

[Definition]

_daemon = postfix(-\w+)?/(?:submission/|smtps/)?smtp[ds]

failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1 .*$
            ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 Client host rejected: cannot find your hostname, (\[\S*\]); from=<\S*> to=<\S+> proto=ESMTP helo=<\S*>$
            ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo= *$
            ^%(__prefix_line)sNOQUEUE: reject: EHLO from \S+\[<HOST>\]: 504 5\.5\.2 <\S+>: Helo command rejected: need fully-qualified hostname;
            ^%(__prefix_line)sNOQUEUE: reject: VRFY from \S+\[<HOST>\]: 550 5\.1\.1 .*$
            ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.1\.8 <\S*>: Sender address rejected: Domain not found; from=<\S*> to=<\S+> proto=ESMTP helo=<\S*>$
            ^%(__prefix_line)simproper command pipelining after \S+ from [^[]*\[<HOST>\]:?$

ignoreregex =

[Init]

journalmatch = _SYSTEMD_UNIT=postfix.service

# Author: Cyril Jaquier
0644 root root /data/filter.d/mail-postfix-rbl.conf
# Fail2Ban filter for Postfix's RBL based Blocked hosts
#
#

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf

[Definition]

_daemon = postfix(-\w+)?/smtpd

failregex = ^%(__prefix_line)sNOQUEUE: reject: RCPT from \S+\[<HOST>\]: 454 4\.7\.1 Service unavailable; Client host \[\S+\] blocked using .* from=<\S*> to=<\S+> proto=ESMTP helo=<\S*>$

ignoreregex =

# Author: Lee Clemens
0644 root root /data/filter.d/mail-postfix-sasl.conf
# Fail2Ban filter for postfix authentication failures
#

[INCLUDES]

before = common.conf

[Definition]

_daemon = postfix(-\w+)?/(?:submission/|smtps/)?smtp[ds]

failregex = ^%(__prefix_line)swarning: [-._\w]+\[<HOST>\]: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed(:[ A-Za-z0-9+/:]*={0,2})?\s*$

ignoreregex = authentication failed: Connection lost to authentication server$

[Init]

journalmatch = _SYSTEMD_UNIT=postfix.service


# Author: Yaroslav Halchenko
ignoreregex =
0644 root root /data/jail.d/dovecot.conf
[dovecot]
enabled  = true
port     = pop3,pop3s,imap,imaps,imap2,imap3,smtp,ssmtp
filter   = dovecot
logpath  = /var/log/mail/mail.log
bantime  = -1
findtime = 86400
maxretry = 3

[mail-dovecot]
enabled  = true
port     = pop3,pop3s,imap,imaps,imap2,imap3,smtp,ssmtp
filter   = mail-dovecot
logpath  = /var/log/mail/mail.log
bantime  = -1
findtime = 86400
maxretry = 3
0644 root root /data/jail.d/postfix.conf
[postfix]
enabled  = true
port     = pop3,pop3s,imap,imaps,imap2,imap3,smtp,ssmtp
filter   = postfix
logpath  = /var/log/mail/mail.log
bantime  = -1
findtime = 86400
maxretry = 3

[mail-postfix]
enabled  = true
port     = pop3,pop3s,imap,imaps,imap2,imap3,smtp,ssmtp
filter   = mail-postfix
logpath  = /var/log/mail/mail.log
bantime  = -1
findtime = 86400
maxretry = 3

[mail-postfix-rbl]
enabled  = true
port     = pop3,pop3s,imap,imaps,imap2,imap3,smtp,ssmtp
filter   = mail-postfix-rbl
logpath  = /var/log/mail/mail.log
bantime  = -1
findtime = 86400
maxretry = 3

[mail-postfix-sasl]
enabled  = true
port     = pop3,pop3s,imap,imaps,imap2,imap3,smtp,ssmtp
filter   = mail-postfix-sasl
logpath  = /var/log/mail/mail.log
bantime  = -1
findtime = 86400
maxretry = 3
  • Restart f2b-system.