commit b07532be752668be5eb5dbd0a8303abf5c219c99 当 gunicorn 绑定到 unix 域套接字时,“禁止矛盾的安全方案标头”破坏了“request.scheme”和“wsgi.url_scheme”。 当 gunicorn 绑定到 tcp 套接字时,一切正常。
当在 gunicorn 19.8 之前与 nginx 一起使用时,nginx 传递HTTP_X_FORWARDED_PROTO
== https
,但是在 gunicorn/http/message.py 中的remote_addr = self.unreader.sock.getpeername()
中的 unix 域套接字remote_addr
是空字符串,然后secure_scheme_headers
是空字典,并且方案最终是“http”,尽管 Web 服务器仅支持 https 并传递了所有标头。
我在 python 3.6 上使用 gevent worker 和最新的一切,但我认为这并不重要。
@zt-initech 感谢您的报告。 我马上去看看。
@zt-initech #1767
我不确定还有谁会在附近审查它,但这是一个非常简单的补丁,我在本地对其进行了测试。 如果你能验证它,我会合并并发布。
@tilgovi你的补丁为我解决了这个问题。 但我会这样做:
if self.unreader.sock.family == socket.AF_UNIX:
secure_scheme_headers = cfg.secure_scheme_headers
else:
remote_addr = self.unreader.sock.getpeername()
if isinstance(remote_addr, tuple):
remote_host = remote_addr[0]
if remote_host in cfg.forwarded_allow_ips:
secure_scheme_headers = cfg.secure_scheme_headers
而且我不是网络专家,所以我不知道依靠 getpeername() 返回字符串是否可以。
19.8.1 发布
@zt-initech 啊,是的。 至少我所做的与我们过去所做的一致,但我认为你是对的,那会更好。
我在 gunicorn 前面有 nginx,即使我设置了 proxy_set_header X-Forwarded-Proto $scheme,19.8.1 仍然会导致“Contradictory scheme headers”错误; 在 nginx 配置中。
@danielskun在 nginx 前面是否有另一个反向代理(如负载均衡器)设置不同的标头?
如果您可以制作一个重现问题的小型存储库,我可以看看。
你好,
就像@danielskun一样,我昨天就陷入了这种情况。 在对我的 django 应用程序进行标准部署后,我注意到在连接到其 https 版本时出现了一个无限的 301 重定向循环。
这是SECURE_SSL_REDIRECT安全选项导致的,原因是gunicorn从19.7.1更新到19.8.1
在 19.7.1 中,使用SECURE_SSL_REDIRECT=True
,Django 正确检测到使用 https 方案访问该网站。 环境wsgi.url_scheme
设置正确。 现在,在 gunicorn 19.8.1 中, wsgi.url_scheme
包含http
。
我的 wsgi 应用程序在 nginx 反向代理后面运行(没有其他前端应用程序,没有负载平衡器)并通过 unix 套接字(在磁盘上)连接。
我不知道如何调试这个,因为我在 Windows 上开发,我通常无法在我的开发机器上模拟 https 连接。 但我想我可以在我的服务器上设置一个迷你 wsgi/django 应用程序来轻松演示这个问题。 使用开源代码,您可以自己查看。 当然,我已经准备好帮助你调试这个,我只是不知道如何继续;)
我明天可能会发布这个迷你应用程序,如果您需要更多信息,请告诉我。 祝你今天过得愉快
你好
正如承诺的那样,我迅速在测试服务器上部署了一个非常简单的 django 应用程序,以证明问题仍然存在。
您可以从以下网址显示它: http : https://gunicorn-test.exige.info
来源可在https://github.com/alorence/gunicorn-test
请注意,在大多数浏览器上,当您第一次访问 https:// 链接时,无法返回http://。 使用 curl 访问页面确实效果很好。
如您所见,在gunicorn 19.8.1 中,使用unix 套接字提供nginx 反向代理和gunicorn 实例之间的连接, wsgi.url_scheme
未正确设置。
如果您告诉我您需要什么,我可以提供更多信息;)
谢谢@alorence ,我的配置和你的一样。 @tilgovi我在 nginx 面前什么都没有。
我遇到了同样的问题,试图在与@alorence类似的条件下从19.17.1
到19.9.0
我有同样的问题。 在 19.7.1 之前,它使用 unix 套接字。 在 19.8.0、19.8.1 和 19.9.0 中,方案设置为 http,尽管 X-Forwarded-Proto 设置为 https。
当我编写 #1767 并发布 19.8.1 时,我肯定在本地进行了测试。 如果有人可以做一些简单的事情来重现该问题,那将为我节省一些调查时间! 也许是一个带有 Dockerfile 和 nginx 和 gunicorn 设置的存储库? 否则,我会在可能的情况下查看此内容,并且很高兴查看任何拉取请求。 我很抱歉打扰了。
这个问题只出现在 django 中吗? 哪个版本?
@benoitc这可能可以用任何版本的 Django 复制。 我个人在 Django 1.11.14 和 Django 2.0.7 中看到了这一点。
@tilgovi我已经在网上发布了一个非常简单的问题演示。 代码可从https://github.com/alorence/gunicorn-test 获得,它不是基于 Docker 的设置,但它包含:
项目已经上线,可以从http://gunicorn-test.exige.info和https://gunicorn-test.exige.info获取。 请注意,一旦您在最近的浏览器中打开了 https 版本,您可能无法访问 http 版本。
需要注意的最重要的事情是wsgi.url_scheme
环境设置为http
即使从 https url
nginx 配置文件:
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name gunicorn-test.exige.info;
access_log /var/log/nginx/gunicorn-test.access.log;
error_log /var/log/nginx/gunicorn-test.error.log;
location / {
include proxy_params;
proxy_pass http://unix:/var/tmp/gunicorn_tests.sock;
}
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/exige.info/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/exige.info/privkey.pem;
# Common SSL config
include common_lets_encrypt;
}
启动脚本
#!/usr/bin/env bash
source venv/bin/activate
python manage.py migrate
gunicorn --bind unix:/var/tmp/gunicorn_tests.sock gunicorn_https.wsgi:application
@alorence你的 proxy_params.conf 中有什么?
@tilgovi
~ ᐅ cat /etc/nginx/proxy_params
proxy_set_header Host $http_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-Proto $scheme;
~ ᐅ sudo nginx -v
nginx version: nginx/1.10.3
我有
proxy_set_header X-Forwarded-Proto https;
此致
贡纳尔
Randall Leeds通知@github.com schrieb am Do.,2018 年 7 月 19 日,
16:31:
@alorence https://github.com/alorence你的 proxy_params.conf 里有什么?
—
您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/benoitc/gunicorn/issues/1766#issuecomment-406296854 ,
或静音线程
https://github.com/notifications/unsubscribe-auth/ACu0K8NKp6Lm5Y5KdN9X6SqPmp92JVjMks5uIJgvgaJpZM4Ts9rh
.
遇到同样的错误,但上面的提交似乎对我有帮助,即设置proxy_set_header X-Forwarded-Proto $scheme;
@alorence感谢您的回购。 我能够在我自己的 nginx 下使用相同的代理参数在本地运行它,但在本地主机上运行。 为了让它工作,我必须生成一个自签名证书并将 localhost 添加到ALLOWED_HOSTS
,但我得到了正确的结果。 wsgi.url_scheme
已设置, request.is_secure()
返回True
。
我有这个问题,我不知道如何解决它。 有多少人也有这个问题? 我的配置与@alorence发送的配置相同(django==1.11.15 和 python 2.4 除外,如果重要的话)
需要明确的是:我没有复制这个:-/。 如果有人可以分享重现的步骤,请这样做。
我只是将东西推向生产并自己遇到了这个问题。 我不得不降级回 19.7.1 。
这是我使用的版本:
枪炮 19.9.0
蟒蛇 2.7.6
Django 1.11.15
nginx 1.0.15
我的 nginx 配置如下:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
我的测试环境中没有 https,但我会看看我是否可以设置它并追踪问题。
至少我在搜索“Contradictory Scheme headers”时能够快速找到这个问题,并且能够快速降级以解决问题。 我认为这个问题可能应该重新打开,因为它似乎影响了几个人。
我同意该错误可能会重新打开,或者可能会创建一个新错误,因为 @zt-initech 确认他的原始问题已由 PR #1767 修复
@tisdall返回的 wsgi 环境是什么? 另外你如何发射 gunicorn ? 你能分享你正在使用的配置和命令行参数吗?
@alorence不确定是否会遵循,但如果错误仍然存在,请为它打开一张票:)
@alorence已经在上面的消息中分享了他的配置,我的配置中有相同的行。
我通过主管,命令行参数启动 gunicorn:
枪炮应用程序。 wsgi:application --bind 127.0.0.1:8000 -w12 --max-requests 10000 --timeout 180
@predatell所以您的问题至少有所不同,因为您没有绑定到 UNIX 套接字。
其他人能否提供他们的 gunicorn 启动命令以及您认为可能有帮助的任何其他配置? 如果这里有问题,我非常想修复它。
有关负载平衡器和代理的任何详细信息也可能有所帮助。 例如,如果您在 Heroku、AWS 或其他一些服务上进行部署。 或者即使你有一个像 k8s 这样的路由编排器。 任何可能影响 gunicorn 接收的标头的东西。 另外,我不知道是否每个人都在使用 nginx。
请提供尽可能多的详细信息。 如果可能,带有部署说明的完整示例存储库会很棒。
我现在在我的测试环境中很好地解决了这个问题,所以我可以尝试不同的修复程序,看看它是如何工作的。
gunicorn 命令(为了隐私而更改): gunicorn -c /data/web/gunicorn_config.py -b unix:/tmp/wsgi_application.sock wsgi:application
gunicorn_config.py:
workers = 4
limit_request_line = 8190
max_requests = 8000 # number of requests before restart worker
max_requests_jitter = 500
它作为具有以下配置的代理在 nginx 后面运行:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://unix:/tmp/wsgi_application.sock/;
proxy_redirect off;
proxy_headers_hash_bucket_size 96;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
@benoitc - 我不知道你所说的“返回的 wsgi 环境”是什么意思,或者我如何报告......你能澄清我在哪里可以找到这些信息吗?
我打开调试并注意到这一点: secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
但是,nginx 配置将X-Forwarded-Protocol
为https
而不是ssl
。 这可能是原因吗?
好的,这绝对是问题......我更改了我的 gunicorn_config.py 并添加了以下内容来临时修复它:
secure_scheme_headers = {'X-FORWARDED-PROTOCOL': 'https', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
也在这里评论: https :
@tisdall的分析是正确的,并且也解释了@predatell的问题。
我很好奇你们中的任何一个是否熟悉默认设置X-Forwarded-Protocol
的软件以及它期望的值。 我假设我们的默认secure_scheme_headers
设置为期望ssl
因为某些软件会这样做。
请注意,没有必要设置多个头文件,因此您可以删除X-Forwarded-Protocol
并保留X-Forwarded-Proto
。
“Contradictory scheme headers”错误旨在检测您的反向代理仅设置部分secure_scheme_headers
但恶意客户端试图通过在请求上设置另一个来欺骗安全状态的情况。 除非您期望另一个不受您控制的反向代理来指定另一个标头,否则无需在 nginx 中设置多个标头。
我的 nginx 配置中没有proxy_set_header X-Forwarded-Protocol
,所以就我而言,我认为这不是问题的根本原因。 但是按照@benoitc 的建议,我将尽快创建一个新问题,并附上问题和调查的摘要,以清理讨论。
感谢你的帮助
@tilgovi和@tisdall感谢您的帮助。 我在我的 nginx 中遇到了这个问题:
proxy_set_header X-Forwarded-Protocol $scheme;
@alorence - 将secure_scheme_headers
更改{'X-FORWARDED-PROTO': 'https'}
解决您的问题?
抱歉不行。 见https://gunicorn-test.exige.info/
当前的配置文件是
secure_scheme_headers = {
# 'X-FORWARDED-PROTOCOL': 'ssl',
'X-FORWARDED-PROTO': 'https',
# 'X-FORWARDED-SSL': 'on'
}
运行 gunicorn 的更新命令行是: gunicorn --bind unix:/var/tmp/gunicorn_tests.sock -c ./gunicorn_config.py gunicorn_https.wsgi:application
@alorence - gunicorn_https/wsgi.py
什么?
@alorence - 顺便说一句,我可以成功加载https: //gunicorn-test.exige.info /...
这个应用程序在 github 上仍然可用。 这是wsgi.py的内容:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gunicorn_https.settings")
application = get_wsgi_application()
我不明白你的下一句话。 我也可以打开https://gunicorn-test.exige.info/ ,但是内容无效:
request.is_secure: False
request.environ['wsgi.url_scheme']: http
我们应该有request.is_secure = True
和request.environ['wsgi.url_scheme']: https
作为记录,这是一个小应用程序,我快速编写并在我的测试服务器 1 上联机以演示该问题。 https://github.com/benoitc/gunicorn/issues/1766#issuecomment -406161275中的更多信息
哦,对不起.. 我想一度我看到它给出了“矛盾的方案标题”错误而不是内容。 我也忘了你提到代码在 github 上。
是的,我检查了一下,我的正确设置了request.is_secure
到True
和request.environ['wsgi.url_scheme']
到'https'
。
如果您显式地将proxy_set_header X-Forwarded-Proto $scheme;
到 nginx 配置中会怎样? 我认为 nginx 可能不会自动使用proxy_params
,您必须在location /
块中实际包含include proxy_params;
。
呃.. 你有include
声明.. 我想我需要休息一下。 ^_^
我将这些修改推送到 nginx 配置文件,并重新启动了应用程序。 结果保持不变。 https://gunicorn-test.exige.info/
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name gunicorn-test.exige.info;
access_log /var/log/nginx/gunicorn-test.access.log;
error_log /var/log/nginx/gunicorn-test.error.log;
location / {
proxy_set_header Host $http_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-Proto $scheme;
proxy_pass http://unix:/var/tmp/gunicorn_tests.sock;
}
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/exige.info/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/exige.info/privkey.pem;
# Common SSL config
include common_lets_encrypt;
}
令人沮丧! request.META
任何线索?
我必须更新项目的源代码才能在此环境中打印request.META
内容。 不幸的是,我将无法在 2 天前执行此操作。 对于那个很抱歉。 尽快,我会更新项目,在测试服务器上重新启动它,并且可能会打开一个新问题,以便我们可以在开放票中继续讨论......非常感谢您的帮助
好的。 在新问题上标记我,当你改变它时我会看看......
@alorence我也有同样的问题,请问怎么解决的? 现在我将 gunicorn 从19.9.0
降级为19.7.1
。
该修复程序已推送到主分支,但目前尚未发布。 版本 19.9 仍然存在问题。 您可以关注#1861 获取更新信息。
就我而言,我选择将大多数网站从 Unix 套接字切换到 HTTP 套接字,因此我不再受此问题的影响。
在我的一个环境中将gunicorn
从 19.8.0 提高到 19.9.0 之后,我还花了很多时间来追踪这个问题的根源。 由于这是一个相当严重的回归,我很惊讶还没有 19.9.1 版本?
@villebro你说的是哪个问题? 如果您在谈论 #1861 或 #1882,您可能应该在那里发表评论。 (我认为这是最新版本中唯一没有的东西)
@villebro我很抱歉。 我们给 Gunicorn 的时间和注意力都是有限的。 我们正在努力推出第 20 版。
@tisdall你是对的,我指的是 #1861(我认为)。 我会在正确的问题中发表评论。 @tilgovi请不要误解,我完全理解开发人员的时间有限,并不意味着对正在完成的工作感到忘恩负义。 在我的情况下,回归使 19.9.0 无法使用,我希望许多 gunicorn 用户都是这种情况。 就我个人而言,我更愿意看到 19.9.1 版本与新的主要版本 20.0.0 相比,所有已知的主要回归都已修复(=从主版本反向移植)。
@villebro完全不用担心。 最重要的是,我只是确保你听到了一些回应,这样你就知道自己被听到了。
我应该提到我们承诺再发布一个 19.x 版本,以便在我们放弃 Python 2 支持之前获得一些修复。
看看#2022,如果我们错过了什么,请告诉我们。
最有用的评论
你好
正如承诺的那样,我迅速在测试服务器上部署了一个非常简单的 django 应用程序,以证明问题仍然存在。
您可以从以下网址显示它: http : https://gunicorn-test.exige.info
来源可在https://github.com/alorence/gunicorn-test
请注意,在大多数浏览器上,当您第一次访问 https:// 链接时,无法返回http://。 使用 curl 访问页面确实效果很好。
如您所见,在gunicorn 19.8.1 中,使用unix 套接字提供nginx 反向代理和gunicorn 实例之间的连接,
wsgi.url_scheme
未正确设置。如果您告诉我您需要什么,我可以提供更多信息;)