waf

:vertical_traffic_light:Web Application Firewall or API Gateway(应用防火墙/API网关)

Github stars Tracking Chart

Join the chat at https://gitter.im/chengdedeng/waf
MIT Licence
LICENSE

WAF是使用Java开发的API Gateway,由于WAF构建在开源代理LittleProxy之上,所以说WAF底层使用的是Netty

特性

  1. 安全拦截,支持各种分析检测,支持脚本(沙箱);
  2. 流控/CC防护,支持IP粒度,可扩展;
  3. HTTP代理,支持"hijacking" HTTPS connection using "Man in the Middle" style attack;
  4. URL Rewrite;
  5. HTTP Redirect;
  6. Socks5;
  7. 集中式配置;
  8. 自定义协议转化,HTTP->DUBBO,HTTP->GRPC等(可导入swagger接口文档);

Quick Start

编译:
 mvn package
运行:

由于使用appassembler-maven-plugin
打成了符合JSW规范的包,所以解压target目录下的Zip文件,
然后在bin目录下运行对应平台的脚本,以Linux为例:

bin/waf { console, start, stop, restart, status, dump }
配置:

2.0开始配置分为基础配置和应用配置,基础配置就是Classpath下的waf.properties、admin.properties、application.properties、cluster.properties;
应用配置则需要通过接口进行设置,waf目前还没有一套简易的UI来进行设置,不过集成了swagger,可以通过swagger ui界面来配置。2.0相较于1.0,配置集中化之后的好处在于配置修改
不再需要停机重新加载,规则随时可以添加、禁用删除。配置中心目前zookeeper是稳定的,atomix自研的配置中心目前还不完善,建议别开启atomix的spi实现。尽管没有UI,但是接口做了
详细的校验,大家可以放心设置,不大会出现配置参数设置错误导致的系统崩溃。

架构

HTTP Proxy选择了基于Netty研发的LittleProxy,
LittleProxy是LANTERN的维护者发起的开源项目,是一款非常优秀的Java HTTP Proxy.
关于Loadbalance,WAF有两种模式可以供选择,一种基于Proxy Chain,另一种是基于HostResolver.Proxy Chain是把目标机的映射交给
下游的Proxy,而HostResolver则是WAF自身完成映射.需要特别注意的是,Proxy Chain中如果存在多Proxy是不会负载均衡的,只有前一个不可用时才会用下一个.

HttpRequestFilterChainHttpResponseFilterChain 责任链,分别对进来和出去的数据进行拦截分析.Request拦截又分为黑白名单两种,Response拦截主要给输出的数据进行安全加固.在Request的拦截规则方面,我参考了loveshell/ngx_lua_waf.

更多技术详情请移步个人Java版WAF技术细节
HttpProxy研发心得

性能

测试目的

Nginx的性能是有目共睹的,WAF既然作为一个HTTP Proxy,所以需要跟Nginx对比一下,看看性能的差距有多大.

因为目的是要压出中间Proxy的性能极限,所以后端服务性能要非常高,至少要比中间Proxy性能好,所以选用了Nginx模拟后端服务.
为了减少网络开销对测试影响,所有的测试都是在一台机器上完成的.

测试基准:

1.AB->Nginx_Proxy->Nginx_AS

2.AB->WAF->Nginx_AS

3.ab -k -c 100 -n 1000000 目标地址(HTTP长链)

4.ab -c 100 -n 1000000 目标地址(HTTP短链)

