lua设计与实现_Openresty初探:使用Nginx Lua设计自己的WAF

Openresty初探:使用Nginx Lua设计自己的WAF

本文介绍基于Openresty设计一个根据IP+URL做访问频率限制的web应用防火墙(WAF),其中涉及到Nginx、Redis等相关内容会做简单介绍。

一、为什么选择Openresty

通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台 。

根据官网( http://openresty.org/cn/ )介绍,Openresty是通过Lua拓展Nginx的高性能Web平台。在保证基础Nginx功能的情况下,Openresty通过其内部集成的大量Lua库,可以方便地使用Lua语言拓展自己的Web服务。

所以,Openresty使用Lua作为业务功能的开发,在保留Nginx高并发的特性的同时,又更加方便拓展。

二、准备

在写一个简易的WAF之前,先要对Openresty的组件和Nginx知识有基本的了解。

2.1 Openresty安装

http:// openresty.org/cn/instal lation.html

使用官网提供的源码包进行编译安装,安装方法和Nginx相同。安装目录结构:

[root@SC openresty]# ll
total 248
drwxr-xr-x 1 root root    512 Nov 16 19:06 bin
-rw-r--r-- 1 root root  22924 Nov 16 19:06 COPYRIGHT
drwxr-xr-x 1 root root    512 Nov 16 19:06 luajit
drwxr-xr-x 1 root root    512 Nov 16 19:06 lualib
drwxr-xr-x 1 root root    512 Nov 16 19:08 nginx
drwxr-xr-x 1 root root    512 Nov 16 19:06 pod
-rw-r--r-- 1 root root 226755 Nov 16 19:06 resty.index
drwxr-xr-x 1 root root    512 Nov 16 19:06 site

这里需要关注两个目录,bin/ 和 nginx/ 。bin目录是openresty编译后的一些基本工具,nginx目录类似Nginx源码编译之后的安装目录,使用方法也相同。

Openresty相关命令和Nginx类似:install_path/nginx/sbin/nginx ; install_path/nginx/sbin/nginx -t/-s 等。

2.2 Hello World

http://openresty.org/cn/getting-started.html

可不必关注工作目录,直接修改install_path/nginx/sbin/nginx/conf/nginx.conf文件编写官网上的示例demo。

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello, world</p>")
            }
        }
    }
}

启动

[root@SC conf]# /opt/openresty/nginx/sbin/nginx
[root@SC conf]# curl 'http://localhost:8080'
<p>hello, world</p>
[root@SC conf]#

2.3 Demo:获取HTTP客户端访问IP地址

客户端IP常用来做HTTP访问白名单,在Nginx日志中,$remote_addr或者$http_x_forwarded_for字段记录了实际HTTP请求时客户端IP地址。$http_x_forwarded_for主要是在有反向代理的情况下,客户端实际IP会写在这个字段里,否则$remote_addr记录的是代理服务器的IP地址。

另外,在使用NAT的网络里,可以根据Openresty的返回获取到实际的出口IP。和curl http://ifconfig.me的功能类似。

-- 获取客户端IP地址

nginx.conf中类似如下配置

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua_block {
                local uri = ngx.var.uri
                local headers=ngx.req.get_headers()
                local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
                ngx.say(ip)
            }
        }
    }
}

重启Openresty

[root@SC conf]# /opt/openresty/nginx/sbin/nginx -s reload
[root@SC conf]# curl 'http://localhost:8080'
127.0.0.1
[root@SC conf]#

PS:在服务器有外网IP的情况下,curl后返回的就是客户端实际出口IP地址。

三、如何写一个WAF

上面介绍了Openresty的基本使用,接下来谈下如何写一个基于IP+URL的WEB应用防火墙。

3.1 设计

Openresty代理,最基本的需求是希望能够做到对于不同的IP有频率访问控制,阻挡恶意请求。写一个简易的CC防护WAF主要有如下几点:

1、客户端IP+URL拼接作为Redis的key

2、Redis的key设置过期时间

3、每次访问相同时Redis的key做自增

4、配置访问频率,如10/900(15分钟只能访问10次)。结合2,3两点做频控。

5、黑白名单配置

3.2 流程图

7a0a78bc02915963bf24af9904fda1fc.png

3.3 代码组织

3.3.1 nginx.conf

# 主要部分
http {
    charset  utf-8;
    # lua文件查找路径
    lua_package_path "$prefix/lua/?.lua;$prefix/lua/vendor/?.lua;$prefix/lua/waf/?.lua;;";
​
    server {
        listen 80;
        location / {
            default_type text/html;
            # 入口
            access_by_lua_block {
                require("gw_waf").go()
            }
        }
    }
}

3.3.2 Lua脚本

3.3.2.1 代码结构

[root@pc-nginx-az3 lua]# pwd
/opt/openresty/nginx/lua
[root@pc-nginx-az3 lua]# tree .
.
├── gw_waf.lua
├── vendor
│   ├── ipmatcher.lua
│   └── redis.lua
└── waf
    ├── config.lua
    ├── controller.lua
    ├── init.lua
    └── lib.lua
​
2 directories, 7 files

gw_waf.lua:类似main函数,是access_by_lua的入口

vendor:存放三方库源码,Openresty官方的lua-resty-redis,lua-resty-mysql之类的都可以存放在这里。Nginx Lua标准包可以在Lua中直接引用,其他优秀的三方库可以直接放在自定义目录下

waf:具体的实现逻辑

config.lua:程序配置文件

controller.lua:控制逻辑

init.lua:具体实现

lib.lua:其他通用函数

3.3.2.2 控制-controller.lua

local 

3.3.2.3 逻辑实现-init.lua

截取部分代码段说明
--allow white ip

3.4 建议

3.4.1 Redis服务端配置

timeout 60 ,设置服务端超时时间,否则高并发下TCP连接未及时释放导致Redis压力过大;

设置Redis密码

3.4.2 Lua 客户端

Lua客户端设置超时时间;

Lua客户端主动释放Redis连接,否则客户端请求出现超时导致拒绝连接

四、附录

4.1 验证

使用jemter做压测,验证功能和并发。访问的优先级的黑名单-白名单-频控,功能上测试完成可以对Redis中存储的数据进行验证。这里贴出的是部分Redis存储内容:

db0:keys=236200,expires=236200,avg_ttl=463353

TTL指令查看key的过期时间,其他Redis命令请参考 http://redis.cn/commands.html

4.2 更进一步

以上实现的只是WAF中比较小的一个方面。Lua中配置文件缺乏一个可视化的管理后台,攻击记录也没具体留存分析。后续的文章中将完善这两方面。

五、参考

5.1 Openresty官网

http://openresty.org

5.2 Nginx Lua库

Lua-resty-redis:https://github.com/openresty/lua-resty-redis

Lua-resty-ipmatcher: https://github.com/iresty/lua-resty-ipmatcher

5.3 Nginx WAF 实现

Ngx_lua_waf: https://github.com/loveshell/ngx_lua_waf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值