05 SSL support for ML API

In this post, we are going to set up a domain for our web app and also provide HTTPS support for better security. Let's add some volume mapping and 443 port mapping in the nginx service and add a new service called certbot in docker-compose-prod.yml

version: '3.9'

services:
  web:
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      - ENV_VAR_NAME=VALUE
    volumes:
      - ./:/app
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"    #new
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d 
      - ./nginx/certbot/www:/var/www/certbot/:ro  #new
      - ./nginx/certbot/conf/:/etc/nginx/ssl/:ro  #new
    depends_on:
      - web
    restart: always

#new
  certbot:
    image: certbot/certbot:latest
    command: certonly --webroot --webroot-path=/var/www/certbot --force-renewal --email [email protected] --agree-tos --no-eff-email -d despam.io
    volumes:
      - ./nginx/certbot/www/:/var/www/certbot/:rw
      - ./nginx/certbot/conf/:/etc/letsencrypt/:rw

NGINX Service: Exposes ports 80 and 443 for HTTP and HTTPS traffic respectively. volumes: Mounts directories from your host machine into the Nginx container. By defining the volumes we are creating a mapping, Whatever we put in these volumes will be pushed in the docker container. If we put something in the docker container it will appear in the host Virtual machine.  :ro at the end of the volume declaration. ro means "read-only". The container will never have the right to update a file in this folder.

Certbot Service: certonly: Requests and obtains SSL/TLS certificates. --webroot --webroot-path=/var/www/certbot: Specifies webroot authentication method and path. --agree-tos --no-eff-email: Agrees with the terms of service and disables the EFF email subscription. -d despam.io: Specifies the domain for which to obtain the certificate.

Now, let's modify the nginx/nginx.conf file to accommodate for certbot challenge.

worker_processes 4;

events { worker_connections 1024; }

http {
    sendfile on;
    server_tokens off;

    server {
        listen 80;
        server_name despam.io;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            proxy_pass http://web:8000;
            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;
        }
    }
}

worker_processes: This directive sets the number of worker processes that Nginx should use. In this case, you've specified 4 worker processes. Nginx uses multiple worker processes to handle incoming client requests efficiently. The worker_connections directive sets the maximum number of connections each worker process can handle.
server: This block defines the configuration for a specific server block. In this case, it's the configuration for the HTTP server on port 80. listen 80; listen [::]:80;: These directives specify that the server should listen on both IPv4 and IPv6 addresses on port 80. location /.well-known/acme-challenge/ { ... }: This location block handles requests to the .well-known/acme-challenge/ path, which is used by Certbot for domain validation during certificate issuance. It specifies that the files for validation are located in the /var/www/certbot directory.

location / { ... }: This location block handles all other requests and acts as a reverse proxy to your FastAPI application running on port 8000. It passes the requests to the web service defined in your Docker Compose configuration. The proxy_set_header directives are used to forward necessary headers to the FastAPI application. Now, we can try to up our services. If you face a permission denied error the you will have to change the necessary permissions of filesystem as below.

despam@mlapi:~/mlapi-spam-prediction$ docker-compose -f docker-compose-prod.yml up --build
[+] Building 0.3s (5/9)                                                                                                                                                   
------
failed to solve: error from sender: open /home/despam/mlapi-spam-prediction/nginx/certbot/conf/accounts: permission denied

despam@mlapi:~/mlapi-spam-prediction$ sudo chown -R despam:despam /home/despam/mlapi-spam-prediction/nginx/certbot
[sudo] password for despam:

Now, We can definitely up the services, additionally, look at the logs very carefully. Logs will inform if we have successfully received the certificates.

despam@mlapi:~/mlapi-spam-prediction$ docker-compose -f docker-compose-prod.yml up --build
[+] Building 0.3s (10/10) FINISHED

mlapi-spam-prediction-certbot-1  | Account registered.
mlapi-spam-prediction-certbot-1  | Requesting a certificate for despam.io
# Below are hits for the well-known challenge.
mlapi-spam-prediction-nginx-1    | 23.178.112.209 - - [13/Sep/2023:04:58:57 +0000] "GET /.well-known/acme-challenge/K2FSI9az7Zdbi6F0I2WysLVHrqzfcugCJWFwMBPOh-Q HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"

mlapi-spam-prediction-web-1      | INFO:     Started server process [7]
mlapi-spam-prediction-web-1      | INFO:     Waiting for application startup.
mlapi-spam-prediction-web-1      | INFO:     Application startup complete.
mlapi-spam-prediction-certbot-1  |
mlapi-spam-prediction-certbot-1  | Successfully received certificate.
mlapi-spam-prediction-certbot-1  | Certificate is saved at: /etc/letsencrypt/live/despam.io/fullchain.pem
mlapi-spam-prediction-certbot-1  | Key is saved at:         /etc/letsencrypt/live/despam.io/privkey.pem

Once everything works correctly we can manually check if the certificate is synced to our local VM directory. We can also open the file and check the contents using the below code.

despam@mlapi:~/codeshare$ cd nginx/certbot/conf/
despam@mlapi:~/codeshare/nginx/certbot/conf$ ls
accounts  archive  live  renewal  renewal-hooks

#to see private key
despam@mlapi:~/codeshare/nginx/certbot/conf$ sudo cat ./live/despam.io/privkey.pem  

Warning: if we repeatedly keep starting and shutting down the certbot service then we can hit the max rate-limit of certificate generation. So, once the certificate is generated switch to renew command in the docker-compose-prod.yml file.

certbot:
    image: certbot/certbot:latest
    command: renew   #use renew command after your ssl is configured correctly and you can visit on HTTPS
    volumes: 
      - ./nginx/certbot/www/:/var/www/certbot/:rw
      - ./nginx/certbot/conf/:/etc/letsencrypt/:rw

Also, now we can start redirecting requests to https version with the ssl key. Modify the nginx.conf as below:
 

worker_processes 4;

events {
    worker_connections 1024;
}

http {
    sendfile on;
    server_tokens off;

    server {
        listen 80;
        server_name despam.io;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

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


    server {
        listen 443 ssl;
        server_name despam.io;

        ssl_certificate /etc/nginx/ssl/live/despam.io/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/live/despam.io/privkey.pem;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            proxy_pass http://web:8000;
            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;
        }
    }
}

Finally, we can do a docker-compose -f docker-compose-prod.yml up --build -d to start the server in the background.

Challenge: Set up a cronjob that renews the certificate every 3 months.

Challenge Solution: Please try yourself first.

despam@mlapi:~/codeshare$ crontab -e


#add this line
0 0 1 */2 * docker-compose -f /home/full_path_to_prod_.yml run certbot renew


 

FastAPITutorial

Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.

We know, we might make it hard for you but definitely worth the efforts.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated.

© Copyright 2022-23 Team FastAPITutorial