如何限制Flask访问仅对单个IP地址?
我正在用Python的Flask框架开发一个网站,现在我在进行一些开发工作,把我的修改推送到一个远程的开发服务器上。我设置这个远程开发服务器,让它可以公开提供网站,使用的命令是app.run(host='0.0.0.0')
。
这方法运行得很好,但我不想让其他人现在就能看到我的网站。因此,我想要设置一个白名单,只允许我的IP地址访问这个开发服务器,其他IP地址访问时要么不给响应,要么返回404或其他无用的响应。当然,我可以设置服务器用apache或nginx来真正提供网站,但我喜欢在开发时代码修改后能自动刷新网站。
所以,有没有人知道怎么用Flask自带的开发服务器做到这一点?欢迎任何建议!
3 个回答
我觉得这个方法很有用,但如果你有多个IP地址,其实还有更简单的方法。
trusted_ips = ['42.42.42.42', '82.42.82.42', '127.0.0.1']
@app.before_request
def limit_remote_addr():
if request.remote_addr not in trusted_ips:
abort(404) # Not Found
这个方法会检查你信任的IP列表,如果远程IP不在这个列表里,就会返回“404 - 找不到页面”。
你也可以通过改动一些设置来屏蔽特定的IP。
bad_ips = ['42.42.42.42', '82.42.82.42', '127.0.0.1']
@app.before_request
def limit_remote_addr():
if request.remote_addr in bad_ips:
abort(404) # Not Found
这个方法和上面的一样,但它会屏蔽你在坏IP列表中的IP。
这个IPTABLES/Netfilter规则可以满足你的需求,它会阻止所有进入的流量,除了来自your_ip_address
到80端口的流量:
$ /sbin/iptables -A INPUT -s ! your_ip_address --dport 80 -j DROP
这里有一些在很多论坛上提到的内容,它允许本地流量和来自your_ip_address
的外部访问你的Flask应用,但会拒绝其他IP地址的所有流量:
$ /sbin/iptables -A INPUT -i lo -j ACCEPT
$ /sbin/iptables -A INPUT -s your_ip_address --dport 80 -j DROP
$ /sbin/iptables -A INPUT --dport 80 -j REJECT
虽然你可以通过Flask轻松实现预期的效果(正如选定的答案中提到的),但这种问题应该在操作系统的网络层处理。考虑到你使用的是类似Unix的操作系统,你可以通过Netfilter和IPTABLES来拒绝或允许进入的连接,使用这样的规则。
进入的流量/数据包首先会经过你操作系统内核的分析。要拒绝或允许来自任何源到特定端口的流量,这是操作系统防火墙的工作,属于内核的网络层。如果你的服务器上没有运行防火墙,你应该配置一个。
这里有一个要点:
- 流量必须在你操作系统的网络层处理。不要让应用程序来处理这个任务,至少在生产环境中不要这样做。没有人能比你的操作系统内核更好地完成这个任务(希望你使用的是类似Unix的操作系统)。Linux内核及其模块(Netfilter)在处理这些任务时更加可靠、有效。
如果你只用Flask的功能,可以使用一个叫做 before_request()
的钩子 来测试 request.remote_addr
属性:
from flask import abort, request
@app.before_request
def limit_remote_addr():
if request.remote_addr != '10.20.30.40':
abort(403) # Forbidden
不过,在服务器上使用防火墙规则可能是更安全、更可靠的选择。
需要注意的是,如果浏览器和你的服务器之间有反向代理,Remote_Addr 可能会被隐藏;所以在限制这个的时候要小心,别把自己锁在外面。如果这个代理离服务器很近(比如负载均衡器或前端缓存),你可以查看 request.access_route
列表 来获取实际的IP地址。只有在 remote_addr
本身也是一个可信的IP地址时,才这样做:
trusted_proxies = ('42.42.42.42', '82.42.82.42', '127.0.0.1')
def limit_remote_addr():
remote = request.remote_addr
route = list(request.access_route)
while remote in trusted_proxies:
remote = route.pop()
if remote != '10.20.30.40':
abort(403) # Forbidden