带有 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 SSLProxyEngine
、ProxyPass
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 off
和 SSLProxyCheckPeerCN off
是不好的做法、错误或不安全吗?或者有没有更好的方法,因为我认为没有办法在本地主机上订购官方 SSL 证书?
您正在使用
ProxyPass / http://localhost:8080/
和
ProxyPass / https://localhost:8080/
(注意1个字母的区别)。
您的 localhost:8080
将提供 http 或 https。根据您的描述(和普遍期望),它正在提供 http.如果你甚至将你的 :443 虚拟主机代理到 http,它会工作得更好。
您可能 运行 遇到更多问题,因为代理应用程序并不真正知道它 实际上 通过 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 SSLProxyEngine
、ProxyPass
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 off
和 SSLProxyCheckPeerCN off
是不好的做法、错误或不安全吗?或者有没有更好的方法,因为我认为没有办法在本地主机上订购官方 SSL 证书?
您正在使用
ProxyPass / http://localhost:8080/
和
ProxyPass / https://localhost:8080/
(注意1个字母的区别)。
您的 localhost:8080
将提供 http 或 https。根据您的描述(和普遍期望),它正在提供 http.如果你甚至将你的 :443 虚拟主机代理到 http,它会工作得更好。
您可能 运行 遇到更多问题,因为代理应用程序并不真正知道它 实际上 通过 https 提供服务,但这与这个问题不同.