Let's Encrypt with AWS Elastic Beanstalk
Setting up Let's Encrypt on AWS EB can be a little bit tricky. In this post I'm going to explain you how to configure your environment correctly for one or several domains / subdomains.
Update 07-02-2021
In December 2020, Certbot-auto was officially deprecated. Since then, we have to move to Certbot. The problem is, if you were using an Amazon Linux AMI image on your Elastic beanstalk environment, you will be surprised to see that you can't install Cerbot because Epel is not compatible with your system anymore.
Please refer to the new article here : Let's Encrypt with Amazon-linux-2
.ebextensions folder
Ebextensions folder contains the configuration of your AWS EB application. This folder contains .config files. This folder is NOT inside your Docker image. Just create the .ebextensions folder in the root folder of your git repository :
➜ webapp git:(master) ✗ ls -la
drwxr-xr-x 20 michaelcaraccio admin 640 Apr 10 01:17 .
drwxrwxr-x 12 michaelcaraccio admin 384 Mar 30 22:48 ..
drwxr-xr-x 4 michaelcaraccio admin 128 Jul 1 2019 .ebextensions
drwxr-xr-x 15 michaelcaraccio admin 480 Apr 29 14:19 .git
-rw-r--r--@ 1 michaelcaraccio admin 4314 Jul 23 2019 Dockerfile
drwxr-xr-x 28 michaelcaraccio admin 896 Mar 24 15:16 laravel
If you need more information about this folder, take a look at the documentation.
For this example, we create a new file named : AWS_letsencrypt_config.config.
➜ webapp git:(master) ✗ ls -la .ebextensions
total 24
drwxr-xr-x 4 michaelcaraccio admin 128 Jul 1 2019 .
drwxr-xr-x 20 michaelcaraccio admin 640 Apr 10 01:17 ..
-rw-r--r--@ 1 michaelcaraccio admin 4710 Mar 27 19:23 AWS_letsencrypt_config.config
In this example, I have one domain and two subdomains :
- mywebsite.com
- admin.mywebsite.com
- test.mywebsite.com
Step 1 : Configure the security groups
This step will allow traffic on port 443 (SSL). Don't forget to open port 443 on your docker image if you have an existing instance.
Resources:
sslSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
IpProtocol: tcp
ToPort: 443
FromPort: 443
CidrIp: 0.0.0.0/0
Step 2 : Configure Nginx
Now, we are going to create two configuration files.
The first file (000_http_redirect_custom.conf), will tell to the Nginx server to listen to any requests that come on port 80 (default HTTP) and redirect them to HTTPS.
files:
/etc/nginx/conf.d/000_http_redirect_custom.conf:
mode: "000644"
owner: root
group: root
content: |
server {
listen 80;
return 301 https://$host$request_uri;
}
The second file (https_custom.pre), is about the SSL configuration : files location for our certificates and proxy.
/etc/nginx/conf.d/https_custom.pre:
mode: "000644"
owner: root
group: root
content: |
# HTTPS server
server {
listen 443 default ssl;
server_name localhost;
error_page 497 https://$host$request_uri;
ssl_certificate /etc/letsencrypt/live/ebcert/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ebcert/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://docker;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Step 3 : Container configuration
packages : package option to grab EPEL (Extra Packages for Enterprise Linux) for our certbot.
container_commands : the following code is divided in 6 steps :
- 00_create_dir : create a directory for certbot
- 10_installcertbotandchmod : download certbot-auto and give permission
- 20_getcert : generate certificate
- 30_link : create a symbolic link between our certificate and the live configuration directory
- 40_config : certificates are now created, so we can rename the .pre to .config
- 50_cronjobsetrenewal : create a cron job
Don't forget to set the EMAIL variable of your environment configuration (from your AWS Console).
packages:
yum:
epel-release: []
container_commands:
00_create_dir:
command: "mkdir -p /opt/certbot"
10_installcertbotandchmod:
command: "wget https://dl.eff.org/certbot-auto -O /opt/certbot/certbot-auto;chmod a+x /opt/certbot/certbot-auto"
20_getcert:
command: "sudo /opt/certbot/certbot-auto certonly --standalone --debug --non-interactive --email ${EMAIL} --agree-tos --domains mywebsite.com \
-d admin.mywebsite.com \
-d test.mywebsite.com \
--expand --renew-with-new-domains --pre-hook \"service nginx stop\""
30_link:
command: "sudo ln -sf /etc/letsencrypt/live/mywebsite.com /etc/letsencrypt/live/ebcert"
40_config:
command: "mv /etc/nginx/conf.d/https_custom.pre /etc/nginx/conf.d/https_custom.conf"
50_cronjobsetrenewal:
command: '(crontab -l ; echo ''0 6 * * * root /opt/certbot/certbot-auto renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start" --force-renew'') | crontab -'
That's it! Commit your changes and deploy it : eb deploy
Complete code :
Resources:
sslSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]}
IpProtocol: tcp
ToPort: 443
FromPort: 443
CidrIp: 0.0.0.0/0
files:
/etc/nginx/conf.d/000_http_redirect_custom.conf:
mode: "000644"
owner: root
group: root
content: |
server {
listen 80;
return 301 https://$host$request_uri;
}
/etc/nginx/conf.d/https_custom.pre:
mode: "000644"
owner: root
group: root
content: |
# HTTPS server
server {
listen 443 default ssl;
server_name localhost;
error_page 497 https://$host$request_uri;
ssl_certificate /etc/letsencrypt/live/ebcert/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ebcert/privkey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://docker;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
packages:
yum:
epel-release: []
container_commands:
00_create_dir:
command: "mkdir -p /opt/certbot"
10_installcertbotandchmod:
command: "wget https://dl.eff.org/certbot-auto -O /opt/certbot/certbot-auto;chmod a+x /opt/certbot/certbot-auto"
20_getcert:
command: "sudo /opt/certbot/certbot-auto certonly --standalone --debug --non-interactive --email ${EMAIL} --agree-tos --domains mywebsite.com \
-d admin.mywebsite.com \
-d test.mywebsite.com \
--expand --renew-with-new-domains --pre-hook \"service nginx stop\""
30_link:
command: "sudo ln -sf /etc/letsencrypt/live/mywebsite.com /etc/letsencrypt/live/ebcert"
40_config:
command: "mv /etc/nginx/conf.d/https_custom.pre /etc/nginx/conf.d/https_custom.conf"
50_cronjobsetrenewal:
command: '(crontab -l ; echo ''0 6 * * * root /opt/certbot/certbot-auto renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start" --force-renew'') | crontab -'
Photo by Paul Esch-Laurent on Unsplash
Sources :
- https://matthewoden.com/setting-up-ssl-on-elastic-beanstalk/
- https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions.html
- https://www.digitalocean.com/community/tutorials/how-to-configure-nginx-with-ssl-as-a-reverse-proxy-for-jenkins
- https://gist.github.com/tony-gutierrez/198988c34e020af0192bab543d35a62a