Openresty通过Lua+Redis 实现动态封禁IP

求背景

为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的 IP 黑名单。对于黑名单之内的 IP ,拒绝提供服务。并且可以设置失效

1.安装Openresty(编译安装)

bash 复制代码
wget https://openresty.org/download/openresty-1.19.3.1.tar.gz
# 解压openresty
tar -zxvf openresty-1.19.3.1.tar.gz 

下载缓存插件

bash 复制代码
wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
 #解压缓存插件
tar -zxvf ngx_cache_purge-2.3.tar.gz
cd openresty-1.19.3.1/
mkdir modules
# 把刚解压的ngx_cache_purge移动到该目录下

安装依赖

bash 复制代码
yum install pcre-devel openssl-devel gcc curl -y

编译OpenResty

选择需要的插件启用, --with-Components 激活组件,--without 则是禁止组件 ,--add-module是安装第三方模块。

进入刚刚解压好的openresty-1.19.3.1根目录下执行命令

bash 复制代码
./configure --prefix=/usr/local/openresty --with-luajit --without-http_redis2_module --with-http_stub_status_module --with-http_v2_module --with-http_gzip_static_module --with-http_sub_module --add-module=/usr/local/openresty-1.19.3.1/modules/ngx_cache_purge-2.3 

--prefix=/usr/local/openresty: 刚自己创建的目录,用来存放编译后的openresty
--add-module=/usr/local/openresty-1.19.3.1/xxx: 存放第三方插件的位置

2.安装redis

这里我是基于docker安装的redis

bash 复制代码
docker run --restart=always -p 6379:6379 --name myredis -d redis:7.0.12  --requirepass xxx
  • -requirepass 是redis密码

3.写lua脚本

Lua 复制代码
ip_bind_time = 30  --封禁IP多长时间
ip_time_out = 6    --指定统计ip访问频率时间范围
connect_count = 10 --指定ip访问频率计数最大值
--上面的意思就是6秒内访问超过10次,自动封 IP 30秒。
 
--连接redis
local redis = require "resty.redis"
local cache = redis.new()
local ok , err = cache.connect(cache,"127.0.0.1","6379")
-- redis密码
local res, err = cache:auth("xxx")
cache:set_timeout(60000)
 
--如果连接失败,跳转到脚本结尾
if not ok then
  goto Lastend
end
 
--查询ip是否在封禁段内,若在则返回403错误代码
--因封禁时间会大于ip记录时间,故此处不对ip时间key和计数key做处理
is_bind , err = cache:get("bind_"..ngx.var.remote_addr)
 
if is_bind == '1' then
  ngx.exit(ngx.HTTP_FORBIDDEN)
  -- 或者 ngx.exit(403)
  -- 当然,你也可以返回500错误啥的,搞一个500页面,提示,亲您访问太频繁啥的。
  goto Lastend
end
 
start_time , err = cache:get("time_"..ngx.var.remote_addr)
ip_count , err = cache:get("count_"..ngx.var.remote_addr)
 
--如果ip记录时间大于指定时间间隔或者记录时间或者不存在ip时间key则重置时间key和计数key
--如果ip时间key小于时间间隔,则ip计数+1,且如果ip计数大于ip频率计数,则设置ip的封禁key为1
--同时设置封禁key的过期时间为封禁ip的时间
 
if start_time == ngx.null or os.time() - start_time > ip_time_out then
  res , err = cache:set("time_"..ngx.var.remote_addr , os.time())
  res , err = cache:set("count_"..ngx.var.remote_addr , 1)
else
  ip_count = ip_count + 1
  res , err = cache:incr("count_"..ngx.var.remote_addr)
  if ip_count >= connect_count then
    res , err = cache:set("bind_"..ngx.var.remote_addr,1)
    res , err = cache:expire("bind_"..ngx.var.remote_addr,ip_bind_time) --fix keys
  end
end
--结尾标记
::Lastend::
local ok, err = cache:close()
bash 复制代码
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
	        access_by_lua_file "/usr/local/openresty/nginx/lua/access_by_redis.lua";
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

启动下就可以了

相关推荐
爱上语文1 分钟前
Java LeetCode每日一题
java·开发语言·leetcode
Манго нектар28 分钟前
JavaScript for循环语句
开发语言·前端·javascript
程序猿小D36 分钟前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
阿华的代码王国44 分钟前
【JavaEE】——文件IO的应用
开发语言·python
satan–01 小时前
R语言的下载、安装及环境配置(Rstudio&VSCode)
开发语言·windows·vscode·r语言
电饭叔1 小时前
《python语言程序设计》2018版第8章19题几何Rectangle2D类(下)-头疼的几何和数学
开发语言·python
Eternal-Student1 小时前
everyday_question dq20240731
开发语言·arm开发·php
卑微求AC2 小时前
(C语言贪吃蛇)11.贪吃蛇方向移动和刷新界面一起实现面临的问题
c语言·开发语言
程序猿小D2 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
Yvemil72 小时前
RabbitMQ 入门到精通指南
开发语言·后端·ruby