nginx openresty waf动态黑名单 白名单 、centos 、windows下部署

柴赞
2023-12-01

1、WAF

使用Nginx+Lua实现自定义WAF(Web application firewall)

参考网址https://github.com/unixhot/waf

功能列表:

  1. 支持IP白名单和黑名单功能,直接将黑名单的IP访问拒绝。
  2. 支持URL白名单,将不需要过滤的URL进行定义。
  3. 支持User-Agent的过滤,匹配自定义规则中的条目,然后进行处理(返回403)。
  4. 支持CC攻击防护,单个URL指定时间的访问次数,超过设定值,直接返回403。
  5. 支持Cookie过滤,匹配自定义规则中的条目,然后进行处理(返回403)。
  6. 支持URL过滤,匹配自定义规则中的条目,如果用户请求的URL包含这些,返回403。
  7. 支持URL参数过滤,原理同上。
  8. 支持日志记录,将所有拒绝的操作,记录到日志中去。
  9. 日志记录为JSON格式,便于日志分析,例如使用ELK进行攻击日志收集、存储、搜索和展示。

WAF实现

WAF一句话描述,就是解析HTTP请求(协议解析模块),规则检测(规则模块),做不同的防御动作(动作模块),并将防御过程(日志模块)记录下来。所以本文中的WAF的实现由五个模块(配置模块、协议解析模块、规则模块、动作模块、错误处理模块)组成。

安装部署

以下方案选择其中之一即可:

  • 选择1: 可以选择使用原生的Nginx,增加Lua模块实现部署。
  • 选择2: 直接使用OpenResty

OpenResty安装

1 Yum安装OpenResty(推荐)

源码安装和Yum安装选择其一即可,默认均安装在/usr/local/openresty目录下。

[root@opsany ~]# wget https://openresty.org/package/centos/openresty.repo
[root@opsany ~]# sudo mv openresty.repo /etc/yum.repos.d/
[root@opsany ~]# sudo yum install -y openresty
  1. 测试OpenResty和运行Lua
[root@opsany ~]# vim /usr/local/openresty/nginx/conf/nginx.conf
#在默认的server配置中增加
        location /hello {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello, world</p>")
            }
        }
[root@opsany ~]# /usr/local/openresty/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/openresty-1.17.8.2/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/openresty-1.17.8.2/nginx/conf/nginx.conf test is successful
[root@opsany ~]# /usr/local/openresty/nginx/sbin/nginx
  1. 测试访问
[root@opsany ~]# curl http://127.0.0.1/hello
<p>hello, world</p>

WAF部署

[root@opsany ~]# git clone https://github.com/unixhot/waf.git
[root@opsany ~]# cp -r ./waf/waf /usr/local/openresty/nginx/conf/
[root@opsany ~]# vim /usr/local/openresty/nginx/conf/nginx.conf
#在http{}中增加,注意路径,同时WAF日志默认存放在/tmp/日期_waf.log
#WAF
    lua_shared_dict limit 50m;
    lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua";
    init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua";
    access_by_lua_file "/usr/local/openresty/nginx/conf/waf/access.lua";
[root@opsany ~]# ln -s /usr/local/openresty/lualib/resty/ /usr/local/openresty/nginx/conf/waf/resty
[root@opsany ~]# /usr/local/openresty/nginx/sbin/nginx -t
[root@opsany ~]# /usr/local/openresty/nginx/sbin/nginx -s reload

2、附录

Nginx + Lua源码编译部署(不推荐)

  1. Nginx安装必备的Nginx和PCRE软件包。
[root@nginx-lua ~]# cd /usr/local/src
[root@nginx-lua src]# wget http://nginx.org/download/nginx-1.12.1.tar.gz
[root@nginx-lua src]# wget https://nchc.dl.sourceforge.net/project/pcre/pcre/8.41/pcre-8.41.tar.gz
#其次,下载当前最新的luajit和ngx_devel_kit (NDK),以及春哥(章)编写的lua-nginx-module
[root@nginx-lua src]# wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz
[root@nginx-lua src]# wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
[root@nginx-lua src]# wget wget https://github.com/chaoslawful/lua-nginx-module/archive/v0.10.10.zip
  1. 最后,创建Nginx运行的普通用户
