0

Install and configure a mailserver in docker container – Migrated to a new server 3 of 4

This is the third section in the 4 part series of migrating my server

  1. Install and configure the host machine
  2. Install and configure a database and webserver
  3. Install and configure a mailserver - we are here
  4. Install and configure vaultwarden
  5. Tie everything back to 1. for backups, misc, etc

Dockerized mailserver

We will install a dockerized mailserver, a sort of all in one that will run a full fledged secure mail service. There are many containers that can perform this and on my previous servers I used iRedmail. The main issue with most containerized mail solutions is that it requires port 80 and 443 for various webmail and it is tied into various other services so its hard to remove, same for other parts of the service. I found and chose docker-mailserver and it pretty much did almost everything I needed and wanted.

Why docker-mailserver

  1. documentation is better than most other projects
  2. looks to be maintained and updated frequently
  3. does not need port 80 or 443
  4. comes with a very simple setup.sh that you can use to manage your mailboxes
  5. configuration options are a plenty where you can configure what you need.

Install

I will assume that you configured your DNS correctly and created all the TXT and MX records. If not docker-mailserver does have pretty decent docs about how to configure your DNS.

configure a system wide certificate

docker-maliserver can use letsencrypt to generate its own certs but it requires port 80 and 443 since it uses the http-01 method to generate the cert. So instead I will use certbot in a container to generate my cert. My domains are hosted on namecheap so I will use a namecheap plugin for certbot to use dns-01 validation to create my domain and wildcard certs. I will create 1 cert for all of my domains and their wildcards.

  • Create directories
# mkdir -p /opt/certbot/logs
  • Create /opt/config/namecheap.ini
certbot_dns_namecheap:dns_namecheap_username=namecheap.com-username
certbot_dns_namecheap:dns_namecheap_api_key=namecheap.com-apitoken-generated-from-settings
  • Generate certs - user the following to generate the certs
docker run -it --rm \
  -v /opt/certbot:/etc/letsencrypt \
  -v /opt/certbot/logs:/var/log/letsencrypt \
  -v /opt/config/namecheap.ini:/namecheap.ini \
  schubc/certbot-dns-namecheap certonly \
  --non-interactive \
  -a certbot-dns-namecheap:dns-namecheap \
  --certbot-dns-namecheap:dns-namecheap-credentials=/namecheap.ini \
  --agree-tos \
  --email "your@email.address" \
  -d domain1.com -d *.domain1.com -d domain2.com -d *.domain2.com
  
  • Link certs to /etc/pki
    The above will create certs(symlinks) onto /opt/certbot/live/domain1.com I will create symlinks to /etc/pki/tls
ln -s /opt/certbot/live/domain1.com/fullchain.pem /etc/pki/tls/certs/fullchain.pem
ln -s /opt/certbot/live/domain1com/privkey.pem /etc/pki/tls/private/privkey.pem
  • Create a renew script and bounce any services when the cert is renewed
    Now we need a script to check and renew our certificates and restart any services like docker-mailserver whenever the cert renews. I created a script named /opt/bin/renew-certs.sh
#!/bin/sh

# renew
# need to run periodically
# add to cron to run once a week
# 10 3 * * 0 /opt/bin/renew-certs.sh

WORKDIR=/opt/certbot

# snag a copy of old fullchain
mkdir ${WORKDIR}/tmp
cp ${WORKDIR}/live/domain1.com/fullchain.pem ${WORKDIR}/tmp/fullchain.pem

# run the renewal
docker run -it --rm \
  -v /opt/certbot:/etc/letsencrypt \
  -v /opt/certbot/logs:/var/log/letsencrypt \
  -v /opt/config/namecheap.ini:/namecheap.ini \
  schubc/certbot-dns-namecheap renew

# check for diff
if diff -q "${WORKDIR}/tmp/fullchain.pem" "${WORKDIR}/live/domain1.com/fullchain.pem"; then
  # no difference
  logger "[DEBUG] cert did not change"
else
  logger "[DEBUG] cert changed and will restart containers"

  # restart containers that use the cert
  docker restart mailserver
