浏览 1.6k
通过nginx plus 实现动态黑白名单
前言:
随着现代应用架构的发展和演进,特别是容器云的盛行,越来越多的用户将7层流量的控制从前端网络的应用交付设备转向到了在每个应用,微服务前的软负载SLB上。典型的SLB,如nginx,部署位置可以在网络边界,在服务器的前端,在容器云中,以反代,微网关,sidecar的形式存在。 因为业务在不断的变化,特别是随着devops文化的流行,业务变化的频率和业务对基础架构的诉求也在不断上升。因此,我们也在nginx上交付了越来越多的应用服务,其中就包括了大家熟悉的黑白名单功能。
从现代的安全架构上来看,内网是不安全的,这件事情也得到了共识,从而出现了零信任架构,出现了无界的安全,出现了双模安全,基于每个应用的定制化精细化安全策略的诉求也就出现。
那么如何基于每个应用或微服务,进行频率的控制是保证应用的稳定性,健壮性,可用性的关键。限流是一个很好的方法,但是静态的限流很容易被绕过,应用层的ddos攻击往往会压着访问频率,那么如果能够准确定位出每个客户端,每个session的请求频率,自动的把超出阈值的客户端加黑封禁一段时间,比如说3分钟,这样就可以实现自动化的黑白名单,保护服务的健壮性。 当然,还有更有意思的玩法,比如设置每秒100rps,超出阈值,动态设置50rps的限流阈值,再超,拉黑封禁,这样就是自动化阶梯限流。
本次我将介绍动态黑白名单的具体实现。
需求介绍 :
1 设置动态黑名单,判断客户端的每秒的请求数,如果超过阈值,自动的将该客户端地址写入动态黑名单,封锁一段时间,之后再解封锁。
2 设置静态白名单,在白名单的IP优先级最高,直接放过。
相关技术:
Nginx plus , njs,limit_req,kv
实现方法 :
1 通过limit_req_zone 设置限流的阈值;
2 通过limit_req_dry_run 不进行真正限流,仅通过dry run功能输出如果采用限流技术后的结果的变量,也就是limit_req_status。
3 通过在njs中匹配变量limit_req_status的值是否是REJECTED_DRY_RUN,判断是否达到限流阈值。
4. 通过kv设置动态黑名单,key为客户端地址,value为是否block封锁的变量,如1为封锁,0为不封锁。
5. 在njs中完成逻辑功能实现:
5.1 如果单次请求,源地址在白名单中,放过,通过subrequest转回nginx中的id2 location,proxy 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 location,proxy 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/ { #接收njs中subrequest 返回的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; # 将KV的value变量传入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};
注意事项:
1 设置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;
3 具体实现上,可以基于客户端的ip,或者基于cookie来实现
4 Subrequest功能非常强大,具体细节请参考https://nginx.org/en/docs/njs/reference.html#http
5 关于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
总结
本文只针对功能需要的最小配置。可以看到NJS和KV可以应用到需要较为复杂的应用场景中,给我们带来更多的想象空间。
按点赞数排序
按时间排序