HTTPs redirection for Mailcow

Christoph Dähne12.05.2021

At Sandstorm we use Mailcow. It comes with a very nice Web-UI both for administration and accessing mails, calendars and contacts. User can enable 2-factor-authentication which is great.

One details caught our attention though: users can access the Web-UI via an unencrypted HTTP-connection. Here I want to explain how we fixed that.

Desired behavior - what we want.

It is fine that the Web-UI is reachable via HTTP at port 80. However users should be redirected to the encrypted HTTPs connection on port 443. Nothing else than redirects should ever happen at port 80:

  • http://mailserver/ -> 301 Moved Permanently https://mailserver/
  • http://mailserver/sogo -> 301 Moved Permanently https://mailserver/sogo

The patch - where to change what configuration.

As I did not find any built-in flag like enableHttpsRedirect I patched a configuration file. Hopefully it will survive updates, time will tell. Mailcow uses a nginx server which runs in an own Docker container. The nginx container handles connections incoming on the host server at ports 80 and 443.

$ docker-compose ps … mailcowdockerized_nginx-mailcow_1 … 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp …

A volume contains the nginx configuration for this container. You can find it on the host machine at data/conf/nginx/. We would have to adjust the sites.active file. However a template auto-generates this file so all changes will be lost as soon as we restart the nginx container. We have to adjust the template instead. You find it at data/conf/nginx/templates/sites.template.sh.

We have to adjust the server configuration a bit. Here you see the template file. All changes will go to the highlighted area.

echo ' server { listen 127.0.0.1:65510; include /etc/nginx/conf.d/listen_plain.active; include /etc/nginx/conf.d/listen_ssl.active; ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; include /etc/nginx/conf.d/server_name.active; include /etc/nginx/conf.d/includes/site-defaults.conf; } '; for cert_dir in /etc/ssl/mail/*/ ; do if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then continue fi # do not create vhost for default-certificate. the cert is already in the default server listen domains="$(cat ${cert_dir}domains | sed -e 's/^[[:space:]]*//')" case "${domains}" in "") continue;; "${MAILCOW_HOSTNAME}"*) continue;; esac echo -n ' server { include /etc/nginx/conf.d/listen_ssl.active; ssl_certificate '${cert_dir}'cert.pem; ssl_certificate_key '${cert_dir}'key.pem; '; echo -n ' server_name '${domains}'; include /etc/nginx/conf.d/includes/site-defaults.conf; } '; done

Now move the listen_plain.active include into an own server block, add the permanent redirect and we are good to go.

echo ' server { include /etc/nginx/conf.d/listen_plain.active; return 301 https://$host$request_uri; } server { listen 127.0.0.1:65510; include /etc/nginx/conf.d/listen_ssl.active; ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate_key /etc/ssl/mail/key.pem; include /etc/nginx/conf.d/server_name.active; include /etc/nginx/conf.d/includes/site-defaults.conf; } '; for cert_dir in /etc/ssl/mail/*/ ; do if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then continue fi # do not create vhost for default-certificate. the cert is already in the default server listen domains="$(cat ${cert_dir}domains | sed -e 's/^[[:space:]]*//')" case "${domains}" in "") continue;; "${MAILCOW_HOSTNAME}"*) continue;; esac echo -n ' server { include /etc/nginx/conf.d/listen_ssl.active; ssl_certificate '${cert_dir}'cert.pem; ssl_certificate_key '${cert_dir}'key.pem; '; echo -n ' server_name '${domains}'; include /etc/nginx/conf.d/includes/site-defaults.conf; } '; done

As you see we added four lines and removed one line. After a restart of the nginx container all unencrypted HTTP requests are redirected to HTTPs.

$ docker-compose restart nginx-mailcow

A quick test in the end with curl confirms that the change works and is deployed.

$ curl -I http://mailserver HTTP/1.1 301 Moved Permanently Server: nginx Date: Wed, 12 May 2021 06:38:51 GMT Content-Type: text/html Content-Length: 162 Connection: keep-alive Location: https://mailserver/

Thanks for reading. I hope you found what you where looking for. If you have any comments or questions feel free to contact us.

Dein Besuch auf unserer Website produziert laut der Messung auf websitecarbon.com nur 0,28 g CO₂.