fi

# cleanup
rm -rf ${WORKDIR}/tmp

exit 0

If you have other services that uses the default certs you can add it to the script to be restarted.

  • Create cronjob
# Every Sunday at 3:10 check for cert renewal
10 3 * * 0 /opt/bin/renew-certs.sh > /dev/null 2>&1

configure docker-mailserver

Now for the mail server

  • Create directories
# mkdir -p /opt/mailserver
  • Install docker-mailserver
DMS_GITHUB_URL='https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master'
wget "${DMS_GITHUB_URL}/docker-compose.yml"
wget "${DMS_GITHUB_URL}/mailserver.env"
wget "${DMS_GITHUB_URL}/setup.sh"

chmod a+x ./setup.sh
./setup.sh help
  • Add a user
./setup.sh email add  
  • Setup DKIM
$ ./setup.sh config dkim domain 'domain1.com,domain2.com`
  • Configure - Edit /opt/mailserver/mailserver.env for your needs. My config looks like the following but its really up to you
OVERRIDE_HOSTNAME=
DMS_DEBUG=0
SUPERVISOR_LOGLEVEL=
ONE_DIR=1
POSTMASTER_ADDRESS=jlim@domain1.com
ENABLE_UPDATE_CHECK=1
UPDATE_CHECK_INTERVAL=1d
PERMIT_DOCKER=host
NETWORK_INTERFACE=
TLS_LEVEL=
SPOOF_PROTECTION=1
ENABLE_SRS=1
ENABLE_POP3=
ENABLE_CLAMAV=1
ENABLE_AMAVIS=1
AMAVIS_LOGLEVEL=0
ENABLE_FAIL2BAN=1
FAIL2BAN_BLOCKTYPE=drop
ENABLE_MANAGESIEVE=
POSTSCREEN_ACTION=enforce
SMTP_ONLY=
SSL_TYPE=manual
SSL_CERT_PATH=/tmp/dms/custom-certs/live/domain1.com/fullchain.pem
SSL_KEY_PATH=/tmp/dms/custom-certs/live/domain1.com/privkey.pem
SSL_ALT_CERT_PATH=
SSL_ALT_KEY_PATH=
VIRUSMAILS_DELETE_DELAY=
ENABLE_POSTFIX_VIRTUAL_TRANSPORT=
POSTFIX_DAGENT=lmtp:unix:private/dovecot-lmtp
POSTFIX_MAILBOX_SIZE_LIMIT=
ENABLE_QUOTAS=0
POSTFIX_MESSAGE_SIZE_LIMIT=
PFLOGSUMM_TRIGGER=logrotate
PFLOGSUMM_RECIPIENT=jlim@domain1.com
PFLOGSUMM_SENDER=postmaster@domain1.com
LOGWATCH_INTERVAL=weekly
LOGWATCH_RECIPIENT=jlim@domain1.com
REPORT_RECIPIENT=1
REPORT_SENDER=
REPORT_INTERVAL=daily
POSTFIX_INET_PROTOCOLS=all
ENABLE_SPAMASSASSIN=1
SPAMASSASSIN_SPAM_TO_INBOX=1
MOVE_SPAM_TO_JUNK=1
SA_TAG=2.0
SA_TAG2=6.31
SA_KILL=6.31
SA_SPAM_SUBJECT=***SPAM*****
ENABLE_FETCHMAIL=0
FETCHMAIL_POLL=300
ENABLE_LDAP=
LDAP_START_TLS=
LDAP_SERVER_HOST=
LDAP_SEARCH_BASE=
LDAP_BIND_DN=
LDAP_BIND_PW=
LDAP_QUERY_FILTER_USER=
LDAP_QUERY_FILTER_GROUP=
LDAP_QUERY_FILTER_ALIAS=
LDAP_QUERY_FILTER_DOMAIN=
DOVECOT_TLS=
DOVECOT_USER_FILTER=
DOVECOT_PASS_FILTER=
DOVECOT_MAILBOX_FORMAT=maildir
DOVECOT_AUTH_BIND=
ENABLE_POSTGREY=1
POSTGREY_DELAY=300
POSTGREY_MAX_AGE=35
POSTGREY_TEXT="Delayed by Postgrey"
POSTGREY_AUTO_WHITELIST_CLIENTS=5
ENABLE_SASLAUTHD=0
SASLAUTHD_MECHANISMS=
SASLAUTHD_MECH_OPTIONS=
SASLAUTHD_LDAP_SERVER=
SASLAUTHD_LDAP_BIND_DN=
SASLAUTHD_LDAP_PASSWORD=
SASLAUTHD_LDAP_SEARCH_BASE=
SASLAUTHD_LDAP_FILTER=
SASLAUTHD_LDAP_START_TLS=
SASLAUTHD_LDAP_TLS_CHECK_PEER=
SASLAUTHD_LDAP_TLS_CACERT_FILE=
SASLAUTHD_LDAP_TLS_CACERT_DIR=
SASLAUTHD_LDAP_PASSWORD_ATTR=
SASL_PASSWD=
SASLAUTHD_LDAP_AUTH_METHOD=
SASLAUTHD_LDAP_MECH=
SRS_SENDER_CLASSES=envelope_sender
SRS_EXCLUDE_DOMAINS=
SRS_SECRET=
DEFAULT_RELAY_HOST=
RELAY_HOST=
RELAY_PORT=25
RELAY_USER=
RELAY_PASSWORD=
  • Configure /opt/mailserver/docker-compose.yml - mine looks like
