Using pi-hole as your external-dns provider in Kubernetes

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!

image showing a list of network resources from Safari Web Inspector, showing an advertising domain not loaded.

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 as piholeipaddress)

  • The admin credentials for your Pi-Hole instance (referenced as piholeadminpassword)

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.