NGINX and HTTPs With Let’s Encrypt, Certbot, and Cron Dockerization In Production
Automatically create and renew website SSL certificates using the Let's Encrypt and its client certbot. Nginx server dockerization and crontab configuration.
Join the DZone community and get the full member experience.
Join For FreeDocker is a popular open-source containerization platform and it frees your hands to build your applications in development and production. In this post, I'm going to walk you through how to build a production-grade HTTPs secured Nginx server with Docker, Docker Compose, Let’s Encrypt (its client certbot). Let’s Encrypt certificates last 90 days and will need to be renewed after the certificate expires. So I will also provide details to script the renewal in crontab in Docker container.
1. Basic Example
In development, we need a basic Nginx container without HTTPs to fast setup our local test environment. I use Nginx official docker image and wrap up all the stuff with docker-compose.
x
version'3.4'
services
nginx
container_name nginx
image nginx stable
restart always
volumes
./nginx/config/nginx.conf:/etc/nginx/nginx.conf
./nginx/config/conf.d/local:/etc/nginx/conf.d
/tmp/logs/nginx:/var/log/nginx
./nginx/html:/var/www/public/content
ports
"80:80"
I choose to use nginx.conf along with conf.d folder to manage all the configurations. So nginx.conf is for generic configuration while conf.d folder is for site specific configurations like below.
xxxxxxxxxx
server {
listen 80;
server_name localhost;
gzip on;
gzip_comp_level 2;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types application/x-javascript application/javascript application/xml application/json text/xml text/css text$
client_body_timeout 12;
client_header_timeout 12;
reset_timedout_connection on;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
server_tokens off;
client_max_body_size 50m;
expires 1y;
access_log off;
log_not_found off;
root /var/www/public/content;
location / {
proxy_pass http://ui:3000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $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 $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
}
2. Configure HTTPs
2.1 Let’s Encrypt
To enable HTTPS on your website, you need to get a certificate from a Certificate Authority (CA). Let’s Encrypt is a free, automated, and open certificate authority (CA). In order to get a certificate for your website’s domain from Let’s Encrypt, you have to demonstrate control over the domain. With Let’s Encrypt, you do this using software that uses the ACME protocol which typically runs on your web host.
2.1 Certbot
we recommend using Certbot needs port 80 to be enabled, so the host firewall should allow incoming traffic on port 80 (HTTP) from anywhere. I'm using Oracle cloud, I need to open up port 80 on the security list, and also the VM firewall(as below):certbot-auto
, which automates the process of installing Certbot on your system.The certbot-auto
wrapper script installs Certbot, obtaining some dependencies from your web server OS and putting others in a python virtual environment. You can download and run it as follows. In addition,
xxxxxxxxxx
sudo firewall-cmd --permanent --zone=public --add-port=80/tcp
sudo firewall-cmd --reload
2.3 Setup NGINX
We need to configure ports, domain names, certificates as well as reverse proxy mappings for the servers. Here is a quick example grabbed from my project which contains two servers: one is for HTTPs and another is for HTTP that will redirect to HTTPs.
x
server {
listen 443 ssl default_server;
server_name yourcompany.com;
gzip on;
gzip_comp_level 2;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types application/x-javascript application/javascript application/xml application/json text/xml text/css text$
client_body_timeout 12;
client_header_timeout 12;
reset_timedout_connection on;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
server_tokens off;
client_max_body_size 50m;
expires 1y;
access_log off;
log_not_found off;
root /var/www/public/content/default;
ssl_certificate /etc/letsencrypt/live/yourcompany.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourcompany.com/privkey.pem;
location / {
proxy_pass http://ui:3000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $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 $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
}
server {
listen 80;
server_name yourcompany.com;
return 301 https://$host$request_uri;
}
2.4 Automatically Renew Certificates
We just need to add the following script to crontab, which will run monthly to check and renew the certificate.
xxxxxxxxxx
@monthly /usr/local/bin/certbot-auto renew --nginx >> /var/log/cron.log 2>&1
2.5 Wrap all in Docker
We will need a Dockerfile and docker-compose.yml.
Dockerfile
FROM nginx:stable
ARG CERTBOT_EMAIL=info@domain.com
ARG DOMAIN_LIST
RUN apt-get update \
&& apt-get install -y cron certbot python-certbot-nginx bash wget \
&& certbot certonly --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \
&& rm -rf /var/lib/apt/lists/* \
&& echo "PATH=$PATH" > /etc/cron.d/certbot-renew \
&& echo "@monthly certbot renew --nginx >> /var/log/cron.log 2>&1" >>/etc/cron.d/certbot-renew \
&& crontab /etc/cron.d/certbot-renew
VOLUME /etc/letsencrypt
CMD [ "sh", "-c", "cron && nginx -g 'daemon off;'" ]
docker-compose.yml
We use network mode - host at the time of docker build so that it can share host network, which is quite tricky because the port mapping(80,443) are not ready at building phrase. Otherwise, running certbot-auto will fail due to HTTP port 80 is not reachable.
x
version'3.4'
services
nginx
container_name nginx
build
context ./nginx
network host
args
#replace with your own email CERTBOT_EMAIL=hello@yourcompany.com
#replace with your own domains DOMAIN_LIST=yourcompany.com,api.yourcompany.com,www.yourcompany.com
restart always
volumes
./nginx/config/conf.d/prod:/etc/nginx/conf.d
letsencrypt:/etc/letsencrypt
ports
"80:80"
"443:443"
That's it. You're good to go. Contact me if you need the whole source code.
Published at DZone with permission of Kunkka Li. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments