Automating DNS Management in Kubernetes with External-DNS and Cloudflare

Use External-DNS with FluxCD to automate DNS management for Kubernetes deployments, integrated with Cloudflare as the DNS provider

kubernetes gitops cloud
2024-10-09
Thomas Kooi

Automating DNS Management with External-DNS, FluxCD, and Cloudflare

Managing DNS records can invole a lot of manual work. If you’ve ever had to manually copy and paste IP addresses to create DNS records, or tried to rely on wildcard entries that point to a single load balancer IP or CNAME, you know the pain. It often goes something like this: you run kubectl to grab the load balancer IP, then hop over to your Terraform DNS repo, make the necessary changes, create a Merge Request, wait for the review, and only after all that, you finally get the DNS record updated. It’s a workflow that’s slow, prone to mistakes, and just doesn’t scale well.

That’s where External-DNS makes a difference. It automates the entire DNS management process, updating records automatically as services change within your Kubernetes cluster. No more manual updates, no more waiting for approvals—just up-to-date DNS records in real-time.

In this post, I’ll walk you through you how to deploy External-DNS with FluxCD, using Cloudflare as the DNS provider.

Why Use External-DNS?

external-dns logo

External-DNS automatically manages DNS records based on the resources defined in your Kubernetes cluster. As services are created or removed, External-DNS ensures that DNS entries are updated to reflect the current state of your applications. This automation saves time, reduces the potential for misconfigurations, and provides dynamic scaling for environments that frequently change.

In our setup, we’ll be using FluxCD to manage the deployment of External-DNS as part of our GitOps workflow, ensuring that all configurations and changes are version-controlled and declarative.

Setting Up External-DNS with FluxCD and Cloudflare

Let’s dive into the specifics of how to configure External-DNS with FluxCD and Cloudflare as the DNS provider. Below, we outline the necessary Kubernetes resources and configurations required to make it all work.

1. Setting Up the Namespace

We’ll start by setting up a specific namespace for External-DNS. Here all the External-DNS resources will be deployed and managed.

apiVersion: v1
kind: Namespace
metadata:
  name: external-dns
  labels: {}

2. Adding the External-DNS HelmRepository

External-DNS provides us with a convienient Helm Chart that we can use to rollout in our cluster. We utilize FluxCD to deploy it, so we define the HelmRepository for External-DNS to make it accessible for FluxCD.

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: external-dns
  namespace: external-dns
spec:
  interval: 160m
  url: https://kubernetes-sigs.github.io/external-dns/

This specifies where to pull the chart from and how often to check for updates (in this case, every 160 minutes).

3. Deploying External-DNS via HelmRelease

Next, we configure the HelmRelease for External-DNS, which tells FluxCD to install and manage the External-DNS chart. We’ll also configure it to use Cloudflare as the DNS provider.

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: external-dns
  namespace: external-dns
spec:
  chart:
    spec:
      chart: external-dns
      sourceRef:
        kind: HelmRepository
        name: external-dns
        namespace: external-dns
      version: 1.14.5
  interval: 1h
  releaseName: external-dns
  values:
    provider: 
      name: cloudflare
    env:
    - name: CF_API_TOKEN
      valueFrom:
        secretKeyRef:
          name: cloudflare-api-token
          key: apiToken

    affinity:
      nodeAffinity:
          # prefer scheduling on control-plane machines
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 1
              preference:
                matchExpressions:
                  - key: node-role.kubernetes.io/control-plane
                    operator: Exists
                topologyKey: kubernetes.io/hostname
    nodeSelector:
      kubernetes.io/os: linux
    tolerations:
      - key: node-role.kubernetes.io/control-plane
        effect: NoSchedule

In our setup, we use Cloudflare as the DNS provider for External-DNS. To allow External-DNS to authenticate with Cloudflare, we reference the Cloudflare API token from a Kubernetes secret, which stores the token and makes it accessible for the External-DNS deployment.

We provide any additional settings that the External-DNS helm chart supports. In this case, the example comes from a set-up were we deploy controllers on Control Plane machines within an Kubernetes cluster.

Creating the Cloudflare API Token Secret

To allow External-DNS to manage DNS records in Cloudflare, you’ll need to create an API token that grants the necessary permissions. The API token will be securely stored as a Kubernetes secret, enabling External-DNS to authenticate with Cloudflare and update DNS records automatically as services change in your Kubernetes cluster.

Creating a Cloudflare API Token

To create a token, follow these steps:

  1. Login to Cloudflare: Go to the Cloudflare dashboard and log into your account.
  2. Navigate to API Tokens: Click on your profile in the top right corner, and select “My Profile.” In the left-hand menu, click on “API Tokens.”
  3. Create a Custom Token: Under the API Tokens tab, click “Create Token.”
  4. Set Permissions: To ensure External-DNS can manage DNS records but nothing else, configure the token with the following permissions:
    • Zone → DNS → Edit: This allows External-DNS to create, update, and delete DNS records.
    • Zone → Zone → Read: This allows External-DNS to read the details of your DNS zones.
  5. Specify the Zone: If you only want External-DNS to manage DNS for specific zones, you can restrict the token to those zones by selecting them in the “Zone Resources” section. Otherwise, you can allow it access to all zones.
  6. Create the Token: Once the permissions and zones are configured, click “Create Token.” Make sure to copy the token somewhere safe, as you will not be able to view it again once you leave the page.

Storing the Token as a Kubernetes Secret

With the token created, we now need to store it as a Kubernetes secret so that External-DNS can use it for authentication. Here’s the command to create the secret, replacing YOUR_CLOUD_FLARE_API_TOKEN with the actual token you generated:

