Containers
Maintaining Transport Layer Security all the way to your container: using the Application Load Balancer with Amazon ECS and Envoy
NOTICE: October 04, 2024 – This post no longer reflects the best guidance for configuring a service mesh with Amazon ECS and its examples no longer work as shown. Please refer to newer content on Amazon ECS Service Connect.
——–
This post is contributed by Sri Mallu, Re Alvarez-Parmar, and Sahil Thapar
Application Load Balancer has been an instrumental element when it comes to building highly available, scalable, and secure applications. AWS customers can rely on ALB to perform functions that have been traditionally implemented in application’s code. Let’s take connection security as an example, ALB can be used to offload the work of encryption and decryption so that your applications can focus on business logic.
The Application Load Balancer allows you to create an HTTPS listener, which uses encrypted connections (also known as SSL offload). This feature enables traffic encryption between your load balancer and the clients that initiate SSL or TLS sessions. When you create an HTTPS listener, you deploy a SSL/TLS server certificate on your load balancer. The load balancer uses this server certificate to terminate the front-end connection and then decrypt requests from clients before sending them to the targets. Why does ALB need to decrypt requests?
The reason why ALB needs to decrypt the request is because it operates at the application layer of the Open Systems Interconnection (OSI) model and needs to inspect the requests to perform request routing. You can use ALB to route requests based on HTTP headers, methods, query parameters, and source IP CIDRs. That’s why when you use ALB to load balance your applications, SSL/TLS termination is done at ALB, and typically the connection between ALB and the backend application is left unencrypted. Terminating secure connections at the load balancer and using HTTP on the backend might be sufficient for your application. Network traffic between AWS resources can only be listened to by the instances that are part of the connection.
However, if you are developing an application that needs to comply with strict external regulations, you might be required to secure all network connections.
I am going to show you how to encrypt connection between clients and the load balancer and from the load balancer to your application container running in an ECS cluster. In this post, we run ECS containers on Fargate but the solution applies to ECS containers running on EC2 as well. To follow along you will need a VPC with public and private subnets, appropriate route tables, an internet gateway, and NAT gateway(s).
We will also use Envoy as a front proxy that terminates TLS and we will run Envoy as a sidecar along with the application container. With this method, we do not need to handle encryption in the application code. Envoy will send traffic, unencrypted, to the application container over localhost
.
Prerequisites
In order to successfully carry out steps outlined:
Architecture
?You may use this template as a base line for the setup.
We will start by creating a self-signed certificate, this will be embedded into the Envoy container image. Then we will create an ACM certificate using the same private key and certificate and import it into ACM. We will use this ACM certificate on the ALB as the server certificate.
Even though this solution uses a self-signed certificate, you can use an ACM private CA to generate the certificate, as well. You cannot use ACM public Certificate Authority (CA) for this solution, as it does not allow you to export private keys. However, you can use any commercially available trusted CA that exports private keys.
?Since we are using a self-signed certificate, if you access the application from a browser, you will need to set up the trust in the browser. This is not required if you use a commercial trusted CA.
Tutorial
Let’s define a few environment variables that will be used throughout the tutorial.
If you are using the CloudFormation template mentioned above, an ECS task execution IAM role will be created for you. If you are creating the role yourself, please verify that it has the following permissions and trust policy.
Create a ecsTaskExecutionRoleArn
environment variable.
ECR repository setup
Create two ECR repositories to store the application and Envoy container images.
Repository 1:
Output:
Repository 2:
Output:
Certificate setup
Let’s create a key pair and import it as an ACM certificate. We will associate this certificate with the ALB.
The same key and certificate will be used to enable TLS encryption in the Envoy proxy.
Use OpenSSL to create the certificate signing authority and then generate the private key and certificate using it.
Output:
Export the certificate Arn.
Let’s verify in the ACM console that the certificate was imported.
Build Docker images and push them to ECR
Let’s create two Docker images, one for Envoy proxy and another for a demo hello application. We will later create a task definition will have this pair of containers defined. Envoy proxy will run as sidecar and route requests to the hello application container over localhost
.
Define a simple hello service, with following content:
Create a Dockerfile for the application container.
Create an Envoy configuration file. It will used for proxying and routing requests. All requests from ALB will encrypted using TLS. The proxy will route requests to the application container over HTTP.
Create a startup script to run Envoy.
Create a Dockerfile for the Envoy proxy.
Let’s build the Docker images and push them to ECR.
Verify that the images have been pushed to ECR.
Create cluster and task definition
Create a task definition with both the container definitions.
Substitute the environment variables, create a log group, an ECS cluster, and register the task definition.
Output:
Register the task definition.
Verify the creation of the task definition on the AWS Management Console.
Create the Application Load Balancer
Create the Application Load Balancer and setup listener rules and target groups for the application.
Output:
Output:
Output:
Create the service
Create the ECS service definition template. Replace the values in the file to match your account.
And create the ECS Service, using the registered task definition and the Application Load Balancer.
Verify the service in the AWS Management Console.
Setup DNS
Let’s now setup a Route 53 record set on the domain I host on Route 53. I have created a hosted zone. I will create and point a record set to the ALB I created earlier. I have setup the Common Name (CN) on the cert with this domain [ CN= ecs-end-end-encryption.awsblogs.info ]
Test
Let’s test the TLS handshake with the application using a curl command.
Output:
Output:
There you have it. We encrypted traffic from client to ALB and from ALB to the application (through Envoy front proxy) and we didn’t have to modify the application.
By the way, Envoy has many other uses and many popular Service Meshes use Envoy for data plane. Just like I transparently added the encryption to my application, I could also use Envoy to generate access logs, traces and network metrics. Instead of managing and configuring individual Envoy proxies, Service Meshes give you centralized management of service proxies. Learn more about App Mesh here.
Resources
AWS Cloud Formation templates for ECS infrastructure setup
Maintaining Transport Layer Security
Encryption end-end using NLB
Encryption All The Way To The Container In ECS With Envoy