|
| 1 | +--- |
| 2 | +title: External DNS with pihole |
| 3 | +published: true |
| 4 | +excerpt_separator: <!--more--> |
| 5 | +--- |
| 6 | + |
| 7 | +The [external-dns operator for kubernetes](https://kubernetes-sigs.github.io/external-dns/) is simple enough that running it at home is as easy as **π**. In this post I will show how to set up external-dns with pihole as the DNS provider, and cover some of the quirks when you use [Istio](https://istio.io/) instead of a traditional ingress. |
| 8 | + |
| 9 | +<!--more--> |
| 10 | + |
| 11 | +## Background |
| 12 | + |
| 13 | +In order to route traffic to an ingress you will need DNS records. In my home lab I run Istio ingress gateways, and previously I had to manually create DNS records for each hostname in my [DNS server which is pihole](https://pi-hole.net/). I have used external-dns in the past when I've helped run kubernetes on AKS or have routed traffic through my firewall to a k3s cluster where I have then used public DNS with Azure for AKS and Cloudflare for k3s respecively. When I found out there was a configuration for pihole I had to try it out with my [Talos Linux](https://www.talos.dev/) cluster. |
| 14 | + |
| 15 | +## Pihole settings |
| 16 | + |
| 17 | +In order to be able to automate the DNS configuration of pihole you need only the hostname and password for pihole. |
| 18 | + |
| 19 | +I run with sealed secrets so my creation of the the password needed to go through the sealed secrets process before ending up in git to be then deployed by [ArgoCD](https://argo-cd.readthedocs.io). |
| 20 | + |
| 21 | +``` powershell |
| 22 | +$somepassword = Read-Host -Prompt "Enter password" -MaskInput |
| 23 | +kubectl create secret generic -n external-dns pihole-password ` |
| 24 | + --from-literal EXTERNAL_DNS_PIHOLE_PASSWORD=$somepassword -o yaml --dry-run=client ` |
| 25 | + | kubeseal -o yaml > gitops\apps\rh-appset\mgmt\default\external-dns\external-dns-pihole\dns-pihole-ss.yaml |
| 26 | +Remove-Variable somepassword |
| 27 | +``` |
| 28 | + |
| 29 | +This renders the new sealed secrets in the folder for my external-dns-pihole application which is destined for the external-dns namespace. |
| 30 | + |
| 31 | +The rest of the configuration can be found in the [documentation for external-dns](https://kubernetes-sigs.github.io/external-dns/latest/docs/tutorials/pihole/). |
| 32 | + |
| 33 | +I went ahead and just put each resource in a separate file, and bundled it all with kustomize. I replaced some of the names to include my `-pihole` suffix in case I want to deploy another external-dns-cloudflare app for public access to this cluster in the future. |
| 34 | + |
| 35 | +``` yaml |
| 36 | +# kustomize.yaml |
| 37 | +apiVersion: kustomize.config.k8s.io/v1beta1 |
| 38 | +kind: Kustomization |
| 39 | +resources: |
| 40 | + - dns-pihole-crb.yaml |
| 41 | + - dns-pihole-dp.yaml |
| 42 | + - dns-pihole-sa.yaml |
| 43 | + - dns-pihole-cr.yaml |
| 44 | + - dns-pihole-ss.yaml |
| 45 | +``` |
| 46 | +
|
| 47 | +Lastly I made sure that I included istio-gateways for discovering the hostnames I wanted to register. |
| 48 | +
|
| 49 | +{% highlight yaml mark_lines="14" %} |
| 50 | +# dns-pihole-dp.yaml |
| 51 | +apiVersion: apps/v1 |
| 52 | +kind: Deployment |
| 53 | +metadata: |
| 54 | + name: external-dns-pihole |
| 55 | +spec: |
| 56 | + # Removed for brevity |
| 57 | + spec: |
| 58 | + serviceAccountName: external-dns-pihole |
| 59 | + containers: |
| 60 | + - name: external-dns-pihole |
| 61 | + image: registry.k8s.io/external-dns/external-dns:v0.15.1 |
| 62 | + args: |
| 63 | + - --source=istio-gateway # ingress is also possible |
| 64 | + - --source=service # for additional loadbalancer services in the future |
| 65 | + - --domain-filter=mgmt.dsoderlund.consulting # home office domain for talos mgmt cluster |
| 66 | + - --provider=pihole |
| 67 | + - --policy=upsert-only # to avoid nuking existing records |
| 68 | + - --pihole-server=https://pihole.office.dsoderlund.consulting # hostname of pihole |
| 69 | + - --registry=noop |
| 70 | + # Removed for brevity |
| 71 | +{% endhighlight %} |
| 72 | +
|
| 73 | +Upon sync through argocd the app appears with the five different resources that I specified. |
| 74 | +
|
| 75 | + |
| 76 | +
|
| 77 | +## Issues I ran into |
| 78 | +
|
| 79 | +### External-dns cluster role doesn't cover istio resources |
| 80 | +
|
| 81 | +I got some fun errors like |
| 82 | +
|
| 83 | +> istio external-dns pihole "failed to sync *v1.Service: context deadline exceeded" |
| 84 | +
|
| 85 | +This is simply due to the pod not being able to query kubernetes for all of the resources it wants. Inspecting the default cr (ClusterRole) it became clear that I needed to add the istio resources. |
| 86 | +
|
| 87 | +{% highlight yaml mark_lines="16 17 18" %} |
| 88 | +# dns-pihole-cr.yaml |
| 89 | +apiVersion: rbac.authorization.k8s.io/v1 |
| 90 | +kind: ClusterRole |
| 91 | +metadata: |
| 92 | + name: external-dns-pihole |
| 93 | +rules: |
| 94 | + - apiGroups: [""] |
| 95 | + resources: ["services","endpoints","pods"] |
| 96 | + verbs: ["get","watch","list"] |
| 97 | + - apiGroups: ["extensions","networking.k8s.io"] |
| 98 | + resources: ["ingresses"] |
| 99 | + verbs: ["get","watch","list"] |
| 100 | + - apiGroups: [""] |
| 101 | + resources: ["nodes"] |
| 102 | + verbs: ["list", "watch"] |
| 103 | + - apiGroups: ["networking.istio.io"] |
| 104 | + resources: ["gateways", "virtualservices"] |
| 105 | + verbs: ["get","watch","list"] |
| 106 | +{% endhighlight %} |
| 107 | +
|
| 108 | +### Wildcard CNAMEs are not supported |
| 109 | +
|
| 110 | +I had previously set up my gateway to pick up traffic from any matching DNS name on my home office network. Pihole doesn't support wildcard DNS records which is made very clear but the next error message I got. |
| 111 | +
|
| 112 | +> time="2025-01-16T19:53:40Z" level=info msg="add *.mgmt.dsoderlund.consulting IN A -> 192.168.0.32" |
| 113 | +
|
| 114 | +> time="2025-01-16T19:53:40Z" level=error msg="Failed to do run once: soft error\nUNSUPPORTED: Pihole DNS names cannot return wildcard" |
| 115 | +
|
| 116 | +The IP address *192.168.0.32* is the one [metallb](https://metallb.io/) assigned to my istio ingress service, which is how external-dns knows what it is supposed to tell pihole that the DNS record is for. |
| 117 | +
|
| 118 | +I simply updated my hostname list in the gateway with the names I needed, and after syncing with argocd things started happening in the pihole. |
| 119 | +
|
| 120 | + |
| 121 | +
|
| 122 | +One that was in sync, hostnames started to appear in the pihole UI and DNS records started to work against the cluster again. |
| 123 | +
|
| 124 | + |
| 125 | +
|
| 126 | + |
| 127 | +
|
| 128 | +## Conclusion |
| 129 | +
|
| 130 | +My cluster is now equiped to automatically make sure dns names that I use on my istio gateways get mapped to the istio ingress service. This can be further used if I create new services that I don't want Istio for. |
0 commit comments