❃博主首页 : 「程序员1970」 ,同名公众号「程序员1970」
☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关>

在运维和安全实践中,经常需要根据实时情况封禁某些恶意 IP。但传统的Nginx 配置是静态的——一旦写死 deny 1.2.3.4;,就必须重启或重载服务才能生效。那么,有没有办法在不中断服务的前提下,实现动态、实时、可编程的 IP 封禁呢?


一、方案对比

方案 是否需 reload 实时性 技术栈 适用场景
geo + map + 文件 是(nginx -s reload 秒~分钟级 原生 Nginx 手动或定时脚本封禁
OpenResty + Lua 毫秒级 OpenResty(Nginx + Lua) 自动化、高并发、API 化管理
fail2ban + 日志分析 否(系统级封禁) 秒级 fail2ban + iptables 安全防护、日志驱动型封禁

二、方案一:原生 Nginx + 外部文件(简单可靠)

适用于不需要极高实时性的场景,比如每天批量封禁一批扫描 IP。

1. 创建封禁 IP 列表文件

# /etc/nginx/conf.d/blockips.conf
192.168.1.100 1;
203.0.113.5    1;

格式为:IP 值;,值通常设为 1 表示“命中”。

2. 在 nginx.conf 中配置

http {
    # 从外部文件加载黑名单
    geo $block_ip {
        default 0;
        include /etc/nginx/conf.d/blockips.conf;
    }

    map $block_ip $deny_ip {
        1 "denied";
        0 "";
    }

    server {
        listen 80;

        if ($deny_ip = "denied") {
            return 403;
        }

        location / {
            # 正常业务
        }
    }
}

3. 动态更新方式

  • 修改 blockips.conf
  • 执行平滑重载:
    nginx -s reload
    

✅ 优点:无需额外依赖,配置简单。
⚠️ 缺点:每次更新需 reload,不适合高频操作。


三、方案二:OpenResty + Lua(推荐用于自动化)

如果你需要通过 API 实时封禁 IP(例如 WAF、风控系统调用),OpenResty 是最佳选择。它基于 Nginx,嵌入 Lua 脚本引擎,支持共享内存,无需 reload 即可生效。

1. 安装 OpenResty

# Ubuntu 示例
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
echo "deb http://openresty.org/package/debian $(lsb_release -sc) openresty" | sudo tee /etc/apt/sources.list.d/openresty.list
sudo apt update && sudo apt install openresty

2. 配置 nginx.conf

http {
    lua_shared_dict ip_blacklist 10m;  # 共享内存存储黑名单

    server {
        listen 80;

        # 【核心】访问前检查 IP
        access_by_lua_block {
            local ip = ngx.var.remote_addr
            if ngx.shared.ip_blacklist:get(ip) then
                ngx.log(ngx.WARN, "Blocked IP: ", ip)
                ngx.exit(403)
            end
        }

        # 动态封禁接口(建议加 IP 白名单保护)
        location = /ban {
            content_by_lua_block {
                local args = ngx.req.get_uri_args()
                local ip = args.ip
                local seconds = tonumber(args.seconds) or 3600

                if not ip or not ip:match("^%d+%.%d+%.%d+%.%d+$") then
                    ngx.status = 400
                    ngx.say("Invalid or missing 'ip'")
                    return
                end

                ngx.shared.ip_blacklist:set(ip, true, seconds)
                ngx.say("Banned ", ip, " for ", seconds, "s")
            }
        }

        # 解封 & 查询接口
        location / { echo "Hello! Your IP: $remote_addr"; }
    }
}

3. 使用示例

# 封禁 IP 5 分钟
curl "http://your-server/ban?ip=1.2.3.4&seconds=300"

# 测试是否被封
curl http://your-server/  # 返回 403

✅ 优点:毫秒级生效、支持 TTL 自动过期、可集成到自动化系统。
🔐 安全提示:务必限制 /ban 接口的访问来源!


四、方案三:fail2ban + Nginx 日志(自动防御)

适用于防御暴力破解、高频 404 扫描等行为。fail2ban 会监控日志,自动封禁异常 IP。

1. 确保 Nginx 记录详细日志

log_format main '$remote_addr - $remote_user [$time_local] "$request" $status ...';
access_log /var/log/nginx/access.log main;

2. 创建过滤器 /etc/fail2ban/filter.d/nginx-bad-request.conf

[Definition]
failregex = ^<HOST> -.*"(GET|POST).*" 404 .*
ignoreregex =

3. 配置 jail(/etc/fail2ban/jail.local

[nginx-bad-request]
enabled  = true
port     = http,https
filter   = nginx-bad-request
logpath  = /var/log/nginx/access.log
maxretry = 10      # 10 次失败
findtime = 600     # 在 10 分钟内
bantime  = 3600    # 封 1 小时
action   = iptables-multiport[name=nginx, port="http,https"]

4. 启动并查看状态

sudo systemctl start fail2ban
sudo fail2ban-client status nginx-bad-request

✅ 优点:全自动、成熟稳定、社区支持好。
⚠️ 注意:默认使用 iptables 封禁,影响整个服务器,非仅 Nginx。


五、如何选择?

  • 只想偶尔手动封几个 IP? → 用 方案一(原生 Nginx)。
  • 需要程序自动封禁、支持 API 调用? → 用 方案二(OpenResty)。
  • 想自动防御扫描、爆破? → 用 方案三(fail2ban)。

💡 进阶技巧:可让 fail2ban 调用 OpenResty 的 /ban 接口,实现“仅封 Nginx + 自动过期”的完美组合。



关注公众号获取更多技术干货 !

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