Ir al contenido

Unbound Cheat Sheet

Overview

Unbound is a validating, recursive, and caching DNS resolver developed by NLnet Labs. It is designed for high performance, security, and standards compliance, with built-in DNSSEC validation as a core feature. Unbound performs recursive resolution starting from the root servers, caching results to speed up subsequent queries. It is widely deployed as a local recursive resolver on servers, network appliances, and as the default DNS resolver in many Linux distributions and firewalls (including OPNsense and pfSense).

Unbound supports modern DNS privacy protocols including DNS-over-TLS (DoT) on port 853 and DNS-over-HTTPS (DoH), allowing encrypted DNS queries. It offers extensive access control, response policy zones (RPZ) for DNS-based filtering, local zone definitions for internal DNS, and stub/forward zones for split-horizon DNS. Unbound can also serve as a DNS forwarder, sending queries to upstream resolvers instead of performing full recursion. Its modular architecture includes support for Python scripting, Redis caching, and DNSTAP logging for analysis.

Installation

Package Installation

# Ubuntu/Debian
sudo apt install unbound unbound-anchor

# RHEL/CentOS/Rocky
sudo dnf install unbound

# macOS
brew install unbound

# FreeBSD
pkg install unbound

# Fetch root trust anchor for DNSSEC
sudo unbound-anchor -a /var/lib/unbound/root.key

# Fetch root hints
sudo wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.cache

# Enable and start
sudo systemctl enable unbound
sudo systemctl start unbound

Core Commands

CommandDescription
unbound-control statusShow server status
unbound-control reloadReload configuration
unbound-control statsShow statistics
unbound-control stats_noresetStats without resetting counters
unbound-control dump_cacheDump cache contents
unbound-control load_cacheLoad cache from file
unbound-control flush <name>Flush specific name from cache
unbound-control flush_zone <zone>Flush entire zone from cache
unbound-control flush_type <name> <type>Flush specific record type
unbound-control list_forwardsShow configured forwarders
unbound-control list_stubsShow configured stub zones
unbound-control list_local_zonesList local zones
unbound-control local_data <rr>Add local data entry
unbound-control local_data_remove <name>Remove local data

Cache Management

# View cache statistics
sudo unbound-control stats_noreset | grep cache

# Flush single name
sudo unbound-control flush example.com

# Flush entire zone
sudo unbound-control flush_zone example.com

# Flush all cached data
sudo unbound-control flush_zone .

# Dump cache to file
sudo unbound-control dump_cache > /tmp/cache.dump

# Restore cache from file
sudo unbound-control load_cache < /tmp/cache.dump

Configuration

Basic Recursive Resolver (/etc/unbound/unbound.conf)

server:
    # Network
    interface: 0.0.0.0
    interface: ::0
    port: 53

    # Access control
    access-control: 127.0.0.0/8 allow
    access-control: 10.0.0.0/8 allow
    access-control: 172.16.0.0/12 allow
    access-control: 192.168.0.0/16 allow
    access-control: ::1/128 allow
    access-control: 0.0.0.0/0 refuse

    # Performance
    num-threads: 4
    msg-cache-slabs: 4
    rrset-cache-slabs: 4
    infra-cache-slabs: 4
    key-cache-slabs: 4
    msg-cache-size: 256m
    rrset-cache-size: 512m
    outgoing-range: 8192
    num-queries-per-thread: 4096

    # DNSSEC validation
    auto-trust-anchor-file: /var/lib/unbound/root.key
    val-clean-additional: yes

    # Root hints
    root-hints: /var/lib/unbound/root.hints

    # Privacy
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-referral-path: yes
    use-caps-for-id: yes
    qname-minimisation: yes

    # Logging
    verbosity: 1
    log-queries: no
    log-replies: no
    logfile: /var/log/unbound/unbound.log

    # Cache settings
    cache-min-ttl: 300
    cache-max-ttl: 86400
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    serve-expired-ttl: 86400

    # Misc
    do-ip4: yes
    do-ip6: yes
    do-tcp: yes
    do-udp: yes
    edns-buffer-size: 1232

# Remote control
remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
    control-port: 8953
    server-key-file: /etc/unbound/unbound_server.key
    server-cert-file: /etc/unbound/unbound_server.pem
    control-key-file: /etc/unbound/unbound_control.key
    control-cert-file: /etc/unbound/unbound_control.pem

Setup Remote Control

# Generate control keys
sudo unbound-control-setup

# Test
sudo unbound-control status

DNS-over-TLS (DoT) / DNS-over-HTTPS (DoH)

Forwarding to DoT Upstream

