For those who do not know, Domain Name System is the internet component that translate human friendly domain names (like blog.piasecki.it) to IP addresses. The system is hierarchical, and before getting and IP, you need to ask a couple servers first.
- Recursive DNS resolver – usually handled by your ISP. Alternatively can be provided by third-party services like Google or Cloudflare.
- Root server lookup – the answer will be authoritative server for top-level domain (TLD)
- TLD query – this will return authoritative server for specific domain and the subdomains
Unbound is a recursive DNS resolver, so you can use it without relying on 3rd party resolvers.
The main consideration behind using Unbound is privacy – you will not use Google or ISP as a proxy to fetch the IP.
Another Unbound usecase is bypassing content-blocking mechanisms implemented by the ISP. The provider can block the traffic in two ways:
- DNS blocking – it is very easy to set up and maintain, but not quite effective
- IP blocking – implementing is challenging. There is a risk of overblocking, for example, when dealing with Content Delivery Networks. It requires constant updates, because sites can change the IP addresses. And it can be bypassed by using the VPN.
Because of that, domain name blocking is most popular content blocking attempt. Unbound can be used to bypass domain blocking by the ISP, by executing those queries directly.
Other unbound features are:
- Caching – frequently used domains will be fetched faster. It takes about 10-12ms to ping google/cloudflare, but only 3ms for my k3s host
- Prefetching – when DNS entry is about to expire, Unbound will automatically refresh it, making the subsequent query even faster
- DNSSEC – guarantee authenticity of DNS record, using public key infrastructure. It also confirms that DNS responses have not modified in transit. I have not configured it yet – I will cover it later.
Without further ado, here is configuration I used:
apiVersion: v1
kind: ConfigMap
metadata:
name: unbound-main-conf
data:
unbound.conf: |
server:
verbosity: 0
interface: 0.0.0.0
port: 53
do-ip4: yes
do-udp: yes
do-tcp: yes
cache-max-ttl: 86400 # Maximum time (in seconds) to cache a record
cache-min-ttl: 0 # Minimum time (in seconds) to cache a record
msg-cache-size: 50m # Message cache size
rrset-cache-size: 100m # Resource record cache size
infra-cache-numhosts: 10000 # Number of hosts in the infrastructure cache
prefetch: yes # Enable prefetching of popular cache entries
prefetch-key: yes # Also prefetch DNSKEY and other key information
access-control: 0.0.0.0/0 allow # Allow all IPv4 clients
access-control: ::0/0 allow # Allow all IPv6 clients
do-ip6: no
prefer-ip6: no
#root-hints: "/var/lib/unbound/root.hints"
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: no
edns-buffer-size: 1232
prefetch: yes
num-threads: 1
so-rcvbuf: 1m
private-address: 192.168.1.0/24
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unbound-a-records-conf
data:
a-records.conf: |
server:
# Your internal network name and addresses go here
private-domain: "your-awesome-domain.local"
domain-insecure: "your-awesome-domain.local"
local-zone: "your-awesome-domain.local" transparent
local-data: "k8s.your-awesome-domain.local IN A 172.30.0.1"
#local-data-ptr: "172.30.0.1 your-awesome-domain.local"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unbound-forward-records-conf
data:
forward-records.conf: |
forward-zone:
# Forward all queries (except those in cache and local zone) to
# upstream recursive servers
name: "."
# Queries to this forward zone use TLS
forward-tls-upstream: yes
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unbound-srv-records-conf
data:
srv-records.conf: |
# SRV records
# _service._proto.name. | TTL | class | SRV | priority | weight | port | target.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dns
spec:
replicas: 1
selector:
matchLabels:
app: unbound-dns
strategy:
type: Recreate
template:
metadata:
labels:
app: unbound-dns
spec:
nodeSelector:
kubernetes.io/arch: amd64
containers:
- image: mvance/unbound
imagePullPolicy: IfNotPresent
name: dns-unbound
ports:
- containerPort: 53
protocol: UDP
resources: {}
volumeMounts:
- name: unbound-main-conf-volume
mountPath: /opt/unbound/etc/unbound/unbound.conf
subPath: unbound.conf
- name: unbound-a-conf-volume
mountPath: /opt/unbound/etc/unbound/a-records.conf
subPath: a-records.conf
- name: unbound-forward-conf-volume
mountPath: /opt/unbound/etc/unbound/forward-records.conf
subPath: forward-records.conf
- name: unbound-srv-conf-volume
mountPath: /opt/unbound/etc/unbound/srv-records.conf
subPath: srv-records.conf
restartPolicy: Always
volumes:
- name: unbound-main-conf-volume
configMap:
name: unbound-main-conf
- name: unbound-a-conf-volume
configMap:
name: unbound-a-records-conf
- name: unbound-forward-conf-volume
configMap:
name: unbound-forward-records-conf
- name: unbound-srv-conf-volume
configMap:
name: unbound-srv-records-conf
All that’s left, is to reconfigure Pi-hole: remove external upstream DNS servers, and add Unbound.
And since we are talking about security and privacy in the context of DNS, here are handy filters for Pi-hole:
Leave a Reply