JDK版本
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
WAF JVM配置:
wrapper.java.additional.1=-server
wrapper.java.additional.2=-Xms2048m
wrapper.java.additional.3=-Xmx2048m
wrapper.java.additional.4=-Xmn800m
wrapper.java.additional.5=-XX:+UseG1GC
wrapper.java.additional.6=-Xloggc:/tmp/log/gc.log
wrapper.java.additional.7=-XX:+HeapDumpOnOutOfMemoryError
wrapper.java.additional.8=-XX:+PrintGCDetails
wrapper.java.additional.9=-XX:+PrintGCTimeStamps
wrapper.java.additional.10=-XX:+PreserveFramePointer
WAF基础配置:
#on表示waf支持loadbalance,需要配置upstream.properties,与waf.proxy.chain和waf.mitm互斥
waf.lb=on
#设置重试间隔时间,默认10秒
waf.lb.fail_timeout=10
#是否路由到waf下游的proxy,与waf.proxy.lb互斥
waf.chain=off
#waf下游的proxy,多个用","分隔.注意只有前一个不可用,才会用下一个,下游proxy不会负载均衡
waf.chain.servers=127.0.0.1:4321
#是否启用TLS,与waf.mitm互斥
waf.tls=off
#是否HTTPS开启中间人拦截,与waf.tls和waf.proxy.lb互斥
waf.mitm=off
#接收者线程数,如果系统只有一个服务端port需要监听,则BossGroup线程组线程数设置为 1。
#https://stackoverflow.com/questions/22280916/do-we-need-more-than-a-single-thread-for-boss-group
waf.acceptorThreads=1
#处理client请求的工作线程数
waf.clientToProxyWorkerThreads=100
#处理proxy与后端服务器的工作线程数
waf.proxyToServerWorkerThreads=100
#waf服务器端口
waf.serverPort=9091
#是否开启Socks5支持
waf.ss=off
waf.ss.server.host=127.0.0.1
waf.ss.server.port=1080
#The timeout (in seconds) for auto-closing idle connections.
waf.idleConnectionTimeout=70
服务器/虚拟机(测试机)配置:
4  Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz

结果:

CPU(id基本在10以内)

%Cpu0  : 49.8 us, 33.7 sy,  0.0 ni,  6.1 id,  0.0 wa,  0.0 hi, 10.4 si,  0.0 st
%Cpu1  : 48.0 us, 33.9 sy,  0.0 ni,  7.4 id,  0.0 wa,  0.0 hi, 10.7 si,  0.0 st
%Cpu2  : 49.8 us, 33.0 sy,  0.0 ni,  7.4 id,  0.0 wa,  0.0 hi,  9.8 si,  0.0 st
%Cpu3  : 48.8 us, 31.5 sy,  0.0 ni,  8.5 id,  0.0 wa,  0.0 hi, 11.2 si,  0.0 st

QPS

测试场景, 测试条件, QPS
-------, -------, -------
AB->Nginx_AS, HTTP长链, 64815
AB->Nginx_AS, HTTP短链, 6174
AB->Nginx_Proxy->Nginx_AS, HTTP长链, 16924
AB->Nginx_Proxy->Nginx_AS, HTTP短链, 13137
AB->WAF->Nginx_AS, HTTP长链, 5566
AB->WAF->Nginx_AS, HTTP短链, 5559

火焰图:

github不支持火焰图显示,点击下载源文件.

常见问题

  1. 开启TLS or MITM后,会在项目的目录下生成waf_cert证书,TLS会自动下发证书,MITM需要手动加入证书,信任之后就可以正常工作了.
  2. waf.proxy.lbwaf.proxy.mitm,waf.tlswaf.proxy.mitm,waf.proxy.chainwaf.proxy.lb两两之间只能开启其中之一.
  3. 如果只是HTTP或者HTTPS抓包,可以关闭所有的安全拦截.
  4. 如果Gateway前面还有loadbalance,例如Nginx,Nginx->Gateway一定要用HTTP1.1,否者会报upstream prematurely closed connection while reading upstream。出现该问题的原因
    可以仔细阅读HttpFilterAdapterImpl.java中方法
    proxyToServerRequestSending的注释。

Main metrics

Overview
Name With Ownerchengdedeng/waf
Primary LanguageJava
Program languageJava (Language Count: 2)
Platform
License:Other
所有者活动
Created At2017-06-02 06:24:50
Pushed At2024-01-17 08:56:29
Last Commit At2022-12-20 21:29:02
Release Count2
Last Release Name2.0.0 (Posted on 2018-08-01 15:52:16)
First Release Name1.0.0 (Posted on 2018-08-01 15:53:09)
用户参与
Stargazers Count695
Watchers Count49
Fork Count210
Commits Count201
Has Issues Enabled
Issues Count10
Issue Open Count0
Pull Requests Count2
Pull Requests Open Count0
Pull Requests Close Count1
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private