Networking AWS

Unbound on AWS User Guide

| Product: Unbound

Overview

This guide covers the deployment and configuration of Unbound on AWS using cloudimg AWS Marketplace images. Unbound is a fast, lean, validating, recursive, caching DNS resolver developed by NLnet Labs. It performs full recursive resolution for the clients it serves, cryptographically validates every answer with DNSSEC, and caches aggressively for low latency, all driven by a single plain text configuration file with no database and no web console.

The image installs the maintained Unbound 1.19.2 package from the Ubuntu 24.04 LTS archive, which is security patched by the Ubuntu security team and kept current on your running instance by unattended upgrades. It is the resolver you point your own workloads, containers and office networks at instead of a third party public resolver, keeping your DNS traffic private and under your control.

Security by design, not an open resolver. An open recursive resolver, one that will recursively resolve arbitrary names for anyone on the internet, is a well known DNS amplification and reflection DDoS vector. Unbound listens on all interfaces so it is useful inside your VPC, but its access-control policy allows recursion only from the local host and the RFC1918 private ranges and refuses every public source. A recursive query from a public address is answered with REFUSED, so this server can never be abused as an amplifier. You widen the policy to your own trusted subnets when you need to (Step 6), never to 0.0.0.0/0.

Security by design, DNSSEC validation on. Unbound validates the DNSSEC signature chain on every recursive answer against the root trust anchor (/var/lib/unbound/root.key, kept current automatically via RFC 5011). A correctly signed name is returned with the AD (Authenticated Data) flag; a name whose signatures fail to validate is rejected with SERVFAIL rather than being handed to your clients.

Security by design, no baked control credential. A DNS resolver has no admin login. The only management surface is unbound-control, and this image exposes it over a root only unix socket (/run/unbound.ctl) rather than a network TLS port, so there is no control key or certificate baked into the image and nothing to rotate. Manage the resolver locally with sudo unbound-control.

What is included:

  • Unbound 1.19.2 (from the Ubuntu 24.04 LTS archive), run under systemd as the unprivileged unbound user under its AppArmor profile (unbound.service)

  • DNSSEC validation on against the root trust anchor, with harden-dnssec-stripped and aggressive NSEC caching

  • A responsible not an open resolver access-control policy: recursion for 127.0.0.0/8, ::1 and the RFC1918 private ranges only; 0.0.0.0/0 and ::0/0 are refused

  • unbound-control on a root only unix socket (/run/unbound.ctl), so no control credential ships in the image

  • Privacy and performance defaults: qname-minimisation, prefetching, cache prefetch, and per IP and per domain rate limiting to blunt reflection abuse

  • Identity hardening: hide-identity and hide-version so the resolver does not leak its software version

  • The systemd-resolved stub listener disabled so Unbound owns port 53 cleanly, while the OS keeps resolving names through the VPC upstream resolver

  • Unattended security upgrades left enabled so the resolver keeps receiving patches

Connecting to your instance

Connect over SSH on port 22 as the default login user for the AMI variant you launched. There is no application password to retrieve: Unbound is managed locally with sudo unbound-control over a root only unix socket.

OS variant SSH login user Notes
Ubuntu 24.04 ubuntu Unbound 1.19.2 from the Ubuntu 24.04 LTS archive

Prerequisites

  • An AWS account and an EC2 key pair in the target region

  • A subscription to the Unbound listing on AWS Marketplace

  • A security group allowing TCP 22 (administration) and both UDP 53 and TCP 53 (DNS) from the client networks that will query the resolver. DNS uses UDP 53 for most queries and falls back to TCP 53 for large responses, so open both.

Recommended instance type: m5.large (2 vCPU, 8 GB RAM) for a general resolver. Resolvers serving heavy query volumes can move to a larger m5 or c5 size.

Step 1: Deploy from the AWS Console

In the EC2 console choose Launch instance, select the cloudimg Unbound AMI, pick an instance type (m5.large recommended), your key pair and subnet, and attach a security group that allows UDP 53 and TCP 53 from your client networks plus TCP 22 for administration. Launch the instance.

Step 2: Deploy from the AWS CLI

Replace the placeholders with your own AMI ID, key pair, subnet and security group, then launch:

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <your-key-pair> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=unbound-resolver}]'

The security group <security-group-id> must allow inbound UDP 53 and TCP 53 from your client networks and TCP 22 from your administrator network.

Step 3: First boot

On first boot the image resolves the instance public IP for its documentation, confirms DNSSEC validation and the not an open resolver access-control policy are in place, starts Unbound, proves the resolver is up and controllable over the unix socket, and writes /root/unbound-credentials.txt. This completes within a minute. SSH in as ubuntu and read the details:

sudo cat /root/unbound-credentials.txt

Step 4: Confirm the resolver is running

unbound.service is active and unbound-control status reports the server running with the validator module loaded (DNSSEC), using the root only unix socket control channel. Unbound listens on port 53 over both UDP and TCP on all interfaces.

