How to Secure Your Dockerized Django App with Nginx, Certbot, and HTTPS

Django Docker

A complete guide to deploying a secure, Dockerized Django app. Learn how to configure Nginx and use Certbot for HTTPS certificates, ensuring robust security for your web app.

Introduction

When deploying web applications, securing the connection between the server and clients is essential. In this post, we’ll walk through configuring Nginx to serve as a reverse proxy for your Dockerized Django app, securing your application with HTTPS using Certbot, and automating certificate renewals to keep everything up-to-date.

Add Nginx to compose.yaml

Begin by defining your docker-compose.yml file to include an Nginx service that will serve as a reverse proxy for your Django application:

nginx:
    build:
        context: ./nginx
        dockerfile: Dockerfile
    image: nginx
    container_name: container_name
    volumes:
      - static-volume:/var/www/app/staticfiles
      - media-volume:/var/www/app/media
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - /etc/letsencrypt:/etc/letsencrypt
      - /var/www/certbot:/var/www/certbot
    ports:
      - "80:80"
      - "443:443"
    networks:
      - production-network
    depends_on:
- app


volumes:
  db-volume:
    name: db_name
  static-volume:
    name: static
  media-volume:
    name: media

This configuration does the following: • Exposes ports 80 and 443 for HTTP and HTTPS traffic. • Mounts nginx.conf to configure Nginx, and sets up folders for Certbot to store SSL certificates.

Configure Nginx

Next, create an Nginx configuration file (nginx.conf) on the host. This file will define how Nginx forwards traffic to your Django app, handle SSL certificates, and manage redirections from HTTP to HTTPS.

    # Load Balancing Configuration
    upstream backend {
        server app:8000;
    }

    # Server Blocks
    server {
        listen 80;
        server_name example.com www.example.com;

        # Route for Certbot challenge
        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

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

    server {
        listen 443 ssl;
        server_name example.com www.example.com;

        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $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;
        }

        location /static/ {
            alias /var/www/app/staticfiles/;
            # Cache Configuration
            #expires 30d;
            #add_header Cache-Control "public";
        }

        location /media/ {
            alias /var/www/app/media/;
            # Cache Configuration
            #expires 7d;
            #add_header Cache-Control "public";
        }
    }
}

In this setup:

  • Port 80 handles HTTP requests and redirects them to HTTPS.
  • Port 443 serves secure connections with the SSL certificates generated by Certbot.
  • The .well-known/acme-challenge/ directory is required for the initial Certbot challenge to verify domain ownership.

Set Up Certbot to Secure Your Site with HTTPS

Install Certbot and generate SSL certificates to secure your site:

# Install and update snapd core
sudo snap install core; sudo snap refresh core

# Remove older version of Certbot if installed on server
sudo apt remove certbot

# Install Certbot package
sudo snap install --classic certbot

# Link the Certbot command from the snap install directory to your path
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Obtaining SSL Certificate
sudo certbot certonly --webroot -w /var/www/certbot  -d example.com -d www.example.com

Set Certbot Auto-Renewal

To keep your SSL certificate valid, set up a cron job or Docker container to renew it automatically. Create the Deploy Hook Script to Restart Nginx Container after renew:

sudo nano /usr/local/bin/deploy-hook.sh

Add commands to the script :

#!/bin/bash
# Script to restart Nginx and adjust permissions after Certbot renewal

# Path to docker-compose.yaml (update this with your actual path)
DOCKER_COMPOSE_PATH="/path/to/your/docker-compose.yaml"

# Restart the Nginx container to load the new certificate
docker-compose -f $DOCKER_COMPOSE_PATH -p project_name restart nginx

echo "Deployment hook completed: Certificates renewed and Nginx restarted."

Make the Script Executable :

sudo chmod +x /usr/local/bin/deploy-hook.sh

Set Up Certbot to Use the Deploy Hook Script:

# Open crontab for editing
sudo crontab -e

# Add this line
0 0 * * * certbot renew --quite --deploy-hook /usr/local/bin/deploy-hook.sh

This cron job will run the renewal script daily. If Certbot detects that the certificates are close to expiring, it will renew them, and Nginx will be reloaded with the updated certificates.

Test the Script renew the certificates with --force-renewal command :

sudo certbot renew --force-renewal --deploy-hook /usr/local/bin/deploy-hook.sh

Reload Nginx container and go online to check if the site works :

# Reload nginx container to load the new conf
docker compose -p django-project restart nginx 

Conclusion

With these steps, you've now secured your Dockerized Django app using Nginx and Certbot for HTTPS. Setting up Nginx as a reverse proxy and using Certbot to manage SSL certificates protects your application and ensures your users' data remains encrypted. For a production-ready setup, automated certificate renewal keeps your deployment secure with minimal maintenance effort.

0