[root@nginx-lua src]# useradd -s /sbin/nologin -M www
  1. 解压NDK和lua-nginx-module
[root@openstack-compute-node5 src]# tar zxvf v0.3.0.tar.gz
[root@openstack-compute-node5 src]# unzip -q v0.10.10.zip
  1. 安装LuaJIT Luajit是Lua即时编译器。
[root@webs-ebt src]# tar zxvf LuaJIT-2.0.5.tar.gz 
[root@webs-ebt src]# cd LuaJIT-2.0.5
[root@webs-ebt LuaJIT-2.0.5]# make && make install
  1. 安装Nginx并加载模块
[root@webs-ebt src]# tar zxf nginx-1.12.1.tar.gz
[root@webs-ebt src]# tar zxvf pcre-8.41.tar.gz 
[root@webs-ebt src]# cd nginx-1.12.1
[root@webs-ebt nginx-1.12.1]# export LUAJIT_LIB=/usr/local/lib
[root@webs-ebt nginx-1.12.1]# export LUAJIT_INC=/usr/local/include/luajit-2.0
[root@webs-ebt nginx-1.12.1]#./configure --user=www --group=www --prefix=/usr/local/nginx-1.12.1/ --with-pcre=/usr/local/src/pcre-8.41 --with-http_stub_status_module --with-http_sub_module --with-http_gzip_static_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module  --add-module=../ngx_devel_kit-0.3.0/ --add-module=../lua-nginx-module-0.10.10/
[root@webs-ebt nginx-1.12.1]# make -j2 && make install
[root@webs-ebt nginx-1.12.1]# ln -s /usr/local/nginx-1.12.1 /usr/local/nginx
[root@webs-ebt nginx-1.12.1]# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

如果不创建符号链接,可能出现以下异常:

error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
  1. 测试安装

安装完毕后,下面可以测试安装了,修改nginx.conf 增加第一个配置。

        location /hello {
                default_type 'text/plain';
                content_by_lua 'ngx.say("hello,lua")';
        }
 
[root@webs-ebt src]# /usr/local/nginx/sbin/nginx -t
[root@webs-ebt src]# /usr/local/nginx/sbin/nginx -t

然后访问http://xxx.xxx.xxx.xxx/hello 如果出现hello,lua。表示安装完成,然后就可以。

OpenResty源码编译部署(不推荐)

  1. 安装依赖软件包
[root@opsany ~]# yum install -y readline-devel pcre-devel openssl-devel
  1. 安装OpenResty

2.1 下载并编译安装OpenResty

[root@opsany ~]# cd /usr/local/src
[root@opsany src]# wget https://openresty.org/download/openresty-1.17.8.2.tar.gz
[root@opsany src]# tar zxf openresty-1.17.8.2.tar.gz
[root@opsany src]# cd openresty-1.17.8.2
[root@opsany openresty-1.17.8.2]# ./configure --prefix=/usr/local/openresty-1.17.8.2 \
--with-luajit --with-http_stub_status_module \
--with-pcre --with-pcre-jit \
--with-file-aio --with-threads
[root@opsany openresty-1.17.8.2]# gmake && gmake install
[root@opsany openresty-1.17.8.2]# cd
[root@opsany ~]# ln -s /usr/local/openresty-1.17.8.2/ /usr/local/openresty

3、动态黑名单部分

3.1、自行安装redis。

3.2配置文件如下

http {

  include    mime.types;

  default_type application/octet-stream;

  \#waf

  lua_shared_dict limit 50m;

 <font color=red> lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua";</font>

   <font color=red> init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua";</font>

   <font color=red> access_by_lua_file "/usr/local/openresty/nginx/conf/waf/access.lua";</font>



  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;

​    }

 

 

 

 

