Deployment guide

SmartDNS

Overview

This guide covers the deployment and configuration of SmartDNS on Ubuntu 24.04 on Azure, using SecureAnyCloud Azure Marketplace images. SmartDNS is a local DNS server. It takes each lookup, sends it to several upstream resolvers concurrently, measures how quickly each returned address responds, and hands your client the fastest one.

SmartDNS caches results aggressively for low latency and speaks encrypted upstream protocols (DNS over TLS, DNS over HTTPS and QUIC). It can also apply per-domain rules, such as pinning an address or blocking a name outright.

A name like github.com or a large CDN hostname resolves to many different addresses depending on which resolver you ask. The address you are given matters as much as how fast you were given it. SmartDNS is built around that idea: it does not just forward, it compares and chooses.

The image installs the official SmartDNS Release 48.4 package published by the upstream project. The package is pinned to an exact release and verified against a known SHA256 checksum during the build, so you always know precisely what is on the image.

Security by design: not an open resolver. A DNS resolver that answers arbitrary lookups for anyone on the internet is a well-known DNS amplification and reflection DDoS vector. SmartDNS listens on all interfaces, so it is useful across your whole VNet. Unlike some resolvers, it has no admission control of its own, because its client-rules directive changes per-client behaviour, not who is allowed in.

SecureAnyCloud therefore enforces the boundary where it cannot be bypassed, in the kernel with nftables. DNS on UDP and TCP 53, and the web interface on TCP 6080, are accepted from the local host and private ranges only, and dropped from every other source. The image ships an audit tool that proves this functionally rather than by assertion (see Step 7).

Security by design: no published default password. The SmartDNS web interface documents a default login of admin / password. That credential is never written into this image. In the shipped image, the web interface is not even loaded, because its configuration include is blank. There is no window in which a known credential is live.

At first boot, your instance generates a 32-character random password for itself, writes the configuration, and starts the interface. The in-browser terminal feature is deliberately disabled, because a remote shell on a network appliance is a remote root surface.

What you get:

  • SmartDNS Release 48.4 (official upstream package, checksum pinned), run under systemd as smartdns.service
  • Fastest address selection: response-mode fastest-ip with a TCP-based speed check (tcp:443,tcp:80). TCP is chosen over ICMP ping because outbound ICMP is unreliable in cloud networks, and a silently failing check would degrade the very measurement the product exists for
  • Six upstream resolvers, encrypted first: DNS over TLS to Cloudflare, Google and Quad9 with hostname verification, plus plain UDP equivalents as a fallback for networks that block port 853
  • Aggressive caching with prefetch and stale-answer serving, so repeat lookups are answered in well under a millisecond
  • A web interface on port 6080 with live query logs, per-upstream response times, cache statistics and client breakdown
  • A kernel-level nftables boundary that restricts DNS and the web interface to private networks, plus smartdns-openresolver-check.sh, an audit tool that proves it
  • A per-instance web password generated at first boot and stored root-only at /root/smartdns-credentials.txt
  • Unattended security upgrades left enabled, so the underlying OS keeps receiving patches

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
  • A subscription to the SmartDNS listing on Azure Marketplace
  • Network Security Group rules that allow TCP 22 for administration, and both UDP and TCP 53 from the client subnets that will query the resolver
  • Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a resolver serving a VNet. Use Standard_D2s_v5 or larger for very heavy query volumes
  • Keep port 53 closed to the internet in your NSG. The image already drops public DNS traffic in the kernel, but for defence in depth, your NSG should not offer it either
1

Deploy from the Azure Portal

Search for SmartDNS in Marketplace, select the SecureAnyCloud publisher, and click Create. Configure the Network Security Group to allow UDP 53 and TCP 53 from your client subnets only, and TCP 22 for administration. DNS uses UDP 53 for most queries and falls back to TCP 53 for large responses, so open both.

Do not add an NSG rule that exposes port 6080 to the internet. Reach the web interface over an SSH tunnel or from inside your VNet.

2

Deploy from the Azure CLI

RG="dns-prod"; LOCATION="eastus"; VM_NAME="smartdns1" az group create --name "$RG" --location "$LOCATION" az vm image terms accept --urn sac1647283583153:smartdns-ubuntu-24-04:default:latest az vm create \
  --resource-group "$RG" \
  --name "$VM_NAME" \
  --image sac1647283583153:smartdns-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard \
  --location "$LOCATION"
# Open DNS to your CLIENT SUBNET only - never to the internet:
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 53 --priority 1001
3

First boot

On first boot, the instance generates its own web interface password, writes the resolver configuration, loads the kernel firewall, and verifies that it is not an open resolver. This takes a few seconds. Connect over SSH and read the details, which are unique to your instance:

sudo cat /root/smartdns-credentials.txt

The file records the DNS endpoint, the web interface URL, the generated username and password, and the result of the open-resolver audit. The password shown in the screenshot above is masked. On your instance, it is a real 32-character value.

4

Confirm the resolver is running

systemctl is-active smartdns.service nftables.service

Both must report active. Then check that the resolver and web interface are listening, and that both services are enabled so that they survive a reboot:

sudo ss -tulnp | grep -E ':53 |:6080' systemctl is-enabled smartdns.service nftables.service
5

Resolve a name

Query the resolver directly:

dig @127.0.0.1 github.com A +short

You should get one or more addresses back. Next, look at the timing behaviour that makes SmartDNS different. Run the same lookup twice:

dig @127.0.0.1 cloudflare.com A | grep -E 'status:|Query time' dig @127.0.0.1 cloudflare.com A | grep -E 'Query time'

The first query is resolved upstream. The second is answered from cache, typically in 0 ms.

6

See the fastest address selection at work

The resolver configuration sets the selection strategy and the upstream pool:

