Настроить Nginx в качестве HTTPS-прокси для вашего REST приложения Python на Flask и Waitress на Windows Server 2019

#17  понедельник, 3 июля 2023 г.  13 минут(ы)  1165 слов

Настройка Nginx в качестве HTTPS прокси на Windows Server включает в себя установку и конфигурацию Nginx, настройку SSL для HTTPS и настройку обратного прокси. Это очень похоже на процесс для Linux, но есть некоторые различия в установке и настройке.

Установка Nginx

Загрузите Nginx для Windows с официального сайта. Затем извлеките архив в нужное место, например C:\nginx.

См.: Установка Nginx на Windows 10, Windows 11, Windows Server 2019

Настройка SSL для HTTPS

Вам нужно получить сертификат SSL и ключ. Вы можете приобрести их у доверенного поставщика или получить бесплатные сертификаты от Let's Encrypt.

См.: Настройка Certbot и Nginx для работы HTTPS соединений в Windows

Допустим, вы сохраните их в следующих местах:

C:\nginx\cert\myapp.crt
C:\nginx\cert\myapp.key

Настройка Nginx

Теперь вам нужно настроить Nginx, чтобы использовать SSL и перенаправлять запросы на ваше приложение Flask.

См.: Настройка Nginx для обработки HTTP, HTTPS соединений в Windows

Редактируйте файл C:\nginx\conf\nginx.conf следующим образом:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        return 301 https://$host$request_uri;
    }

    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate     C:/nginx/cert/myapp.crt;
        ssl_certificate_key C:/nginx/cert/myapp.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        gzip  on;

        location / {
            proxy_pass http://localhost:8080;
            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-Host $host:$server_port;
            proxy_set_header X-Forwarded-Port $server_port; 

        }
    }
}

Давайте разберем каждую директиву внутри блока "location":

  • proxy_pass http://localhost:8080; Эта директива указывает адрес бэкэнд-сервера http://localhost:8080, на который будут перенаправляться входящие запросы.

  • proxy_set_header Host $host; Эта директива устанавливает заголовок "Host" в перенаправляемом запросе со значением из исходного заголовка "Host". Она обеспечивает передачу правильного имени хоста на бэкэнд-сервер.

  • proxy_set_header X-Real-IP $remote_addr; Эта директива устанавливает заголовок "X-Real-IP" в перенаправляемом запросе со значением IP-адреса клиента, совершающего запрос. Она может быть полезна для логирования или определения IP-адреса клиента на бэкэнд-сервере.

  • proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; Эта директива устанавливает заголовок "X-Forwarded-For" в перенаправляемом запросе. Если заголовок "X-Forwarded-For" уже присутствует, то она добавляет IP-адрес клиента к существующему значению. Этот заголовок обычно используется для отслеживания исходного IP-адреса клиента при прохождении через промежуточные прокси.

  • proxy_set_header X-Forwarded-Host $host:$server_port; Эта директива устанавливает заголовок "X-Forwarded-Host" в перенаправляемом запросе со значением из исходного заголовка "Host", за которым следует номер порта сервера.

  • proxy_set_header X-Forwarded-Port $server_port; Эта директива устанавливает заголовок "X-Forwarded-Port" в перенаправляемом запросе со значением номера порта сервера.

Вместе эти директивы настраивают обратный прокси для передачи оригинального запроса на бэкэнд-сервер, указанный как http://localhost:8080, с сохранением соответствующих заголовков для правильной работы и идентификации.

В этой конфигурации все запросы HTTP перенаправляются на HTTPS, а запросы HTTPS передаются на ваше приложение Flask, которое, как предполагается, работает на порту 8080.

Запуск Nginx

Чтобы запустить Nginx, выберите меню "Пуск", введите cmd (для запуска CMD.EXE) или powershell (для запуска PowerShell) и нажмите "Запуск от имени администратора" в появившемся контекстном меню.

В командной строке, перейдите в директорию C:\nginx\ и введите start nginx.

cd C:\nginx
start nginx

Теперь ваше приложение Flask должно быть доступно по HTTPS через ваш сервер Nginx. Если вам нужно остановить Nginx, вы можете использовать команду nginx -s quit в командной строке.

nginx -s quit

Простое приложение на Python, Flask и Waitress

Вот пример простого REST приложения на Flask и Waitress.

Сначала установим необходимые библиотеки через pip:

pip install flask waitress

Далее, создадим простое Flask приложение с одним маршрутом:

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api', methods=['GET'])
def hello_world():
    return jsonify(message='Hello, World!')


if __name__ == '__main__':
    from waitress import serve
    serve(app, host="0.0.0.0", port=8080)

