Setting up Ingress on EKS with TLS Certificate from ACM

How to terminate SSL at Elastic Load Balancer hosted in EKS using Ingress

The scenario is when you want to terminate SSL at Elastic Load Balancer hosted in EKS.

There is an article from AWS explaining how to achieve this using Kubernetes Service. But Services on EKS create Classic Load Balancer instead of Application Load Balancer which is created if you use Ingress.

Note: Application Load Balancer is more flexible than Classic one but this is not the subject of this article.

For those who just want to see the code, GitHub repository is here.

Prerequisites

To do this, please make sure you have all other things setup such as having a domain set up on Route53 and a certificate requested from ACM. And make sure you have already had AWS Load Balancer controller set up in your EKS cluster.

You can check if you have already set up AWS Load Balancer by running the command:

kubectl get deployment -n kube-system aws-load-balancer-controller

If you don't it set up, then follow this guide to create AWS Load Balancer Controller.

Process

Once all the prerequisites are in place, create a file called deployment.yaml with the content below:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: echo-pod
  template:
    metadata:
      labels:
        app: echo-pod
    spec:
      containers:
      - name: echoheaders
        image: k8s.gcr.io/echoserver:1.10
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

Then, run

kubectl apply -f deployment.yaml

Now, the pods should be up and running

kubectl get pods -l app=echo-pod

Next, create a service with a file called service.yaml. Note that we are using NodePort instead of LoadBalancer type.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: echo-pod
    app.kubernetes.io/name: echo-pod
  name: echo-pod
spec:
  ports:
  - name: http
    nodePort: 30162
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: echo-pod
  type: NodePort

NodePort is being used here but ClusterIP can also be used.

Now, the service should be created.

kubectl get service -l app=echo-pod

Next, create ingress file. Take note the certificate-arn annotation, where you have to use your certificate ARN from ACM. Host name must be updated to your host name, too.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-west-2:xxxxx:certificate/xxxxxxx
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
  labels:
    app: echo-pod
    app.kubernetes.io/name: echo-pod
  name: echo-pod-ingress
spec:
  ingressClassName: my-alb-ing-class # or whatever ingress class name of ELB
  rules:
  - host: nayyaung.com # change it to your host name here
    http:
      paths:
      - path: /*
        pathType: ImplementationSpecific
        backend:
          service:
            name: ssl-redirect
            port:
              name: use-annotation
      - path: /*
        pathType: ImplementationSpecific
        backend:
          service:
            name: echo-pod
            port:
              name: http        
  tls:
  - hosts:
    - nayyaung.com # change it to your host name here

Finally, you will see ALB being created. Wait for it to reach Active stage then go over to Route53 console to create host record using your host name and map it to the ALB just being created.

One interesting things to notice:

"listen-ports" annotation has both p0rt 80 and 443. And the actions.ssl-redirect annotation is set up to redirect http traffic to https using ssl-redirect action. ssl-redirect action must be the first rule so that ALB will evaluate it first, details in here.

So, if the user uses port 80, ALB will reroute it to port 443, with given SSL certificate on that port, and terminate it before routing it via the ingress rule to echo-pod which is using only port 80 (without SSL).

Again, the code is available on my GitHub repository.

Thanks for reading.