April 29, 2021

Automatically manage Mailcow Certificates

Use SFTP to grab your Swag Letsencrypt certificate to use with a self hosted Mailcow server

Automatically manage Mailcow Certificates

If like me you host your own Mailcow server on the same IP as your web server and use letsencrypt for SSL encryption, then you will need a way to move and install that certificate to your Mailcow server.

First a note on how my servers are set up. All of my services are running on Unraid with the majority of my services running in containers with my mail server running on a separate VM. My swag container issues my letsencrypt certificate which I then move and install onto my mailcow server.

To do this I have a script that runs every week. Certbot automatically renews the certificate every two months so by running the script every week I ensure that I have the most recent certificate installed.

Because we will be using SFTP to grab the certificate we will first create a ssh-key so we can access SSH without the need for a password.

First step is to create a ssh key, you can do this with ssh-keygen -t rsa -b 4096

If this is your first or only ssh key you can use the default name and location for the ssh key. We will save it under a different name so we can identify it easily in the future.

I will save my file under this name ~/.ssh/unraid-mailcow I am also not going to set a passphrase for this key.

once it has generated we need to copy the file to unraid so we can use the file. To do that enter:

ssh-copy-id root@<your_unraid_IP>

If you changed the name or location of your ssh key you will need to specify that before copying the file. As I changed mine to be saved under ~/.ssh/unraid-mailcow I will need to add this as bellow:

ssh-copy-id -i ~/.ssh/unraid-mailcow.pub root@<your_unraid_IP>

You will need to enter the password for your unraid server when prompted

Now we need to check that we can log in using the key file rather than a password. We do this with the following command:

ssh root@<your_unraid_IP>

Or if you changed the name/location of your file you need to specify the file, so I would need to run the below command:

ssh -i ~/.ssh/unraid-mailcow root@<your_unraid_IP>

Once you have confirmed that you can log into your unraid server we need to make sure that the key will survive a reboot. As unraid loads the OS into RAM when booted the SSH keys will be deleted at each reboot/shutdown of the server.

To make the key survive a reboot we need to copy the authorized_keys file to the boot usb stick. Which you can do with the following:

cp ~/.ssh/authorized_keys /boot/config/ssh/

If this is the first key you are adding to unraid, you will likely get an error saying /boot/config/ssh doesn't exist, so we will need to create the ssh directory on your USB stick.

mkdir /boot/config/ssh

Then you can try copying the file again.

To make sure that the file is correct run: cat /boot/config/ssh/authorized_keys you should see a long string of characters with the user of your Mailcow server at the end.

Now we need to make sure that the file gets copied back to the correct location on boot so we can actually use it. We do this by adding a cople of lines to the /boot/config/go file. This file tells unraid to do certain things at boot. We will add the following to the end of the file:

mkdir /root/.ssh
chmod 700 /root/.ssh
cp /boot/config/ssh/authorized_keys /root/.ssh/
chmod 600 /root/.ssh/*

This will copy the authorized_keys file back to the correct directory each time the server reboots meaning that a reboot won't break the passwordless login if the server needs to be rebooted for whatever reason.

Now we can log in to the unraid server we can write our simple script to grab the ssl certificate from the Swag container and install it into Mailcow.

We will create a text document which we will use to tell sftp where to grab the certificate files, we will put them into our /tmp directory for now due to permission issues.

nano mailcow-cert-sftp.txt

get /mnt/<your_appdata_location>/swag/etc/letsencrypt/live/<your_DOMAIN>/fullchain.pem /tmp/cert.pem
get /mnt/<your_appdata_location>/swag/etc/letsencrypt/live/<your_DOMAIN>/privkey.pem /tmp/key.pem

Make sure to use your domain and app data locations.

Save the file and exit nano with ctrl+x.

Now we can test the commands by running the command:

sftp -b ~/mailcow-cert-sftp.txt root@<your_unraid_IP>

or if you need to specify the SSH key:

sftp -b ~/mailcow-cert-sftp.txt -i ~/.ssh/unraid-mailcow root@<your_unraid_IP>

Check the files are there with ls /tmp You should see your files listed in the output.

Now we need to move the files to the correct location. We will need to run these commands with elevated privileges due to the permissions of the ssl directory in Mailcow.

sudo mv /tmp/cert.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem
sudo mv /tmp/key.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem

Once we have done this we need to restart a couple of the Mailcow containers so they can use the new certificates:

docker restart $(docker ps -qaf name=postfix-mailcow)
docker restart $(docker ps -qaf name=nginx-mailcow)
docker restart $(docker ps -qaf name=dovecot-mailcow)

Once that has finished Mailcow should be up and running with your new certificate files.

Now all of these commands work, we need to put them into a script that we can run using a cronjob so we don't need to manually need to do this every time the certificate updates in our swag container. I like to keep the script next to the mailcow install in the /opt directory.

sudo nano /opt/mailcow-cert-update.sh

#you need to run this command as your user otherwise the authentication will fail. 
sudo -u <your username> sftp -b /home/<yourUsername>/mailcow-cert-sftp.txt -i /home/<your Username>/.ssh/unraid-mailcow root@<your_unraid_IP>

# notice the absence of sudo in these commands

mv /tmp/cert.pem /opt/mailcow-dockerized/data/assets/ssl/cert.pem
mv /tmp/key.pem /opt/mailcow-dockerized/data/assets/ssl/key.pem

docker restart $(docker ps -qaf name=postfix-mailcow)
docker restart $(docker ps -qaf name=nginx-mailcow)
docker restart $(docker ps -qaf name=dovecot-mailcow)

Save the file and make it executable with:

sudo chmod +x /opt/mailcow-cert-update.sh

It is a good idea to run this script so you can see any potential errors that pop up before you set and forget it. Make sure to run it as a super user for the correct permissions:

sudo /opt/mailcow-cert-update.sh

Now we need to create the cron job to run this script. As the Mailcow ssl folder needs elevated permissions we will add this to the root user crontab.

sudo crontab -e

0 5 * * 0 /opt/mailcow-cert-update.sh

This will run the script every Sunday at 5am. And that's it. Unless anything changes this should just keep ticking along indefinitely.