external-dns/docs/tutorials/rfc2136.md
Michel Loiseleur 7bfc12612d
chore(release): updates kustomize & docs with v0.18.0 (#5573)
* chore(release): updates kustomize & docs with v0.18.0

Signed-off-by: Michel Loiseleur <michel.loiseleur@traefik.io>

* add endpointslices rbac

* fix version updater script

---------

Signed-off-by: Michel Loiseleur <michel.loiseleur@traefik.io>
2025-06-26 06:14:30 -07:00

534 lines
16 KiB
Markdown

# RFC2136 provider
This tutorial describes how to use the RFC2136 with either BIND or Windows DNS.
## Using with BIND
To use external-dns with BIND: generate/procure a key, configure DNS and add a
deployment of external-dns.
### Server credentials
- RFC2136 was developed for and tested with [BIND](https://www.isc.org/downloads/bind/) DNS server.
This documentation assumes that you already have a configured and working server. If you don't,
please check BIND documents or tutorials.
- If your DNS is provided for you, ask for a TSIG key authorized to update and
transfer the zone you wish to update. The key will look something like below.
Skip the next steps wrt BIND setup.
```text
key "externaldns-key" {
algorithm hmac-sha256;
secret "96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=";
};
```
- If you are your own DNS administrator create a TSIG key. Use
`tsig-keygen -a hmac-sha256 externaldns` or on older distributions
`dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST externaldns`. You will end up with
a key printed to standard out like above (or in the case of dnssec-keygen in a
file called `Kexternaldns......key`).
### BIND Configuration
If you do not administer your own DNS, skip to RFC provider configuration
- Edit your named.conf file (or appropriate included file) and add/change the
following.
- Make sure You are listening on the right interfaces. At least whatever
interface external-dns will be communicating over and the interface that
faces the internet.
- Add the key that you generated/was given to you above. Copy paste the four
lines that you got (not the same as the example key) into your file.
- Create a zone for kubernetes. If you already have a zone, skip to the next
step. (I put the zone in it's own subdirectory because named,
which shouldn't be running as root, needs to create a journal file and the
default zone directory isn't writeable by named).
```text
zone "k8s.example.org" {
type master;
file "/etc/bind/pri/k8s/k8s.zone";
};
```
- Add your key to both transfer and update. For instance with our previous
zone.
```text
zone "k8s.example.org" {
type master;
file "/etc/bind/pri/k8s/k8s.zone";
allow-transfer {
key "externaldns-key";
};
update-policy {
grant externaldns-key zonesub ANY;
};
};
```
- Create a zone file (k8s.zone):
```text
$TTL 60 ; 1 minute
k8s.example.org IN SOA k8s.example.org. root.k8s.example.org. (
16 ; serial
60 ; refresh (1 minute)
60 ; retry (1 minute)
60 ; expire (1 minute)
60 ; minimum (1 minute)
)
NS ns.k8s.example.org.
ns A 123.456.789.012
```
- Reload (or restart) named
### Using external-dns
To use external-dns add an ingress or a LoadBalancer service with a host that
is part of the domain-filter. For example both of the following would produce
A records.
```text
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: svc.example.org
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: ingress.example.org
http:
paths:
- path: /
backend:
serviceName: my-service
servicePort: 8000
```
### Custom TTL
The default DNS record TTL (Time-To-Live) is 0 seconds. You can customize this value by setting the annotation `external-dns.alpha.kubernetes.io/ttl`. e.g., modify the service manifest YAML file above:
```yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com
external-dns.alpha.kubernetes.io/ttl: 60
spec:
...
```
This will set the DNS record's TTL to 60 seconds.
A default TTL for all records can be set using the the flag with a time in seconds, minutes or hours, such as `--rfc2136-min-ttl=60s`
There are other annotation that can affect the generation of DNS records, but these are beyond the scope of this
tutorial and are covered in the main documentation.
### Generate reverse DNS records
If you want to generate reverse DNS records for your services, you have to enable the functionality using the `--rfc2136-create-ptr`
flag. You have also to add the zone to the list of zones managed by ExternalDNS via the `--rfc2136-zone` and `--domain-filter` flags.
An example of a valid configuration is the following:
```sh
--domain-filter=157.168.192.in-addr.arpa --rfc2136-zone=157.168.192.in-addr.arpa
```
PTR record tracking is managed by the A/AAAA record so you can't create PTR records for already generated A/AAAA records.
### Test with external-dns installed on local machine (optional)
You may install external-dns and test on a local machine by running:
```sh
external-dns --txt-owner-id k8s --provider rfc2136 \
--rfc2136-host=192.168.0.1 --rfc2136-port=53 \
--rfc2136-zone=k8s.example.org \
--rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8= \
--rfc2136-tsig-secret-alg=hmac-sha256 \
--rfc2136-tsig-keyname=externaldns-key \
--rfc2136-tsig-axfr \
--source ingress --once \
--domain-filter=k8s.example.org --dry-run
```
- host should be the IP of your master DNS server.
- tsig-secret should be changed to match your secret.
- tsig-keyname needs to match the keyname you used (if you changed it).
- domain-filter can be used as shown to filter the domains you wish to update.
### RFC2136 provider configuration
In order to use external-dns with your cluster you need to add a deployment
with access to your ingress and service resources. The following are two
example manifests with and without RBAC respectively.
- With RBAC:
```text
apiVersion: v1
kind: Namespace
metadata:
name: external-dns
labels:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
namespace: external-dns
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- pods
- nodes
verbs:
- get
- watch
- list
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- list
- watch
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
namespace: external-dns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: external-dns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
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.18.0
args:
- --registry=txt
- --txt-prefix=external-dns-
- --txt-owner-id=k8s
- --provider=rfc2136
- --rfc2136-host=192.168.0.1
- --rfc2136-port=53
- --rfc2136-zone=k8s.example.org
- --rfc2136-zone=k8s.your-zone.org
- --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=
- --rfc2136-tsig-secret-alg=hmac-sha256
- --rfc2136-tsig-keyname=externaldns-key
- --rfc2136-tsig-axfr
- --source=ingress
- --domain-filter=k8s.example.org
```
- Without RBAC:
```text
apiVersion: v1
kind: Namespace
metadata:
name: external-dns
labels:
name: external-dns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.18.0
args:
- --registry=txt
- --txt-prefix=external-dns-
- --txt-owner-id=k8s
- --provider=rfc2136
- --rfc2136-host=192.168.0.1
- --rfc2136-port=53
- --rfc2136-zone=k8s.example.org
- --rfc2136-zone=k8s.your-zone.org
- --rfc2136-tsig-secret=96Ah/a2g0/nLeFGK+d/0tzQcccf9hCEIy34PoXX2Qg8=
- --rfc2136-tsig-secret-alg=hmac-sha256
- --rfc2136-tsig-keyname=externaldns-key
- --rfc2136-tsig-axfr
- --source=ingress
- --domain-filter=k8s.example.org
```
## Microsoft DNS
While `external-dns` was not developed or tested against Microsoft DNS, it can be configured to work against it. YMMV.
### Secure Updates Using RFC3645 (GSS-TSIG)
#### DNS-side configuration
1. Create a DNS zone
2. Enable **secure** dynamic updates for the zone
3. Enable Zone Transfers to all servers and/or other domains
4. Create a user with permissions to create/update/delete records in that zone
If you see any error messages which indicate that `external-dns` was somehow not able to fetch
existing DNS records from your DNS server, this could mean that you forgot about step 3.
##### Kerberos Configuration
DNS with secure updates relies upon a valid Kerberos configuration running within the `external-dns` container.
At this time, you will need to create a ConfigMap for the `external-dns` container to use and mount it in your deployment.
Below is an example of a working Kerberos configuration inside a ConfigMap definition. This may be different depending on many factors in your environment:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: null
name: krb5.conf
data:
krb5.conf: |
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt
default_ccache_name = KEYRING:persistent:%{uid}
default_realm = YOUR-REALM.COM
[realms]
YOUR-REALM.COM = {
kdc = dc1.yourdomain.com
admin_server = dc1.yourdomain.com
}
[domain_realm]
yourdomain.com = YOUR-REALM.COM
.yourdomain.com = YOUR-REALM.COM
```
In most cases, the realm name will probably be the same as the domain name, so you can simply replace `YOUR-REALM.COM` with something like `YOURDOMAIN.COM`.
Once the ConfigMap is created, the container `external-dns` container needs to be told to mount that ConfigMap as a volume at the default Kerberos configuration location. The pod spec should include a similar configuration to the following:
```yaml
...
volumeMounts:
- mountPath: /etc/krb5.conf
name: kerberos-config-volume
subPath: krb5.conf
...
volumes:
- configMap:
defaultMode: 420
name: krb5.conf
name: kerberos-config-volume
...
```
##### `external-dns` configuration
You'll want to configure `external-dns` similarly to the following:
```text
...
- --provider=rfc2136
- --rfc2136-gss-tsig
- --rfc2136-host=dns-host.yourdomain.com
- --rfc2136-port=53
- --rfc2136-zone=your-zone.com
- --rfc2136-zone=your-secondary-zone.com
- --rfc2136-kerberos-username=your-domain-account
- --rfc2136-kerberos-password=your-domain-password
- --rfc2136-kerberos-realm=your-domain.com
- --rfc2136-tsig-axfr # needed to enable zone transfers, which is required for deletion of records.
...
```
As noted above, the `--rfc2136-kerberos-realm` flag is completely optional and won't be necessary in many cases.
Most likely, you will only need it if you see errors similar to this: `KRB Error: (68) KDC_ERR_WRONG_REALM Reserved for future use`.
The flag `--rfc2136-host` can be set to the host's domain name or IP address.
However, it also determines the name of the Kerberos principal which is used during authentication.
This means that Active Directory might only work if this is set to a specific domain name, possibly leading to errors like this:
`KDC_ERR_S_PRINCIPAL_UNKNOWN Server not found in Kerberos database`.
To fix this, try setting `--rfc2136-host` to the "actual" hostname of your DNS server.
### Insecure Updates
#### DNS-side configuration
1. Create a DNS zone
2. Enable insecure dynamic updates for the zone
3. Enable Zone Transfers to all servers and/or other domains
#### `external-dns` configuration
You'll want to configure `external-dns` similarly to the following:
```text
...
- --provider=rfc2136
- --rfc2136-host=192.168.0.1
- --rfc2136-port=53
- --rfc2136-zone=k8s.example.org
- --rfc2136-zone=k8s.your-zone.org
- --rfc2136-insecure
- --rfc2136-tsig-axfr # needed to enable zone transfers, which is required for deletion of records.
...
```
## DNS Over TLS (RFCs 7858 and 9103)
If your DNS server does zone transfers over TLS, you can instruct `external-dns` to connect over TLS with the following flags:
- `--rfc2136-use-tls` Will enable TLS for both zone transfers and for updates.
- `--tls-ca=<cert-file>` Is the path to a file containing certificate(s) that can be used to verify the DNS server
- `--tls-client-cert=<client-cert-file>` and
- `--tls-client-cert-key=<client-key-file>` Set the client certificate and key for mutual verification
- `--rfc2136-skip-tls-verify` Disables verification of the certificate supplied by the DNS server.
It is currently not supported to do only zone transfers over TLS, but not the updates. They are enabled and disabled together.
## Configuring RFC2136 Provider with Multiple Hosts and Load Balancing
This section describes how to configure the RFC2136 provider in ExternalDNS to support multiple DNS servers and load balancing options.
### Enhancements Overview
The RFC2136 provider now supports multiple DNS hosts and introduces load balancing options to distribute DNS update requests evenly across available DNS servers. This helps prevent a single server from becoming a bottleneck in environments with multiple DNS servers.
### Configuration Steps
1. **Allow Multiple Hosts for `--rfc2136-host`**
- Modify the `--rfc2136-host` command-line option to accept multiple hosts.
- Example: `--rfc2136-host="dns-host-1.yourdomain.com" --rfc2136-host="dns-host-2.yourdomain.com"`
2. **Introduce Load Balancing Options**
- Add a new command-line option `--rfc2136-load-balancing-strategy` to specify the load balancing strategy.
- Supported options:
- `round-robin`: Distributes DNS updates evenly across all specified hosts in a round-robin manner.
- `random`: Randomly selects a host for each DNS update.
- `disabled` (default): Uses the first host in the list as the primary, only moving to the next host if a failure occurs.
### Example Configuration
```shell
external-dns \
--provider=rfc2136 \
--rfc2136-host="dns-host-1.yourdomain.com" \
--rfc2136-host="dns-host-2.yourdomain.com" \
--rfc2136-host="dns-host-3.yourdomain.com" \
--rfc2136-load-balancing-strategy="round-robin" \
--rfc2136-port=53 \
--rfc2136-zone=example.com \
--rfc2136-tsig-secret-alg=hmac-sha256 \
--rfc2136-tsig-keyname=example-key \
--rfc2136-tsig-secret=example-secret \
--rfc2136-insecure
```
### Helm
```yaml
extraArgs:
- --rfc2136-host="dns-host-1.yourdomain.com"
- --rfc2136-port=53
- --rfc2136-zone=example.com
- --rfc2136-tsig-secret-alg=hmac-sha256
- --rfc2136-tsig-axfr
env:
- name: "EXTERNAL_DNS_RFC2136_TSIG_SECRET"
valueFrom:
secretKeyRef:
name: rfc2136-keys
key: rfc2136-tsig-secret
- name: "EXTERNAL_DNS_RFC2136_TSIG_KEYNAME"
valueFrom:
secretKeyRef:
name: rfc2136-keys
key: rfc2136-tsig-keyname
```
#### Secret creation
```shell
kubectl create secret generic rfc2136-keys --from-literal=rfc2136-tsig-secret='xxx' --from-literal=rfc2136-tsig-keyname='k8s-external-dns-key' -n external-dns
```
### Benefits
- Distributes the load of DNS updates across multiple data centers, preventing any single DC from becoming a bottleneck.
- Provides flexibility to choose different load balancing strategies based on the environment and requirements.
- Improves the resilience and reliability of DNS updates by introducing a retry mechanism with a list of hosts.