带有 Apache 代理 + Gunicorn 的 Flask 应用程序无法在 HTTPS 上运行

Flask app with Apache proxy + Gunicorn not working on HTTPS

在底部更新,我有点解决了但不确定解决方案是否正确。


我在 CentOS 上安装了 Apache 运行ning,代理到本地主机端口 8080,我在其中安装了使用 Gunicorn 的 Flask 应用程序 运行ning。此设置适用于 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 请求 returns 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

适用于 HTTP 的 Apache 配置 (/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

不适用于 HTTPS 的 Apache 配置 (/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>

如果我 remove/comment SSLProxyEngineProxyPass amd ProxyPassReverse 行并重新启动 Apache,我将获得默认的 Apache 欢迎页面,并且 HTTPS 工作正常所以问题很明显不知何故在代理中?

Flask 应用程序由 Gunicorn 启动:

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)

再一次,这在使用 HTTP 导航到我的域时有效,但在使用 HTTPS 时无效。

有什么帮助吗?


更新:

我遇到了另一个错误。现在导航到 https://example.com 会立即加载并显示 500 错误页面:

Proxy Error
The proxy server could not handle the request GET /.
Reason: Error during SSL Handshake with remote server

Apache 错误日志:

[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 ()

Gunicorn 错误日志中不再有任何错误。

我将这两行添加到 gunicorn_config.py:

keyfile = '/etc/pki/tls/private/cert.key'
certfile = '/etc/pki/tls/certs/cert.pem'

并确保用户 运行ning Gunicorn 可以访问这两个文件(chmod o+r cert.key/pem)。

不知道我是否应该像这样更改它,因为我认为流量应该是这样的:客户端 --https--> Apache,然后是 Apache --http--> Gunicorn。

此外 HTTP (http://example.com) 不再有效并给出之前的 502 错误页面,但我猜 运行ning Gunicorn 与证书配置不再允许 HTTP 并且需要 运行 两次使用不同配置的应用程序)。


更新 2:

我通过将此行添加到虚拟主机内的 /etc/httpd/conf.d/ssl.conf 来添加更多 Apache 日志记录:

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 正常工作,我从应用程序返回“Hello world”!

现在我想我的问题也需要更新:在这种情况下使用 SSLProxyCheckPeerName offSSLProxyCheckPeerCN off 是不好的做法、错误或不安全吗?或者有没有更好的方法,因为我认为没有办法在本地主机上订购官方 SSL 证书?

您正在使用

ProxyPass / http://localhost:8080/

ProxyPass / https://localhost:8080/

(注意1个字母的区别)。

您的 localhost:8080 将提供 http https。根据您的描述(和普遍期望),它正在提供 http.如果你甚至将你的 :443 虚拟主机代理到 http,它会工作得更好

您可能 运行 遇到更多问题,因为代理应用程序并不真正知道它 实际上 通过 https 提供服务,但这与这个问题不同.