Some of you may know, I've been in the process of containerizing many of my services in my 'free' time. This is the first post, of what I hope to be many more, on the migration of services – specifically, Postfix this go around.
It should be noted that this is a work in progress. Please don't use this in production. <Insert other IANAL stuff here>.
When I started looking at containerizing services by first step was to start looking for prior art to either use out-of-the-box or modify to suit my needs. I really didn't see anything that sparked my interest in hub.docker.com or a (very) quick github.com search.
After a few sorry attempts to roll my own completely from scratch I decided to look more closely into the linuxserver.io image build process.
So far I am quiet content.
What I really want(ed) is a good CI/CD implementation to build my containers through my private Jenkins implementation and upon successful build, deploy the container to my infrastructure.
Full disclosure: I haven't reached nirvana yet.
As I eluded to, the first "service" I chose to containerize was my postfix setup. I use postfix, in this instance, to relay e-mail out to the rest of the world. My ISP actually filters port 25 out and I have access to other systems which can do that work for me already.
My requirements are:
- A single egress point from the home network for e-maill traffic
- Must be able to specify a 'relay_host' to funnel all mail through
- Random applications need to be able to send e-mail
- (Eventually) Be easy to build
Let's get started...
If all you want to do is run a basic Postfix server that can relay mail it should be easy enough to just run my container. I wrote a small bash script to build the container.
#!/bin/bash docker create \ --name=tkf-postfix \ -e PUID=11 \ -e PGID=952 \ -e TZ=US/Mountain \ -e TKF_MAILDOMAIN=copperdale.teknofile.net \ -e TKF_RELAY_HOST="$(cat ./tkf.relayhost)" \ -e TKF_MYNETWORKS="$(cat ./tkf.mynetworks)" \ -e TKF_RELAY_PASSWORD="$(cat ./relay_password)" \ -e TKF_MYHOSTNAME="$(cat ./tkf.myhostname)" \ -v /mnt/usb/docker/tkf-postfix/:/config \ -p 25:25 \ --restart unless-stopped \ --net=tkfdocker \ teknofile/tkf-docker-postfix:devel
Let's break down the parameters being passed to docker create:
- -e PUID/PGID - Used to map internal container UID/GID to external (host) UID. For more info: https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf
- -e TZ - The timezone you want to set to inside the container, I'm in the US/Denver area so I like times to make sense to me at a quick glance
- -e TKF_MAILDOMAIN - In the postfix configuration, this is set smtpd_banner and smtp_helo_name
- -e TKF_RELAY_HOST - This is the hostname of the system you wish to relay mail through. This sets the relayhost parameter within postfix. The "$(cat ...)" syntax you see here reads the contents of the file to populate the variable, it keeps some of my specific configuration out of github
- -e TKF_MYNETWORKS - Sets the postfix mynetworks parameter. This should be set to the IPv4/IPv6 network addresses you want to accept mail for. This is your internal IP networks (i.e. 192.168.0.0/24)
- -e TKF_RELAY_PASSWORD - This is where things may get a little ugly. My relay host requires authentication. Because of that, I set this variable to: "my.mail.relay.example.org MYUSERNAME:MYPASSWORD". When the container starts up, it will take this variable and put it in /config/relay_passwords, then run the 'postmap' command on the relay_passwords file, changes the permissions of the .db file that postmap created and cleans up the original relay_passwords file itself
- -e TKF_MYHOSTNAME - Sets the postfix myhostname variable
- -v /mnt/usb/docker/tkf-postfix/:/config - On this line, we are presenting the local path (in my case /mnt/usb/docker/tkf-postfix) to be mounted within the container at the location of /config. This should be optional, but as this still a 0.0.1 release, I want to watch the configs in a little bit easier method
- -p 25:25 - This parameter exposes the host TCP port 25 and presents the traffic to the container on the container's port 25
- --restart until-stopped - https://blog.codeship.com/ensuring-containers-are-always-running-with-dockers-restart-policy/
- --net=tkf-docker - I have an internal docker network that I wanted to expose this container to. This is probably optional for you too
- teknofile/tkf-docker-postfix:devel - the image that we're going to pull down from hub.docker.com
And that's it! Well, it should be anyways. Truth be told, since I started working on this container image I have since switched from a username/password authentication model to a certificate based authentication model to allow for relay access. I'll be updating the image (and this blog post) when I merge those changes in.
At this point you should be up and running with a container that can relay mail for you.
But wait! There's more!
More technical details at least...
As I mentioned previouslly, I decided to base my containers off of the linuxserver.io container base. They have done a lot of the hardware for me. Things I like about them:
- They build multi-arch (x86/ARM) images via Jenkins (Disclaimer - i am only building x86 right now)
- Images are based off of a small alpine base image
- The s6-overlay system, IMHO, makes building the image a breeze
The bulk of the 'work' in the container happens when we COPY root/ / int he Dockerfile. If you look at the root/etc/cont-init.d/45-postfix file what you'll see is:
- /config/main.cf we'll bootstrap a basic postfix configuration
- Then we'll configure some basic postfix items like biff, smtp_always_send_ehlo, append_dot_mydomain and the like. Nothing too fancy
- Next we'll setup a /etc/aliases files. Not really used now but it cleaned up some warnings
- We setup the relayhost parameter and set relay restrictions to only allow networks defined in mynetworks
- There are some TLS pieces setup (that still need more testing, in case anyone is interested)
- Some SASL authentication bits are setup, but also needs to be tested