Caddy and Mail in a Box

Installation

Recently I’ve decided to move away from using Vesta CP as my web control panel for my domain, since there hasn’t been any major releases for it in over a year (as of the time of this writing). I also found out that the EC2 instance ran out of disk space, despite me configuring Vesta CP to notify me when this happens. I researched alternative open source control panels and could not find a suitable replacement, so I decided to skip a web control panel all together and configure everything on the box myself. My setup is pretty simple (A WordPress installation, Mail server, and reverse proxy endpoints for my home VPN) so I took the plunge.

Tinc VPN

This was pretty simple, I installed tinc via my package repository and copied over the configuration files from my previous (now defunct) EC2 instance. I configured a systemd service in order to manage the VPN tunnel connection and keep it alive in the background, and the rest is history. Here is my systemd file for tinc:

[Unit] 
Description=Tinc VPN After=network.target 

[Service] 
Type=simple 
WorkingDirectory=/etc/tinc/vpn 
ExecStart=/usr/sbin/tincd -n vpn-D -d3 
ExecReload=/usr/sbin/tincd -n vpn -kHUP 
TimeoutStopSec=5 
Restart=always 
RestartSec=60 

[Install] 
WantedBy=multi-user.target

Mail Server

I have heard good things about Mail-in-a-Box, and wanted to try it. One of the first issues I experience is that it only supports Ubuntu 14.04 and any attempts to use it on a later version causes the script to abort after notifying you that the version is not supported by MIAB. Luckily an Ubuntu 16.04 fork exists that requires you to take a few extra steps in order to get it working, but after that everything ran smoothly. During the installation process, MIAB installs and nginx webserver to be the fronted for managing the installation (adding new users, aliases, webmail access, etc.). It also wipes any pre-existing nginx configuration files and uninstalls apache2, so be wary if you are trying to run this on a box not solely dedicated to email. Once the setup is complete, you can access the following endpoints:

  • Webmail via Roundcube at domain.com/mail
  • Administrative portal via a custom Flask server instance at domain.com/admin
  • Nextcloud instance (for contacts/calendar) at domain.com/cloud/contacts or domain.com/cloud/calendar

It also installs a few other features (DNS resolver, small static site hosting, autodiscover for Outlook/iOS), but more importantly the web endpoints (and nginx) would be clashing with my reverse proxy setup and my intent to use Caddy as a webserver instead.

Caddy

This was new to me, I have been a long time user of nginx and never had any complaints about it. However one of Caddy’s main features is it’s automatic HTTPS. No more dealing with SSL certificates, keys, expiration dates, etc. It was all handled automatically, and was definitely not one of my favorite parts of maintaining a web server. The documentation seemed simple enough, so I decided to migrate from nginx to Caddy as part of my new web server stack. The first issue I had to deal with is nginx and caddy fighting over port 80/443. I thought about hosting nginx on a different port and using caddy to reverse proxy traffic to the required nginx port, but ultimately I thought it would be a better learning experience if I rewrote the necessary nginx server blocks into caddy server blocks. I decided to skip over the nextcloud related ones as I would not be actually using the MIAB provided instance of nextcloud, since I run my own instance on my home server. So that leaves roundcube and the administrative panel. After much struggling and consulting the caddy documentation, I was able to successfully access the administrative panel and roundcube using the caddy file below:

mail-time.tpage.io {
      tls user@tpage.io
      gzip

      basicauth / user hunter2

      root /usr/local/lib/roundcubemail/
      fastcgi / /run/php/php7.0-fpm.sock php
}

admin-panel.tpage.io {
      tls user@tpage.io
      gzip

      header / {
            X-Frame-Options "DENY"
            X-Content-Type-Options "nosniff"
            Content-Security-Policy "frame-ancestors 'none';"
            Strict-Transport-Security "max-age=31536000"
      }

      proxy / http://127.0.0.1:10222/ {
            transparent
            websocket
      }
}

Moving forward, I had to setup a WordPress server block for my WordPress installation later on. This time it was pretty easy:

https://www.tpage.io https://tpage.io {
      tls user@tpage.io
      root /var/www/wordpress
      gzip
      fastcgi / /run/php/php7.0-fpm.sock php

      rewrite {
            if {path} not_match ^\/wp-admin
            to {path} {path}/ /index.php?_url={uri}
      }
}

http://www.tpage.io {
      tls user@tpage.io
      redir https://www.tpage.io
}

http://tpage.io {
      tls user@tpage.io
      redir https://tpage.io
}

Note the extra directives for tpage.io and www.tpage.io over HTTP. This is to make Caddy provision/manage TLS certificates for Dovecot/Postfix (which uses the server’s FQDN: tpage.io), instead of Mailinabox. I chose to do it this way because Caddy handles the provisioning of TLS certificates effortlessly and it makes me feel at ease knowing I don’t have to worry about my TLS certificates expiring. Without the extra directives, Caddy doesn’t automatically provision TLS certificates for tpage.io and www.tpage.io, it just redirects them to their HTTPS equivalent.

After configuring the domains and subdomains for automatic TLS certificate provisioning, I then had to modify the TLS certificate paths Dovecot/Postfix use when configured to use TLS (Spoiler: Mailinabox configures them this way). It all came down to editing /etc/dovecot/conf.d/10-ssl.conf and /etc/postfix/main.cf

Then swapping out the TLS/SSL certificate/key paths with the path of where Caddy stores the FQDN (tpage.io) TLS certs, for me this was /etc/ssl/caddy/acme/auto-generated-folder/tpage.io/tpage.io.crt and /etc/ssl/caddy/acme/auto-generated-folder/tpage.io/tpage.io.key

Afterwards I restarted Dovecot/Postfix and everything was golden!

Now for the reverse proxy connections over Tinc VPN. This can be accomplished because web servers bind to 0.0.0.0, which means all interfaces available (including VPN tunnels!). So you can build server blocks that reverse proxy public origin HTTP(S) connections to private web servers via the VPN tunnel. A few sample server blocks for basic services (Plex, HomeAssistant, Nextcloud, etc.) are below:

hass.tpage.io {
      tls user@tpage.io

      proxy / 15.21.5.2:80 {
            websocket
            transparent
      }
}

plex-tv.tpage.io {
      tls user@tpage.io
      gzip
      timeouts none

      proxy / 15.21.5.4:32400 {
            transparent
            websocket
      }
}

nextcloud.tpage.io {
      tls user@tpage.io
      gzip

      proxy / 15.21.5.4:9090 {
            transparent
            websocket
      }
}

guacamole.tpage.io {
      tls user@tpage.io
      gzip

      basicauth / user hunter3

      proxy / 15.21.5.4:8080/guacamole/ {
            transparent
            websocket
      }
}

Wordpress

This part was the easiest, I used cURL to grab the latest package release from WordPress.org and decompressed it into /var/www/. After configuring the rest of my setup, I simply restored my previous WordPress installation with a recent backup and everything just worked! The plugin I use for backup/restores is: All-in-One WP Migration

Edit (3/21/2018): I’ve updated the post to describe how to make Caddy manage/renew the TLS certificates for Dovecot and Postfix

Edit (4/11/2018): Looks like there was a 0-day for Vesta being actively used - https://www.digitalocean.com/community/questions/how-do-i-determine-the-impact-of-vestacp-vulnerability-from-april-8th-2018

Edit (10/27/18): I’ve since migrated away from MIAB to using ProtonMail as my MX server, self-hosting email is too much of a burden for where I’m at in my life right now