grep -E '^(response-mode|speed-check-mode|server|server-tls)' /etc/smartdns/conf.d/10-sac-resolver.conf

response-mode fastest-ip tells SmartDNS to return the address that responded fastest, and speed-check-mode tcp:443,tcp:80 is how it measures. The six server and server-tls lines are the resolvers that it asks in parallel. You can see the measurements it has collected for each upstream in the web interface (Step 9).

7

Verify that it is not an open resolver

This is the most important check on a DNS appliance, and the image can prove the result rather than just claim it. Run the bundled audit:

sudo /usr/local/sbin/smartdns-openresolver-check.sh

The audit builds two temporary network namespaces attached to the host. One holds a public source address and the other holds a private source address, and the audit queries the resolver from each. Because SmartDNS listens on all interfaces, it is reachable from both. The only thing that can make one succeed and the other fail is the firewall. The audit requires that:

  • the query from the private source is answered, so the resolver is usable inside your VNet
  • the query from the public source is dropped, so the resolver cannot be abused as an amplifier

The audit fails closed. If the boundary is missing or weakened, or if a blanket accept rule has been added, the audit exits non-zero. You can inspect the rules directly:

sudo nft list table inet sac_smartdns | grep -E 'dport (53|6080)'
8

Point your clients at the resolver

From another VM in the same VNet, query the resolver by its private address:

dig @<SMARTDNS_HOST> github.com A +short

To make it the system resolver on an Ubuntu client, set the DNS server on the interface. Do not edit /etc/resolv.conf directly, because that file is managed:

sudo resolvectl dns eth0 <SMARTDNS_HOST>
sudo resolvectl domain eth0 '~.' resolvectl status eth0

In production, set the DNS server on the Azure VNet itself (Virtual network, DNS servers, Custom), so that every VM in the network picks it up automatically.

9

Open the web interface

The web interface is bound to private addresses only. Reach it from your workstation over an SSH tunnel:

ssh -L 6080:127.0.0.1:6080 azureuser@YOUR_VM_PUBLIC_IP

With the tunnel open, browse to http://127.0.0.1:6080/ and sign in with the username and password from /root/smartdns-credentials.txt.

The dashboard shows live totals: how many queries the resolver has answered, the cache hit rate, and the average query time.

Upstream Servers is where the product's behaviour becomes visible. Each configured resolver is listed with its measured average response time, its success rate and how many queries it has handled. This is the comparison that SmartDNS uses to decide what to return. The tls rows are the encrypted upstreams.

Query Log shows every lookup with its client, its type, and the measured response time for the answer that was chosen.

You can confirm that the interface is authenticated, and that the published default credential does not work, without leaving the shell:

curl -s -o /dev/null -w 'unauthenticated: HTTP %{http_code}\n' http://127.0.0.1:6080/api/stats/overview
curl -s -o /dev/null -w 'upstream default admin/password: HTTP %{http_code}\n' \
  -X POST http://127.0.0.1:6080/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"password"}'

Both must report 401. Next, sign in with your own instance's credential:

curl -s -X POST http://127.0.0.1:6080/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"<SMARTDNS_UI_USER>","password":"<SMARTDNS_UI_PASSWORD>"}' \
  | head -c 80

This returns a JSON web token, which the browser interface uses for subsequent calls.

10

Serve additional networks

If you need to serve a network that is not in the private ranges that the image allows by default, add its CIDR to the nftables set rather than removing the boundary. Edit /etc/nftables.conf and add your range to sac_private_v4. Then reload and re-audit:

sudo nft -f /etc/nftables.conf
sudo /usr/local/sbin/smartdns-openresolver-check.sh

Never replace these rules with a blanket accept on port 53. That turns your instance into an open resolver, and the audit above will fail.

11

Customise the resolver

All SecureAnyCloud configuration lives in /etc/smartdns/conf.d/, and /etc/smartdns/smartdns.conf simply includes that directory. To extend or override behaviour, add your own *.conf file there. Then restart the service:

sudo systemctl restart smartdns.service

These directives are useful to explore:

  • address /ads.example.com/# blocks a name
  • nameserver /internal.example.com/office sends a domain to a specific upstream group
  • cache-size tunes the cache

The upstream project documents the full configuration reference.

12

Rotate the web interface password

The password is stored in /etc/smartdns/conf.d/50-sac-webui.conf, which is readable by root only. Change the smartdns-ui.password line, then restart the service:

sudo systemctl restart smartdns.service

Update /root/smartdns-credentials.txt as well, so that your record stays accurate.

13

Security recommendations

  • Keep port 53 closed to the internet in your NSG. The kernel boundary already drops public DNS, but your NSG should agree with it.
  • Never expose port 6080 to the internet. Use an SSH tunnel, or reach it from inside the VNet.
  • Re-run smartdns-openresolver-check.sh after any firewall or configuration change, and treat a failure as a production incident.
  • Rotate the web interface password if it has ever been shared.
  • Leave unattended security upgrades enabled, so that the underlying OS receives later security updates.
  • Restrict SSH (TCP 22) to known administrative addresses.
14

Support and Licensing

SmartDNS is open source software licensed under the GPL-3.0 licence. The licence text is included on the image at /usr/share/doc/sac/smartdns/LICENSE.

SecureAnyCloud provides the packaged, hardened Azure image and 24/7 support for the image itself. SecureAnyCloud is not affiliated with or endorsed by the SmartDNS project. SmartDNS is the name of the upstream project and is used here only to identify the software that this image packages.

Deploy on Azure

Find SmartDNS on Ubuntu 24.04 LTS by SecureAnyCloud on the Azure Marketplace.

Need Help?

Contact SecureAnyCloud support at sales@secureanycloud.com.

Deploy on Azure

Launch SmartDNS Local DNS Resolver on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure