Intro
dnsmasq, being simple and small, can be a pain to configure. Plus, it doesnt have a GUI (unless you count openwrt).
Anyhow, its time for something better, more easily managed.
Requirements are:
- adblocking
- authoritative dns for my custom domain and lan
- GUI
- IPv6
The options are:
- PiHole (php)
- Technitium DNS (.net)
- AdGuard DNS (go)
- PowerDNS (c++)
- PowerDNS-Admin for a community maintained gui
In first attempt i opted for technitium via docker.
Technitium DNS
Technitium satisfies everything i wanted, but there is one annoyance: GUI looks a bit dated. Since i wont be looking at it often, it doesnt really matter.
The server i have is a fedora server (currently 40) running podman (currently 5.2.3).
Since the service is not available outside the lan, podman is running in rootful mode so i dont have to mess around with ports.
/etc/containers/containers.conf
File /etc/containers/containers.conf
controls a bunch of stuff regarding podman. Good docu on what can be set there is at https://man.archlinux.org/man/containers.conf.5.en.
Most of the problems i had when trying this out was from these two config options which must be there.
First, container wouldnt start due to some annoying error with iptables. Seems it was failing due to some kernel regression, so i had to force it to use nftables (which anyway should have been a default since at least 2020).
Second, when running in rootful mode, netavark
and its dns friend aardavark
wouldnt let traefik open port 53 to forward it to the technitium dns. It seems that i wasnt only person wanting to have dns in a container and proxying requests via traefik, but solution wasnt really published anywhere. People usually just dropped it and went away.
Solution was to move the default port from 53 to something else. When done so, port 53 would be available to traefik.
[network]
firewall_driver = "nftables"
dns_bind_port = 5553
Third thing was systemd-resolved, which was starting on port 53. Disabling it wouldnt really be good, so there is a config option needed to be changed in /etc/systemd/resolved.conf
.
Editing /etc/systemd/resolved.conf
made it work:
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com 2606:4700:4700::1111#cloudflare-dns.com 2606:4700:4700::1001#cloudflare-dns.com
DNSStubListener=no
/etc/containers/systemd/technitium.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
bind-mount-options: /var/run/podman/podman.sock:z
creationTimestamp: "2024-02-18T16:02:00Z"
labels:
app: technitium
traefik.enable: true
traefik.http.routers.technitium.entrypoints: websecure
traefik.http.routers.technitium.rule: "Host(`technitium.flippityflop.com`)"
traefik.http.routers.technitium.service: technitium
traefik.http.routers.technitium.tls: true
traefik.http.routers.technitium.tls.certresolver: lets-encr-porkbun
traefik.http.services.technitium.loadbalancer.server.port: 5380
traefik.http.routers.dns-over-https.entrypoints: websecure
traefik.http.routers.dns-over-https.rule: "Host(`dns-over-https.flippityflop.com`)"
traefik.http.routers.dns-over-https.service: dns-over-https
traefik.http.routers.dns-over-https.tls: true
traefik.http.routers.dns-over-https.tls.certresolver: lets-encr-porkbun
traefik.http.services.dns-over-https.loadbalancer.server.port: 8053
traefik.tcp.routers.dns-over-tls.entrypoints: dns-over-tls
traefik.tcp.routers.dns-over-tls.service: dns-over-tls
traefik.tcp.services.dns-over-tls.loadbalancer.server.port: 853
traefik.tcp.routers.dns-over-tls.rule: "HostSNI(`*`)"
traefik.tcp.routers.dns-tcp.entrypoints: dns-tcp
traefik.tcp.routers.dns-tcp.service: dns-tcp
traefik.tcp.services.dns-tcp.loadbalancer.server.port: 53
traefik.tcp.routers.dns-tcp.rule: "HostSNI(`*`)"
traefik.udp.routers.dns-udp.entrypoints: dns-udp
traefik.udp.routers.dns-udp.service: dns-udp
traefik.udp.services.dns-udp.loadbalancer.server.port: 53
name: technitium-pod
spec:
securityContext:
seLinuxOptions:
type: spc_t
containers:
- image: docker.io/technitium/dns-server:latest
name: technitium
args:
env:
- name: DNS_SERVER_DOMAIN
value: flippityflop.com
- name: DNS_SERVER_ADMIN_PASSWORD
value: x
- name: DNS_SERVER_FORWARDERS
value: 1.1.1.1
volumeMounts:
- mountPath: /etc/dns/:z
name: technitium-etc
readOnly: false
restartPolicy: Always
volumes:
- hostPath:
path: /data/technitium/
type: Directory
name: technitium-etc
/etc/containers/systemd/technitium.kube
[Unit]
Description=Technitium DNS Server
After=network.target
[Kube]
Yaml=technitium.yaml
Network=systemd-traefik
[Install]
WantedBy=multi-user.target default.target
Traefik
Second thing that took a while to figure out is that i forgot to export ports on traefik. Port 53 should be exported via ports in the kube spec. Hving an entry point is not enough, so update your yaml configuration.
This is a reminder to NOT FORGET IT AGAIN.
/etc/containers/systemd/traefik.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
bind-mount-options: /var/run/podman/podman.sock:z
creationTimestamp: "2024-02-18T16:02:00Z"
labels:
app: traefik
traefik.enable: true
traefik.http.routers.traefik.entrypoints: websecure
traefik.http.routers.traefik.rule: "Host(`traefik.flippityflop.com`)"
traefik.http.routers.traefik.service: api@internal
traefik.http.routers.traefik.tls: true
traefik.http.routers.traefik.tls.certresolver: lets-encr-porkbun
name: traefik-pod
spec:
securityContext:
seLinuxOptions:
type: spc_t
containers:
- image: docker.io/traefik:latest
name: traefik
args:
ports:
- containerPort: 443
hostPort: 443
protocol: TCP
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 53
hostPort: 53
protocol: UDP
- containerPort: 53
hostPort: 53
protocol: TCP
env:
- name: PORKBUN_API_KEY
value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: PORKBUN_SECRET_API_KEY
value: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
volumeMounts:
- mountPath: /etc/traefik/traefik.yaml
name: traefik-static
readOnly: true
- mountPath: /etc/traefik/acme.json:z
name: acme
readOnly: false
- mountPath: /etc/traefik/porkbun.env
name: porkbun-env
readOnly: true
- mountPath: /var/run/docker.sock:z
name: podman-socket
readOnly: false
restartPolicy: Always
volumes:
- hostPath:
path: /data/traefik/config/traefik-static.yaml
type: File
name: traefik-static
- hostPath:
path: /data/traefik/config/traefik-dynamic.yaml
type: File
name: traefik-dynamic
- hostPath:
path: /data/traefik/config/acme.json
type: File
name: acme
- hostPath:
path: /data/traefik/config/porkbun.env
type: File
name: porkbun-env
- hostPath:
path: /var/run/podman/podman.sock
type: File
name: podman-socket
/etc/containers/systemd/traefik.kube
[Unit]
Description=The sleep container
After=network.target
[Kube]
Yaml=traefik.yaml
Network=systemd-traefik
[Install]
WantedBy=multi-user.target default.target
/data/traefik/config/traefik-static.yaml
## STATIC CONFIGURATION
log:
level: DEBUG
api:
insecure: false
dashboard: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
dns-over-tls:
address: ":853"
dns-over-quic:
address: ":853/udp"
dns-tcp:
address: ":53"
dns-udp:
address: ":53/udp"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
watch: true
certificatesResolvers:
lets-encr-porkbun:
acme:
email: you@magnificent.com
storage: /etc/traefik/acme.json
dnsChallenge:
provider: porkbun
delayBeforeCheck: 0
resolvers:
- "curitiba.ns.porkbun.com"
- "1.1.1.1:53"
/data/traefik/config/traefik-dynamic.yaml
## DYNAMIC CONFIGURATION
http:
routers:
api:
entryPoints: [ websecure ]
rule: "Host(`traefik.flippityflop.com`)"
service: "api@internal"
tls:
certResolver: lets-encr-porkbun
domains:
- main: "traefik.flippityflop.com"
Podman
Of course, after setting this up, few other commands are needed:
systemctl daemon-reload
systemctl restart traefik
systemctl restart technitium