Using pi-hole as your external-dns provider in Kubernetes
Learn how to enhance your Kubernetes home lab by using external-dns with Pi-Hole to map DNS records to your ingress IP, blocking ads and improving DNS
environment assumptions
A locally-running Kubernetes cluster
Pi-Hole on the network configured as the primary DNS
what is external-dns?
When you build an ingress (which is essentially a layer-7 host and path-based load-balancer) in Kubernetes to bring web traffic to a cluster, you specify a host
name. This creates a load-balancer entry in whatever load balancer you're using. If you're in public cloud, Kubernetes will call cloud load balancer APIs. In my home lab setup, I'm using metallb
for this purpose. The ingress below listens for requests to openweb-ui.lan
and sends them to the http
service (a web UI) called openweb-ui
.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: open-webui
namespace: openweb-ui
spec:
ingressClassName: nginx
rules:
- host: openweb-ui.lan
http:
paths:
- backend:
service:
name: open-webui
port:
name: http
path: /
pathType: Prefixyaml
It reaches out to metallb
to assign an IP address from a reserved pool. The address below, 192.168.126.50
was handed out by metallb
.
Now that that's in place, devices on my network need to know that this hostname resolves to the ingress ip
. That's where external-dns
comes in. Typically, external-dns
is leveraged to write DNS records to providers like Cloudflare, Route53, etc. However, it also supports writing DNS records to your locally-installed Pi-Hole
!
what is pi-hole?
It's no secret that tech workers are amongst the biggest users of ad-blocking software, and one of the major players in this space is the venerable Pi-Hole.
If you don't know what Pi-Hole is, it's a piece of software originally designed to run on a Raspberry Pi (although it runs anywhere now, even Docker and Kubernetes) that blocks ads at the DNS level. You configure the Pi-Hole to be your DNS provider. When an app or website calls out to a domain associated with the configured block list(s), Pi-Hole simply responds that the advertising domain is unresolvable, and content from the advertising domain is not shown. All the while, the useful, good, content you were looking for is displayed!
Notice in the above image that analytics.js
from google-analytics.com
isn't loading, thanks to Pi-Hole!
However, the feature of Pi-Hole I'll be discussing in this article is Local DNS
. We'll be using it to map a DNS record to the ingress IP on a homelab Kubernetes cluster.
installing external-dns
The Helm chart is the best way to install external-dns.
required information
Collect the following:
The IP address of your
Pi-Hole
instance (referenced aspiholeipaddress
)The
admin
credentials for yourPi-Hole
instance (referenced aspiholeadminpassword
)
add and update repo
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm update/
create a secret for pi-hole
In this step, we'll create a Kubernetes Secret
for authenticating to the Pi-Hole
.
kubectl create secret generic pihole-password \
--namespace external-dns \
--from-literal EXTERNAL_DNS_PIHOLE_PASSWORD={{piholeadminpassword}}
create values.yaml
Next, create a values.yaml
to pass configuration values to the Helm chart.
# https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#values
provider:
name: pihole
# https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/pihole.md#arguments
env: # configured your pi-hole password and ip address
- name: EXTERNAL_DNS_PIHOLE_PASSWORD
valueFrom:
secretKeyRef:
name: pihole-password
key: EXTERNAL_DNS_PIHOLE_PASSWORD
- name: EXTERNAL_DNS_PIHOLE_SERVER
# make sure NOT to put a trailing slash, as external-dns adds its own
value: http://{{piholeipaddress}}
install
helm upgrade --install external-dns external-dns/external-dns \
--namespace external-dns \
--create-namespace \
--values values.yaml
make sure it's working
If you check the deployment logs for external-dns
, you'll see that external-dns
has been hard at work creating a DNS record for your ingress
.
kubectl logs --namespace external-dns deployments/external-dns external-dns
time="2024-05-01T20:14:10Z" level=info msg="add openweb-ui.lan IN A -> 192.168.126.50"
time="2024-05-01T20:14:10Z" level=warning msg="Skipping unsupported endpoint openweb-ui.lan TXT \"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/openweb-ui/open-webui\""
time="2024-05-01T20:14:10Z" level=warning msg="Skipping unsupported endpoint a-openweb-ui.lan TXT \"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/openweb-ui/open-webui\""
time="2024-05-01T20:15:10Z" level=info msg="All records are already up to date"
That's it!
If you have trouble, again make sure you take out any trailing slashes on the EXTERNAL_DNS_PIHOLE_SERVER
environment variable. And also make sure the IP is correct! Don't ask me how I spent like 20 minutes wondering why external-dns
wouldn't connect.