AKS: publish services with ExternalDNS
ExternalDNS watches your
Kubernetes services and ingresses and publishes DNS records for them. Hayami DTM
speaks the standard wire protocols ExternalDNS already supports: RFC 2136
dynamic updates for writes, TSIG for authentication, and AXFR zone
transfers for reads. Point ExternalDNS’s rfc2136 provider at a DTM node and
your AKS services get names in a DTM zone automatically, with no controller or
agent to install on the DTM side.
Throughout this page the fictional zone is aks.internal.example., the DTM
nodes are 10.0.0.4 and 10.0.0.5, and the AKS cluster’s egress IP is
203.0.113.7. For the same mechanics outside Kubernetes (plain hosts, DHCP
servers, and keeping zones clean with scavenging), see
dynamic DNS registration.
Set up the DTM side
Section titled “Set up the DTM side”Four admin-API steps, in order: create the zone, enable dynamic updates, create a TSIG key, and enable AXFR pinned to the cluster’s egress IP.
1. Create the zone
Section titled “1. Create the zone”The target zone must exist and be Active before ExternalDNS can write to it:
curl -X POST https://<dtm>/api/v1/zones \ -H "Authorization: Bearer <token>" \ -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{ "name": "aks.internal.example." }'Note the zone id in the response; you need it for the TSIG key binding below.
2. Enable dynamic updates, requiring TSIG
Section titled “2. Enable dynamic updates, requiring TSIG”curl -X PUT https://<dtm>/api/v1/config/dynamic-dns \ -H "Authorization: Bearer <token>" \ -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "require_tsig": true }'3. Create a TSIG key for ExternalDNS
Section titled “3. Create a TSIG key for ExternalDNS”Generate a strong secret and bind the key to the zone:
SECRET=$(openssl rand -base64 32)
curl -X POST https://<dtm>/api/v1/tsig-keys \ -H "Authorization: Bearer <token>" \ -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{ "name": "externaldns", "algorithm": "hmac-sha256", "secret": "'"$SECRET"'", "zone_ids": ["<zone-id>"] }'- Algorithms:
hmac-sha256,hmac-sha384, andhmac-sha512are accepted;hmac-md5andhmac-sha1are refused. - Secret strength: DTM enforces a minimum 128-bit secret; 32 random bytes (as above) gives you a comfortable margin.
- Names: key names match with or without a trailing dot, so
externaldnsandexternaldns.refer to the same key. Creating a key with a duplicate name returns409 Conflict.
4. Enable AXFR, pinned to the cluster egress
Section titled “4. Enable AXFR, pinned to the cluster egress”ExternalDNS needs to read the zone (via AXFR) as well as write to it, so it can see what already exists and delete records for services you remove:
curl -X PUT https://<dtm>/api/v1/config/axfr \ -H "Authorization: Bearer <token>" \ -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "allowed_cidrs": ["203.0.113.7/32"], "require_tsig": true }'Set up the ExternalDNS side
Section titled “Set up the ExternalDNS side”Deploy ExternalDNS in the cluster with the rfc2136 provider aimed at a DTM
node. With the ExternalDNS Helm chart, the values look like this:
provider: name: rfc2136
extraArgs: - --rfc2136-host=10.0.0.4 - --rfc2136-port=53 - --rfc2136-zone=aks.internal.example - --rfc2136-tsig-keyname=externaldns - --rfc2136-tsig-secret-alg=hmac-sha256 - --rfc2136-tsig-secret=<the TSIG secret from step 3> - --rfc2136-tsig-axfr - --domain-filter=aks.internal.example - --policy=sync - --registry=txt - --txt-owner-id=aks-eastus-1The settings that matter:
--rfc2136-host/--rfc2136-port: a DTM node IP, port 53.--rfc2136-tsig-keyname: must equal the key name you created in DTM (externaldns), with the matching algorithm and secret.--rfc2136-tsig-axfr: enables signed AXFR so ExternalDNS can read the zone’s current contents. Without it, ExternalDNS is upsert-only: it can create and update records but never sees what exists, so it never cleans up.--policy=sync: allows deletes. The default,upsert-only, never deletes anything, even with AXFR working.--registry=txtwith a unique--txt-owner-idper cluster: ExternalDNS writes TXT ownership records alongside your service records so it only ever touches records it created. Give every cluster writing into DTM its own owner ID.- One DNS zone per ExternalDNS instance: run one ExternalDNS deployment per zone it manages.
Once running, annotate a service to publish it:
apiVersion: v1kind: Servicemetadata: name: web annotations: external-dns.alpha.kubernetes.io/hostname: web-eastus.svc.aks.internal.examplespec: type: LoadBalancer # ...ExternalDNS creates web-eastus.svc.aks.internal.example in the DTM zone with
the service’s load-balancer IP, and removes it if you delete the service.
Resolve the names from pods
Section titled “Resolve the names from pods”Pods resolve through AKS’s CoreDNS, which does not know about your DTM zone by
default. Add a stub domain that forwards the zone to the DTM nodes using the
coredns-custom ConfigMap AKS provides:
apiVersion: v1kind: ConfigMapmetadata: name: coredns-custom namespace: kube-systemdata: aks-internal.server: | aks.internal.example:53 { errors cache 5 forward . 10.0.0.4 10.0.0.5 }Apply it and restart CoreDNS (kubectl -n kube-system rollout restart deployment coredns), then verify from a pod:
kubectl run -it --rm dnstest --image=busybox --restart=Never -- \ nslookup web-eastus.svc.aks.internal.exampleGotchas
Section titled “Gotchas”- The AXFR source is the cluster’s egress IP, not a node IP. Traffic
leaving the cluster is NATed through your outbound path (NAT gateway or
load balancer outbound rules), so
allowed_cidrsmust contain that egress address. If AXFR fails while updates succeed, check which address the transfer actually arrives from. - Deletes need both AXFR and
--policy=sync. Missing either one silently degrades ExternalDNS to upsert-only, and records for deleted services linger in the zone. - TXT registry records appear next to your A records. For each published name you will see one or more TXT records recording ownership. This is expected; do not delete them, or ExternalDNS loses track of what it owns.
Need a hand? Email [email protected].