[PYTHON] Display a web page with FastAPI + uvicorn + Nginx (SSL / HTTPS)

0. Introduction

-Build FastAPI + uvicorn + nginx with docker-compose -Display Web page with FastAPI + uvicorn + Nginx (Templates function by Jinja2)

As a sequel to, I will further edit and add Nginx settings and try to add SSL (HTTPS) and BASIC authentication

  1. SSL

Directory structure (excerpt)

$ tree 
.
├── app
├── docker-compose.yml
└── web
    ├── conf.d
    │   └── app.conf
    └── ssl
        ├── server.crt
        └── server.key

--Fixed docker-compose.yml --Modify / add files in the web directory --Modify conf.d / app.conf --Preparation of certificate / key (ssl / directory)

Need each

the detail is right below:

docker-compose.yml

Fixed as follows:

docker-compose.yml


version: '3'

services:
  web:
    container_name: web
    image: nginx:alpine
    depends_on:
      - app
    ports:
      - "80:80"
      - "${PORT:-8443:443}"
    volumes:
      - ./web/conf.d:/etc/nginx/conf.d
      - ./web/ssl:/etc/nginx/ssl
    networks:
      - nginx_network

  app:
    container_name: app
    image: test_fastapi_app
    build:
      context: ./app
      dockerfile: Dockerfile
    expose:
      - 8000
    networks:
      - nginx_network
    # volumes:
    #   - ./app/app:/app/app
    # command: "uvicorn app.main:app --host 0.0.0.0 --proxy-headers --forwarded-allow-ips * --reload"

networks:
  nginx_network:
    driver: bridge

If you extract only the part where there is a difference,

docker-compose.yml(Excerpt)


services:
  web:
    ports:
      # - "80:80"
      - "${PORT:-8443:443}"
    volumes:
      - ./web/conf.d:/etc/nginx/conf.d
      - ./web/ssl:/etc/nginx/ssl

--Changing port mapping for SSL --Host OS 8443 port (default, set by environment variable) and container (Nginx) 443 port ――It should be noted that using environment variables to set 8443 does not have a deep meaning for personal reasons, so change it as necessary. -See docker-compose document etc. --The web / ssl directory containing the certificate / key is mounted in / etc / nginx / ssl of the Nginx container. --The path filename here will also be used later in the Nginx configuration

web / ssl (key / certificate)

web
└── ssl
    ├── server.crt
    └── server.key

Prepare the ones corresponding to server.crt and server.key in ↑ and place them in the web / ssl directory respectively.

In this example, the script (one-liner) that automatically generated the oleore certificate is placed as it is:

make_key.sh


#!/usr/bin/env sh

openssl req -batch -new -x509 -newkey rsa:4096 -nodes -sha256 \
  -subj /CN=example.com/O=example -days 3650 \
  -keyout ./server.key \
  -out ./server.crt

web/conf.d/app.conf

Rewrite the Nginx configuration file.

conf:conf.d/app.conf


upstream backend {
    server app:8000;
}

server {
    listen 443 ssl;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_protocols        TLSv1.2 TLSv1.3;

    # server_name  _;
    index index.html index.htm;

    location / {
        proxy_set_header    Host    $http_host;
        proxy_set_header    X-Real-IP    $remote_addr;
        proxy_set_header    X-Forwarded-Host      $http_host;
        proxy_set_header    X-Forwarded-Server    $http_host;
        proxy_set_header    X-Forwarded-Server    $host;
        proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto  $scheme;
        proxy_redirect      http:// https://;

        proxy_pass http://backend;
    }

    # log
    # access_log /var/log/nginx/access.log;
    # error_log /var/log/nginx/error.log;
}

server_tokens off;

Critical changes:

--By rewriting the part that was listen 80 as follows, the key / certificate placed by volume mounting in the container is specified and made HTTPS. --By the way, the TLS version is also restricted.

    listen 443 ssl;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_protocols        TLSv1.2 TLSv1.3;

