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.env index 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=postfix POSTGRES_PASSWORD=<hidden password> POSTGRES_DB=postfix
And the postfixadmin instance through an .env file:
postfixadmin.env
POSTFIXADMIN_DB_TYPE=pgsql #... - sqlite, mysqli, pgsql POSTFIXADMIN_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=<hidden 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 configuration
Why 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 sort cat main.cf | grep -v -E "^#|^$" | sort > old-main.cf # doing this with both main.cf files, lets you take the approach of vimdiff 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 = postfix password = <hidden password> dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = 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 = postfix password = <hidden password> dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = SELECT goto FROM alias where address = '%s' and active = '1' table = alias select_field = goto where_field = address additional_conditions = and active = '1'
sql/virtual_mailbox_domains.cf
user = postfix password = <hidden password> dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = SELECT domain FROM domain where domain = '%s' and backupmx = '0' and active = '1' table = domain select_field = domain where_field = domain additional_conditions = and backupmx = '0' and active = '1'
sql/virtual_mailbox_maps.cf
user = postfix password = <hidden password> dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = SELECT CONCAT(domain, '/', local_part) FROM mailbox WHERE username = '%s' and active = '1' table = mailbox select_field = CONCAT(domain, '/', local_part) where_field = username additional_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 data driver = pgsql connect = host=mailserverdb dbname=postfix user=postfix password=<hidden password> default_pass_scheme = SHA512-CRYPT user_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 bash apt-get update apt-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/
- e.g. with this command:
- Run the command
rspamadm pw
inside the running containerdocker-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.html bind_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: <the 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=0 LOG_LEVEL=info SUPERVISOR_LOGLEVEL= ONE_DIR=1 ACCOUNT_PROVISIONER= POSTMASTER_ADDRESS= ENABLE_UPDATE_CHECK=1 UPDATE_CHECK_INTERVAL=1d PERMIT_DOCKER=none TZ=Europe/Berlin NETWORK_INTERFACE= TLS_LEVEL= SPOOF_PROTECTION= ENABLE_SRS=0 ENABLE_OPENDKIM=0 ENABLE_OPENDMARC=0 ENABLE_POLICYD_SPF=0 ENABLE_POP3= ENABLE_CLAMAV=0 ENABLE_RSPAMD=1 ENABLE_RSPAMD_REDIS=1 RSPAMD_LEARN=1 RSPAMD_GREYLISTING=1 RSPAMD_HFILTER=1 RSPAMD_HFILTER_HOSTNAME_UNKNOWN_SCORE=6 ENABLE_AMAVIS=0 AMAVIS_LOGLEVEL=0 ENABLE_DNSBL=1 ENABLE_FAIL2BAN=1 FAIL2BAN_BLOCKTYPE=drop ENABLE_MANAGESIEVE=1 POSTSCREEN_ACTION=enforce SMTP_ONLY= SSL_TYPE=manual SSL_CERT_PATH=/tmp/dms/custom-certs/fullchain.cer SSL_KEY_PATH=/tmp/dms/custom-certs/cert.key SSL_ALT_CERT_PATH= SSL_ALT_KEY_PATH= VIRUSMAILS_DELETE_DELAY= POSTFIX_DAGENT= POSTFIX_MAILBOX_SIZE_LIMIT= ENABLE_QUOTAS=1 POSTFIX_MESSAGE_SIZE_LIMIT=20480000 CLAMAV_MESSAGE_SIZE_LIMIT= PFLOGSUMM_TRIGGER= PFLOGSUMM_RECIPIENT= PFLOGSUMM_SENDER= LOGWATCH_INTERVAL= LOGWATCH_RECIPIENT= LOGWATCH_SENDER= REPORT_RECIPIENT= REPORT_SENDER= LOGROTATE_INTERVAL=weekly POSTFIX_REJECT_UNKNOWN_CLIENT_HOSTNAME=0 POSTFIX_INET_PROTOCOLS=all DOVECOT_INET_PROTOCOLS=all ENABLE_SPAMASSASSIN=0 SPAMASSASSIN_SPAM_TO_INBOX=1 ENABLE_SPAMASSASSIN_KAM=0 MOVE_SPAM_TO_JUNK=1 SA_TAG=2.0 SA_TAG2=6.31 SA_KILL=10.0 SA_SPAM_SUBJECT=***SPAM***** ENABLE_FETCHMAIL=0 FETCHMAIL_POLL=300 ENABLE_GETMAIL=0 GETMAIL_POLL=5 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=0 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= 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=
postfixadmin.env
POSTFIXADMIN_DB_TYPE=pgsql POSTFIXADMIN_DB_NAME=postfix POSTFIXADMIN_DB_USER=postfix POSTFIXADMIN_DB_HOST=mailserverdb #POSTFIXADMIN_DB_PORT= POSTFIXADMIN_DB_PASSWORD=<hidden 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 # default ROUNDCUBEMAIL_SMTP_SERVER=tls://domain.to #ROUNDCUBEMAIL_SMTP_PORT=587 # default #ROUNDCUBEMAIL_REQUEST_PATH=/ # default ROUNDCUBEMAIL_PLUGINS=archive,zipdownload,password,managesieve ROUNDCUBEMAIL_SKIN=elastic # defaults to larry ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=20M # ROUNDCUBEMAIL_SPELLCHECK_URI ROUNDCUBEMAIL_ASPELL_DICTS=de,en
db.env
POSTGRES_USER=postfix POSTGRES_PASSWORD=<hidden password> POSTGRES_DB=postfix
dovecot-override/conf.d/10-auth.conf
disable_plaintext_auth = yes auth_realms = domain.to auth_default_realm = domain.to auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@ auth_username_format = %Lu auth_mechanisms = plain login !include auth-sql.conf.ext
dovecot-override/conf.d/10-mail.conf
mail_location = maildir:/var/mail/%d/%n namespace inbox { inbox = yes } mail_uid = 5000 mail_gid = 5000 mail_privileged_group = docker mail_plugins = $mail_plugins quota maildir_stat_dirs = yes maildir_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 = pgsql connect = host=mailserverdb dbname=postfix user=postfix password=<hidden password> default_pass_scheme = SHA512-CRYPT user_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/aliases append_dot_mydomain = no biff = no broken_sasl_auth_clients = yes compatibility_level = 2 disable_vrfy_command = yes dms_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, permit header_checks = pcre:/etc/postfix/maps/header_checks.pcre inet_interfaces = all inet_protocols = all local_recipient_maps = $virtual_alias_maps mailbox_size_limit = 0 maximal_backoff_time = 8000s maximal_queue_lifetime = 7d message_size_limit = 21504000 milter_default_action = accept milter_protocol = 6 minimal_backoff_time = 1000s mua_sender_restrictions = reject_authenticated_sender_login_mismatch, $dms_smtpd_sender_restrictions mydestination = localhost.$mydomain, localhost myhostname = domain.to mynetworks = 172.0.0.0/24 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 [fe80::]/64 mynetworks_style = host myorigin = localhost non_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_maps postscreen_bare_newline_action = enforce postscreen_dnsbl_action = enforce postscreen_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]*3 postscreen_dnsbl_threshold = 3 postscreen_dnsbl_whitelist_threshold = -1 postscreen_greet_action = enforce readme_directory = no recipient_delimiter = + relayhost = smtpd_banner = $myhostname ESMTP $mail_name (Arch Linux/GNU) smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining smtpd_delay_reject = yes smtpd_helo_required = yes smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, reject_invalid_helo_hostname, permit rspamd_milter = inet:localhost:11332 smtpd_milters = $rspamd_milter smtpd_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:65265 smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination smtpd_sasl_auth_enable = yes smtpd_sasl_authenticated_header = yes smtpd_sasl_local_domain = $mydomain smtpd_sasl_path = /dev/shm/sasl-auth.sock smtpd_sasl_security_options = noanonymous smtpd_sasl_type = dovecot smtpd_sender_login_maps = pgsql:/etc/postfix/sql/virtual_sender_login_maps.cf smtpd_sender_restrictions = $dms_smtpd_sender_restrictions smtpd_soft_error_limit = 3 smtpd_tls_CApath = /etc/ssl/certs smtpd_tls_chain_files = /tmp/dms/custom-certs/cert.key /tmp/dms/custom-certs/fullchain.cer smtpd_tls_dh1024_param_file = /etc/postfix/dhparams.pem smtpd_tls_exclude_ciphers = aNULL, SEED, CAMELLIA, RSA+AES, SHA1 smtpd_tls_loglevel = 1 smtpd_tls_received_header = yes smtpd_tls_mandatory_ciphers = high smtpd_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1 smtpd_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1 smtpd_tls_security_level = may smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_session_cache_timeout = 3600s smtp_helo_timeout = 60s smtp_tls_note_starttls_offer = yes smtp_header_checks = pcre:/etc/postfix/maps/sender_header_filter.pcre smtp_tls_CApath = /etc/ssl/certs smtp_tls_loglevel = 1 smtp_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1 smtp_tls_security_level = may tls_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-SHA384 tls_preempt_cipherlist = yes tls_ssl_options = NO_COMPRESSION, NO_RENEGOTIATION smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache tls_random_source = dev:/dev/urandom unknown_local_recipient_reject_code = 450 virtual_alias_maps = pgsql:/etc/postfix/sql/virtual_alias_maps.cf virtual_mailbox_base = /var/mail virtual_mailbox_domains = pgsql:/etc/postfix/sql/virtual_domains_maps.cf virtual_mailbox_maps = pgsql:/etc/postfix/sql/virtual_mailbox_maps.cf virtual_transport = lmtp:unix:/var/run/dovecot/lmtp
postfix-override/sql/virtual_*_maps.cf (all in one, separated by comment)
# virtual_alias_maps.cf user = postfix dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = SELECT goto FROM alias where address = '%s' and active = '1' table = alias select_field = goto where_field = address additional_conditions = and active = '1' # virtual_domains_maps.cf user = postfix dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = SELECT domain FROM domain where domain = '%s' and backupmx = '0' and active = '1' table = domain select_field = domain where_field = domain additional_conditions = and backupmx = '0' and active = '1' # virtual_mailbox_maps.cf user = postfix dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = SELECT CONCAT(domain, '/', local_part) FROM mailbox WHERE username = '%s' and active = '1' table = mailbox select_field = CONCAT(domain, '/', local_part) where_field = username additional_conditions = and active = '1' # virtual_sender_login_maps.cf user = postfix dbname = postfix hosts = postgresql://postfix:<hidden password>@mailserverdb/postfix query = 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.