Это пример приложения слушает на порту 8080 и возвращает приветственное сообщение при обращении к маршруту '/api'.

Вы можете запустить приложение из командной строки:

python app.py

Теперь, при переходе на http://localhost:8080/api, вы получите ответ { "message": "Hello, World!" }.

Если вы хотите добавить больше маршрутов или функциональности, вы можете сделать это, добавив больше маршрутов и функций в ваше приложение Flask.

REST приложение на Python, Flask и Waitress

Мы определим несколько базовых REST маршрутов, которые будут возвращать некоторые данные в формате JSON.

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/tasks', methods=['GET'])
def get_tasks():
    tasks = [
        {"id": 1, "title": "Buy groceries", "completed": False},
        {"id": 2, "title": "Study for test", "completed": True},
    ]
    return jsonify(tasks)

@app.route('/api/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
    tasks = [
        {"id": 1, "title": "Buy groceries", "completed": False},
        {"id": 2, "title": "Study for test", "completed": True},
    ]
    for task in tasks:
        if task["id"] == task_id:
            return jsonify(task)
    return jsonify({"error": "Task not found"}), 404


if __name__ == '__main__':
    from waitress import serve
    serve(app, host="0.0.0.0", port=8080)

В этом примере мы имеем два маршрута. Один для получения всех задач (GET /api/tasks), и один для получения конкретной задачи по её id (GET /api/tasks/).

Мы используем waitress для запуска нашего приложения. Это production-ready WSGI сервер, который рекомендован для использования с Flask в production.

Для запуска приложения используйте команду:

python app.py

Теперь приложение будет слушать на порту 8080 и будет доступно по адресу http://localhost:8080/api/tasks и http://localhost:8080/api/tasks/.

POST приложение на Python, Flask и Waitress

Сначала установим необходимые библиотеки через pip:

pip install flask waitress

Flask приложение на Python обрабатывает разные типы входящих данных:

# app.py
from flask import Flask, request, jsonify
from waitress import serve

app = Flask(__name__)

#app.config['SECRET_KEY'] = 'df0331cefc6c2b9a5d0208a726a5d1c0fd37324feba25506'

@app.route('/post', methods=['POST'])
def post():
    if request.is_json:
        # For JSON data, use request.get_json()
        data = request.get_json()
        print(f"Received JSON data: {data}")
    elif request.headers['Content-Type'] == 'application/x-www-form-urlencoded':
        # For form data, use request.form
        data = request.form
        print(f"Received form data: {data}")
    else:
        # For plain text, use request.data
        data = request.data.decode('utf-8') 
        print(f"Received plain text data: {data}")

    return "Data received and processed", 200

#if __name__ == '__main__':
#    app.run(debug=True)

if __name__ == "__main__":
    serve(app, host='0.0.0.0', port=8080, url_scheme='https')

Для запуска приложения используйте команду:

python app.py

С помощью команды curl отправим POST запрос с JSON данными на конечную точку /post по указанному URL.

C:\curl\curl -X POST -H "Content-Type: application/json" -d "{\"name\":\"John\", \"age\":30}" https://test.hexplay.com/post

Заголовок "Content-Type: application/json" сообщает серверу, что клиент (в данном случае curl) отправляет данные в формате JSON. Это позволяет серверу правильно интерпретировать и обрабатывать отправленные данные.

В контексте Flask приложения, когда вы делаете POST запрос и указываете "Content-Type: application/json", Flask знает, что ему следует получить данные из request.get_json().

C:\curl\curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "name=John&age=30" https://test.hexplay.com/post

Эта команда curl отправляет POST запрос с данными, закодированными как application/x-www-form-urlencoded, на конечную точку /post по указанному URL.

Формат "application/x-www-form-urlencoded" обычно используется при отправке данных формы HTML. В этом формате, пары имя/значение записываются как name=value, с разделением пар амперсандом (&).

В контексте Flask приложения, когда вы делаете POST запрос и указываете "Content-Type: application/x-www-form-urlencoded", Flask будет искать данные в request.form.

C:\curl\curl -X POST -H "Content-Type: text/plain" -d "Hello, World!" https://test.hexplay.com/post

Эта команда curl отправляет POST запрос с данными в формате text/plain на конечную точку /post по указанному URL.

Формат text/plain используется для отправки обычного текста. В данном случае, вы отправляете строку "Hello, World!".

В контексте Flask приложения, когда вы делаете POST запрос и указываете "Content-Type: text/plain", Flask будет искать данные в request.data.

Ваш URL https://test.hexplay.com/post в этом примере должен быть заменен на реальный URL вашего сервера.