Zimbra split horizon DNS on EC2 with Unbound

Email server behind NAT is a pain, but it can be further aggravated if your email server has a dynamic IP address. Which is the case with EC2. Granted, it will only change on stop/start of the instance, but still. Here’s a recipe to deal with it (a kludge, of course):

1. Make sure Zimbra’s “dnscache” is installed. That’s Unbound, a DNS server.

2. Configure Unbound to respond authoritatively on Zimbra’s mail domains:
su - zimbra
mkdir conf/unbound.d

Create a zone file for each email domain. Example, conf/unbound.d/mail.domain.tld:
local-zone: "domain.tld." static
local-data: "mail.domain.tld. IN A 172.30.0.136"
local-data: "domain.tld. IN MX 10 mail.domain.tld."

3. Next, edit /opt/zimbra/conf/unbound.conf.in to include those zones (between local-zone and forward-zone directives):
...
local-zone: "168.192.in-addr.arpa." nodefault
include: "/opt/zimbra/conf/unbound.d/*"
forward-zone:
...

4. Create the script that will handle rewriting Zimbra’s internal IP on reboots, /opt/scripts/zimbra-fix-dns.sh:
#!/bin/bash
exec >> /var/log/zimbra-fix-dns.log 2>&1
set -xeu -o pipefail
zimbra_dir='/opt/zimbra'
unbound_dir="$zimbra_dir/conf/unbound.d"
ip=$(ip a s | grep inet | grep -v 127.0.0.1 | awk '{print $2}' | awk -F '/' '{print $1}')
for mdomain in $(ls $unbound_dir); do
sed -i "s|\(.*\) IN A.*|\1 IN A $ip\"|" $unbound_dir/$mdomain
sed -i "s|.*$mdomain.*|$ip $mdomain|" /etc/hosts
done

Don’t forget to make it executable.

5. A cherry on top: add calling this script to /etc/init.d/zimbra, start and restart clauses.

Now it will automatically update its internal ip on reboots and restarts. Kludges and band-aids for everyone! Cheers!