Nginx 实现动态封禁IP,详细教程来了

文摘   2024-11-24 14:01   陕西  

说起动态封禁IP,真是一个“明明做着很累,但总有人觉得你在摸鱼”的活儿。这次就和大家聊聊如何用Nginx来实现动态封禁IP,尤其是防止爬虫、恶意访问的场景。

先别急着皱眉,我会讲得清楚又风趣,带上代码示例,保证你轻松上手。

为什么要动态封禁IP?

这个问题听起来像是老板问的“为啥预算超了”,其实很简单:你总不能放任爬虫和恶意用户为所欲为吧。咱们得给这些家伙上一课,动态封禁IP说白了就是给访问过于“勤快”的用户踩个急刹车。

核心需求总结:

  1. 封禁恶意请求:比如那些试图暴力破解、SQL注入的攻击者。
  2. 动态管理黑名单:自动化拉黑,不用手动更新配置文件。
  3. 封禁时间设置:可控时长,让被封禁的IP有“改过自新”的机会。

技术选型:Nginx + Lua + Redis

整个方案看起来就像一顿营养均衡的程序员套餐:Nginx负责拦截,Lua负责逻辑,Redis负责存储。具体来说:
  • Nginx:作为反向代理,用它的性能处理高并发。
  • Lua:通过OpenResty框架,我们可以灵活编写动态逻辑。
  • Redis:存储IP黑名单,高效快速,支持过期时间。
如果你还在用iptables封IP,那就像用Excel做演示文稿,虽然能用,但不优雅,效率也感人。

环境配置

  1. 操作系统:CentOS 7 或 Ubuntu,其他主流发行版也行。
  2. Redis:建议使用 5.0.5 或更高版本。
  3. Nginx:推荐 Nginx-OpenResty,它让Lua配置更加方便。

示例环境安装(CentOS为例):

# 安装OpenResty
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
sudo yum install -y openresty

# 安装Redis
sudo yum install -y epel-release
sudo yum install -y redis
sudo systemctl start redis
sudo systemctl enable redis

配置实现

下面是一个实际配置的完整流程:

1. Nginx 配置

在Nginx配置文件的server块中,添加Lua脚本的位置:
http {
lua_shared_dict ip_blacklist 10m; # 定义共享字典存储黑名单
server {
listen 80;
location / {
access_by_lua_file /etc/nginx/lua/block_ip.lua;
proxy_pass http://backend;
}
}
}

2. Lua 脚本

这段脚本会连接Redis并检查访问频率,逻辑大致如下:
  1. 获取用户IP。
  2. 检查IP是否在黑名单中。
  3. 如果IP访问频率超标,加入黑名单。
  4. 黑名单的封禁时间由Redis设置。
-- 引入Redis库
local redis = require "resty.redis"

-- 定义Redis连接函数
local function connect_redis()
    local red = redis:new()
    red:set_timeout(1000-- 1秒超时
    local ok, err = red:connect("127.0.0.1"6379)
    if not ok then
        ngx.log(ngx.ERR, "Failed to connect to Redis: ", err)
        return nil
    end
    return red
end

-- 获取客户端IP
local client_ip = ngx.var.remote_addr

-- Redis存储逻辑
local red = connect_redis()
if not red then
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

-- 检查是否在黑名单
local is_blocked = red:get("block:" .. client_ip)
if is_blocked == "1" then
    ngx.exit(ngx.HTTP_FORBIDDEN) -- 返回403
end

-- 记录访问次数
local count, err = red:incr("count:" .. client_ip)
if not count then
    ngx.log(ngx.ERR, "Failed to increment count: ", err)
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

-- 设置访问次数过期时间
if count == 1 then
    red:expire("count:" .. client_ip, 10-- 10秒过期
end

-- 如果超过阈值,加入黑名单
if count > 100 then
    red:set("block:" .. client_ip, "1")
    red:expire("block:" .. client_ip, 60-- 封禁1分钟
    ngx.exit(ngx.HTTP_FORBIDDEN)
end

工作原理小解析

  1. IP 获取与检测:脚本中ngx.var.remote_addr直接获取客户端IP。
  2. Redis 计数:每次请求都会增加计数器,如果超出阈值,则将IP加入黑名单。
  3. 黑名单封禁:Redis的expire功能自动处理封禁时效,不需要我们操心。

优势分析

  1. 轻量级:Nginx和Lua结合,性能开销极小。
  2. 动态性强:IP黑名单动态更新,无需重启服务。
  3. 可扩展:只需修改脚本逻辑,就可以实现更复杂的规则,比如白名单、访问频率统计等。

应用场景扩展

除了拦截爬虫和恶意用户,这套方案还能应用在:
  • 防DDoS攻击:结合Nginx的限速模块。
  • 防止数据滥用:比如对API接口进行访问频率限制。
  • 异常检测:进一步扩展,可以接入日志分析系统。
有次朋友部署类似方案时,发现黑名单全是公司的内网IP。原因呢?因为所有请求都走了负载均衡,Nginx获取到的是负载均衡器的IP。
😅 小提示:这种情况可以通过X-Forwarded-For头获取真实IP。
最后,动态封禁IP虽然不能防住“所有坏人”,但就像“锁车门不能防小偷,但能让他去偷别人的车”,还是很有必要的!赶紧试试吧~

-END-


ok,今天先说到这,老规矩,给大家分享一份不错的副业资料,感兴趣的同学找我领取。

以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言

程序员老鬼
10年+老程序员,专注于AI知识普及,已打造多门AI课程,本号主要分享国内AI工具、AI绘画提示词、Chat教程、AI换脸、Chat中文指令、Sora教程等,帮助读者解决AI工具使用疑难问题。
 最新文章