Securing Amazon EKS: A Comprehensive Guide to Implementing HTTPS with AWS Application Load Balancer, Route 53, and ACM
JM Rifkhan
Posted on January 13, 2024
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
Step-by-Step Guide
1. Creating the EKS Cluster
Cluster creation process with the following command:
eksctl create cluster -f cluster-config.yaml
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
- 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.
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.
- Download an IAM policy for the LBC using one of the following commands:
curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.6.1/docs/install/iam_policy.json
- reate an IAM policy named AWSLoadBalancerControllerIAMPolicy. If you downloaded a different policy, replace iam-policy with the name of the policy that you downloaded.
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam-policy.json
Take note of the policy ARN that's returned.
- Create an IAM role and Kubernetes ServiceAccount for the LBC. Use the ARN from the previous step.
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
- Helm install command for clusters with IRSA:
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
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:
kubectl get pod -n kube-system
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:
---
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:
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.
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.
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.
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.
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.
---
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.
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.
Posted on January 13, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.