Securing Amazon EKS: A Comprehensive Guide to Implementing HTTPS with AWS Application Load Balancer, Route 53, and ACM
Tags: eks, alb, route53, aws
Published: 2024-01-13 09:19:14

## Introduction
Begin by explaining the importance of using SSL (HTTPS) for enhancing the security and credibility of web services. Emphasize the role of SSL in encrypting data and protecting sensitive information. Introduce the use of Amazon EKS for container orchestration, AWS Application Load Balancer for distributing incoming application traffic, Route 53 for domain name system (DNS) web services, and ACM for managing secure sockets layer/transport layer security (SSL/TLS) certificates.

## Prerequisites
List the prerequisites for the reader:

- An active AWS account with necessary permissions.
- AWS cli
- eksctl cli
- helm

## Architecture Overview

![Architecture Overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/av35r98lx8v6d3ihd1v4.jpg)

## Step-by-Step Guide

### 1. Creating the EKS Cluster
Cluster creation process with the following command:

```
eksctl create cluster -f cluster-config.yaml
```

```yml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: basic-cluster
  region: us-west-2
  version: "1.28"

nodeGroups:
  - name: ng-1
    instanceType: m5.large
    desiredCapacity: 2
    ssh:
      allow: true # if you want to SSH into your nodes, set this to true
      publicKeyPath: ~/.ssh/id_rsa.pub # path to your SSH public key file
```

### 2. Deploy the AWS Load Balancer Controller

AWS Elastic Load Balancing Application Load Balancer (ALB) is a popular AWS service that load balances incoming traffic at the application layer (layer 7) across multiple targets, such as Amazon EC2 instances, in multiple Availability Zones.

ALB supports multiple features including:

- host or path based routing
- TLS (Transport Layer Security) termination, WebSockets
- HTTP/2
- AWS WAF (Web Application Firewall) integration
- integrated access logs, and health checks

1. Create IAM OIDC provider

The primary purpose of this command is to enable IAM roles for service accounts in your EKS cluster. This feature allows Kubernetes service accounts to assume IAM roles, giving you a way to manage permissions for the pods that run in your cluster. By using IAM roles for service accounts, you can follow the best practices of least privilege and not have to assign broad IAM permissions to the nodes in your cluster.

```bash
eksctl utils associate-iam-oidc-provider \
    --region us-west-2 \
    --cluster basic-cluster \
    --approve
```
This setup is especially useful when you need to give specific AWS permissions to certain pods running in your EKS cluster without granting those permissions to all pods on the node. It's a security best practice in managing Kubernetes clusters on AWS.

2. Download an IAM policy for the LBC using one of the following commands:

```bash
curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.6.1/docs/install/iam_policy.json
```
3. reate an IAM policy named AWSLoadBalancerControllerIAMPolicy. If you downloaded a different policy, replace iam-policy with the name of the policy that you downloaded.

```bash
aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam-policy.json
```

> Take note of the policy ARN that's returned.

4. Create an IAM role and Kubernetes ServiceAccount for the LBC. Use the ARN from the previous step.

```bash
eksctl create iamserviceaccount \
--cluster=<cluster-name> \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::<AWS_ACCOUNT_ID>:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--region <region-code> \
--approve
```
5. Helm install command for clusters with IRSA:

```bash
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=basic-cluster \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller 
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n9dexd5xsh0kuk1sptu.png)

After installing the AWS ALB Ingress Controller in your Amazon EKS cluster, it's important to ensure that it has been successfully deployed and is running as expected. You can verify this by checking the pods in the kube-system namespace, where the ALB Ingress Controller should be running.

Run the following command in your terminal:

```bash
kubectl get pod -n kube-system
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3jwr14xqk344ircvdosk.jpg)

## Deploy Sample Application

Now let’s deploy a sample 2048 game into our Kubernetes cluster and use the Ingress resource to expose it to traffic:

Deploy 2048 game resources:

```yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: game-2048
  name: deployment-2048
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: app-2048
  replicas: 5
  template:
    metadata:
      labels:
        app.kubernetes.io/name: app-2048
    spec:
      containers:
      - image: alexwhen/docker-2048
        imagePullPolicy: Always
        name: app-2048
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: game-2048
  name: service-2048
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector:
    app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: game-2048
  name: ingress-2048
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: service-2048
              servicePort: 80
```

After few seconds, verify that the Ingress resource is enabled:

```bash
kubectl get ingress/ingress-2048 -n game-2048
```

You should be able to see the following output

```
NAME           HOSTS   ADDRESS                                                                  PORTS   AGE
ingress-2048   *       k8s-game2048-ingress2-8ae3738fd5-251279030.us-east-2.elb.amazonaws.com   80      6m20s
```

## Configuring a Custom Domain and Enabling HTTPS

Up to this point, we have successfully deployed our application in the Amazon EKS cluster and accessed it via the default DNS name provided by the AWS Application Load Balancer (ALB). Now, let's take it a step further by configuring a separate, more user-friendly domain name for our application and enabling HTTPS to ensure secure communication.

