"An SSL certificate is a digital certificate that authenticates a website's identity and enables an encrypted connection. SSL stands for Secure Sockets Layer, a security protocol that creates an encrypted link between a web server and a web browser." - kaspersky
Majority of websites built today use SSL certificates, and if you are building a website in 2024, you will need to learn how to get one too!
There are several approaches to getting an SSL certificate for your domain. But in this article, we will take a look at generating SSL certificates with Let's Encrypt - a nonprofit certificate authority (CA) that provides free SSL/TLS certificates for enabling HTTPS (secure HTTP) on websites. In addition to this, automating the renewal process with Certbot and using a Docker image that was built on top of the official Nginx Docker images.
Prerequisites
To successfully complete this guide, you should be familiar with the following:
- Docker and Docker Compose
- Virtual machines from cloud providers, e.g. Azure VMs, AWS EC2 etc.
- Linux
It is a good idea to containerize your app with Docker, but if you don't want to, you can still follow along just fine!
Configuring Certbot and Nginx
In the root directory of your project, please create a docker-compose file.
touch docker-compose.yml
Then paste the following code in the docker-compose.yml file.
version: '3'
services:
nginx:
image: jonasal/nginx-certbot:latest
restart: unless-stopped
environment:
- CERTBOT_EMAIL
env_file:
- ./nginx-certbot.env
ports:
- 80:80
- 443:443
volumes:
- nginx_secrets:/etc/letsencrypt
- ./user_conf.d:/etc/nginx/user_conf.d
volumes:
nginx_secrets:
The code we just pasted above is a YAML configuration for our Nginx docker image. It makes use of the jonasal/nginx-certbot:latest
image that has certbot built on top of Nginx.
Now, let us create a nginx-certbot.env
file to store the variables needed for our Nginx image.
touch nginx-certbot.env
Then paste this into your nginx-certbot.env
file
# Required
CERTBOT_EMAIL=your@email.com
# Optional (Defaults)
DHPARAM_SIZE=2048
RSA_KEY_SIZE=2048
ELLIPTIC_CURVE=secp256r1
RENEWAL_INTERVAL=8d
USE_ECDSA=1
STAGING=1
Next, we create a server configuration file to configure our Nginx server.
touch server.conf
server {
# Listen to port 443 on both IPv4 and IPv6.
listen 443 ssl default_server reuseport;
listen [::]:443 ssl default_server reuseport;
# Domain names this server should respond to.
server_name yourdomain.com www.yourdomain.com;
# Load the certificate files.
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
# Load the Diffie-Hellman parameter.
ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;
# Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
return 200 'Let\'s Encrypt certificate successfully installed!';
add_header Content-Type text/plain;
}
Now, all we need to do is build the docker image and run it on our server.
docker compose -f docker-compose.yml build
Next, move the images to the server whose IP address is mapped to a domain name you own and run this command:
docker compose -f docker-compose.yml up
The command starts the server and Certbot automatically creates new SSL certificates for us. Try navigating to the domain name mapped to the server, you should see a "not trusted" page.
This is because we used "test" certificates to make sure that our configuration works fine. Issuing of live certificates is rate limited, so using test/staging certificates first is a better approach to ensure that it runs fine.
Now, we will update our nginx-certbot.env
file to enable us generate live SSL certificates. Change the staging value from 1 to 0.
...
STAGING=0
Stop the server with ctrl+c and run the command below to regenerate SSL certificates.
docker exec -it <container_name> /scripts/run_certbot.sh force
Start up the server again
docker compose -f docker-compose.yml up
Now, navigate to your domain name. Your site should open up just fine!
Certbot and Nginx configurations for Dockerized apps
Assuming you dockerized your app, you will need to make a few more changes to what we have done so far.
First, we update the docker-compose.yml file so that the Nginx service depends on your app.
...
volumes:
- nginx_secrets:/etc/letsencrypt
- ./user_conf.d:/etc/nginx/user_conf.d
depends_on:
- web
web:
container_name: core_app
build: .
restart: always
...
volumes:
nginx_secrets:
Where web
is the name of your app service.
Then, we update the server.conf
file to point to our web server.
upstream webapp {
server core_app:8000;
# core_app is the container name of the web server
# 8000 is the port for the web container
}
server {
# Listen to port 443 on both IPv4 and IPv6.
listen 443 ssl default_server reuseport;
listen [::]:443 ssl default_server reuseport;
# Domain names this server should respond to.
server_name yourdomain.com www.yourdomain.com;
server_tokens off;
client_max_body_size 20M;
# Load the certificate files.
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
# Load the Diffie-Hellman parameter.
ssl_dhparam /etc/letsencrypt/dhparams/dhparam.pem;
# Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
location / {
proxy_pass http://webapp;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
Now, Nginx should be routing traffic directly to your web server (docker container).
Conclusion
We have successfully created free SSL certificates for our domain name that renew automatically, so we never have to worry about it unless our server goes down.
In addition to this article, you can learn more about this approach here: https://github.com/JonasAlfredsson/docker-nginx-certbot/tree/master
Thanks for reading.
¡Hasta luego!