Skip to content

External DNS - Used for Updating Route53 RecordSets from Kubernetes

Image

Step-01: Introduction

  • We need to create IAM Policy, k8s Service Account & IAM Role and associate them together for external-dns pod to add or remove entries in AWS Route53 Hosted Zones.
  • Update External-DNS default manifest to support our needs
  • Deploy & Verify logs

Step-02: Create IAM Policy

  • This IAM policy will allow external-dns pod to add, remove DNS entries (Record Sets in a Hosted Zone) in AWS Route53 service
  • Go to Services -> IAM -> Policies -> Create Policy
  • Click on JSON Tab and copy paste below JSON
  • Click on Visual editor tab to validate
  • Click on Review Policy
  • Name: AllowExternalDNSUpdates
  • Description: Allow access to Route53 Resources for ExternalDNS
  • Click on Create Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
- Make a note of Policy ARN which we will use in next step
arn:aws:iam::180789647333:policy/AllowExternalDNSUpdates

Step-03: Create IAM Role, k8s Service Account & Associate IAM Policy

  • As part of this step, we are going to create a k8s Service Account named external-dns and also a AWS IAM role and associate them by annotating role ARN in Service Account.
  • In addition, we are also going to associate the AWS IAM Policy AllowExternalDNSUpdates to the newly created AWS IAM Role.

Create IAM Role, k8s Service Account & Associate IAM Policy

# Template
eksctl create iamserviceaccount \
    --name service_account_name \
    --namespace service_account_namespace \
    --cluster cluster_name \
    --attach-policy-arn IAM_policy_ARN \
    --approve \
    --override-existing-serviceaccounts

# Replaced name, namespace, cluster, arn 
eksctl create iamserviceaccount \
    --name external-dns \
    --namespace default \
    --cluster eksdemo1 \
    --attach-policy-arn arn:aws:iam::180789647333:policy/AllowExternalDNSUpdates \
    --approve \
    --override-existing-serviceaccounts

Verify the Service Account

  • Verify external-dns service account, primarily verify annotation related to IAM Role
    kubectl get sa external-dns
    

Verify CloudFormation Stack

  • Go to Services -> CloudFormation
  • Verify the latest CFN Stack created.
  • Click on Resources tab
  • Click on link in Physical ID field which will take us to IAM Role directly

Verify IAM Role & IAM Policy

  • With above step in CFN, we will be landed in IAM Role created for external-dns.
  • Verify in Permissions tab we have a policy named AllowExternalDNSUpdates
  • Now make a note of that Role ARN, this we need to update in External-DNS k8s manifest
    arn:aws:iam::180789647333:role/eksctl-eksdemo1-addon-iamserviceaccount-defa-Role1-1O3H7ZLUED5H4
    

AWS EKS - Elastic Kubernetes Service - Masterclass

Image

Step-04: Update External DNS Kubernetes manifest

  • Original Template you can find in https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md
  • File Location: kube-manifests/01-Deploy-ExternalDNS.yml

Change-1: Line number 9: IAM Role update

  • Copy the role-arn you have made a note at the end of step-03 and replace at line no 9.
        eks.amazonaws.com/role-arn: arn:aws:iam::411686525067:role/eksctl-demo1-addon-iamserviceaccount-default-Role1-M7IEPRHZYLPB   
    

Chnage-2: Line 55, 56: Commented them

  • We used eksctl to create IAM role and attached the AllowExternalDNSUpdates policy
  • We didnt use KIAM or Kube2IAM so we don't need these two lines, so commented
          #annotations:  
            #iam.amazonaws.com/role: arn:aws:iam::ACCOUNT-ID:role/IAM-SERVICE-ROLE-NAME    
    

Change-3: Line 65, 67: Commented them

        # - --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
       # - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
  • Final Template
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  # If you're using Amazon EKS with IAM Roles for Service Accounts, specify the following annotation.
  # Otherwise, you may safely omit it.
  annotations:
    # Substitute your account ID and IAM service role name below. #Change-1: Replace with your IAM ARN Role for extern-dns
    eks.amazonaws.com/role-arn: arn:aws:iam::180789647333:role/eksctl-eksdemo1-addon-iamserviceaccount-defa-Role1-1QHG9KUCNQB50
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
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: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
      # If you're using kiam or kube2iam, specify the following annotation.
      # Otherwise, you may safely omit it.  #Change-2: Commented line 55 and 56
      #annotations:  
        #iam.amazonaws.com/role: arn:aws:iam::ACCOUNT-ID:role/IAM-SERVICE-ROLE-NAME    
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.opensource.zalan.do/teapot/external-dns:latest
        args:
        - --source=service
        - --source=ingress
        # Change-3: Commented line 65 and 67 - --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
        - --provider=aws
       # Change-3: Commented line 65 and 67  - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
        - --registry=txt
        - --txt-owner-id=my-hostedzone-identifier
      securityContext:
        fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files

Step-05: Deploy ExternalDNS

  • Deploy the manifest
    # Deploy external DNS
    kubectl apply -f kube-manifests/
    
    # Verify Deployment by checking logs
    kubectl logs -f $(kubectl get po | egrep -o 'external-dns[A-Za-z0-9-]+')
    
    # List pods (external-dns pod should be in running state)
    kubectl get pods
    

References

  • https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/alb-ingress.md
  • https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md