Nginx 实现动态封禁IP

举报
林欣 发表于 2024/10/24 16:26:48 2024/10/24
【摘要】 Nginx 是一个高性能的 HTTP 和反向代理服务器,通过配置模块可以实现很多强大的功能,包括动态封禁 IP 地址。实现动态封禁 IP 通常涉及以下几步:使用 Nginx 第三方模块:Nginx 本身并不直接支持动态封禁 IP,但可以通过第三方模块如 ngx_http_access_module 或者 ngx_http_auth_request_module 来实现。此外,还可以结合外部脚...

Nginx 是一个高性能的 HTTP 和反向代理服务器,通过配置模块可以实现很多强大的功能,包括动态封禁 IP 地址。实现动态封禁 IP 通常涉及以下几步:

  1. 使用 Nginx 第三方模块:Nginx 本身并不直接支持动态封禁 IP,但可以通过第三方模块如 ngx_http_access_module 或者 ngx_http_auth_request_module 来实现。此外,还可以结合外部脚本和 Redis 等工具来实现动态封禁。

  2. 配置 Nginx 和 Lua 脚本:通过 Nginx 的 ngx_lua 模块,可以编写 Lua 脚本来动态地处理封禁逻辑。Lua 脚本可以查询 Redis 或其他存储系统,来决定是否封禁某个 IP。

  3. 使用第三方工具:比如 Fail2ban,它可以与 Nginx 日志结合,动态封禁多次失败的请求 IP。

下面是一个使用 ngx_lua 和 Redis 实现动态封禁 IP 的示例:

环境准备

  1. 安装 Nginx 和 LuaJIT:确保 Nginx 支持 Lua 模块,通常需要安装 openresty,它包含了 Nginx 和 ngx_lua 模块。

  2. 安装 Redis:用于存储封禁 IP 列表。

配置示例

  1. 安装 OpenResty 和 LuaRocks(假设在 Linux 系统上):

    sudo apt-get update
    sudo apt-get install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring
    curl -fsSL https://openresty.org/package/pubkey.gpg | sudo tee /usr/share/keyrings/openresty-archive-keyring.gpg > /dev/null
    echo "deb [signed-by=/usr/share/keyrings/openresty-archive-keyring.gpg] http://openresty.org/package/debian $(lsb_release -cs) openresty" | sudo tee /etc/apt/sources.list.d/openresty.list
    sudo apt-get update
    sudo apt-get install -y openresty
    sudo apt-get install -y luarocks
    
  2. 安装 Lua 依赖库

    sudo luarocks install lua-resty-redis
    sudo luarocks install lua-resty-http
    
  3. 配置 Nginx

    编辑 Nginx 配置文件(例如 /etc/openresty/nginx.conf),添加以下配置:

    http {
        lua_shared_dict my_cache 10m;
    
        server {
            listen 80;
    
            location / {
                access_by_lua_block {
                    local redis = require "resty.redis"
                    local red = redis:new()
                    red:set_timeout(1000)  -- 1 sec
    
                    local ok, err = red:connect("127.0.0.1", 6379)
                    if not ok then
                        ngx.say("failed to connect: ", err)
                        return ngx.exit(500)
                    end
    
                    local client_ip = ngx.var.remote_addr
                    local blocked, err = red:sismember("blocked_ips", client_ip)
                    if not blocked then
                        -- Proceed with the request
                    else
                        ngx.say("Access forbidden")
                        return ngx.exit(403)
                    end
    
                    -- Close the redis connection
                    local ok, err = red:close()
                    if not ok then
                        ngx.log(ngx.ERR, "failed to close: ", err)
                    end
                }
    
                # Your other configuration directives
                proxy_pass http://your_backend;
            }
    
            # Add a location for blocking/unblocking IPs via HTTP (for admin use)
            location /block_ip {
                content_by_lua_block {
                    local redis = require "resty.redis"
                    local red = redis:new()
                    red:set_timeout(1000)  -- 1 sec
    
                    local ok, err = red:connect("127.0.0.1", 6379)
                    if not ok then
                        ngx.say("failed to connect: ", err)
                        return ngx.exit(500)
                    end
    
                    local ip = ngx.req.get_uri_args()["ip"]
                    if not ip then
                        ngx.say("IP not provided")
                        return ngx.exit(400)
                    end
    
                    local ok, err = red:sadd("blocked_ips", ip)
                    if not ok then
                        ngx.say("failed to block IP: ", err)
                        return ngx.exit(500)
                    end
    
                    ngx.say("IP blocked successfully")
    
                    -- Close the redis connection
                    local ok, err = red:close()
                    if not ok then
                        ngx.log(ngx.ERR, "failed to close: ", err)
                    end
                }
            }
    
            location /unblock_ip {
                content_by_lua_block {
                    local redis = require "resty.redis"
                    local red = redis:new()
                    red:set_timeout(1000)  -- 1 sec
    
                    local ok, err = red:connect("127.0.0.1", 6379)
                    if not ok then
                        ngx.say("failed to connect: ", err)
                        return ngx.exit(500)
                    end
    
                    local ip = ngx.req.get_uri_args()["ip"]
                    if not ip then
                        ngx.say("IP not provided")
                        return ngx.exit(400)
                    end
    
                    local ok, err = red:srem("blocked_ips", ip)
                    if not ok then
                        ngx.say("failed to unblock IP: ", err)
                        return ngx.exit(500)
                    end
    
                    ngx.say("IP unblocked successfully")
    
                    -- Close the redis connection
                    local ok, err = red:close()
                    if not ok then
                        ngx.log(ngx.ERR, "failed to close: ", err)
                    end
                }
            }
        }
    }
    

运行和测试

  1. 启动 Redis

    sudo systemctl start redis
    
  2. 启动 Nginx

    sudo systemctl start openresty
    
  3. 测试封禁功能

    • 访问 http://your_server_ip/block_ip?ip=192.168.1.100 来封禁某个 IP。
    • 访问 http://your_server_ip/unblock_ip?ip=192.168.1.100 来解封某个 IP。
    • 使用被封禁的 IP 访问你的 Nginx 服务器,应该会收到 Access forbidden 的响应。

这样,你就实现了一个简单的动态封禁 IP 的系统。根据需求,你可以进一步优化和扩展这个系统,比如添加更多的日志记录、更复杂的封禁规则等。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。