This article shows you how to install, configure, and run Unbound as your local DNS cache on Ubuntu Linux, on an AWS EC2 server. This way you’re not running too many DNS lookups from, say, your web app to your managed AWS RDS database, and seeing weird errors like “Temporary failure in name resolution”…
Here’s a link to the Unbound documentation, which is really helpful.
I previously wrote an article about how to install dnsmasq, working together with systemd-resolved, but Unbound is so much easier, and it just works.
Credit to this tutorial at Yandex
Run all of the following in order
- It’s pretty much a script, but best to do it one line at a time
Check whether this is to run on VPC (default) or EC2 classic and set NAMESERVER accordingly
INTERFACE=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/ | head -n1)
IS_IT_CLASSIC=$(curl --write-out %{http_code} --silent --output /dev/null http://169.254.169.254/latest/meta-data/network/interfaces/macs/${INTERFACE}/vpc-id)
if [[ $IS_IT_CLASSIC == '404' ]]
then
NAMESERVER="172.16.0.23"
else
NAMESERVER="169.254.169.253"
fi
echo "IS_IT_CLASSIC = $IS_IT_CLASSIC (old Kapua 200 response, new reserved instance, 401)"
echo "NAMESERVER = $NAMESERVER"
You should see something like NAMESERVER = 169.254.169.253
(the main AWS Route53 nameserver)
Install the Unbound package, Unbound-Anchor package for DNS security, the dig
package, and the DHCP client.
sudo apt-get update -y
sudo apt-get install unbound unbound-anchor dnsutils isc-dhcp-client -y
Create a configuration file, including the Amazon DNS resolver for resolving hosts on your private VPC.
echo -e "\n\
server:\n\
# Listen for queries on port 53\n\
port: 53\n\
# Listen for DNS queries only on the loopback interface (localhost). This means it's only accessible from the local machine\n\
interface: 127.0.0.1\n\
# Allow DNS queries from the 127.0.0.0/8 address range (localhost). Restrict access only to the local machine\n\
access-control: 127.0.0.0/8 allow\n\
# Set the logging level for validation. Level 2 provides detailed logs for DNSSEC validation\n\
# Change this if you don't want to see so many log messages\n\
val-log-level: 2\n\
# Level 2 provides detailed logging information\n\
verbosity: 2\n\
\n\
do-ip4: yes\n\
do-ip6: no\n\
do-udp: yes\n\
do-tcp: yes\n\
num-threads: 2\n\
num-queries-per-thread: 1024\n\
# Hide the server's identity in responses\n\
hide-identity: yes\n\
hide-version: yes\n\
# Enables DNS prefetching to improve response times for frequently requested records\n\
prefetch: yes\n\
# Root hints (can be updated by unbound-anchor)\n\
# root-hints: "/var/lib/unbound/root.hints"\n\
\n\
# Configures Unbound to treat amazonaws.com as a private domain, which might affect how queries for this domain are handled\n\
# private-domain: "amazonaws.com"\n\
\n\
# The following line includes additional configuration files from the\n\
# /etc/unbound/unbound.conf.d directory.\n\
# include-toplevel: "/etc/unbound/unbound.conf.d/*.conf"\n\
\n\
# The following line will configure unbound to perform cryptographic\n\
# DNSSEC validation using the root trust anchor.\n\
# Thich is crucial for DNSSEC validation.\n\
# auto-trust-anchor-file: "/var/lib/unbound/root.key"\n\
\n\
# Defines a zone where DNS queries are forwarded to another DNS server\n\
forward-zone:\n\
# Forward all DNS queries (for any domain) to the specified DNS server. \n\
# Unbound will not perform any DNS resolution itself but will forward all requests\n\
# to the server defined in the forward-addr directive.\n\
# While Unbound itself is not resolving the queries, it will cache the responses it gets\n\
# from the AWS VPC private resolver. Subsequent queries for the same domain within the cache's\n\
# TTL (Time-To-Live) will be served from Unbound's local cache rather than being forwarded again.\n\
name: \".\"\n\
# Amazon's VPC private DNS resolver (likely 169.254.169.253)\n\
forward-addr: $NAMESERVER\n\
\n\
# remote-control:\n\
# control-enable: yes\n\
# by default the control interface is is 127.0.0.1 and ::1 and port 8953\n\
# it is possible to use a unix socket too\n\
# control-interface: /run/unbound.ctl\n\
" | sudo tee /etc/unbound/unbound.conf > /dev/null && \
echo "" && cat /etc/unbound/unbound.conf
Manually update the root trust anchor for DNSSEC validation
sudo mv /var/lib/unbound/root.key /var/lib/unbound/root.key.bak
sudo unbound-anchor -c -v -a "/var/lib/unbound/root.key"
Ensure the unbound
user can access the required /var/lib folder
sudo chown -R unbound:root /var/lib/unbound
sudo chmod -R 755 /var/lib/unbound
ls -la /var/lib/unbound
Check the Unbound configuration files for syntax errors or misconfigurations
unbound-checkconf -f /etc/unbound/unbound.conf
Reload the Unbound config
sudo systemctl reload-or-restart unbound.service
sudo systemctl status unbound.service
Check the logs for the service
journalctl --unit unbound --lines 1000 --pager-end --follow
Check DNSSEC DNS Security
Run the following and you should see the ad
flag there. If things go wrong, try the unbound option val-log-level: 2
that will log explanations why the DNSSEC validation fails (one line per failed query). See this link for more.
dig amazonaws.com
dig @8.8.8.8 com. SOA +dnssec
dig +dnssec A www.dnssec.cz
Create /etc/dhcp3/dhclient.conf
sudo mkdir -p /etc/dhcp3
sudo touch /etc/dhcp3/dhclient.conf || true
echo -e "
#supersede domain-name "fugue.com home.vix.com";\n\
prepend domain-name-servers 127.0.0.1;\n\
request subnet-mask, broadcast-address, time-offset, routers,\n\
domain-name, domain-name-servers, host-name,\n\
netbios-name-servers, netbios-scope;" | sudo tee /etc/dhcp3/dhclient.conf > /dev/null
Check it
cat /etc/dhcp3/dhclient.conf
Make the localhost 127.0.0.1 the main (local) DNS resolver, if you have systemd-resolved installed
echo "Old /etc/systemd/resolved.conf contents: " && cat /etc/systemd/resolved.conf
sudo sed -i 's/^#DNS=.*$/DNS=127.0.0.1/' /etc/systemd/resolved.conf
Enable DNS security (highly recommended)
sudo sed -i 's/^#DNSSEC=yes.*$/DNSSEC=yes/' /etc/systemd/resolved.conf
Use Unbound instead of systemd-resolved (i.e. we don’t want systemd-resolved to be the DNS Stub Listener)
sudo sed -i 's/^#DNSStubListener=yes.*$/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo sed -i 's/^#DNSStubListener=no.*$/DNSStubListener=no/' /etc/systemd/resolved.conf
echo "New /etc/systemd/resolved.conf contents: " && cat /etc/systemd/resolved.conf
sudo systemctl reload-or-restart systemd-resolved
Check if it works at this time
dig aws.amazon.com
sudo systemctl status systemd-resolved
For dnsmasq to work, iptables mustn’t block the DHCP port
sudo ufw allow bootps
Stop systemd-resolved and dnsmasq first, if they’re running
sudo systemctl stop dnsmasq.service
sudo systemctl disable dnsmasq.service
sudo systemctl restart systemd-resolved
sudo systemctl status systemd-resolved
sudo systemctl status dnsmasq.service
Edit the /etc/resolv.conf file manually and ensure it contains the following line at the top:
nameserver 127.0.0.1
Start Unbound
sudo systemctl start unbound.service
sudo systemctl status unbound.service
Test the service and configure dhclient accordingly. Set the dnsmasq DNS cache as the default DNS resolver. Note: You must suppress the default DNS resolver that DHCP provides. To do this, change or create the /etc/dhcp/dhclient.conf file.
echo "supersede domain-name-servers 127.0.0.1, ${NAMESERVER};" | sudo tee /etc/dhcp/dhclient.conf > /dev/null
Apply the change (or… sudo systemctl restart network
)
sudo dhclient
Test that DNS lookups work now!
dig aws.amazon.com @127.0.0.1
dig google.com @127.0.0.1 | grep -B3 Query
dig microsoft.com
Optional Step
By default, systemd-resolved creates a symbolic link at /etc/resolv.conf that points to a local DNS stub (127.0.0.53). You can remove this link and replace it with a standard /etc/resolv.conf file if you like.
echo "/etc/resolv.conf contents:" && cat /etc/resolv.conf
ls -la /etc/resolv.conf
cat /etc/resolv.conf
Make the file not immutable, so we can delete, move, or change it.
sudo chattr -i /etc/resolv.conf
# sudo unlink /etc/resolv.conf
sudo mv /etc/resolv.conf /etc/resolv.conf.bak-Sep-11-2024
Create a new /etc/resolv.conf file manually, specifying a DNS server (e.g., Google’s public DNS or another one you prefer):
sudo bash -c 'echo "nameserver ${NAMESERVER}" > /etc/resolv.conf'
Prevent systemd or other services from modifying your new /etc/resolv.conf file, you can set it as immutable
sudo chattr +i /etc/resolv.conf
To undo the above and make the file mutable again, run sudo chattr -i /etc/resolv.conf
Check the permissions, and whether it’s a file. Previously it was a symlink.
ls -la /etc/resolv.conf
Verify Unbound DNS resolver works correctly
dig aws.amazon.com @127.0.0.1
Cheers,
Sean
Comments