Beanstalk is often configured to terminate SSL at the load balancer then make the connection to the web server/application instances using unencrypted HTTP. That’s usually okay as the AWS network is designed to keep such traffic private, but under certain conditions, such as those requiring PCI compliance, DoD/government rules, or simply out of an abundance of caution, there’s a desire to have all traffic encrypted – including that between the Beanstalk load balancer and servers.
There are two approaches for implementing end to end encryption on Beanstalk:
- Use a layer 4 load balancer (Network or Classic Elastic Load Balancer.
Using this approach, the load balancer never decrypts the traffic. The downside is that advanced reporting isn’t possible and layer 7 features, such as session affinity, cannot be implemented. - Use a layer 7 load balancer (Application or Classic Load Balancer).
Using this approach, traffic is decrypted at the load balancer. The load balancer would then re-encrypt traffic to the servers. Session affinity and traffic reporting are available.
The preferred solution is to use the layer 7 approach with an Application Load Balancer. This preference is due to the additional features the layer 7 offers, because Network Load Balancers are more expensive, and because AWS is deprecating Classic Load Balancers.
The simplest way to accomplish this goal is to use a self signed certificate on the servers and then use HTTPS from the load balancer to the server. Application Load Balancers do not currently perform validation of certificates which is why the self signed approach works and why there’s no advantage to using a CA issued certificate.
The following approach will work on any Beanstalk supported platform that uses nginx as the proxy server. This configuration is based on AWS’s documentation, but trimmed for only Application Load Balancers and to include the nginx configuration and self-signed certificate generation.
In your Beanstalk application archive, add these files:
.ebextensions/nginx/conf.d/https.conf
# HTTPS server
server {
listen 443;
server_name localhost;
ssl on;
# The certificate is generated in generate-certificate.sh
ssl_certificate /etc/pki/tls/certs/server.crt;
ssl_certificate_key /etc/pki/tls/certs/server.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
.ebextensions/nginx-https.conf
option_settings:
aws:elasticbeanstalk:application:
Application Healthcheck URL: HTTPS:443/
aws:elasticbeanstalk:environment:process:default:
Port: '443'
Protocol: HTTPS
aws:elbv2:listener:443:
DefaultProcess: https
ListenerEnabled: 'true'
Protocol: HTTPS
aws:elasticbeanstalk:environment:process:https:
Port: '443'
Protocol: HTTPS
packages:
yum:
bash: []
openssl: []
files:
"/tmp/generate_nginx_certificate.sh":
mode: "000755"
owner: root
group: root
content: |
#!/usr/bin/env bash
set -Eeuo pipefail # stop on all errors
# These files are used by nginx, see nginx/conf.d/https.conf
openssl genrsa 2048 > /etc/pki/tls/certs/server.key
openssl req -new -x509 -nodes -sha1 -days 3650 -extensions v3_ca -key /etc/pki/tls/certs/server.key -subj "/CN=localhost" > /etc/pki/tls/certs/server.crt
commands:
01generate_nginx_certificate:
command: "/tmp/generate_nginx_certificate.sh"