Docker has become one of my go to tools for managing software stacks. There are a couple of clever things that come together to make docker awesome, but the bit that works well for this purpose is the Dockerfile configuration file that makes it easy to manage a given software stack deployment and the containerization let’s you run it without interacting with other components. So I get a nice repeatable way to install the software I want, and I don’t have to worry about it messing with another part of the system when I deploy it.
I’d say that data files are a weakness of docker, and most people tend to map (large) portions of the host filesystem into the container to get persistence. This is where you have to be careful. I keep thinking that someone needs to invent “docker for data”, or maybe I just need to wrap my head around the right model to make it more natural.
In any case, I hadn’t installed docker yet. The Ubuntu repository doesn’t have the latest version, but it is fairly current so I’ll just stick with that vs. using the docker repository.
|
$ sudo apt install docker.io |
I found someone who’s blazed a trail on setting up a full mail server in docker, so I’ll start with it and modify it to meet my needs. I liked the philosophy they used of basing it on config files, instead of a SQL DB with configuration information which a lot of mail setups in docker seem to go for. They also shared the files on github, making it easy to look at.
We’ll probably want to be able to run docker as a normal user (vs. needing to sudo every time).
|
$ sudo gpasswd -a ${USER} docker $ sudo service docker restart |
Don’t forget to log out then back in to get your user to have the right group permissions. Note: there are some interesting security implications here. Docker doesn’t yet have strong security controls, so if you can run a container you can basically have root on the host.
Now, this setup needs docker-compose, so we’ll install that too.
|
$ sudo apt install docker-compose |
And here is where we get some friction, his setup requires compose 1.6 or higher. Well, we’ll probably do some hacking. Let’s get the code from github
|
$ git clone https://github.com/tomav/docker-mailserver |
So we need to create our own docker-compose.yml file, two examples are provided. One with the ELK stack included for logging, the other without. For now we’ll start without ELK as we don’t need those features (yet).
Here is my preliminary docker-compose.yml file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
mail: build: . hostname: testhost domainname: lowtek.ca container_name: mail ports: - "25:25" - "143:143" - "587:587" - "993:993" volumes: - ./maildata:/var/mail - ./config/:/tmp/docker-mailserver/ environment: - ENABLE_FAIL2BAN=1 cap_add: - NET_ADMIN |
I’ve made it work for older compose versions, and ditched the docker volume for a simple directory mapping to ./maildata – at least for now. I’ve also chosen to build my own container locally vs. pull down the dockerhub image that was provided.
Runs for a while and get the container ready to run (builds the image)
|
$ docker-compose up -d Creating mail ERROR: driver failed programming external connectivity on endpoint mail (2f2a7d638f3c8e2e4e751f570a55cfac09550f3842066f39ad0ac0ce74a9eac1): Error starting userland proxy: listen tcp 0.0.0.0:25: bind: address already in use |
Yup, and my concern about a local mail server was right on the money – it’s going to conflict with my mail server in a container (which if you recall was pulled in when I installed logwatch).
So let’s get brave
|
$ sudo apt-get remove postfix |
Since logwatch is a dependency, it will also be removed. Now logwatch doesn’t strictly need postfix, it really just wants a mail transport agent, so we can install a simpler mail forwarder like nullmailer and take care of that for logwatch.
|
$ sudo apt-get install nullmailer $ sudo apt-get install logwatch |
Ta-da, we’ve got logwatch installed and no postfix and thus no ports being held open.
|
$ docker-compose up -d Recreating mail |
Cool, and docker ps shows that all the ports are mapped nicely. Now referencing the README.md from the git repo, we need to create some user accounts and DKIM keys. We’ll also remember to stop running the container we started just a moment ago.
|
$ docker-compose stop Stopping mail ... done $ mkdir -p config $ touch config/postfix-accounts.cf $ docker run --rm \ -e MAIL_USER=mario@lowtek.ca \ -e MAIL_PASS=itsame \ -ti dockermailserver_mail \ /bin/sh -c 'echo "$MAIL_USER|$(doveadm pw -s SHA512-CRYPT -u $MAIL_USER -p $MAIL_PASS)"' >> config/postfix-accounts.cf |
Now we can run the container and try to email mario
And now type in this script a line at a time
|
ehlo lowtek.ca mail from: root@lowtek.ca rcpt to: mario@lowtek.ca data Subject: Testing Postfix This is just a test . |
So receiving email for mario is working. Now can we get this server to send email?
Configuring my desktop install of thunderbird to talk with the new server was simple to do. This let me read the manually sent email to the mario user, and made it easy for me to test sending email using the new docker container server.
So things are going well at this point, we’ve got a mail server appears to be able to send and receive email. However, since mail is now running in a container, the logs are being hidden from logwatch. I can either run logwatch in the container, or expose the logs from the container to the host.
Stuff I still need to do, or at least think through before switching over to this new mail setup
- Let’s encrypt certificate
- DKIM setup
- mail migration
- alias support
- logwatch
- smarthost configuration
- webmail
- dovecot sieve / pigeonhole