server:
    # Enable TLS for outgoing queries
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt

forward-zone:
    name: "."
    forward-tls-upstream: yes
    # Cloudflare
    forward-addr: 1.1.1.1@853#cloudflare-dns.com
    forward-addr: 1.0.0.1@853#cloudflare-dns.com
    # Google
    forward-addr: 8.8.8.8@853#dns.google
    forward-addr: 8.8.4.4@853#dns.google

Serving DoT (Incoming)

server:
    interface: 0.0.0.0@853
    tls-service-key: /etc/unbound/server.key
    tls-service-pem: /etc/unbound/server.pem
    tls-port: 853

Serving DoH (Incoming)

server:
    interface: 0.0.0.0@443
    tls-service-key: /etc/unbound/server.key
    tls-service-pem: /etc/unbound/server.pem
    https-port: 443

Local Zones and DNS Filtering

Local DNS Records

server:
    # Internal domain
    local-zone: "home.local." static
    local-data: "router.home.local. IN A 192.168.1.1"
    local-data: "nas.home.local. IN A 192.168.1.10"
    local-data: "server.home.local. IN A 192.168.1.20"
    local-data-ptr: "192.168.1.1 router.home.local."
    local-data-ptr: "192.168.1.10 nas.home.local."
    local-data-ptr: "192.168.1.20 server.home.local."

    # Block ad domains
    local-zone: "ads.example.com." always_refuse
    local-zone: "tracking.example.com." always_nxdomain

    # Redirect domain
    local-zone: "override.example.com." redirect
    local-data: "override.example.com. A 10.0.0.1"

Response Policy Zone (RPZ)

rpz:
    name: "rpz.block"
    zonefile: /etc/unbound/rpz.block.zone
    rpz-action-override: nxdomain
    rpz-log: yes
    rpz-log-name: "rpz-block"
; /etc/unbound/rpz.block.zone
$TTL 300
@ SOA localhost. root.localhost. 1 3600 900 86400 300
@ NS localhost.
; Block domains
malware.example.com CNAME .
badsite.example.com CNAME .
; Block all subdomains
*.malvertising.example.com CNAME .

Split DNS / Conditional Forwarding

# Forward internal domain to corporate DNS
forward-zone:
    name: "corp.example.com"
    forward-addr: 10.0.0.53
    forward-addr: 10.0.0.54

# Forward reverse DNS for internal ranges
forward-zone:
    name: "10.in-addr.arpa."
    forward-addr: 10.0.0.53

# Stub zone for a specific domain
stub-zone:
    name: "internal.example.com"
    stub-addr: 10.0.0.53

# Forward everything else
forward-zone:
    name: "."
    forward-addr: 1.1.1.1
    forward-addr: 8.8.8.8

Advanced Usage

DNSTAP Logging

dnstap:
    dnstap-enable: yes
    dnstap-socket-path: /var/run/unbound/dnstap.sock
    dnstap-send-identity: yes
    dnstap-send-version: yes
    dnstap-log-resolver-query-messages: yes
    dnstap-log-resolver-response-messages: yes
    dnstap-log-client-query-messages: yes
    dnstap-log-client-response-messages: yes

Python Module

server:
    module-config: "python validator iterator"

python:
    python-script: /etc/unbound/custom_module.py

Redis Cache Backend

server:
    module-config: "cachedb validator iterator"

cachedb:
    backend: "redis"
    redis-server-host: 127.0.0.1
    redis-server-port: 6379
    redis-timeout: 100
    redis-expire-records: yes

Monitoring

# Show all statistics
sudo unbound-control stats_noreset

# Key metrics
sudo unbound-control stats_noreset | grep -E "total\.(num|requestlist)"

# Check DNSSEC validation
dig @127.0.0.1 dnssec-failed.org  # Should return SERVFAIL
dig @127.0.0.1 +dnssec example.com  # Should show AD flag

# Test resolution
dig @127.0.0.1 example.com A
dig @127.0.0.1 example.com AAAA +short

Troubleshooting

IssueSolution
unbound-control connection refusedRun unbound-control-setup to generate keys; enable remote-control
DNSSEC validation failuresUpdate root anchor: unbound-anchor -a /var/lib/unbound/root.key
Slow first queriesEnable prefetch: yes and serve-expired: yes; increase cache sizes
could not bind to port 53Stop systemd-resolved: sudo systemctl disable --now systemd-resolved
Access denied for clientsCheck access-control entries match client subnet
Forward zone not resolvingVerify upstream DNS is reachable; check TLS settings if using DoT
High memory usageReduce msg-cache-size and rrset-cache-size