#apache #flask #ssl #gunicorn #mod-proxy
Вопрос:
Обновления внизу, я вроде как решил это, но не уверен, что решение правильное.
У меня Apache работает на CentOS с прокси-сервером на локальный порт 8080, где у меня запущено приложение Flask с использованием Gunicorn. Эта настройка работает на порту Apache 80 (HTTP), и я могу подключиться к нему, используя свой домен http://example.com с помощью браузера, но теперь я попытался настроить SSL / HTTPS, и это просто не работает.
Переход к https://example.com пытается загрузить страницу в течение некоторого времени (например, 30 секунд), а затем показывает страницу с ошибкой 502:
Proxy Error
The proxy server received an invalid response from an upstream server.
The proxy server could not handle the request GET /.
Reason: Error reading from remote server
Журнал ошибок Apache:
[proxy_http:error] [pid 30209] (103)Software caused connection abort: [client xx.xxx.xxx.xxx:60556] AH01102: error reading status line from remote server localhost:8080
[proxy:error] [pid 30209] [client xx.xxx.xxx.xxx:60556] AH00898: Error reading from remote server returned by /
Журнал ошибок Gunicorn (первые 7 строк взяты из запуска Gunicorn, понятия не имею, почему эта информация находится в журнале ошибок, последние 3 строки — когда запрос HTTPS возвращает ошибку 502):
[29478] [INFO] Listening at: http://127.0.0.1:8080 (29478)
[29478] [INFO] Using worker: sync
[29480] [INFO] Booting worker with pid: 29480
[29481] [INFO] Booting worker with pid: 29481
[29482] [INFO] Booting worker with pid: 29482
[29483] [INFO] Booting worker with pid: 29483
[29484] [INFO] Booting worker with pid: 29484
[29478] [CRITICAL] WORKER TIMEOUT (pid:29480)
[29480] [INFO] Worker exiting (pid: 29480)
[29554] [INFO] Booting worker with pid: 29554
Конфигурация Apache, которая работает для HTTP ( /etc/httpd/conf/httpd.conf
):
Listen 80
#other default config values here
<VirtualHost *:80>
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
</VirtualHost>
IncludeOptional conf.d/*.conf
Конфигурация Apache, которая не работает для HTTPS ( /etc/httpd/conf.d/ssl.conf
):
Listen 443 https
#other default config values here
<VirtualHost *:443>
#other default config values here too
SSLCertificateFile /etc/pki/tls/certs/cert.pem
SSLCertificateKeyFile /etc/pki/tls/private/cert.key
SSLProxyEngine on
ProxyPass / https://localhost:8080/
ProxyPassReverse / https://localhost:8080/
</VirtualHost>
If I remove/comment the SSLProxyEngine
, ProxyPass
amd ProxyPassReverse
lines and restart Apache I get the default Apache welcome page and HTTPS works just fine so clearly the problem is in the Proxy somehow?
The flask app is started with Gunicorn by:
gunicorn --config gunicorn_config.py app:app
gunicorn_config.py:
workers = 5
bind = '127.0.0.1:8080'
umask = 0o007
reload = True
accesslog = 'log_gunicorn_access.txt'
errorlog = 'log_gunicorn_error.txt'
app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello world!'
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080)
And once again, this works when navigating to my domain using HTTP but doesn’t work when using HTTPS.
Any help?
UPDATE:
I managed to get another error. Now navigating to https://example.com loads instantly and shows 500 error page:
Proxy Error
The proxy server could not handle the request GET /.
Reason: Error during SSL Handshake with remote server
Apache error log:
[proxy:error] [pid 32385] (502)Unknown error 502: [client xx.xxx.xxx.xxx:50932] AH01084: pass request body failed to [::1]:8080 (localhost)
[proxy:error] [pid 32385] [client xx.xxx.xxx.xxx:50932] AH00898: Error during SSL Handshake with remote server returned by /
[proxy_http:error] [pid 32385] [client xx.xxx.xxx.xxx:50932] AH01097: pass request body failed to [::1]:8080 (localhost) from xx.xxx.xxx.xxx ()
No more any errors in Gunicorn error log.
Я добавил эти две строки в gunicorn_config.py:
keyfile = '/etc/pki/tls/private/cert.key'
certfile = '/etc/pki/tls/certs/cert.pem'
и убедился, что оба файла доступны пользователю, запускающему Gunicorn (chmod o r cert.key /pem).
Понятия не имею, должен ли я изменить его таким образом, поскольку я думал, что трафик должен выглядеть так: клиент — https-> Apache, а затем Apache -http-> Gunicorn.
Также HTTP (http://example.com ) больше не работает и выдает предыдущую страницу с ошибкой 502, но я предполагаю, что запуск Gunicorn с конфигурациями сертификата больше не разрешает HTTP и должен будет дважды запускать приложение с разными конфигурациями).
ОБНОВЛЕНИЕ 2:
Я добавил больше протоколирования Apache, добавив эту строку во /etc/httpd/conf.d/ssl.conf
внутренний виртуальный хост:
LogLevel info
И теперь я получил дополнительную информацию в журнале ошибок Apache:
[ssl:info] [pid 3808] [remote 127.0.0.1:8080] AH02411: SSL Proxy: Peer certificate does not match for hostname localhost
Затем я добавил новую строку на /etc/httpd/conf.d/ssl.conf
внутренний виртуальный хост:
SSLProxyCheckPeerName off
И теперь я получил еще одну ошибку Apache:
[ssl:info] [pid 3999] [remote 127.0.0.1:8080] AH02005: SSL Proxy: Peer certificate CN mismatch: Certificate CN: example.com Requested hostname: localhost
Добавлена новая строка для /etc/httpd/conf.d/ssl.conf
внутреннего виртуального хоста:
SSLProxyCheckPeerCN off
Aaa И теперь переходим к https://example.com правильно работает, и я получаю «Привет, мир» обратно из приложения!
Теперь, я думаю, мой вопрос также нуждается в обновлении: это плохая практика, неправильная или небезопасная в использовании SSLProxyCheckPeerName off
и SSLProxyCheckPeerCN off
в этом контексте? Или есть лучший способ, поскольку я не думаю, что есть способ заказать официальный SSL-сертификат на localhost?
Ответ №1:
Вы используете
ProxyPass / http://localhost:8080/
и
ProxyPass / https://localhost:8080/
(обратите внимание на разницу в 1 букву).
Вы localhost:8080
будете обслуживать либо http, либо https. Основываясь на вашем описании (и общих ожиданиях), оно обслуживает http. Если вы прокси-сервер даже вашего виртуального хоста : 443 для http, он будет работать лучше.
Вы можете столкнуться с дополнительными проблемами, поскольку прокси-приложение на самом деле не знает, что оно действительно обслуживается через https, но это другой зверь, чем этот вопрос.
Комментарии:
1. Ах, это имеет смысл, да, я хотел обслуживать только client -> Apache через https, а не обрабатывать его с помощью внутреннего Apache -> Gunicorn / Flask. Не думайте, что приложение заботится о том, обслуживается ли оно через http / https. Спасибо!
2. ну, некоторым приложениям не все равно — хотя бы для того, чтобы иметь возможность генерировать ссылки, использующие тот же протокол. Или отказаться от принятия любых незашифрованных учетных данных. Повезло (в некотором масштабе), если вы не столкнетесь с этим. С другой стороны, это привело бы к обнаружению целого класса ошибок.
3. Я буду иметь это в виду, если у меня возникнут какие-либо проблемы! Знаете ли вы какие-либо ссылки / ресурсы, где эта проблема объясняется более подробно, мне интересно узнать больше об этом и возможных способах ее решения.