services:
  mailserver:
    image: docker.io/mailserver/docker-mailserver:latest
    container_name: mailserver
    # If the FQDN for your mail-server is only two labels (eg: example.com),
    # you can assign this entirely to hostname and remove domainname.
    hostname: mail
    domainname: domain1.com
    env_file: mailserver.env
    # More information about the mail-server ports:
    # https://docker-mailserver.github.io/docker-mailserver/edge/config/security/understanding-the-ports/
    # To avoid conflicts with yaml base-60 float, DO NOT remove the quotation marks.
    ports:
      - "25:25"    # SMTP  (explicit TLS => STARTTLS)
      - "143:143"  # IMAP4 (explicit TLS => STARTTLS)
      - "465:465"  # ESMTP (implicit TLS)
      - "587:587"  # ESMTP (explicit TLS => STARTTLS)
      - "993:993"  # IMAP4 (implicit TLS)
    volumes:
      - ./docker-data/dms/mail-data/:/var/mail/
      - ./docker-data/dms/mail-state/:/var/mail-state/
      - ./docker-data/dms/mail-logs/:/var/log/mail/
      - ./docker-data/dms/config/:/tmp/docker-mailserver/
      - /opt/certbot:/tmp/dms/custom-certs/:ro
      - /etc/localtime:/etc/localtime:ro
      - /opt/temp:/temp/
    restart: always
    stop_grace_period: 1m
    cap_add:
      - NET_ADMIN
      - NET_RAW
      - SYS_PTRACE
  • Start container - this is the only containers that is not in systemd. Since its using docker-compose it will return when the host is rebooted.
$ cd /opt/mailserver
$ docker-compose -f docker-compose.yml up -d # this will start the mailserver container with everything from docker-compose.yml
$ # docker-compose -f docker-compose.yml down # this will take down the container and the network
$ # docker-compose restart mailserver # this will restart the mailserver container
  • Some tips
    1. You can view the mail logs via docker logs -f mailserver or going into the container docker exec -it mailserver bash and tail -f /var/log/mail.log
    2. On my host(Rocky) and the mailserver container I was unable to get fail2ban to work correctly in the container. I updated the iptables and ip6tables alternatives to get it working. # update-alternatives --all and select iptables-nft and ip6tables-nft`
    3. You can goto various mail server test sites to test your mail server and when I did this all the tests came out GREEN

Please view Linux basic install – Migrated to a new server 1 of 4 about how to configure your host OS to relay mail via the container.

jlim0930

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.