Changes for page How to setup a postfix SMTP server
Last modified by Alexandru Pentilescu on 2025/02/09 14:17
From version 6.1
edited by Alexandru Pentilescu
on 2024/05/19 13:48
on 2024/05/19 13:48
Change comment:
There is no comment for this version
To version 7.1
edited by Alexandru Pentilescu
on 2024/05/19 14:20
on 2024/05/19 14:20
Change comment:
There is no comment for this version
Summary
-
Page properties (1 modified, 0 added, 0 removed)
Details
- Page properties
-
- Content
-
... ... @@ -87,12 +87,33 @@ 87 87 88 88 Basically, what this means is that, all your docker services need to have their IPs listed in this parameter for Postfix to relay their emails further. This is mandatory. If you omit any docker service's IP from this list, that service will not be able to use Postfix as its SMTP server to relay email even if the Postfix server is technically reachable by it via ICMP echo packets. 89 89 90 +== Getting the right IP list == 90 90 To find the IP address for a specific docker container, please run "docker inspect <container_id>" and then look up the "IPAddress" field from the resulting output, under the "Networks" JSON property. Note: it's not the "Gateway" field, that's something else! 91 91 92 -Please be aware, though, that docker allocates IPs dynamically. So even if a container has a specific IP at one point, it doesn't mean that it will have the same IP next time a new container is spawned from the same image (i.e. after a system reboot). As such, this can, in theory, mean that your configuration will work at one point but, after a system reboot, it won't work anymore. This would mean that you either have to specify manual static IP addresses for your docker images so that they will always take the exact same IP all the time (not recommended and it goes against the entire philosophy of docker) or, you can just do what I did and simply whitelist all the possible private IPs under "172.16.0.0/12". This basically resolves to all the 16 continous class B private IP addresses in the IPv4 address space, as seen [[here>>https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses]]. --Docker will, by default, use IPs in a subrange in this address space, when allocating IPs to newly spawned containers.-- As I have found out recently, this may not be the case. Docker can use any private IP address that it wishes to use and, as such, it's best to not rely on this. 93 +Please be aware, though, that docker allocates IPs dynamically. So even if a container has a specific IP at one point, it doesn't mean that it will have the same IP next time a new container is spawned from the same image (i.e. after a system reboot). As such, this can, in theory, mean that your configuration will work at one point but, after a system reboot, it won't work anymore. This would mean that you either have to specify manual static IP addresses for your docker images so that they will always take the exact same IP all the time (not recommended and it goes against the entire philosophy of docker) or, you can just do what I did and simply whitelist all the possible private IPs under "172.16.0.0/12". This basically resolves to all the 16 continous class B private IP addresses in the IPv4 address space, as seen [[here>>https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses]]. --Docker will, by default, use IPs in a subrange in this address space, when allocating IPs to newly spawned containers.-- As I have found out recently, this may not be the case. Docker can use any private IP address that it wishes to use and, as such, it's best to not rely on this. 93 93 94 94 Instead, a better means of configuring docker to respect a specific IP address range is by restricting it from its own configuration, as the administrator. 95 95 97 +To do so, please open the docker config file (in my case, this was under "/etc/docker/daemon.json") and edit it with your favorite text editor: 98 + 99 +{{code language="JSON"}} 100 +{ 101 + "storage-driver": "fuse-overlayfs", 102 + "default-address-pools": 103 + [ 104 + {"base":"172.16.0.0/16", "size":24} 105 + ] 106 +} 107 +{{/code}} 108 + 109 +You can safely omit the "storage-driver" property, as this is unnecessary for our needs. 110 + 111 +I really can't even remember why I needed to specify it to "fuse-overlayfs". I remember doing so fixed a particular bug that I had encountered in the past, but not what the bug was. 112 + 113 +Alas, the only relevant configuration above is the "default-address-pools". This configuration basically tells the docker daemon that, whenever it needs to spawn a container, this container's network will have an address from that addres range specified to it (in our case, the 172.16.0.0/16 network). 114 + 115 +Very handy 116 + 96 96 This approach has the advantage that whichever IP docker will assign to a newly created container, that IP will always fall somewhere in this range, so it will already be whitelisted. Moreover, since this is a private address range, not a public one, nobody outside the current LAN of the server can impersonate it, nor can they breach the local network from the outside if proper firewall and NAT rules are set in place by the network administrator, which means there's never a risk that someone might try misusing our Postfix server from outside our network. 97 97 98 98 Finally, there's the "inet_interfaces" configuration parameter. This one specifies under which identities the current installation of Postfix will be assumed by the server. Postfix will accept all requests destined to any of these addresses as its own and will handle them. ... ... @@ -99,7 +99,66 @@ 99 99 100 100 In a docker configuration, assuming the services are using a "bridge" network driver, they will all have their own IP addresses in the aforementioned address space, and these addresses will be distinct from the proper address of the machine where Postfix is installed. As such, they need a target to resolve to reach the machine running Postfix. This target will be IP 172.16.0.1. When configuring each individual docker service, enter that IP as the IP of the SMTP server to use, as well as port 25, as its connection port. These should be the only parameters you should need to configure everything to work properly. 172.16.0.1 was a random address that I decided on. Really, it has no real relevance and can be changed to any private IPv4 address, whether in class B, C or A. The only point is that it should be reachable through this network driver. 101 101 123 += Instructing Postfix to relay emails through Google's servers, instead = 124 +Assuming that running your own postfix server is a pain in the ass (especially since it's quite difficult to get DMARC or other authenticity mechanisms configured by yourself), a proper workaround for that is to simply rely on a third party emailing service to relay your emails for you. 102 102 126 +This has the benefit of being somewhat easier to use and configure, but with the obvious downside that you will need to have an already existing email address on that service to send the emails through. 127 + 128 +To the recipients receiving those emails, it will look like the sender of those emails is that existing email address that you'll be using. 129 + 130 +So, how do we achieve this? 131 + 132 +I'm personally using my Gmail account for this. The way I configured it is as follows: 133 + 134 +{{code language="ini"}} 135 +relayhost = [smtp.gmail.com]:587 136 +smtp_use_tls = yes 137 +smtp_sasl_auth_enable = yes 138 +smtp_sasl_security_options = 139 +smtp_sasl_password_maps = hash:/etc/postfix/config 140 +smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt 141 +{{/code}} 142 + 143 +The above code was appended to the "/etc/postfix/main.cf" configuration file, at the end. It specifies a bunch of necessary configurations, but the most relevant for them is: 144 + 145 +* The relayhost: this specifies the SMTP server itself that needs to relay the email. including the port. This is Google's own SMTP address 146 +* smtp_sasl_password_maps: this specifies the local filesystem file that contains the login credentials that need to be used to authenticate with 147 +Google only allows authenticated services to relay email through their own servers, so the authentication part cannot be skipped. Also, Google requires app passwords to be used when authenticating, not the actual Gmail password of your Google account. App passwords must be manually generated by the end user and, as I've recently found out, are meant to be used only once per app. The system is designed in such a way that the password gets generated once, is meant to be copy-pasted into wherever the configuration requires it, and then be left alone. As such, you will **be unable to later inspect the app password that you've previously generated, even though this password may still be valid and in use**. 148 + 149 +The contents of my "/etc/postfix/config" file looks like this: 150 +[smtp.gmail.com]:587 <username@gmail.com>:<app_password> 151 +I think I might have needed to generate this file using a postfix utility or something, although I'll assume that writing it by hand can also work. 152 + 153 +Once this is configured and ready to go, you can safely restart the postfix service and rely on Google to relay your emails for you. 154 + 155 += Configuring the Postfix service such that it always restarts when the system is running low on RAM = 156 +This has been a major pain in the butt for me. 157 + 158 +Every so often, I would visit one of my services after a long period of being away and then would request for a password to be delivered to me from them, only to then find out that no email is being received. 159 + 160 +I would then SSH into my VPS, only to discover that Postfix had been killed at some point in the past due to low RAM. 161 + 162 +Granted, this is by no means ideal, and it can be very annoying, having to manually restart the Postfix service every single time. 163 + 164 +To solve this, I've rewritten my Postfix service file to account for this. Its location (for me at least) was under /etc/systemd/system/multi-user.target.wants/postfix.service 165 + 166 +{{code language="Systemd"}} 167 +[Unit] 168 +Description=Postfix Mail Transport Agent 169 +Conflicts=sendmail.service exim4.service 170 +ConditionPathExists=/etc/postfix/main.cf 171 + 172 +[Service] 173 +Type=oneshot 174 +RemainAfterExit=yes 175 +ExecStart=/bin/true 176 +ExecReload=/bin/true 177 +Restart=on-failure 178 +RestartSec=1s 179 +{{/code}} 180 + 181 +From that, the only genuinely relevant changes that need to be highlighted are the last two lines (i.e. the "Restart" and "RestartSec" assignments). These tell systemd that that, in the event that the service gets killed due to an abnormality (i.e. it receives a SIGKILL system because it is running low on RAM), to automatically restart it. The second rule (i.e. "RestartSec"), tells it to wait an entire second before performing the restart, so that it gives the system the chance to finish whatever it was doing. 182 + 103 103 = Troubleshooting issues with Postfix reachability from docker containers= 104 104 If whichever docker container you're currently running doesn't seem to connect to 172.17.0.1 and its image contains the ping utility pre-installed in it, you can attach your current terminal session into that container and access it via "docker exec -it <docker_container_id> /bin/bash" and then simply issuing a "ping 172.17.0.1" to send ICMP echo packets to your SMTP server from inside the container itself. If there are replies, this means the container can reach your local Postfix server so the problem is most likely from Postfix dropping the requests intentionally. Alternatively, this could be a firewall misconfiguration problem but this has never happened to me before, although I recognize that it may be theoretically possible. 105 105