After setting up ingress-nginx with an ACM certificate in EKS clusters, we also needed to set up ingress-nginx in GKE and AKS clusters, which was easy since we just needed to use the default ingress-nginx helm chart to deploy to whichever cluster as required. But TLS was an issue.
Solution - since the domain is managed in Amazon Route 53, we can use cert-manager with DNS01 challenge to get TLS working along with Route53 in any cluster, regardless of the cloud provider. Here we’ll see the steps required for this setup. DNS01 challenge essentially involves you proving you own the domain by creating a TXT record, which is then checked by the CA (here it will be Let’s Encrypt) and once verified you’re given the TLS cert. For more details - Ref
AWS setup
- Create the IAM user to be used by
cert-manager
aws iam create-user --user-name route53-certmanager-user- Use the following policy json that allows
cert-managerto access the route53 zone
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "route53:GetChange", "Resource": "arn:aws:route53:::change/*" }, { "Effect": "Allow", "Action": [ "route53:ChangeResourceRecordSets", "route53:ListResourceRecordSets" ], "Resource": "arn:aws:route53:::hostedzone/*", "Condition": { "ForAllValues:StringEquals": { "route53:ChangeResourceRecordSetsRecordTypes": ["TXT"] } } }, { "Effect": "Allow", "Action": "route53:ListHostedZonesByName", "Resource": "*" } ]}- Create and attach policy to the IAM User
aws iam put-user-policy --user-name route53-certmanager-user --policy-name route53-certmanager-policy --policy-document file://cert-manager/route53-policy.json- Create IAM Secret keys for the IAM user. Note the fields -
AccessKeyIdandSecretAccessKey
aws iam create-access-key --user-name route53-certmanager-userCert-Manager setup
For ease of management let’s use helm to deploy cert-manager.
- Run the following command to deploy it in your K8s cluster:
helm install \ cert-manager oci://quay.io/jetstack/charts/cert-manager \ --version v1.18.2 \ --namespace cert-manager \ --create-namespace \ --set crds.enabled=true- Replace
AWS_ACCESS_KEY_ID_BASE64andAWS_SECRET_ACCESS_KEY_BASE64in the following K8s secrets yaml with the base64 encoded values ofAccessKeyIdandSecretAccessKeywe created earlier. Note that the secret should be created in the same namespace wherecert-manageris deployed - here it’scert-manager.
apiVersion: v1kind: Secretmetadata: name: route53-credentials-secret namespace: cert-managerdata: aws-access-key-id: AWS_ACCESS_KEY_ID_BASE64 aws-secret-access-key: AWS_SECRET_ACCESS_KEY_BASE64type: Opaque- Create the secret
kubectl apply -f route53-credentials-secret.yaml- Replace
ROUTE53_DOMAIN_NAMEwith the Route53 domain name. Example:yourdomain.com
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencrypt-prodspec: acme: privateKeySecretRef: name: letsencrypt-prod server: https://acme-v02.api.letsencrypt.org/directory solvers: - dns01: route53: accessKeyIDSecretRef: key: aws-access-key-id name: route53-credentials-secret region: us-east-1 secretAccessKeySecretRef: key: aws-secret-access-key name: route53-credentials-secret selector: dnsZones: - ROUTE53_DOMAIN_NAME- Create the ClusterIssuer. This will be used in our ingress resource to request the TLS certificate.
kubectl apply -f clusterissuer.yamlTesting Ingress-Nginx with TLS
- Now we can deploy the sample workload to test this. Replace
SAMPLE_DOMAINwith your domain as required
apiVersion: v1kind: Podmetadata: name: nginx labels: app: nginxspec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: nginx-servicespec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIP---apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: nginx-ingress annotations: cert-manager.io/cluster-issuer: letsencrypt-prodspec: ingressClassName: nginx # This must match the Ingress Controller's class name tls: - hosts: - SAMPLE_DOMAIN secretName: domain-cert-secret rules: - host: SAMPLE_DOMAIN # Replace with your actual domain http: paths: - path: / pathType: Prefix backend: service: name: nginx-service port: number: 80- Get the
ingress-nginxloadbalancer IP (if applicable). This is usually applicable in most clusters.
kubectl -n ingress-nginx get svc ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'OR- Get theingress-nginxloadbalancer hostname (if applicable). This is usually applicable in EKS.
kubectl -n ingress-nginx get svc ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'- Create Route53 record for
SAMPLE_DOMAINwith the value as either the loadbalancer IP or loadbalancer hostname. - Wait for a few seconds for the DNS01 challenge and DNS propagation to be over.
- Try to access
https://SAMPLE_DOMAIN. Et voilà! The endpoint is accessible with working TLS