This is roughly what I did to have an Nginx web server on the same machine as dockerized Mailcow
Note: mail.xxxxx.com is your mail server (MX), yourdomain.com is whatever domain you wish to use.
# url for letsencrypt
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
# Path can be used for cert-validation on all domains
root /var/www/html/letsencrypt;
break;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name mail.xxxxx.com;
charset UTF-8;
access_log /var/log/nginx/access.mail.xxxxx.com;
error_log /var/log/nginx/error.mail.xxxxx.com;
include snippets/error_pages.conf;
ssl_certificate /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mail.xxxxx.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams-2048.pem;
include /etc/nginx/letsencrypt_path;
location / {
proxy_pass http://127.0.0.1:8480;
proxy_buffering off;
include /etc/nginx/proxy_params;
}
}
server {
listen 80;
listen [::]:80;
server_name mail.xxxxx.com;
return 301 https://mail.xxxxx.com;
}
The problem is that Mailcow can no longer use port 80 to update it's ssl certificates that are used by postfix and dovecot. Instead certbot puts them in /etc/letsencrypt/live. To fix this the following script runs from crontab daily.
#!/usr/bin/env bash
# we are running behind an nginx proxy, where certbot is run by systemd,
# so the mail.xxxxx.com certificates are updated, but not copied to the mailcow folder
# in case the certificate isn't renewed automatically, run this:
# sudo certbot -n certonly --webroot -w /var/www/html/letsencrypt -d mail.xxxxx.com
t1="/etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem"
t2="/opt/mailcow-dockerized/data/assets/ssl/mail.xxxxx.com/cert.pem"
# test if certificate has been updated
differ=$(cmp -b $t1 $t2 | grep -c "differ")
[[ "$differ" = "0" ]] && exit 0
# these files are required in /data/assets/ssh/mail.xxxxx.com
cp /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/mail.xxxxx.com/cert.pem
cp /etc/letsencrypt/live/mail.xxxxx.com/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/mail.xxxxx.com/key.pem
# these files are required in /data/assets/ssl/ (turns out sending failed otherwise)
cp /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem
cp /etc/letsencrypt/live/mail.xxxxx.com/privkey.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem
# update postfix & docker
docker exec $(/usr/bin/docker ps -qaf name=postfix-mailcow) postfix reload
docker exec $(/usr/bin/docker ps -qaf name=dovecot-mailcow) dovecot reload
Inspiration: Felix Moesbauer
Don't forget to check and correct if necessary your DANE and MTA-STS records if you use them
On your mail server:
apt install hash-slinger -y
Create a TLSA record:
tlsa --create --selector 1 -p 25 --certificate /etc/letsencrypt/live/mail.xxxxx.com/fullchain.pem mail.xxxxx.com
The last line or the result will be something like:
_25._tcp.mail.xxxxx.com. IN TLSA 3 1 1 443ac7c5c70fbfbc...
Enter this TLSA line in your mail server's DNS record.
Do the same for the following ports:
_110._tcp.mail.xxxxx.com
_143._tcp.mail.xxxxx.com
_465._tcp.mail.xxxxx.com
_587._tcp.mail.xxxxx.com
_993._tcp.mail.xxxxx.com
_995._tcp.mail.xxxxx.com
You can check your DANE records at huque.com
This is a fallback to DANE, you can run both.
Note: https access to mta-sts.yourdomain.com is obligatory
version: STSv1
mode: enforce
max_age: 172800
mx: mail.xxxxx.com
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
charset UTF-8;
access_log /var/log/nginx/access.mts-sta.yourdomain.com;
error_log /var/log/nginx/error.mts-sta.yourdomain.com;
include snippets/error_pages.conf;
ssl_certificate /etc/letsencrypt/live/mta-sts.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mta-sts.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams-2048.pem;
include /etc/nginx/letsencrypt_path;
root /var/www/html/mta-sts;
index index.html index.htm;
server_name mta-sts.yourdomain.com;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name mta-sts.yourdomain.com;
return 301 https://mta-sts.yourdomain.com;
}
You can check your domain's MTA-STS at mxtoolbox.com
All this worked for me :-) I hope it does for you!
ยง