How to self-host a Chatmail relay
If you want to run your own Chatmail relay but you don’t want to run it on someone else’s computer, follow this guide.
Unfortunately, you will need a VPS to act as a VPN gateway. You will use its reputable public IP address, but the relay will run on your own hardware at home -an old laptop, a Proxmox LXC container or whatever you want- tunneling its traffic through Wireguard. No third-party access.
0. Standard requirements
Check the Minimal requirements and prerequisites section at the official documentation (from now on we will call relay to that Debian system where the Chatmail server will be later installed).
For our setup, you will need a VPS too.
1. Get a VPS
A Chatmail relay is an email server. Denylist providers will consider your residential public IP address as a source of spam and include it in its denylists. So you need a non-residential public IP address to be able to communicate with Delta Chat users that have accounts on mail servers that trust those lists (most mail servers do).
Get the cheapest VPS you can find, the resources needed are minimal.
2. [VPS] Take some notes
- Run
ip aand take note of: The name of its ethernet interface (ens18, eth0, whatever…)
Its public IPv4 address
Its public IPv6 address
3. [VPS] Check external firewall
In the case the cloud provider provides a firewall external to the VPS, make sure you allow every needed port for the relay, as well as for SSH and Wireguard (UDP 51820).
4. [VPS] Allow IP forwarding
Edit /etc/sysctl.conf, uncomment the following lines
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
And do sudo sysctl -p
5. [Both Relay and VPS] Install Wireguard
sudo apt install wireguard
Or adapt to your package manager.
6. [Both Relay and VPS]: Create the keys
cd /etc/wireguard
sudo wg genkey | tee privatekey | wg pubkey > publickey
Check the keys are available at /etc/wireguard/
7. [VPS] Configure wg0.conf
Create the file /etc/wireguard/wg0.conf
Paste the following block and edit as noted.
[Interface]
PrivateKey = [write here the private key of the VPS]
Address = 10.6.0.1/24,fd42:42:42::1/64
ListenPort = 51820
SaveConfig = true
PostUp = bash /etc/wireguard/PostUp.sh
PostDown = bash /etc/wireguard/PostDown.sh
[Peer]
PublicKey = [insert here the public key of the Relay]
AllowedIPs = 10.6.0.2/32,fd42:42:42::2/128
8. [VPS] Configure PostUp.sh
Create the file /etc/wireguard/PostUp.sh
Paste the following block and edit as noted.
# LET VPN CLIENTS ACCESS THE INTERNET (that is chatmail's outgoing port-25/465/587 traffic)
iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -t nat -A POSTROUTING -o [write here the name of the VPS' ethernet interface] -j MASQUERADE;
ip6tables -A FORWARD -i wg0 -j ACCEPT;
ip6tables -t nat -A POSTROUTING -o [write here the name of the VPS' ethernet interface] -j MASQUERADE;
# ICMP
iptables -A INPUT -p icmp -j ACCEPT;
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT;
# ROUTING TCP FROM VPS TO RELAY (in this example 22222 for SSH)
for m in 25 80 143 443 465 587 993 8443 22222
do
iptables -t nat -A PREROUTING -i [write here the name of the VPS' ethernet interface] -p tcp --dport $m -j DNAT --to-destination 10.6.0.2;
iptables -A FORWARD -d 10.6.0.2 -p tcp --dport $m -j ACCEPT;
ip6tables -t nat -A PREROUTING -i [write here the name of the VPS' ethernet interface] -p tcp --dport $m -j DNAT --to-destination fd42:42:42::2;
ip6tables -A FORWARD -d fd42:42:42::2 -p tcp --dport $m -j ACCEPT;
done
# ROUTING UDP FROM VPS TO RELAY
# Control port
iptables -t nat -A PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 3478 -j DNAT --to-destination 10.6.0.2;
iptables -A FORWARD -d 10.6.0.2 -p udp --dport 3478 -j ACCEPT;
ip6tables -t nat -A PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 3478 -j DNAT --to-destination fd42:42:42::2;
ip6tables -A FORWARD -d fd42:42:42::2 -p udp --dport 3478 -j ACCEPT;
# TURN allocation range (ephemeral ports)
iptables -t nat -A PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 49152:65535 -j DNAT --to-destination 10.6.0.2;
iptables -A FORWARD -d 10.6.0.2 -p udp --dport 49152:65535 -j ACCEPT;
ip6tables -t nat -A PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 49152:65535 -j DNAT --to-destination fd42:42:42::2;
ip6tables -A FORWARD -d fd42:42:42::2 -p udp --dport 49152:65535 -j ACCEPT;
# HAIRPIN HANDLING (to avoid port 80 issues on cert creation and renewal)
# IPv4: allow VPS-local processes to reach the public IP and hairpin wg clients
iptables -t nat -C OUTPUT -d [write here the VPS public IPv4 address]/32 -p tcp --dport 80 -j DNAT --to-destination 10.6.0.2:80 2>/dev/null || \
iptables -t nat -A OUTPUT -d [write here the VPS public IPv4 address]/32 -p tcp --dport 80 -j DNAT --to-destination 10.6.0.2:80
iptables -t nat -C POSTROUTING -s 10.6.0.0/24 -d 10.6.0.2/32 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 10.6.0.0/24 -d 10.6.0.2/32 -j MASQUERADE
# IPv6: mirror if ip6tables nat table is available
ip6tables -t nat -C OUTPUT -d [write here the VPS public IPv6 address]/128 -p tcp --dport 80 -j DNAT --to-destination fd42:42:42::2 2>/dev/null || \
ip6tables -t nat -A OUTPUT -d [write here the VPS public IPv6 address]/128 -p tcp --dport 80 -j DNAT --to-destination fd42:42:42::2
ip6tables -t nat -C POSTROUTING -s fd42:42:42::/64 -d fd42:42:42::2/128 -j MASQUERADE 2>/dev/null || \
ip6tables -t nat -A POSTROUTING -s fd42:42:42::/64 -d fd42:42:42::2/128 -j MASQUERADE
# Remove stale HTTP conntrack entries for relay to avoid hairpin state issues
conntrack -D -d 10.6.0.2 -p tcp --dport 80 2>/dev/null || true
conntrack -D -s 10.6.0.2 -p tcp --sport 80 2>/dev/null || true
9. [VPS] Configure PostDown.sh
Create the file /etc/wireguard/PostDown.sh
Paste the following block and edit as noted.
iptables -D FORWARD -i wg0 -j ACCEPT;
iptables -t nat -D POSTROUTING -o [write here the name of the VPS' ethernet interface] -j MASQUERADE;
ip6tables -D FORWARD -i wg0 -j ACCEPT;
ip6tables -t nat -D POSTROUTING -o [write here the name of the VPS' ethernet interface] -j MASQUERADE;
# ICMP
iptables -D INPUT -p icmp -j ACCEPT;
ip6tables -D INPUT -p ipv6-icmp -j ACCEPT;
# ROUTING TCP FROM VPS TO CHATMAIL
for m in 25 80 143 443 465 587 993 8443 22222
do
iptables -t nat -D PREROUTING -i [write here the name of the VPS' ethernet interface] -p tcp --dport $m -j DNAT --to-destination 10.6.0.2;
iptables -D FORWARD -d 10.6.0.2 -p tcp --dport $m -j ACCEPT;
ip6tables -t nat -D PREROUTING -i [write here the name of the VPS' ethernet interface] -p tcp --dport $m -j DNAT --to-destination fd42:42:42::2;
ip6tables -D FORWARD -d fd42:42:42::2 -p tcp --dport $m -j ACCEPT;
done
# ROUTING UDP FROM VPS TO CHATMAIL
# Control port
iptables -t nat -D PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 3478 -j DNAT --to-destination 10.6.0.2;
iptables -D FORWARD -d 10.6.0.2 -p udp --dport 3478 -j ACCEPT;
ip6tables -t nat -D PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 3478 -j DNAT --to-destination fd42:42:42::2;
ip6tables -D FORWARD -d fd42:42:42::2 -p udp --dport 3478 -j ACCEPT;
# TURN allocation range (ephemeral ports)
iptables -t nat -D PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 49152:65535 -j DNAT --to-destination 10.6.0.2;
iptables -D FORWARD -d 10.6.0.2 -p udp --dport 49152:65535 -j ACCEPT;
ip6tables -t nat -D PREROUTING -i [write here the name of the VPS' ethernet interface] -p udp --dport 49152:65535 -j DNAT --to-destination fd42:42:42::2;
ip6tables -D FORWARD -d fd42:42:42::2 -p udp --dport 49152:65535 -j ACCEPT;
# HAIRPIN HANDLING
iptables -t nat -D OUTPUT -d [write here the VPS public IPv4 address]/32 -p tcp --dport 80 -j DNAT --to-destination 10.6.0.2:80 2>/dev/null || true
iptables -t nat -D POSTROUTING -s 10.6.0.0/24 -d 10.6.0.2/32 -j MASQUERADE 2>/dev/null || true
ip6tables -t nat -D OUTPUT -d [write here the VPS public IPv6 address]/128 -p tcp --dport 80 -j DNAT --to-destination fd42:42:42::2 2>/dev/null || true
ip6tables -t nat -D POSTROUTING -s fd42:42:42::/64 -d fd42:42:42::2/128 -j MASQUERADE 2>/dev/null || true
#
#remove stale HTTP conntrack entries for relay to avoid hairpin state issues
conntrack -D -d 10.6.0.2 -p tcp --dport 80 2>/dev/null || true
conntrack -D -s 10.6.0.2 -p tcp --sport 80 2>/dev/null || true
10. [VPS] Give the scripts exec permissions
sudo chmod +x /etc/wireguard/PostUp.sh
sudo chmod +x /etc/wireguard/PostDown.sh
11. [Relay] Configure wg0.conf
Create the file /etc/wireguard/wg0.conf
Paste the following block and edit as noted.
[Interface]
PrivateKey = [write here the relay's private key]
Address = 10.6.0.2/24, fd42:42:42::2/64
# choose your DNS [modify the DNS addresses below as you like]
DNS = 94.247.43.254, 2a00:f826:8:1::254
PostUp = bash /etc/wireguard/PostUp.sh
PostDown = bash /etc/wireguard/PostDown.sh
[Peer]
PublicKey = [write here the VPS' public key]
Endpoint = [write here the VPS' public IPv4 address]:51820
AllowedIPs = 0.0.0.0/0, ::0/0
PersistentKeepalive = 25
12. [Relay] Configure PostUp.sh
Create the file /etc/wireguard/PostUp.sh
Paste the following block and edit the SSH port if needed.
# IPV4
iptables -w -N vpnclient_in;
iptables -w -N vpnclient_out;
iptables -w -N vpnclient_fwd;
iptables -w -A vpnclient_in -p icmp -j ACCEPT;
# TCP (in this example 22222 for SSH)
for i in 25 80 143 443 465 587 993 8443 22222
do
iptables -w -A vpnclient_in -p tcp --dport $i -j ACCEPT;
done
# UDP
# TURN control port
iptables -w -A vpnclient_in -p udp --dport 3478 -j ACCEPT
# TURN allocation range (ephemeral ports)
iptables -w -A vpnclient_in -p udp --dport 49152:65535 -j ACCEPT
iptables -w -A vpnclient_in -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT;
iptables -w -A vpnclient_in -j DROP;
iptables -w -A vpnclient_out -j ACCEPT;
iptables -w -A vpnclient_fwd -j DROP;
iptables -w -I INPUT 1 -i wg0 -j vpnclient_in;
iptables -w -I OUTPUT 1 -o wg0 -j vpnclient_out;
iptables -w -I FORWARD 1 -o wg0 -j vpnclient_fwd;
# IPV6
ip6tables -w -N vpnclient_in;
ip6tables -w -N vpnclient_out;
ip6tables -w -N vpnclient_fwd;
ip6tables -w -A vpnclient_in -p ipv6-icmp -j ACCEPT;
# TCP (in this example 22222 for SSH)
for i in 25 80 143 443 465 587 993 8443 22222
do
ip6tables -w -A vpnclient_in -p tcp --dport $i -j ACCEPT;
done
# UDP
# TURN control port
ip6tables -w -A vpnclient_in -p udp --dport 3478 -j ACCEPT
# TURN allocation range (ephemeral ports)
ip6tables -w -A vpnclient_in -p udp --dport 49152:65535 -j ACCEPT
ip6tables -w -A vpnclient_in -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT;
ip6tables -w -A vpnclient_in -j DROP;
ip6tables -w -A vpnclient_out -j ACCEPT;
ip6tables -w -A vpnclient_fwd -j DROP;
ip6tables -w -I INPUT 1 -i wg0 -j vpnclient_in;
ip6tables -w -I OUTPUT 1 -o wg0 -j vpnclient_out;
ip6tables -w -I FORWARD 1 -o wg0 -j vpnclient_fwd;
13. [Relay] Configure PostDown.sh
Create the file /etc/wireguard/PostDown.sh
Paste the following block.
# IPV4
iptables -w -F vpnclient_in;
iptables -w -F vpnclient_out;
iptables -w -F vpnclient_fwd;
iptables -D INPUT -i wg0 -j vpnclient_in;
iptables -D FORWARD -o wg0 -j vpnclient_fwd;
iptables -D OUTPUT -o wg0 -j vpnclient_out;
iptables -w -X vpnclient_in;
iptables -w -X vpnclient_out;
iptables -w -X vpnclient_fwd;
# IPV6
ip6tables -w -F vpnclient_in;
ip6tables -w -F vpnclient_out;
ip6tables -w -F vpnclient_fwd;
ip6tables -D INPUT -i wg0 -j vpnclient_in;
ip6tables -D FORWARD -o wg0 -j vpnclient_fwd;
ip6tables -D OUTPUT -o wg0 -j vpnclient_out;
ip6tables -w -X vpnclient_in;
ip6tables -w -X vpnclient_out;
ip6tables -w -X vpnclient_fwd;
14. [Relay] Give the scripts exec permissions
sudo chmod +x /etc/wireguard/PostUp.sh
sudo chmod +x /etc/wireguard/PostDown.sh
15. [Both Relay and VPS] Enable Wireguard
sudo systemctl start wg-quick@wg0.service
sudo systemctl enable wg-quick@wg0.service
16. [Relay or VPS] Check tunnel
Run wg show. You should see the peer with some data transfered and you should also see a line reading latest handshake: showing a minute and/or some seconds.
17. Deploy your relay
Now you are ready to follow the official documentation: