点赞
评论
收藏
分享
举报
通过NJS和KV实现动态黑白名单
发表于2021-04-01 15:48

浏览 1.6k

通过nginx plus 实现动态黑白名单

前言:

随着现代应用架构的发展和演进,特别是容器云的盛行,越来越多的用户将7层流量的控制从前端网络的应用交付设备转向到了在每个应用,微服务前的软负载SLB上。典型的SLB,如nginx,部署位置可以在网络边界,在服务器的前端,在容器云中,以反代,微网关,sidecar的形式存在。 因为业务在不断的变化,特别是随着devops文化的流行,业务变化的频率和业务对基础架构的诉求也在不断上升。因此,我们也在nginx上交付了越来越多的应用服务,其中就包括了大家熟悉的黑白名单功能。

从现代的安全架构上来看,内网是不安全的,这件事情也得到了共识,从而出现了零信任架构,出现了无界的安全,出现了双模安全,基于每个应用的定制化精细化安全策略的诉求也就出现。

那么如何基于每个应用或微服务,进行频率的控制是保证应用的稳定性,健壮性,可用性的关键。限流是一个很好的方法,但是静态的限流很容易被绕过,应用层的ddos攻击往往会压着访问频率,那么如果能够准确定位出每个客户端,每个session的请求频率,自动的把超出阈值的客户端加黑封禁一段时间,比如说3分钟,这样就可以实现自动化的黑白名单,保护服务的健壮性。 当然,还有更有意思的玩法,比如设置每秒100rps,超出阈值,动态设置50rps的限流阈值,再超,拉黑封禁,这样就是自动化阶梯限流。

本次我将介绍动态黑白名单的具体实现。

需求介绍 :

设置动态黑名单,判断客户端的每秒的请求数,如果超过阈值,自动的将该客户端地址写入动态黑名单,封锁一段时间,之后再解封锁。

设置静态白名单,在白名单的IP优先级最高,直接放过。

相关技术:

Nginx plus , njs,limit_req,kv

实现方法 :

通过limit_req_zone 设置限流的阈值;

通过limit_req_dry_run 不进行真正限流,仅通过dry run功能输出如果采用限流技术后的结果的变量,也就是limit_req_status

通过在njs中匹配变量limit_req_status的值是否是REJECTED_DRY_RUN,判断是否达到限流阈值。

4. 通过kv设置动态黑名单,key为客户端地址,value为是否block封锁的变量,如1为封锁,0为不封锁。

5. njs中完成逻辑功能实现:

5.1 如果单次请求,源地址在白名单中,放过,通过subrequest转回nginx中的id2 locationproxy pass到后端

5.2 如果单次请求的源地址在kv中没有值,null,那么赋值给ifblock变量为初始值0

5.3 如果单次请求 limit_req_status匹配REJECTED_DRY_RUN,表示达到限流阈值,对应的ifblock变量置为1,加入动态黑名单,响应499

5.4 如果单次请求,ifblock变量为0,表示没达到动态加黑的阈值,放过,通过subrequest转回nginx中的id2 locationproxy pass到后端

5.5 如果单次请求limit_req_status没有匹配REJECTED_DRY_RUN,但是ifblock变量为1,表示虽然现在没达到动态加黑的阈值,但是源地址还在动态黑名单中,还没有达到解冻时间,因此响应499

具体Nginx plus相关配置:

http {

js_import conf.d/kvcount1.js;    #调用njs文件

keyval_zone zone=bl:10m state=/etc/nginx/state_files/bl.json sync timeout=1m;

keyval $remote_addr $ifblock zone=bl;    #设置kv zone bl,设置kv变量

limit_req_zone $remote_addr zone=mylimit:10m rate=5r/s sync;  #设置限流zone

geo $remote_addr $ip_whitelist{     #设置白名单

default 0;

include conf.d/white_ip.conf;    #外置白名单配置文件,便于管理

}

}

white_ip.conf文件:

cat white_ip.conf

172.16.142.1/32 1;

221.176.33.0/24  1;

10.0.0.0/8  1;

192.168.0.0/24  1;

114.114.114.114  1;

location /black1 {

limit_req zone=mylimit;

limit_req_dry_run on;

js_content kvcount1.black;

}

location /id2/ {    #接收njssubrequest 返回的location

proxy_pass http://nplus2.nplus.com/;

}

具体NJS脚本:

function black(r) {

var limit = r.variables.limit_req_status;  # 将限流dryrun结果变量传入JS

var n = r.variables.ifblock;  # 将KVvalue变量传入JS

var b = r.variables.ip_whitelist;  # 将geo中的ip对应value变量传入JS

if ( b == 1 ) {                   # 如果在白名单,通过subrequest转回id2 location

var url = "/id2"+r.uri;

r.subrequest(url, { method: r.method },

function(res) {

r.return(res.status, res.responseBody);

});

} else {

if ( n = n ) {                 # 判断kv中没有值,赋值给ifblock变量为初始值0

if ( limit == "REJECTED_DRY_RUN") {  # 判断当前request超速。

r.variables.ifblock = 1;       #设置黑名单变量为1

r.return(499, "you are blocked in 60s");

} else {

if ( n == 0 ) {        #判断黑名单变量为0,通过subrequest转回id2 location

var url = "/id2"+r.uri;

r.subrequest(url, { method: r.method },

function(res) {

r.return(res.status, res.responseBody);

});

}

if ( n == 1 ) {        #判断黑名单变量为0,禁止访问。

r.return(499, "you are blocked in 60s");

}

}  

} else {

r.variables.ifblock = 0;

var url = "/id2"+r.uri;

r.subrequest(url, { method: r.method },

function(res) {

r.return(res.status, res.responseBody);

});

}

}

}

export default {black};

注意事项:

设置kv变量的时候,$remote_addr可以用$binary_remote_addr代替,例如下面的配置。

keyval_zone zone=bl:10m state=/etc/nginx/state_files/bl.json sync timeout=1m;

keyval $remote_addr $ifblock zone=bl;

原因是The $binary_remote_addr variable’s size is always 4 bytes for IPv4 addresses or 16 bytes for IPv6 addresses. The stored state always occupies 64 bytes on 32-bit platforms and 128 bytes on 64-bit platforms. One megabyte zone can keep about 16 thousand 64-byte states or about 8 thousand 128-byte states.

2 KV zone的容量规划,64位的服务器,1M空间可以存储8k的黑名单条目。

keyval_zone zone=bl:10m state=/etc/nginx/state_files/bl.json sync timeout=1m;

keyval $remote_addr $ifblock zone=bl;

具体实现上,可以基于客户端的ip,或者基于cookie来实现

4 Subrequest功能非常强大,具体细节请参考https://nginx.org/en/docs/njs/reference.html#http

关于njs相关资料

Docs: 

https://nginx.org/en/docs/njs/

Blog: 

https://www.nginx.com/blog/introduction-nginscript/

Github:

https://github.com/f5devcentral/nginx-njs-usecases

https://github.com/xeioex/njs-examples

nginx-cn.net:

https://www.nginx-cn.net

总结

本文只针对功能需要的最小配置。可以看到NJSKV可以应用到需要较为复杂的应用场景中,给我们带来更多的想象空间。

已修改于2023-03-08 02:07
本作品系原创
创作不易,留下一份鼓励
林夏

暂无个人介绍

关注



写下您的评论
发表评论
全部评论(0)

按点赞数排序

按时间排序

关于作者
林夏
这家伙很懒还未留下介绍~
2
文章
0
问答
1
粉丝
相关文章
前言nginx系列之一:nginx入门nginx系列之二:配置文件解读nginx系列之三:日志配置nginx系列之四:web服务器nginx系列之五:负载均衡nginx系列之六:cache服务nginx系列之七:限流配置nginx系列之八:使用upsync模块实现负载均衡转自:在此感谢原博主的整理分享一、nginxaccess日志配置1.1access_log日志配置access_log用来定义日志级别,日志位置。语法如下:日志级别: debug>info>notice>warn>error>crit>alert>emerg语法格式:access_logpath[format[buffer=size][gzip[=level]][flush=time][if=condition]];access_logoff;默认值:access_loglogs/access.logcombined;作用域:http,server,location,ifinlocation,limit_except实例一:access
点赞 4
浏览 1.9k
✨ NGINXConfigNGINXissomuchmorethanjustawebserver.Youalreadyknewthat,probably.WeloveNGINX,because:lowmemoryusagehighconcurrencyasynchronousevent-drivenarchitectureloadbalancingreverseproxyingFastCGIsupportwithcaching(PHP)amazingfasthandlingofstaticfilesTLS/SSLwithSNIAlotoffeatureswithcorrespondingconfigurationdirectives.Youcandeepdiveintothe NGINXdocumentation rightnowORyoucanusethistooltocheckhowNGINXworks,observehowy
点赞 1
浏览 1.7k
这一路走来,自己有一些感悟,也看到其他人的一些经验教训,所以记录下自己近年来的学习心得,包括技术学习、技术管理、提升自我。 随着互联网技术的快速发展,程序员成为了近年来最为热门的职业之一。程序员作为一
点赞 0
浏览 531