Some notes in the beginning:
DMS, docker mail server, is, as of now (2023-08-08), not intended to be used with a database and postfixadmin. It offers its user and domain management through a script and a text file, where you write in your accounts with hashed passwords or your aliases.
This is not a tutorial, it is my personal story with the topic. I might rewrite this as a tutorial at a later point in time.
Motivation
What I want to do is, replacing my current native setup with a docker solution that I can setup easily and reproducible in case of server change/malfunction or the need to restore a backup.
The native setup consists of
- postfix – how obvious
- PostgreSQL
- PostfixAdmin
- Dovecot
- RoundcubeMail
- Rspamd
- imapproxy for Roundcube, for keeping connections low
- (don’t think that I will transfer that thing…)
- opendkimd
And of course, the new setup should be capable of the same.
Why?
My problem is of course a bit self-produced. I am a Arch Linux user – every-fuckin-where. And with that I sometimes get incompatibilities. As with Arch you get almost bleeding edge packages, the applications, e.g. roundcubemail or postfixadmin, aren’t ready for the newest PHP most of the time.
My motivation is now to move the email service with all its components into a docker deployment, so that any upgrade of my base system isn’t interfering with the service at all. Target is to be able to do an upgrade schedule of the machine as I see fit and having a different upgrade schedule for the services.
I did this approach already with a couple of services, e.g. nextcloud (AIO) and quassel-core.
Now there could be someone questioning my Arch Linux usage on server systems, of course…
Yes, I could move to an Ubuntu server with more “stability”, only to have a big act of upgrading to every new LTS version.
And for a move to another Linux distribution, I would have to move the services anyway…
Whatever.
Having the deployment in docker makes it even easier, if I would ever do this.
Get started…
For continuing, I expect that you have at least basic knowledge about all the involved services (postfix, dovecot, postgresql, dns entries, rspamd, postfixadmin, roundcubemail, lets encrypt, etc.). I won’t go into details, but may provide links for further reading, if you’re lucky. 🙂
Now let’s get started…
DMS compose.yml
My first steps were:
First, I retrieved the compose.yaml from the DMS github project repository and as well as the mailserver.env.
Then, I added the necessary images to the compose.yaml
compose.yaml diff to the original diff --git a/compose.orig.yml b/compose.yml--- a/compose.orig.yml+++ b/compose.yml@@ -23,9 +23,34 @@ services: restart: always stop_grace_period: 1m # Uncomment if using `ENABLE_FAIL2BAN=1`:- # cap_add:- # - NET_ADMIN+ cap_add:+ - NET_ADMIN healthcheck:- test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"+ test: ["CMD-SHELL", "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"] timeout: 3s retries: 0+ mailserverdb:+ image: postgres:14+ restart: always+ env_file: db.env+ volumes:+ - ./docker-data/postgres/data:/var/lib/postgresql/data+ healthcheck:+ test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]+ interval: 1s+ timeout: 5s+ retries: 10+ postfixadmin:+ image: postfixadmin:apache+ restart: always+ env_file: postfixadmin.env+ ports:+ - 8080:80+ roundcube:+ image: roundcube/roundcubemail:latest+ restart: always+ env_file: roundcube.env+ ports:+ - 8000:80+ volumes:+ - ./docker-data/roundcube/data:/var/roundcube/db
Then I changed the mailserver.env to reflect my settings (yours might of course differ…)
mailserver.env diff to the original diff --git a/mailserver.orig.env b/mailserver.envindex 038e23b..7a6cbe5 100644--- a/mailserver.orig.env+++ b/mailserver.env@@ -72,7 +72,7 @@ PERMIT_DOCKER=none # `/etc/localtime`, which you can alternatively mount into the container. The value of this variable # must follow the pattern `AREA/ZONE`, i.e. of you want to use Germany's time zone, use `Europe/Berlin`. # You can lookup all available timezones here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List-TZ=+TZ=Europe/Berlin # In case you network interface differs from 'eth0', e.g. when you are using HostNetworking in Kubernetes, # you can set NETWORK_INTERFACE to whatever interface you want. This interface will then be used.@@ -98,12 +98,12 @@ ENABLE_SRS=0 # Enables the OpenDKIM service. # **1** => Enabled # 0 => Disabled-ENABLE_OPENDKIM=1+ENABLE_OPENDKIM=0 # Enables the OpenDMARC service. # **1** => Enabled # 0 => Disabled-ENABLE_OPENDMARC=1+ENABLE_OPENDMARC=0 # Enabled `policyd-spf` in Postfix's configuration. You will likely want to set this@@ -111,7 +111,7 @@ ENABLE_OPENDMARC=1 # # - 0 => Disabled # - **1** => Enabled-ENABLE_POLICYD_SPF=1+ENABLE_POLICYD_SPF=0 # 1 => Enables POP3 service # empty => disables POP3@@ -125,13 +125,13 @@ ENABLE_CLAMAV=0 # Enables Rspamd # **0** => Disabled # 1 => Enabled-ENABLE_RSPAMD=0+ENABLE_RSPAMD=1 # When `ENABLE_RSPAMD=1`, an internal Redis instance is enabled implicitly. # This setting provides an opt-out to allow using an external instance instead. # 0 => Disabled # 1 => Enabled-ENABLE_RSPAMD_REDIS=+ENABLE_RSPAMD_REDIS=1 # When enabled, #@@ -140,7 +140,7 @@ ENABLE_RSPAMD_REDIS= # # **0** => disabled # 1 => enabled-RSPAMD_LEARN=0+RSPAMD_LEARN=1 # Controls whether the Rspamd Greylisting module is enabled. # This module can further assist in avoiding spam emails by greylisting@@ -148,7 +148,7 @@ RSPAMD_LEARN=0 # # **0** => disabled # 1 => enabled-RSPAMD_GREYLISTING=0+RSPAMD_GREYLISTING=1 # Can be used to enable or disable the Hfilter group module. #@@ -164,7 +164,7 @@ RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE=6 # Amavis content filter (used for ClamAV & SpamAssassin) # 0 => Disabled # 1 => Enabled-ENABLE_AMAVIS=1+ENABLE_AMAVIS=0 # -1/-2/-3 => Only show errors # **0** => Show warnings@@ -176,13 +176,13 @@ AMAVIS_LOGLEVEL=0 # Note: Emails will be rejected, if they don't pass the block list checks! # **0** => DNS block lists are disabled # 1 => DNS block lists are enabled-ENABLE_DNSBL=0+ENABLE_DNSBL=1 # If you enable Fail2Ban, don't forget to add the following lines to your `compose.yaml`: # cap_add: # - NET_ADMIN # Otherwise, `nftables` won't be able to ban IPs.-ENABLE_FAIL2BAN=0+ENABLE_FAIL2BAN=1 # Fail2Ban blocktype # drop => drop packet (send NO reply)@@ -191,7 +191,7 @@ FAIL2BAN_BLOCKTYPE=drop # 1 => Enables Managesieve on port 4190 # empty => disables Managesieve-ENABLE_MANAGESIEVE=+ENABLE_MANAGESIEVE=1 # **enforce** => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. # drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects.@@ -246,7 +246,7 @@ ENABLE_QUOTAS=1 # Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) # # empty => 10240000 (~10 MB)-POSTFIX_MESSAGE_SIZE_LIMIT=+POSTFIX_MESSAGE_SIZE_LIMIT=20480000 # Mails larger than this limit won't be scanned. # ClamAV must be enabled (ENABLE_CLAMAV=1) for this.
Notes:
– opendkim and opendmarc are disabled, as rspamd can do that nowadays
– SPF is now in rspamd too, as it seems…
– I don’t need Virus scanning as of now, it anyway has a huge impact on performance, so Amavis or ClamAV are disabled
Continuing with the configuration of the postgresql instance through an .env file:
db.env POSTGRES_USER=postfixPOSTGRES_PASSWORD=POSTGRES_DB=postfix
And the postfixadmin instance through an .env file:
postfixadmin.env POSTFIXADMIN_DB_TYPE=pgsql #... - sqlite, mysqli, pgsqlPOSTFIXADMIN_DB_NAME=postfix #.... - database name or path to database file (sqlite)POSTFIXADMIN_DB_USER=postfix #... - mysqli/pgsql only (db server user name)POSTFIXADMIN_DB_HOST=mailserverdb #... - hostname for database, default is localhost.#POSTFIXADMIN_DB_PORT=#... - port for the database (optional):POSTFIXADMIN_DB_PASSWORD=#... - mysqli/pgsql only (db server user password)POSTFIXADMIN_ENCRYPT='php_crypt:SHA512::{SHA512-CRYPT}'#... - database password encryption (e.g. md5crypt, SHA512-CRYPT)POSTFIXADMIN_SETUP_PASSWORD='$2y$10$hYwsErlyEvAEK8mHuP0lLewicIrnFH6ZaJrCADb62Ann1ExwrAhH.'#... - generated from setup.php or php -r "echo password_hash('mysecretpassword', PASSWORD_DEFAULT);"
Note: Setup password hash is an example.
Actually, postfixadmin is already accessible and can be setup and used. The rest of the services are of course still unusable.
Postfix configurationWhy it was a challenge for me…
It was a challenge for me as my configuration grew hysterically (historically) over eight years. The files of DMS and mine were not really comparable, so I got down to the approach of stripping both versions of all empty lines and comments, sorting the lines and then comparing what’s left. Which got me down to:
Pseudocode and/or example lines! # pseudocode and/or example lines!# remove comments, empty lines and sortcat main.cf | grep -v -E "^#|^$" | sort > old-main.cf# doing this with both main.cf files, lets you take the approach ofvimdiff old-main.cf dms-main.cf
Configure postfix to access the postgresql database
The actual configuration of postfix comes down to the settings for the four mappings of
- smtpd_sender_login_maps
- virtual_alias_maps
- virtual_mailbox_domains
- virtual_mailbox_maps
As we are working with postfixadmin the correct queries need to be set
sql/smtpd_sender_login_maps.cf user = postfixpassword = dbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT username AS allowedUser FROM mailbox WHERE username='%s' AND active = true UNION SELECT goto FROM alias WHERE address='%s' AND active = true
sql/virtual_alias_maps.cf user = postfixpassword = dbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT goto FROM alias where address = '%s' and active = '1'table = aliasselect_field = gotowhere_field = addressadditional_conditions = and active = '1'
sql/virtual_mailbox_domains.cf user = postfixpassword = dbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT domain FROM domain where domain = '%s' and backupmx = '0' and active = '1'table = domainselect_field = domainwhere_field = domainadditional_conditions = and backupmx = '0' and active = '1'
sql/virtual_mailbox_maps.cf user = postfixpassword = dbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT CONCAT(domain, '/', local_part) FROM mailbox WHERE username = '%s' and active = '1'table = mailboxselect_field = CONCAT(domain, '/', local_part)where_field = usernameadditional_conditions = and active = '1'
Configure dovecot
Now dovecot needs the same SQL access to the database managed by postfixadmin and also needs to know about the password hashing algorithm.
In DMS the /etc/dovecot/dovecot-sql.conf.ext needs to be overwritten.
Like this example:
dovecot-sql.conf.ext # I removed all the comments, to only show the actual important datadriver = pgsqlconnect = host=mailserverdb dbname=postfix user=postfix password= default_pass_scheme = SHA512-CRYPTuser_query = \ SELECT '/var/mail/%d/%n' as home, 'maildir:/var/mail/%d/%n' as mail, \ 5000 AS uid, 5000 AS gid, concat('dirsize:storage=', quota) AS quota \ FROM mailbox WHERE username = '%u' AND active = true password_query = \ SELECT username as user, password, '/var/mail/%d/%n' as userdb_home, \ 'maildir:/var/mail/%d/%n' as userdb_mail, 5000 as userdb_uid, 5000 as userdb_gid \ FROM mailbox WHERE username = '%u' AND active = true
pgsql
Remember what I said about DMS and databases? Yes? Good.
That DMS can work with the database you have to tweak it through user-patches.sh. I do not actually like that approach, especially if you would want to run the images without root policies enabled it will fail, but in my case it’s still okayish.
user-patches.sh #!/usr/bin/env bashapt-get updateapt-get install -y postfix-pgsql dovecot-pgsql
Yes, that simple. Still without nothing will work with the postgresql database. In an optimization step the cache of repository and packages could be cleaned aswell, but for now it’s only about a working system.
Sending and receiving mail
To get to the point where the services can send and receive mail, I had to find out that my settings in dovecot were wrong. The sql queries suggested the user vmail with the uid=1001 and the gid=1002 which is obviously not available in DMS. So I changed that to the docker user/group id=5000 and it began working inside dovecot at least. Logging into roundcube without errors at last.
In the postfix-main.cf I still had one error, that I accidentally put in the wrong hostname. Typical typo. Also it complained that the same domain shouldn’t be listed in mydestination and virtual_mailbox_domains, so I removed it from mydestination.
After a restart, postfix was able to receive mail. YAY.
Now for sending mails…
Initially I had problems here. But I found out that the settings in my overwrite main.cf (dms/config/postfix-main.cf) did have an error. Actually, the variable smtpd_tls_chain_files was still set to the default ‘snakeoil’ variant of DMS and I had to set this to my correct lets-encrypt certificate (first the key, then the fullchain.cem) and my postfix could also send mails.
Additional settings (DKIM, DMARC, SPF)
The only additional setting where you should check with dms to finally have the full chain working is the rspamd DKIM setting. Here you should actually follow the guide (rspamd tab!) to generate a DKIM key. It immediately spits out the settings you have to do in your DNS configuration.
Same for DMARC and SPF, as there’s no key required for this, just follow the guide from DMS.
Managesieve, rspamd webinterface
Last but not least I want to see how to get managesieve and rspamd webinterface running.
Managesieve
Managesieve needs some extra configuration in roundcubemail. So you have to add at least this to your config.inc.php
$config['managesieve_host'] = 'tls://domain.to'; $config['managesieve_auth_type'] = 'PLAIN';
Not doing this, managesieve tries to access localhost, and with encryption and authentication, that will fail miserably. Been there, done that. 😉
rspamd web
This is also actually quite easy.
- Copy out of the running container the file /etc/rspamd/local.d/worker-controller.inc
- e.g. with this command:
docker cp mailserver:/etc/rspamd/local.d/worker-controller.inc ./docker-data/dms/config/rspamd/
- Run the command rspamadm pw inside the running container
- docker-compose exec -it mailserver rspamadm pw
- Copy the result into worker-controller.inc, like this:
- password = "$2$wcopyxj5y8zeokzhbc6wm63tpq9magfg$f3omsrw4tgdq6x3pw16inkshms7tn3amix776refrt4pw2pujwdb";
Resulting file content:
# documentation: https://rspamd.com/doc/workers/controller.htmlbind_socket = "0.0.0.0:11334";password = "$2$wcopyxj5y8zeokzhbc6wm63tpq9magfg$f3omsrw4tgdq6x3pw16inkshms7tn3amix776refrt4pw2pujwdb";
Add a forwarding for the compose.yml in the ports section of mailserver.
( - "8003:11334" # rspamd web)
Last but not least, add a line to the volumes section where you mount the worker-controller.inc to it’s place and restart the deployment.
( - ./docker-data/dms/rspamd/worker-controller.inc:/etc/rspamd/local.d/worker-controller.inc:ro)
Finishing up
If you’ve read through this blog post, thank you for bearing with me through this journey.
For completness I will give you now almost all my config files here, with full content, especially those I showed only partly before, the small stuff i left out.
(Obviously anonymized and without passwords… :P)
And I condensed the config files to only contain actual configuration entries, removing empty lines and comments!
All configuration files compose.yml
services: mailserver: image: ghcr.io/docker-mailserver/docker-mailserver:latest container_name: mailserver # Provide the FQDN of your mail server here (Your DNS MX record should point to this value) hostname: env_file: mailserver.env # More information about the mail-server ports: # https://docker-mailserver.github.io/docker-mailserver/latest/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) - "8003:11334" # rspamd web 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/ - ./docker-data/dms/rspamd/worker-controller.inc:/etc/rspamd/local.d/worker-controller.inc:ro - ./docker-data/dms/config/dovecot-override/conf.d/10-auth.conf:/etc/dovecot/conf.d/10-auth.conf:ro - ./docker-data/dms/config/dovecot-override/conf.d/10-mail.conf:/etc/dovecot/conf.d/10-mail.conf:ro - ./docker-data/dms/config/dovecot-override/conf.d/90-sieve.conf:/etc/dovecot/conf.d/90-sieve.conf:ro - ./docker-data/dms/config/dovecot-override/dovecot-sql.conf.ext:/etc/dovecot/dovecot-sql.conf.ext:ro - ./docker-data/dms/config/postfix-override/sql:/etc/postfix/sql:ro - ./docker-data/dms/certs:/tmp/dms/custom-certs/:ro - /etc/localtime:/etc/localtime:ro restart: always stop_grace_period: 1m # Uncomment if using `ENABLE_FAIL2BAN=1`: cap_add: - NET_ADMIN healthcheck: test: ["CMD-SHELL", "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"] timeout: 3s retries: 0 mailserverdb: image: postgres:14 restart: always env_file: db.env volumes: - ./docker-data/postgres/data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] interval: 1s timeout: 5s retries: 10 postfixadmin: image: postfixadmin:apache restart: always env_file: postfixadmin.env ports: - 8002:80 roundcube: image: roundcube/roundcubemail:latest restart: always env_file: roundcube.env ports: - 8001:80 volumes: - ./docker-data/roundcube/html:/var/www/html - ./docker-data/roundcube/data:/var/roundcube/db - ./docker-data/roundcube/config:/var/roundcube/config
mailserver.env
OVERRIDE_HOSTNAME=DMS_DEBUG=0LOG_LEVEL=infoSUPERVISOR_LOGLEVEL=ONE_DIR=1ACCOUNT_PROVISIONER=POSTMASTER_ADDRESS=ENABLE_UPDATE_CHECK=1UPDATE_CHECK_INTERVAL=1dPERMIT_DOCKER=noneTZ=Europe/BerlinNETWORK_INTERFACE=TLS_LEVEL=SPOOF_PROTECTION=ENABLE_SRS=0ENABLE_OPENDKIM=0ENABLE_OPENDMARC=0ENABLE_POLICYD_SPF=0ENABLE_POP3=ENABLE_CLAMAV=0ENABLE_RSPAMD=1ENABLE_RSPAMD_REDIS=1RSPAMD_LEARN=1RSPAMD_GREYLISTING=1RSPAMD_HFILTER=1RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE=6ENABLE_AMAVIS=0AMAVIS_LOGLEVEL=0ENABLE_DNSBL=1ENABLE_FAIL2BAN=1FAIL2BAN_BLOCKTYPE=dropENABLE_MANAGESIEVE=1POSTSCREEN_ACTION=enforceSMTP_ONLY=SSL_TYPE=manualSSL_CERT_PATH=/tmp/dms/custom-certs/fullchain.cerSSL_KEY_PATH=/tmp/dms/custom-certs/cert.keySSL_ALT_CERT_PATH=SSL_ALT_KEY_PATH=VIRUSMAILS_DELETE_DELAY=POSTFIX_DAGENT=POSTFIX_MAILBOX_SIZE_LIMIT=ENABLE_QUOTAS=1POSTFIX_MESSAGE_SIZE_LIMIT=20480000CLAMAV_MESSAGE_SIZE_LIMIT=PFLOGSUMM_TRIGGER=PFLOGSUMM_RECIPIENT=PFLOGSUMM_SENDER=LOGWATCH_INTERVAL=LOGWATCH_RECIPIENT=LOGWATCH_SENDER=REPORT_RECIPIENT=REPORT_SENDER=LOGROTATE_INTERVAL=weeklyPOSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0POSTFIX_INET_PROTOCOLS=allDOVECOT_INET_PROTOCOLS=allENABLE_SPAMASSASSIN=0SPAMASSASSIN_SPAM_TO_INBOX=1ENABLE_SPAMASSASSIN_KAM=0MOVE_SPAM_TO_JUNK=1SA_TAG=2.0SA_TAG2=6.31SA_KILL=10.0SA_SPAM_SUBJECT=***SPAM*****ENABLE_FETCHMAIL=0FETCHMAIL_POLL=300ENABLE_GETMAIL=0GETMAIL_POLL=5ENABLE_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=maildirDOVECOT_AUTH_BIND=ENABLE_POSTGREY=0POSTGREY_DELAY=300POSTGREY_MAX_AGE=35POSTGREY_TEXT="Delayed by Postgrey"POSTGREY_AUTO_WHITELIST_CLIENTS=5ENABLE_SASLAUTHD=0SASLAUTHD_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=SASLAUTHD_LDAP_AUTH_METHOD=SASLAUTHD_LDAP_MECH=SRS_SENDER_CLASSES=envelope_senderSRS_EXCLUDE_DOMAINS=SRS_SECRET=DEFAULT_RELAY_HOST=RELAY_HOST=RELAY_PORT=25RELAY_USER=RELAY_PASSWORD=
postfixadmin.env
POSTFIXADMIN_DB_TYPE=pgsql POSTFIXADMIN_DB_NAME=postfix POSTFIXADMIN_DB_USER=postfix POSTFIXADMIN_DB_HOST=mailserverdb#POSTFIXADMIN_DB_PORT= POSTFIXADMIN_DB_PASSWORD=POSTFIXADMIN_ENCRYPT='php_crypt:SHA512::{SHA512-CRYPT}'POSTFIXADMIN_SETUP_PASSWORD='$2y$10$hYwsErlyEvAEK8mHuP0lLewicIrnFH6ZaJrCADb62Ann1ExwrAhH.'
roundcube.env
ROUNDCUBEMAIL_DEFAULT_HOST=tls://domain.to#ROUNDCUBEMAIL_DEFAULT_PORT=143 # defaultROUNDCUBEMAIL_SMTP_SERVER=tls://domain.to#ROUNDCUBEMAIL_SMTP_PORT=587 # default#ROUNDCUBEMAIL_REQUEST_PATH=/ # defaultROUNDCUBEMAIL_PLUGINS=archive,zipdownload,password,managesieveROUNDCUBEMAIL_SKIN=elastic # defaults to larryROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=20M# ROUNDCUBEMAIL_SPELLCHECK_URIROUNDCUBEMAIL_ASPELL_DICTS=de,en
db.env
POSTGRES_USER=postfixPOSTGRES_PASSWORD=POSTGRES_DB=postfix
dovecot-override/conf.d/10-auth.conf
disable_plaintext_auth = yesauth_realms = domain.toauth_default_realm = domain.toauth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@auth_username_format = %Luauth_mechanisms = plain login!include auth-sql.conf.ext
dovecot-override/conf.d/10-mail.conf
mail_location = maildir:/var/mail/%d/%nnamespace inbox { inbox = yes}mail_uid = 5000mail_gid = 5000mail_privileged_group = dockermail_plugins = $mail_plugins quotamaildir_stat_dirs = yesmaildir_copy_with_hardlinks = yes
dovecot-override/conf.d/90-sieve.conf
plugin { sieve = ~/.dovecot.sieve sieve_dir = ~/sieve sieve_before = /usr/lib/dovecot/sieve-global/before/ sieve_after = /usr/lib/dovecot/sieve-global/after/ sieve_extensions = +notify +imapflags +vnd.dovecot.pipe +vnd.dovecot.filter sieve_plugins = sieve_imapsieve sieve_extprograms recipient_delimiter = + sieve_max_script_size = 1M sieve_max_actions = 32 sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter}
dovecot-override/dovecot-sql.conf.ext
driver = pgsqlconnect = host=mailserverdb dbname=postfix user=postfix password=default_pass_scheme = SHA512-CRYPTuser_query = \ SELECT '/var/mail/%d/%n' as home, 'maildir:/var/mail/%d/%n' as mail, \ 5000 AS uid, 5000 AS gid, concat('dirsize:storage=', quota) AS quota \ FROM mailbox WHERE username = '%u' AND active = true password_query = \ SELECT username as user, password, '/var/mail/%d/%n' as userdb_home, \ 'maildir:/var/mail/%d/%n' as userdb_mail, 5000 as userdb_uid, 5000 as userdb_gid \ FROM mailbox WHERE username = '%u' AND active = true
postfix-accounts.cf (containing actually just a dummy)
dummy@domain.to|{SHA512-CRYPT}$6$0iuxO1kKZ.TjWX6V$zDfx8w11yHcxHogY8itJVnnoJhNDgfRXZFcmIDbzgicqQY/EimV6Zl6QlyU2tCPyLcbNYqFsxgEef31QzFFv91
postfix-main.cf
alias_database = hash:/etc/aliasesappend_dot_mydomain = nobiff = nobroken_sasl_auth_clients = yescompatibility_level = 2disable_vrfy_command = yesdms_smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch, permit_sasl_authenticated, permit_mynetworks, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permitheader_checks = pcre:/etc/postfix/maps/header_checks.pcreinet_interfaces = allinet_protocols = alllocal_recipient_maps = $virtual_alias_mapsmailbox_size_limit = 0maximal_backoff_time = 8000smaximal_queue_lifetime = 7dmessage_size_limit = 21504000milter_default_action = acceptmilter_protocol = 6minimal_backoff_time = 1000smua_sender_restrictions = reject_authenticated_sender_login_mismatch, $dms_smtpd_sender_restrictionsmydestination = localhost.$mydomain, localhostmyhostname = domain.tomynetworks = 172.0.0.0/24 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/64mynetworks_style = hostmyorigin = localhostnon_smtpd_milters =proxy_read_maps = $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $smtpd_sender_login_mapspostscreen_bare_newline_action = enforcepostscreen_dnsbl_action = enforcepostscreen_dnsbl_sites = b.barracudacentral.org*2 bl.mailspike.net=127.0.0.[2;14;13;12;11;10] bl.spameatingmonkey.net=127.0.0.2 dnsbl.sorbs.net list.dnswl.org=127.0.[0..255].0*-2 list.dnswl.org=127.0.[0..255].1*-3 list.dnswl.org=127.0.[0..255].[2..3]*-4 psbl.surriel.com zen.spamhaus.org=127.0.0.[2..11]*3postscreen_dnsbl_threshold = 3postscreen_dnsbl_whitelist_threshold = -1postscreen_greet_action = enforcereadme_directory = norecipient_delimiter = +relayhost =smtpd_banner = $myhostname ESMTP $mail_name (Arch Linux/GNU)smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipeliningsmtpd_delay_reject = yessmtpd_helo_required = yessmtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, reject_invalid_helo_hostname, permitrspamd_milter = inet:localhost:11332smtpd_milters = $rspamd_miltersmtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain, check_policy_service inet:localhost:65265smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destinationsmtpd_sasl_auth_enable = yessmtpd_sasl_authenticated_header = yessmtpd_sasl_local_domain = $mydomainsmtpd_sasl_path = /dev/shm/sasl-auth.socksmtpd_sasl_security_options = noanonymoussmtpd_sasl_type = dovecotsmtpd_sender_login_maps = pgsql:/etc/postfix/sql/virtual_sender_login_maps.cfsmtpd_sender_restrictions = $dms_smtpd_sender_restrictionssmtpd_soft_error_limit = 3smtpd_tls_CApath = /etc/ssl/certssmtpd_tls_chain_files = /tmp/dms/custom-certs/cert.key /tmp/dms/custom-certs/fullchain.cersmtpd_tls_dh1024_param_file = /etc/postfix/dhparams.pemsmtpd_tls_exclude_ciphers = aNULL, SEED, CAMELLIA, RSA+AES, SHA1smtpd_tls_loglevel = 1smtpd_tls_received_header = yessmtpd_tls_mandatory_ciphers = highsmtpd_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1smtpd_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1smtpd_tls_security_level = maysmtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scachesmtpd_tls_session_cache_timeout = 3600ssmtp_helo_timeout = 60ssmtp_tls_note_starttls_offer = yessmtp_header_checks = pcre:/etc/postfix/maps/sender_header_filter.pcresmtp_tls_CApath = /etc/ssl/certssmtp_tls_loglevel = 1smtp_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1smtp_tls_security_level = maytls_high_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384tls_preempt_cipherlist = yestls_ssl_options = NO_COMPRESSION, NO_RENEGOTIATIONsmtp_tls_session_cache_database = btree:${data_directory}/smtp_scachetls_random_source = dev:/dev/urandomunknown_local_recipient_reject_code = 450virtual_alias_maps = pgsql:/etc/postfix/sql/virtual_alias_maps.cfvirtual_mailbox_base = /var/mailvirtual_mailbox_domains = pgsql:/etc/postfix/sql/virtual_domains_maps.cfvirtual_mailbox_maps = pgsql:/etc/postfix/sql/virtual_mailbox_maps.cfvirtual_transport = lmtp:unix:/var/run/dovecot/lmtp
postfix-override/sql/virtual_*_maps.cf (all in one, separated by comment)
# virtual_alias_maps.cfuser = postfixdbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT goto FROM alias where address = '%s' and active = '1'table = aliasselect_field = gotowhere_field = addressadditional_conditions = and active = '1'# virtual_domains_maps.cfuser = postfixdbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT domain FROM domain where domain = '%s' and backupmx = '0' and active = '1'table = domainselect_field = domainwhere_field = domainadditional_conditions = and backupmx = '0' and active = '1'# virtual_mailbox_maps.cfuser = postfixdbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT CONCAT(domain, '/', local_part) FROM mailbox WHERE username = '%s' and active = '1'table = mailboxselect_field = CONCAT(domain, '/', local_part)where_field = usernameadditional_conditions = and active = '1'# virtual_sender_login_maps.cfuser = postfixdbname = postfixhosts = postgresql://postfix:@mailserverdb/postfixquery = SELECT username AS allowedUser FROM mailbox WHERE username='%s' AND active = true UNION SELECT goto FROM alias WHERE address='%s' AND active = true
At last, if anyone spots errors or has suggestions to improve the configurations, feel free to comment.
Share this:
Related #docker #dovecot #email #linux #migration #postfix #postfixadmin #roundcubemail
https://www.mmo.to/2023/08/setting-up-dms-docker-mailserver-with-postgresql-postfixadmin-and-roundcubemail/