kubectl create secret generic -n external-dns cloudflare-api-token --from-literal=apiToken=YOUR_CLOUD_FLARE_API_TOKEN --dry-run=client -o yaml

This command generates the secret containing the API token, which External-DNS will use to authenticate with Cloudflare.

How External-DNS Works with Cloudflare

Once everything is set up, External-DNS watches for changes to Kubernetes resources like services or ingresses. When a new service is created with an external IP, External-DNS automatically creates a corresponding DNS entry in Cloudflare, associating the IP with the correct domain name. As applications scale or change, External-DNS ensures that these DNS records are always accurate and up to date. It also handles cleanup, removing DNS entries when services are deleted, so there are no stale records left behind.

External DNS overview

External DNS with Cloudflare overview

A key part of how External-DNS manages these DNS records is through the use of TXT records. These TXT records are automatically created alongside the A or CNAME records for your services and play a crucial role in maintaining and tracking DNS entries.

The purpose of the TXT Records

TXT records serve as a way for External-DNS to “tag” the DNS entries it manages. Each time External-DNS creates or modifies a DNS record in Cloudflare, it also generates a corresponding TXT record. This TXT record contains metadata that links the DNS entry to the Kubernetes resource that owns it. This tagging system is important for several reasons:

  • Ownership tracking – The TXT record acts as a signature, letting External-DNS know which DNS records it is responsible for managing. This prevents conflicts with manually created DNS records or records managed by another tool.
  • Automated cleanup – When a service or ingress is deleted from your Kubernetes cluster, External-DNS will look for the associated TXT record to determine which DNS entry to remove. This ensures that old, unused DNS records don’t linger after a service is deleted.
  • Avoiding conflicts – By checking for the existence of a TXT record, External-DNS can avoid overwriting or deleting records it doesn’t own. If a DNS entry doesn’t have a matching TXT record, External-DNS knows not to modify it, reducing the chance of accidental changes to important DNS records.
Encrypting TXT Record Values

TXT records may contain sensitive information, such as the internal ingress name or namespace, which attackers could potentially exploit to gather details about your infrastructure. To protect this information from unauthorized access, you can enable encryption of TXT records using External-DNS. Encryption is enabled by setting the –txt-encrypt-enabled flag. Additionally, you must provide a 32-byte encryption key in URL-safe base64 form using the –txt-encrypt-aes-key flag.

To generate the encryption key, you can use the following command:

openssl rand -base64 16 | tr -- '+/' '-_'

We can use this to create a secret in our Kubernetes cluster and configure it in the HelmRelease.

kubectl create secret generic external-dns-txt-encryption-key \
  --from-literal=ENCRYPTION_KEY=$(openssl rand -hex 16) \
  --namespace external-dns

Next, we can add the extra environment variable to the HelmRelease:

    - name: ENCRYPTION_KEY
      valueFrom:
        secretKeyRef:
          name: external-dns-txt-encryption-key
          key: ENCRYPTION_KEY

As well as the arguments that enable the encrypted txt feature:

    extraArgs:
      - --txt-encrypt-enabled
      - --txt-encrypt-aes-key=$(ENCRYPTION_KEY)
Multi-environment setups

When working with multiple environments (e.g., development, staging, and production), it’s important to differentiate the DNS records managed by each environment. This can be done by customizing the controller name in External-DNS. You can use the –txt-owner-id flag to assign a unique identifier for each environment, such as prod-external-dns or dev-external-dns. This ensures that each External-DNS instance manages only the records for its environment, preventing conflicts and ensuring the right records are cleaned up or modified in the correct environment. You can do this by configuring txtOwnerId: prd in the HelmRelease.

Benefits of External-DNS

One of the key advantages of using FluxCD with External-DNS is the automatic deployment and management of External-DNS itself. Instead of manually setting up and configuring External-DNS each time you spin up a new environment, FluxCD ensures that everything is deployed and configured for you.

Whether you’re tweaking the provider settings or rolling out updates, FluxCD handles the deployment seamlessly, ensuring External-DNS is always up and running without you having to lift a finger. This level of automation simplifies operations and guarantees that your DNS management tool is always in sync with the rest of your infrastructure.

1. No more manually copy pasting IP addresses

Another significant benefit is the hands-off approach to managing DNS records. If you’ve ever had to manually copy and paste IP addresses into a DNS provider or run Terraform scripts to update DNS entries, you’ll appreciate how much time and effort External-DNS saves. It automatically updates your DNS provider (in this case, Cloudflare) whenever a service is created or modified in Kubernetes. No more logging into your DNS provider’s dashboard or running scripts—everything is handled in real-time by External-DNS. This not only reduces manual work but also ensures that your DNS records are always accurate and up-to-date with your cluster’s state.

2. Automate the cleanup

One of the most practical improvements is the way External-DNS handles clean-up. In a fast-moving environment, it’s easy to forget about old DNS records when a service is removed, leaving behind unused entries that clutter your DNS configuration. Over time, this can lead to confusion or even security issues. With External-DNS, this becomes a non-issue. It automatically removes DNS records when services are deleted, so you won’t have to worry about cleaning up stale entries manually. This ensures your DNS records stay tidy and relevant, reducing the risk of issues down the line.

Conclusion

Integrating External-DNS with FluxCD and Cloudflare brings significant value to any Kubernetes-based environment. By automating the deployment and ongoing DNS management, you eliminate the manual overhead of maintaining DNS records, reduce the risk of errors, and ensure that your DNS entries stay in sync with your infrastructure.

With the added benefit of automated clean-up, this setup keeps your DNS configurations clean and hassle-free, allowing you to focus on scaling and improving your applications rather than managing DNS manually.


9 min read
Share this post:

Related posts