--Modify / add proxy _ *** in location / ――I don't remember much about the details because I did it so much and I had a lot of trial and error. .. .. ――So it may be mixed with unnecessary settings. .. ..

        proxy_set_header    Host    $http_host;
        proxy_set_header    X-Real-IP    $remote_addr;
        proxy_set_header    X-Forwarded-Host      $http_host;
        proxy_set_header    X-Forwarded-Server    $http_host;
        proxy_set_header    X-Forwarded-Server    $host;
        proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto  $scheme;
        proxy_redirect      http:// https://;

For the time being, with this docker-compose up -d If you access https: // localhost: 8443 (port is set by yourself) etc., you can confirm that https communication is enabled.

2. (Bonus) BASIC authentication with Nginx

By the way, I'll apply BASIC authentication

--Generate .htpasswd in advance --For example, ʻecho "user:" $ (openssl passwd password) >> /path/to/.htpasswd --The content of.htpasswditself is the same as Apache httpd etc. --The location is arbitrary to some extent, but this time I placed it inweb / conf.d`:

.
├── app
├── docker-compose.yml
└── web
    ├── conf.d
    |   ├── .htpasswd
    │   └── app.conf
    └── ssl

--Added to web / conf.d / app.conf:

conf:conf.d/app.conf


upstream backend {
    server app:8000;
}

server {
    listen 443 ssl;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_protocols        TLSv1.2 TLSv1.3;

    # basic-auth
    auth_basic "BASIC AUTH";
    auth_basic_user_file /etc/nginx/conf.d/.htpasswd;

    # server_name  _;
    index index.html index.htm;

    location / {
        proxy_set_header    Host    $http_host;
        proxy_set_header    X-Real-IP    $remote_addr;
        proxy_set_header    X-Forwarded-Host      $http_host;
        proxy_set_header    X-Forwarded-Server    $http_host;
        proxy_set_header    X-Forwarded-Server    $host;
        proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto  $scheme;
        proxy_redirect      http:// https://;

        proxy_pass http://backend;
    }

    # log
    # access_log /var/log/nginx/access.log;
    # error_log /var/log/nginx/error.log;
}

server_tokens off;

↑ I added the following:

app.conf (additional part)


    # basic-auth
    auth_basic "BASIC AUTH";
    auth_basic_user_file /etc/nginx/conf.d/.htpasswd;

--The content of ʻauth_basic may appear as a character string when you are asked to authenticate (maybe it depends on the browser), so rewrite it as appropriate. --ʻAuth_basic_user_file is loaded by specifying the path of .htpasswd that is volume mounted in the container.

With this, for example, the authentication can be done as follows:

スクリーンショット 2020-08-16 14-50-02.png

(* This time it's my certificate, so a warning is issued)

reference

--Nginx reverse proxy settings

--Redirect from http to https with Nginx

--BASIC authentication in Nginx

--Generate .htpasswd

--One-liner of my certificate

Recommended Posts

Display a web page with FastAPI + uvicorn + Nginx (SSL / HTTPS)
Display a web page with FastAPI + uvicorn + Nginx (Templates function by Jinja2)
Extract data from a web page with Python
Launch a Python web application with Nginx + Gunicorn with Docker
Get a Python web page, character encode it, and display it
[python, ruby] fetch the contents of a web page with selenium-webdriver
Web App Development Practice: Create a Shift Creation Page with Django! (Shift creation page)
Web scraping with BeautifulSoup4 (layered page)
Build a web application with Django
Web App Development Practice: Create a Shift Creation Page with Django! (Introduction)
[PyQt] Display a multi-axis graph with QtChart
Display matplotlib diagrams in a web application
Play like a web app with ipywidgets
Daemonize a Python web app with Supervisor
[Personal note] Web page scraping with python3
Monitor web page updates with LINE BOT
[Python] A quick web application with Bottle!
Create a simple web app with flask
Run a Python web application with Docker
Create a web service with Docker + Flask
Web scraping with BeautifulSoup4 (serial number page)
Let's make a web framework with Python! (2)
I made a WEB application with Django