​     location /hello {

​      default_type text/html;

​      content_by_lua_block {

​        ngx.say("<p>hello zhoucheng!</p>")

​      }

​    }

​    

​    error_page  500 502 503 504 /50x.html;

​    location = /50x.html {

​      root  html;

​    }

 

​    location /test {

    ​	access_by_lua_block {

    ​	local redis = require 'resty.redis';

    ​	local red = redis:new();

    ​	red:set_timeouts(1000, 1000, 1000);

​		local ok, err = red:connect("127.0.0.1", 6379);

​		if not ok then

​			ngx.log(ngx.ERR, "failed to connect: ", err)

​		return

​		end

        local remote_addr = ngx.var.remote_addr;

        ngx.log(ngx.ERR, "remote_addr ==> ", remote_addr);

        ngx.log(ngx.ERR, "red:sismember('balck-list', remote_addr)");

        res, err = red:sismember('black-list', remote_addr);

        ngx.log(ngx.ERR, "是否在黑名单 ==> ", res);

 

        if res == 1 then

        ngx.log(ngx.ERR, "输出 403");

        ngx.exit(ngx.HTTP_FORBIDDEN);

        else

        ngx.log(ngx.ERR, "输出 200");

        ngx.exit(ngx.OK);

        end

	}

 

		echo "test";

        content_by_lua_block {

        ngx.say("test");

        }

  }

 

  location /add {

​    content_by_lua_block {

​      local redis = require 'resty.redis';

​      local red = redis:new();

 

​      red:set_timeouts(1000, 1000, 1000);

 

​      local ok, err = red:connect("127.0.0.1", 6379);

​      if not ok then

​        ngx.say("failed to connect: ", err)

​        return

​      end

 

​      local members = red:smembers("black-list");

​      if members then

​        ngx.say("添加前黑名单 ==> ", members);

​      end

 

​      local ip = ngx.var.arg_ip;

 

​      ngx.say("red:sadd('balck-list', ip)");

​      local res, err = red:sadd('black-list', ip);

​      if not res then

​        ngx.say("failed to sadd: ", err)

​        return

​      end

 

​      local members = red:smembers("black-list");

​      if members then

​        ngx.say("添加后黑名单 ==> ", members);

​      end

​     }

  }

 

location /delete {

    content_by_lua_block {

    local redis = require 'resty.redis';

    local red = redis:new();



    red:set_timeouts(1000, 1000, 1000);



    local ok, err = red:connect("127.0.0.1", 6379);

    if not ok then

    ngx.say("failed to connect: ", err)

    return

    end



    local members = red:smembers("black-list");

    if members then

    ngx.say("删除前黑名单 ==> ", members);

    end



    local ip = ngx.var.arg_ip;



    ngx.say("red:srem('balck-list', ip)");

    local res, err = red:srem('black-list', ip);

    if not res then

    ngx.say("failed to srem: ", err)

    return

    end



    local members = red:smembers("black-list");

    if members then

    ngx.say("删除后黑名单 ==> ", members);

    end

    }

}


}

 



}

 

3.3测试效果

# 初始黑名单为空

huli@hudeMacBook-Pro black % curl localhost:6001/test

test

 

# 添加黑名单

huli@hudeMacBook-Pro black % curl --location --request GET 'localhost:6001/add?ip=172.18.0.1'

添加前黑名单 ==> gtlx

red:sadd('balck-list', ip)

添加后黑名单 ==> 172.18.0.1gtlx

 

huli@hudeMacBook-Pro black % curl localhost:6001/test

<html>

<head><title>403 Forbidden</title></head>

<body>

<center><h1>403 Forbidden</h1></center>

<hr><center>openresty/1.21.4.1</center>

</body>

</html>

 

 

# 删除黑名单

huli@hudeMacBook-Pro black % curl --location --request GET 'localhost:6001/delete?ip=172.18.0.1'

删除前黑名单 ==> 172.18.0.1gtlx

