Django ALLOWED_HOSTS IP 范围
Posted
技术标签:
【中文标题】Django ALLOWED_HOSTS IP 范围【英文标题】:Django ALLOWED_HOSTS IPs range 【发布时间】:2016-08-30 03:05:28 【问题描述】:有没有办法在 django 中设置一系列 ALLOWED_HOSTS IP?
类似这样的:
ALLOWED_HOSTS = ['172.17.*.*']
【问题讨论】:
我正要回答“是”,但做了一些谷歌挖掘,找不到有人这样做的具体例子。根据docs,您可以在列表中使用单独的通配符,例如ALLOWED_HOSTS = ['*']
,(出于安全原因不建议这样做),但我还没有看到像您的示例这样的东西。我仍然倾向于“是”,但在我看到明确说明这一点的参考资料之前,我不会相信。这可能是一个愚蠢的问题,但是您是否尝试过它是否会在 django 中引发任何错误?
最好将其卸载到一个可以有效处理此问题的 Web 服务器。或者甚至可以设置防火墙规则,堆栈越高的流量可以被过滤得越好。
@Nez,是的,我愿意。下面看看我的回答。我找到了解决这个问题的方法。
不错。我知道可以做到,但我不知道我在寻找什么样的解决方案。中间件是有道理的。 @serg 的建议应该在堆栈中处理得更高,但这对于流量相对较低的构建应该没问题,对吧?
@serg 的建议绝对比 middlware 更适合高负载项目。但我一直在寻找 django 级别的解决方案。
【参考方案1】:
不,这目前是不可能的。根据the docs,支持以下语法:
['www.example.com'] # Fully qualified domain
['.example.com'] # Subdomain wildcard, matches example.com and www.example.com
['*'] # Matches anything
如果您查看validate_host
方法的实现,您会发现允许单独使用'*'
,但不允许使用*
作为通配符作为字符串的一部分(例如'172.17.*.*'
)支持。
【讨论】:
【参考方案2】:我在 Django 上发布了一张票,但是我发现这可以通过执行以下操作来实现
from socket import gethostname, gethostbyname
ALLOWED_HOSTS = [ gethostname(), gethostbyname(gethostname()), ]
更新。如果您使用的是 docker,下面的代码会更好,因为 gethostbyname 没有得到正确的信息。
from socket import gethostname, gethostbyname, gethostbyname_ex
ALLOWED_HOSTS = [ gethostname(), ] + list(set(gethostbyname_ex(gethostname())[2]))
它被转换为集合的原因是 gethostbyname_ex 可以返回重复项。
django网站上的ticket链接是。
https://code.djangoproject.com/ticket/27485
【讨论】:
Windows 10 的秋季创建者更新为我打破了这种方法,因为gethostname()
正在返回 Hyper-V 以太网交换机地址。您可以改用ALLOWED_HOSTS = [ gethostname(), ] + gethostbyname_ex(gethostname())[2]
。
这真的很有帮助,在 k8s 集群上运行 Django,该节点在运行之前是未知的。
不安全吗?
@KarlZillner 不,那不是真的。它只会添加您机器的 IP 地址,没有域名将起作用,他们将收到 Invalid HTTP_HOST 错误。其中“*”将允许所有 ipaddress 和所有域
不幸的是,这只允许添加机器的 IP 和主机名,而不是我们信任但不知道静态 IP 地址的任何其他服务器,例如亚马逊负载均衡器。如果可以添加通配符,我可以允许子网范围,但现在我必须使用 nginx 来解决这个问题来重写主机。【参考方案3】:
Mozilla 发布了一个名为 django-allow-cidr 的 Python 包,旨在解决这个问题。
announcement blog post 解释说,它对于没有 Host
标头且仅使用 IP 地址的运行状况检查非常有用。
您必须将您的 IP 地址 '172.17.*.*'
稍微更改为 CIDR range,例如 172.17.0.0/16
【讨论】:
请注意,这将允许像172.17.malicious.host.com
这样的恶意内容
这对我有用;由于原始主机位于内部子网上,因此我的运行状况检查失败。将10.0.0.0/16
添加到ALLOWED_CIDR_NETS
效果很好。ALLOWED_CIDR_NETS = ['10.0.0.0/16']
@Cole 不会。查看源代码github.com/mozmeao/django-allow-cidr/blob/master/allow_cidr/… 可以看到它只会匹配特定的IP。 self.allowed_cidr_nets = [IPNetwork(net) for net in allowed_cidr_nets]
for net in self.allowed_cidr_nets:
您可以在“使用生成器确保高效处理大型 IP 子网”下查看有关 IPNetwork netaddr.readthedocs.io/en/latest/tutorial_01.html#slicing 的更多信息。【参考方案4】:
这是一个快速而肮脏的解决方案。
ALLOWED_HOSTS += ['172.17..'.format(i,j) for i in range(256) for j in range(256)]
【讨论】:
['172.17..'.format(i,j) for i in range(256) for j in range(256)]
与上面使用格式相同。喜欢。
['192.168.1.'.format(i) for i in range(256)]
谢谢,我只是想指出你也可以这样做,如果你知道像我一样只需要最后一个数字。
调试不是个好主意,转储的环境看起来很脏【参考方案5】:
我找到了过滤 IP 范围的解决方案:
https://***.com/a/36222755/3766751
使用这种方法,我们可以通过任何方式过滤 IP(例如使用正则表达式)。
from django.http import HttpResponseForbidden
class FilterHostMiddleware(object):
def process_request(self, request):
allowed_hosts = ['127.0.0.1', 'localhost'] # specify complete host names here
host = request.META.get('HTTP_HOST')
if host[len(host)-10:] == 'dyndns.org': # if the host ends with dyndns.org then add to the allowed hosts
allowed_hosts.append(host)
elif host[:7] == '192.168': # if the host starts with 192.168 then add to the allowed hosts
allowed_hosts.append(host)
if host not in allowed_hosts:
raise HttpResponseForbidden
return None
感谢@Zorgmorduk
【讨论】:
【参考方案6】:如果我们看看 Django 如何验证主机,我们可以深入了解如何使ALLOWED_HOSTS
条目更灵活:
def validate_host(host, allowed_hosts):
"""
Validate the given host for this site.
Check that the host looks valid and matches a host or host pattern in the
given list of ``allowed_hosts``. Any pattern beginning with a period
matches a domain and all its subdomains (e.g. ``.example.com`` matches
``example.com`` and any subdomain), ``*`` matches anything, and anything
else must match exactly.
Note: This function assumes that the given host is lowercased and has
already had the port, if any, stripped off.
Return ``True`` for a valid host, ``False`` otherwise.
"""
return any(pattern == '*' or is_same_domain(host, pattern) for pattern in allowed_hosts)
. . .
def is_same_domain(host, pattern):
"""
Return ``True`` if the host is either an exact match or a match
to the wildcard pattern.
Any pattern beginning with a period matches a domain and all of its
subdomains. (e.g. ``.example.com`` matches ``example.com`` and
``foo.example.com``). Anything else is an exact string match.
"""
if not pattern:
return False
pattern = pattern.lower()
return (
pattern[0] == '.' and (host.endswith(pattern) or host == pattern[1:]) or
pattern == host
)
这是一个RegexHost
实用程序,可以通过此验证。
class RegexHost(str):
def lower(self):
return self
def __init__(self, pattern):
super().__init__()
self.regex = re.compile(pattern)
def __eq__(self, other):
# override the equality operation to use regex matching
# instead of str.__eq__(self, other)
return self.regex.match(other)
可以这样使用:
# this matches '172.17.*.*' and also many impossible IPs
host = RegexHost(r'172\.17\.[0-9]1,3\.[0-9]1,3')
# Un-comment the below assertions to prove to yourself that this host
# validation works. Do not leave these assertions active in
# production code for startup performance considerations.
# assert all(host == f'172.17.i.j' for i in range(256) for j in range(256))
# assert not any(host == f'172.18.i.j' for i in range(256) for j in range(256))
ALLOWED_HOSTS = [host]
【讨论】:
您是否建议使用 65K+ 项数组来验证主机? 没有?我希望有人看到这些断言,确认它们通过,并将它们从设置文件中删除(如果这是他们正在测试的地方)。 请记住,这只会在服务器启动时运行,因此即使他们确实将其保留在设置文件中,应用程序的性能也不会受到影响。我会将它们注释掉,以防有人对此感到困惑。以上是关于Django ALLOWED_HOSTS IP 范围的主要内容,如果未能解决你的问题,请参考以下文章
用于 Amazon ELB 的 Django ALLOWED_HOSTS
如何将 EC2 ip 地址动态添加到 Django ALLOWED_HOSTS