External DNS

De DNS records die aangemaakt dienen te worden in mijn DNS server (Pi-Hole) wil ik automatisch laten doen zodra er een Ingress aangemaakt wordt voor de FQDN van de service. Hiervoor is External DNS die ook A-records kan aanmaken in de Local DNS van Pi-Hole.

ExternalDNS

Het installeren van External DNS is een kwestie van de kubectl commando’s ingeven, of zorg ervoor dat het automatisch geïnstalleerd met behulp van ArgoCD of Fleet of wat voor CI/CD tool dan ook.

Installatie

Maak eerst een namespace:

kubectl create namespace externaldns

En dan een secret waarin het wachtwoord van de Pi-Hole API staat:

kubectl -n externaldns create secret generic pihole-password --from-literal EXTERNAL_DNS_PIHOLE_PASSWORD=[het wachtwoord]

en dan kan een service-account aangemaakt worden, een clusterrole en clusterrolebinding in de externaldns namespace voor het service-account en uiteindelijk de deployment zelf.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: externaldns
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.13.5
        envFrom:
        - secretRef:
          name: pihole-password
        args:
        - --source=service
        - --source=ingress
        - --registry=noop      ## omdat pi-hole geen TXT records kan aanmaken
        - --policy=upsert-only ## om te voorkomen dat andere DNS records verwijderd worden
        - --provider=pihole
        - --pihole-server=http://[adres van jouw Pi-Hole server]
      securityContext:
        fsGroup: 65534

Kwestie van het manifest apply’en met kubectl en external DNS is geïnstalleerd.

External-DNS naar Pi-Hole local-DNS records kan helaas niet voorzien worden van TXT-records, vandaar de optie ‘noop‘. Hierdoor kan External-DNS niet bepalen welke records in Pi-Hole door External-DNS zijn aangemaakt.

Indien in Pi-Hole ook DNS records worden gebruikt die niet met external-dns zijn aangemaakt, dan is de optie ‘upsert-only‘ van toepassing om te voorkomen dat die records worden verwijderd door External-DNS. Het nadeel is dat records wel aangemaakt met External-DNS in Pi-Hole handmatig verwijderd dienen te worden als de service (of Ingress) in Kubernetes niet meer gebruikt wordt. (omdat er dus geen TXT-record aan hangt)

Services

Om voor services een DNS record aan te laten maken, is het noodzakelijk om in de service een annotation te zetten, bijvoorbeeld:

kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx-speeltuin.digitalinfo.nl."

eventueel voorzien van een TimeToLive waarde:

kubectl annotate service nginx "external-dns.alpha.kubernetes.io/ttl=10"

De DNS records voor een ClusterIP type service is:

kubectl annotate service nginx "external-dns.alpha.kubernetes.io/internal-hostname=nginx-speeltuin.internal.digitalinfo.nl."

Ingress

Voor het aanmaken van een DNS record aan de hand van een Ingress hostname is geen annotation nodig. De DNS records voor een Ingress worden door external-dns aangemaakt zodra de Ingress wordt aangemaakt.