red:srem('balck-list', ip)

删除后黑名单 ==> gtlx

 

huli@hudeMacBook-Pro black % curl localhost:6001/test

test

 

4、下面部分为widnows版本实现类似效果

1.下载waf,https://github.com/unixhot/waf 下载后的目录如下:

2、将waf文件夹下的4个lua文件拷贝到lualib文件夹下

3、然后将waf文件夹也拷贝到lualib文件夹中,改名为waf_conf,waf_conf文件夹中只有一个rule-config文件夹

4、在nginx.conf文件的http标签中增加配置。配置waf需要的lua文件,在HTTP下面增加两个代码

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;
	lua_shared_dict limit 10m;
	lua_package_path "C:/Users/Administrator/Desktop/openresty-1.21.4.1-win64/lualib/?.lua";
	init_by_lua_file  C:/Users/Administrator/Desktop/openresty-1.21.4.1-win64/lualib/init.lua;
	access_by_lua_file C:/Users/Administrator/Desktop/openresty-1.21.4.1-win64/lualib/access.lua;
	
	
	
	#....后面部分省略
	
	
	

5、在启动过程中如果报错,就是连因为role-config中没有配置

## **在Nginx文件中增加了lua扩展模块,启动时遇到的问题**

2021/09/01 09:06:12 [error] 59260#59260: *3 failed to load external Lua file "/root/openresty/application/nginx/conf/waf/access.lua": cannot open /root/openres

ty/application/nginx/conf/waf/access.lua: Permission denied, client: 192.168.56.1, server: localhost, request: "GET /hello HTTP/1.1", host: "192.168.56.7"

 

2021/09/01 09:11:20 [error] 61312#61312: *1 lua entry thread aborted: runtime error: /root/openresty/application/nginx/conf/waf/init.lua:152: bad argument #1 t

o 'pairs' (table expected, got nil)

stack traceback:

coroutine 0:

[C]: in function 'pairs'

/root/openresty/application/nginx/conf/waf/init.lua:152: in function 'user_agent_attack_check'

/root/openresty/application/nginx/conf/waf/access.lua:6: in function 'waf_main'

/root/openresty/application/nginx/conf/waf/access.lua:18: in main chunk, client: 192.168.56.1, server: localhost, request: "GET /hello HTTP/1.1", host:

"192.168.56.7"

 

# 解决办法:修改/waf/config.lua文件中的规则文件夹参数

--rule setting

config_rule_dir = "/root/openresty/application/nginx/conf/waf/rule-config"

6、配置lualib夹下的的config.lua文件,设置rule-config地址

--WAF config file,enable = "on",disable = "off"

--waf status
config_waf_enable = "on"
--log dir
config_log_dir = "/tmp"
--rule setting
config_rule_dir = "C:/Users/Administrator/Desktop/openresty-1.21.4.1-win64/lualib/waf_conf/rule-config"
--enable/disable white url
config_white_url_check = "on"
--enable/disable white ip
config_white_ip_check = "on"
--enable/disable block ip
config_black_ip_check = "on"
--enable/disable url filtering
config_url_check = "on"
--enalbe/disable url args filtering
config_url_args_check = "on"
--enable/disable user agent filtering
config_user_agent_check = "on"
--enable/disable cookie deny filtering
config_cookie_check = "on"
--enable/disable cc filtering
config_cc_check = "on"
--cc rate the xxx of xxx seconds
config_cc_rate = "10/60"
--enable/disable post filtering
config_post_check = "on"
--config waf output redirect/html
config_waf_output = "html"
--if config_waf_output ,setting url
config_waf_redirect_url = "https://www.unixhot.com"
config_output_html=[[
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="zh-cn" />
<title>OpsAny|Web应用防火墙</title>
</head>
<body>
<h1 align="center"> 欢迎白帽子进行授权安全测试,安全漏洞请联系QQ:57459267
</body>
</html>
]]


7黑白名单的IP在block和white添加或者修改即可。

 类似资料: