HTTPS on Docker Containers using Nginx and LetsEncrypt

HTTPS on Docker Containers using Nginx and LetsEncrypt

Enabling SSL certificate on your docker containers

·

4 min read

Introduction

By default, when docker containers are deployed they run on normal HTTP but most times it's better to run web services using HTTPS which is a secure protocol over the internet. So we're going to see how to enable an SSL certificate on docker containers using LetsEncrypt and Certbot

Prerequisites

Certbot requires a live domain for it to be assigned an SSL certificate to it, you can obtain a domain at your chosen registrar, In this article, I'll be using my own domain mrshanas.com. I've created a subdomain for it.

Also, a virtual server is required with docker and docker-compose installed in it, I'm using Digitalocean droplets that have docker installed in it from the market place but you can use any of your choices but make sure the above-mentioned are installed

Getting Started

Create a simple project with a Dockerfile and docker-compose.yaml files and a config directory that contains an empty nginx.conf .

Dockerfile
FROM nginx:1.21.1-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.conf /etc/nginx/conf.d

The Dockerfile contains a script to pull the Nginx image, delete the default config file, and then copy the defined config file from our project

nginx.conf
server {
    listen 80;
    listen [::]:80;

    server_name test.mrshanas.com;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        allow all;
        root /tmp/acme-challenge;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

This will enable Nginx to run locally at HTTP, and listen on port 80, and all incoming requests will be redirected to https.

the line listen [::]:80 means for IPv6 addresses.

Change the server name to the domain you want to enable the certificate. For me, it is test.mrshanas.com

Certbot makes a challenge to the server by making a request to the special URL ${your_domain}/.well-known/acme-challenge/ so we want to enable it to access the URL and verify the challenge for it to generate the certificate

docker-compose.yml
version: "3"

services:
  nginx:
    container_name: "nginx_server"
    build:
      context: ./config
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./config:/config
      - /etc/letsencrypt:/etc/letsencrypt:ro
      - /tmp/acme-challenge:/tmp/acme-challenge
    networks:
      - app
    restart: always

  letsencrypt:
    container_name: "certbot"
    image: certbot/certbot
    command: sh -c "certbot certonly --webroot -w /tmp/acme-challenge/ -d test.mrshanas.com --text --agree-tos --email shanas@mrshanas.com --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http"
    entrypoint: ""
    volumes:
      - "/etc/letsencrypt:/etc/letsencrypt"
      - "/tmp/acme-challenge:/tmp/acme-challenge"
    environment:
      - TERM=xterm

networks:
  app:
    driver: bridge

The above file defines two docker containers nginx and letsencrypt that will make the task successful.

Running Containers on HTTP

The Nginx container is based on the Dockerfile we created and exposes ports 80 and 443 and volumes that will contain the generated SSL certificates

The certificates will be stored in /etc/letsencrypt

Now run docker-compose up --build nginx and visit your domain name and If it's successful you will see like below

That means the container ran but it is not running on HTTPS, so let's see how to make it secure.

The letsencrypt container pulls the certbot image from docker hub and runs the command certbot certonly --webroot -w /tmp/acme-challenge/ -d test.mrshanas.com --text --agree-tos --email shanas@mrshanas.com --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http

Make sure to replace the domain name with the one you want to configure SSL and the email with your email

Generating SSL Certificates with certbot image

Open a new terminal while the Nginx container is running and run docker-compose up --build letsencrypt , If it's successful you will see the output below

That means certbot was successful and generated certificates that are stored in the directory etc/letsencrypt/live/<your_domain> .

Enabling HTTPS on the domain

Add the following lines below the server block in the nginx.conf

server {
    listen 443 ssl;
    listen [::]:443 ssl http2;

    server_name test.mrshanas.com;

    ssl_certificate /etc/letsencrypt/live/test.mrshanas.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.mrshanas.com/privkey.pem;
}

Replace test.mrshanas.com with your domain whose the certificate belongs to, and stop the Nginx container then rebuild it again with the command docker-compose up --build -d nginx

-d flag means the container will run in detached mode

If the above command was successful then navigate to your domain and you should see as below

That means the process was successful and your domain is running on HTTPS.

Conclusion

With the configuration, you can deploy your projects using Docker and make them run on HTTPS with ease. You can find the complete sample code in my GitHub repo.