#### Acquiring and Configuring a Domain Name

**Domain Registration:** If you don’t already have a domain, you can register one through services like Amazon Route 53 or any other domain registrar.
**Route 53 Hosted Zone:** Once you have your domain, set up a hosted zone in AWS Route 53. This will allow you to manage the DNS records for your domain within AWS.

#### Integrating the Domain with ALB

Updating DNS Records: Point your domain to the ALB by updating the DNS records in the Route 53 hosted zone. You’ll typically add an A record (or CNAME if necessary) that maps your domain name to the ALB’s DNS name.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gfjdggdmmhad0o1x9sdr.jpg)

#### Creating a Hosted Zone

If you haven’t already, create a new hosted zone for your domain. A hosted zone is essentially a container for the DNS settings for your domain.
Once the hosted zone is set up, you'll see a set of default record sets. These are the NS (Name Server) and SOA (Start of Authority) records.

#### Adding DNS Records

Click on the “Create Record” or “Create Record Set” button in the hosted zone for your domain

#### Configuring Record Details

**Record Name:** Enter the desired subdomain or leave it blank for the root domain.
**Record Type:** Choose 'A – IPv4 address' if you are mapping the domain to an IP address or 'CNAME' if you are mapping it to a domain name. For ALB, you’ll typically use an A record.
**Value:** For an A record, input the IP address of your ALB. For a CNAME, enter the ALB’s DNS name.
**Routing Policy:** Select “Simple routing”. This policy is used when you want to route traffic to a single resource, like your ALB.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5x30wo0vhdo3s0j4jms1.jpg)

#### Saving the Record Set

Review the settings and click “Create” to add the record to your hosted zone.
It might take some time for the changes to propagate through the DNS system.

#### Verifying the Configuration

After the records have propagated, verify that your domain correctly points to your ALB. You can do this by accessing your domain in a web browser or using a DNS lookup tool.

## Issuing an SSL/TLS Certificate via AWS Certificate Manager (ACM)

With your domain now correctly pointing to your AWS infrastructure, the next crucial step is to secure it with an SSL/TLS certificate. AWS Certificate Manager simplifies this process. Here’s how to issue a certificate:

#### Requesting a New Certificate

- Click on “Request a certificate”.
- Choose “Request a public certificate” and click “Next”.

#### Adding Domain Names

- Enter your domain name. You can request a certificate for a specific subdomain (like www.yourdomain.com) or for all subdomains under a domain using a wildcard (like *.yourdomain.com).

- Click “Next” after entering your domain details.

#### Selecting Validation Method

- Choose a validation method. ACM offers two methods: DNS validation and email validation.

1. **DNS Validation:** Recommended for its simplicity and integration with Route 53. ACM will provide DNS records that you must add to your Route 53 hosted zone. This proves that you own or control the domain.

2. **Email Validation:** ACM sends emails to the contact addresses listed for the domain in WHOIS and to a set of five common administrative addresses. You must follow the instructions in the email to validate domain ownership.

#### Review and Request

- Review your request details and click “Confirm and request”.

#### Validating the Certificate

- Follow the instructions provided by ACM to validate your domain, depending on the method you chose.

- Once validated, the status of your certificate will change to “Issued”.

## Updating the Ingress Resource for HTTPS

With the SSL/TLS certificate issued and associated with your ALB, the next step is to update the Ingress resource in your Kubernetes deployment. This will direct traffic through the ALB using HTTPS. Here's how to update your Ingress resource with the necessary annotations for the ACM certificate:

- Update with Certificate ARN: Modify the alb.ingress.kubernetes.io/certificate-arn annotation to include the ARN of your newly issued ACM certificate. Replace the actual ARN value with {certificate-arn} placeholder.

Ater all the configurations and updates, your Kubernetes manifest file for the Ingress resource should look like this.

```yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: game-2048
  name: deployment-2048
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: app-2048
  replicas: 5
  template:
    metadata:
      labels:
        app.kubernetes.io/name: app-2048
    spec:
      containers:
      - image: public.ecr.aws/l6m2t8p7/docker-2048:latest
        imagePullPolicy: Always
        name: app-2048
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: game-2048
  name: service-2048
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector:
    app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: game-2048
  name: ingress-2048
  annotations:
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/certificate-arn: {certificate-arn}
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/ssl-redirect: '443'
spec:
  ingressClassName: alb
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service-2048
            port:
              number: 80
```

Apply the updated configuration to your Kubernetes cluster using:

```
kubectl apply -f your-ingress-resource-file.yaml
```

## Verifying the Setup

After applying the changes, it's important to verify that the Ingress is correctly configured:

Check the Ingress status using `kubectl get ingress -n game-2048`.
Ensure that your domain now serves traffic over HTTPS and that the browser shows a secure connection.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4ch812hl414kzga2lgg.png)

## Conclusion

By updating the Ingress resource with the ACM certificate ARN and ensuring the proper routing and SSL redirection, your application is now securely accessible over HTTPS. This not only secures the data transmission but also improves trust with your users.