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-manager
to 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 -
AccessKeyId
andSecretAccessKey
aws iam create-access-key --user-name route53-certmanager-user
Cert-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_BASE64
andAWS_SECRET_ACCESS_KEY_BASE64
in the following K8s secrets yaml with the base64 encoded values ofAccessKeyId
andSecretAccessKey
we created earlier. Note that the secret should be created in the same namespace wherecert-manager
is 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_NAME
with 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.yaml
Testing Ingress-Nginx with TLS
- Now we can deploy the sample workload to test this. Replace
SAMPLE_DOMAIN
with 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-nginx
loadbalancer 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-nginx
loadbalancer 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_DOMAIN
with 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