systemctl is-active unbound.service
sudo unbound-control status

unbound.service reports active, unbound -V shows version 1.19.2, unbound-control status shows the server running with the validator and iterator modules loaded and the control channel using the local namedpipe unix socket, and ss shows Unbound listening on port 53 udp and tcp on all interfaces

Step 5: Resolve a name with DNSSEC validation

Query a DNSSEC signed name against the loopback address. The AD flag in the header confirms the answer was cryptographically validated against the root trust anchor. A second identical query is served from cache, which you can see as a much lower query time.

dig @127.0.0.1 +dnssec cloudflare.com
dig @127.0.0.1 cloudflare.com

dig with the plus dnssec option for cloudflare.com returns status NOERROR with the ad flag set in the header, confirming DNSSEC validation against the root trust anchor, the RRSIG record is present in the answer section, and a second query is served from cache with a query time of 0 msec

Step 6: Confirm DNSSEC rejects bad data

A name whose DNSSEC signatures deliberately fail to validate must be rejected with SERVFAIL rather than returned to clients. dnssec-failed.org is a well known test domain with intentionally broken signatures. unbound-checkconf also validates the whole configuration (no error output means valid).

dig @127.0.0.1 dnssec-failed.org
sudo unbound-checkconf

dig for dnssec-failed.org returns status SERVFAIL with zero answers because its DNSSEC signatures fail to validate, unbound reports version 1.19.2, and unbound-checkconf reports no errors in the configuration

Step 7: Verify it is not an open resolver

This is the key security property. The access-control policy in /etc/unbound/unbound.conf.d/00-cloudimg.conf refuses 0.0.0.0/0 and ::0/0, and allows recursion only from the local host and the RFC1918 private ranges. No public source can use this server to recursively resolve arbitrary names. The control channel is the root only /run/unbound.ctl unix socket with control-use-cert: no, so no control credential ships in the image.

grep -E 'access-control|control-interface|control-use-cert' /etc/unbound/unbound.conf.d/00-cloudimg.conf
sudo unbound-control stats_noreset | grep -E 'total.num.queries=|total.num.cachehits='

the access-control policy shows 0.0.0.0/0 refuse and ::0/0 refuse followed by allow rules for 127.0.0.0/8, ::1 and the RFC1918 private ranges, the control interface is the run unbound ctl unix socket with control-use-cert no, and unbound-control stats show total queries and cache hits

Step 8: Widen access-control to your own networks (optional)

By default recursion is offered to the local host and the RFC1918 private ranges. To serve additional trusted client networks, add an access-control allow line to /etc/unbound/unbound.conf.d/00-cloudimg.conf inside the server: block:

access-control: 203.0.113.0/24 allow

Replace 203.0.113.0/24 with your own trusted client ranges. Never use access-control: 0.0.0.0/0 allow — that turns the server back into an open resolver. Apply the change with:

sudo unbound-checkconf
sudo unbound-control reload

Step 9: Point your clients at the resolver

From a host inside the same VPC (an allowed private range), point it at the resolver's private IP, or test directly against the resolver:

dig @<private-ip> +dnssec example.com

To make a Linux client use the resolver permanently, set it as the DNS server on the client's network interface (through netplan or systemd-resolved), pointing at the resolver's private IP.

Step 10: Manage Unbound with unbound-control

The control channel is a root only unix socket, so unbound-control works locally with no credential to manage. Common operations:

sudo unbound-control status
sudo unbound-control stats
sudo unbound-control reload
sudo unbound-control dump_cache | head
sudo unbound-control flush_zone example.com

Step 11: Security recommendations

  • Restrict the security group. Allow UDP and TCP 53 only from the client networks that need to query the resolver, and TCP 22 for administration only.

  • Keep recursion scoped. Only widen access-control to networks you trust (Step 8) — never 0.0.0.0/0. Even with the security group open, the access-control policy is your second layer of defence against open resolver abuse.

  • Leave DNSSEC validation on. It protects your clients from spoofed or tampered answers. The root trust anchor updates itself via RFC 5011.

  • Consider forwarders if required. If your environment mandates resolving through an upstream (for example a corporate resolver), configure a forward-zone, but note that recursive resolution direct to the authoritative servers is the more private default.

  • Keep the OS and Unbound patched. Unattended security upgrades remain enabled on the running instance.

  • Monitor with the statistics. unbound-control stats exposes query counts, cache hit ratio and validation failures for your monitoring system.

Step 12: Support and Licensing

Unbound is developed by NLnet Labs and distributed under the BSD 3 Clause license. This cloudimg image bundles the unmodified Ubuntu Unbound package. cloudimg provides the packaging, the not an open resolver hardening, the DNSSEC validation defaults, the root only unix socket control channel, and 24/7 support with a one hour average response time for critical issues.

cloudimg is not affiliated with or endorsed by NLnet Labs. Unbound is a mark of its respective owner and is used here only to identify the software.

Need Help?

Email support@cloudimg.co.uk for deployment help, configuration questions, or licensing enquiries.