Using external-dns on Kubernetes - Cloudflare
Published: 2023-08-23 10:43:50


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqybmkim9lxm9ost1twc.png)

When the load balancer's IP address changes, our goal is to avoid the need for manual updates on Cloudflare. Instead, we aim to employ a monitoring service that will automatically ensure the continuity of our DNS records despite these IP changes.

Initially, let's set up external DNS. To do this, you will need the following information from Cloudflare:
- API KEY
- ZONE ID

We need to create a secret that external-dns can use to access the Cloudflare API.

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
  namespace: default
type: Opaque
stringData:
   api-token: ${CLOUDFLARE_API_TOKEN}
```  

### RBAC Configuration
This RBAC configuration grants the external-dns ServiceAccount permissions to interact with namespaces and specific resources in the `gateway.networking.k8s.io API group`.

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get","watch","list"]
- apiGroups: ["gateway.networking.k8s.io"]
  resources: ["gateways","httproutes","grpcroutes","tlsroutes","tcproutes","udproutes"]
  verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: default
```

### Deploying external-dns

- In the deployment replace the $ZONE_ID with the zone ID from cloudflare
- `secretKeyRef` refers to the `cloudflare-api-token-secret` that we created earlier
- Pay attention to `--source=gateway-httproute` as this flag defines what resource will be monitored for DNS changes
```yaml
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
        imagePullPolicy: Always
        args:
        - --source=gateway-httproute
        - --zone-id-filter=$ZONE_ID
        - --provider=cloudflare
        - --cloudflare-proxied
        - --policy=upsert-only
        - --log-level=debug
        env:
          - name: CF_API_TOKEN
            valueFrom:
              secretKeyRef:
                name: cloudflare-api-token-secret
                key: api-token
```

Verify that the deployment is ready and now we can annotate the services that we want to update the dns records for.

- The HTTPRoute resource defines a routing configuration for incoming HTTP requests with a specific host and path. When a request matches the defined conditions, it will be directed to the test-app1 backend service running on port 80. This allows for fine-grained control over how incoming traffic is directed within a Kubernetes cluster.
- <strong>⚠️ Important Note:</strong> Make sure that the CNAME/A name is not already created so external dns can create the record and take ownership from the start.

```yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  name: test-app-1
spec:
  parentRefs:
  - name: fine-ops-gateway
    sectionName: https
  hostnames:
    - "fe-1.fine-ops.com"
  rules:
    - matches:
      - path:
          type: PathPrefix
          value: /
        method: GET
      backendRefs:
      - name: test-app1
        port: 80
``` 
Verify that the route was created be executing `kubectl get httproutes`

After we have applied the HTTP resource lets see if external-dns has created a new DNS record for us.Looking at the logs of the pod `kubectl logs external-dns-548f86c768-82szq` we can see that 

- external-dns dected the HTTP Resource and created a new A record:
  ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/an8v9hkr3555tszwvsv4.png)
- Double-checking also from the DNS cloudflare dashboard :
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ae20v6hobd541urcc974.png)