How to get a SSL certificate running in AWS Elastic Beanstalk using Certbot
When you want to have a SSL certificate to ensure encrypted communication between your applications and the user, AWS gives you the option to use Amazon Certificate Manager (ACM) which you can use only by using Elastic Load Balancer (ELB) too, which has a cost of around 18$/month. If you don’t need the advantages of having a load balancer and you want to save some money, here you have a guide on how to have a free SSL certificate with Let’s Encrypt in your Elastic Beanstalk (EB) for Amazon Linux 2.
Let’s first refresh some concepts:
What is Elastic Beanstalk?
Elastic Beanstalk is a PaaS (Platform as a Service) that allows developers to deploy and scale web applications easily.
What is Certbot?
It’s a tool to enable HTTPS, deploying a Let’s Encrypt certificate easily and for free.
Why shouldn’t we install the certificate like on a normal server?
Every time a new application version is deployed to Elastic Beanstalk or the environment configuration is updated, Nginx/Apache configuration is overwritten, so we are not dealing with a normal EC2 machine where you can connect and install the certificate as in a typical server (or you would have to do it every time, which of course, is not ideal).
To make this work, we need to add to our project some files that Elastic Beanstalk will execute every time the application is deployed.
Summarizing the steps we need to make this work are:
- Install Certbot in Amazon Linux 2
- Open 443 port
- Configure the certificate in Nginx/Apache
- Automatically renew the certificate
Please remember, the domain you want to configure needs to point to the server. So the http connection should be already working to continue with the steps.
Let’s check step by step how to get it working.
1. Install Certbot in Amazon Linux 2
When customizing the machine Elastic Beanstalk deploys, there is a way to run scripts to change the standard configuration. These scripts are known as ebextensions.
Ebextensions are written in YAML or JSON, you can read more about it here (https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/ebextensions.htm)
First of all, you need to create a .ebextensions folder in the root of your project and create a file into it, with .config extension. The script files are executed alphabetically, so it’s recommended to add a number in front of the filename.
For our example we will use 00_install_certbot.config
In this script we download, install and enable Epel (Certbot needs it to run in Amazon Linux 2). In the last step, we finally install Certbot. If you are using Apache, replace python2-certbot-nginx with python2-certbot-apache
When Certbot is installed, it creates the folder /etc/letsencrypt/, so we add the line test: test ! -d “/etc/letsencrypt/”, so if the folder exists, the test fails and the step is not executed, avoiding reinstalling it.
2. Open port 443 in the application security group
The security group created by default in Elastic Beanstalk only has the ports 80 and 22 open. HTTPS default port is 443, so we need to open it. If you don’t, when you try to connect though HTTPS you’ll get a timeout error.
Since the security group is overwritten every time the application is deployed, as in step 1, we need to add to a .config file the following content, you can use the one you already created or a new one.
For our example we will create a new one 01_open_https_port.config
3. Get the certificate and configure Nginx/Apache
In Elastic Beanstalk we can write prebuilt, redeploy and post deploy hooks.
To understand the deployment flow, please take a look to the image below:
For more information please read: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/platforms-linux-extend.html
We would need to run a script during the post deploy stage, to ensure that Certbot Nginx configuration remains.
Therefore, we need to create a folder .platform/hooks/postdeploy in the root of our project, with a .sh file inside it. The recommendation for the name is the same as ebextensions files, a number in the front.
For example: 00_get_certificate.sh
Replace DOMAIN with the instance domain you want to create the certificate for. You can add as many domains you want separating them using a comma.
Replace EMAIL with the email you want for the certificate.
If you use Apache, replace — nginx by — apache
When the script is executed, Certbot configures Nginx or Apache automatically.
Now we have the script file created but we have to grant it executable permissions in your Elastic Beanstalk instance. We have to add the next code to our ebextensions file (again existing or new).
In our example: 02_grant_executable_rights.config
4. Add a task to renew the certificate automatically
Certbot recommends renewing the certificate twice a day, so we can schedule this task using a Cron job.
To do this, we need to add this block to our ebextensions script file.
In our example 03_renew_ssl_certificate_cron_job.config
This code will create a file in /tmp folder, called renew_cert_cron with the content “0 1,13 * * * certbot renew”. So the command will be executed as sudo, all days at 1:00 and 13:00.
Now we need to add this to crontab, so we need to add the next piece of code to 03_renew_ssl_certificate_cron_job.config file.
After all these steps your project structure should looks like this:
And with this we are good to go!
Since some times things go wrong, here some tips for debugging:
How to check problems and useful commands
After you have added all this to your project you only need to zip the code (if it’s a jar or war project, you need to zip the jar or war file, .ebextensions folder, and .platform folder) and deploy it to the Elastic Beanstalk instance.
To check everything went fine, or in case of error, check why it failed, you need to connect to your instance. To do this I recommend using the eb cli, and execute “eb ssh” command. (How to install eb cli)
When you are connected you can perform some checks.
- To check if the ebextension script was executed properly, you can check /var/log/cfn-init.log. You will see something like this:
[INFO] -----------------------Starting build-----------------------[INFO] Running configSets: Infra-EmbeddedPostBuild[INFO] Running configSet Infra-EmbeddedPostBuild[INFO] Running config postbuild_0_people[INFO] Command 00_download_epel succeeded[INFO] Command 10_install_epel_release succeeded[INFO] Command 20_enable_epel succeeded[INFO] Command 30_install_certbot succeeded[INFO] Running config postbuild_1_people[INFO] Command 00_permission_hook succeeded[INFO] Running config postbuild_2_people[INFO] Command 10_create_cert_crontab succeeded[INFO] Command 20_delete_cronjob_file succeeded[INFO] ConfigSets completed[INFO] -----------------------Build complete-----------------------
- To check if the hook was executed, you can check /var/log/eb-hooks.log, and you will see something like this:
[INFO] Running command .platform/hooks/postdeploy/00_get_certificate.sh[INFO]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Congratulations! You have successfully enabled https://DOMAIN_1,https://DOMAIN_2- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -IMPORTANT NOTES:- Congratulations! Your certificate and chain have been saved at:/etc/letsencrypt/live/DOMAIN.com/fullchain.pemYour key file has been saved at:/etc/letsencrypt/live/DOMAIN.com/privkey.pemYour cert will expire on 2020-11-15. To obtain a new or tweakedversion of this certificate in the future, simply run certbot againwith the "certonly" option. To non-interactively renew *all* ofyour certificates, run "certbot renew"- If you like Certbot, please consider supporting our work by:Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donateDonating to EFF: https://eff.org/donate-le
- To check all deployment steps, you should check /var/log/eb-engine.log
- To check if the cronjob was added successfully, you need to execute the command “sudo crontab -l”, its result should show “0 1,13 * * * certbot renew — no-self-upgrade”.
Others useful commands are:
- sudo certbot certificates: To check the certificates installed in the machine
- sudo certbot renew — dry-run: To simulate the certificate renewal and check if there is any problem
If you want to use CodeBuild to deploy the application, you need to to ensure that .platform and .ebextensions folders are being copied to the artifact in CodeBuild.
In Java, for example, we can add this to our buildspec file:
post_build:commands:....- mkdir ./deploy- cp backend/target/*.jar ./deploy/- cp -R ./.platform/ ./deploy/- cp -R ./.ebextensions/ ./deploy/artifacts:files:- '*.jar'- '.platform/**/*'- '.ebextensions/**/*'base-directory: 'deploy'