前言
测试环境为 ubuntu
编译
要开启防火墙模式,需要打开 nfqueue 功能
src/.libs/suricata --build-info
发现不支持NFQueue
NFQueue support: no
./configure --enable-nfqueue
之后,发现还是没有打开,根据网上资料,安装下面三个库,再配置就有了。
sudo apt libunwind-dev
sudo apt install libnetfilter-queue-dev
sudo apt install libnfnetlink-dev
测试环境搭建
[pc1 30.0.0.2] --- [30.0.0.1 gateway 40.0.0.1] --- [40.0.0.2 pc2]
【gateway 】上打开 ip 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
【pc1】 设置 IP ,并配置路由
route add -net 40.0.0.0/24 gw 30.0.0.1
【pc2】 设置 IP ,并配置路由
route add -net 30.0.0.0/24 gw 40.0.0.1
最后 PC1 上应该能 ping 通 40.0.0.2 ,如果不能需要在 gateway 上检测转发策略 iptables -L -n
开启 suricata
netfilter 配置 nf queue ,默认序号为 0
sudo iptables -I FORWARD -j NFQUEUE
修改 suricata 配置文件
##
## Netfilter integration
##
# When running in NFQ inline mode, it is possible to use a simulated
# non-terminal NFQUEUE verdict.
# This permits sending all needed packet to Suricata via this rule:
# iptables -I FORWARD -m mark ! --mark $MARK/$MASK -j NFQUEUE
# And below, you can have your standard filtering ruleset. To activate
# this mode, you need to set mode to 'repeat'
# If you want a packet to be sent to another queue after an ACCEPT decision
# set the mode to 'route' and set next-queue value.
# On Linux >= 3.1, you can set batchcount to a value > 1 to improve performance
# by processing several packets before sending a verdict (worker runmode only).
# On Linux >= 3.6, you can set the fail-open option to yes to have the kernel
# accept the packet if Suricata is not able to keep pace.
# bypass mark and mask can be used to implement NFQ bypass. If bypass mark is
# set then the NFQ bypass is activated. Suricata will set the bypass mark/mask
# on packet of a flow that need to be bypassed. The Nefilter ruleset has to
# directly accept all packets of a flow once a packet has been marked.
nfq:
mode: accept
# repeat-mark: 1
# repeat-mask: 1
# bypass-mark: 1
# bypass-mask: 1
# route-queue: 2
# batchcount: 20
# fail-open: yes
runmode: workers
suricata 开启 nfq 模式
sudo suricata -c etc/suricata/suricata.yaml -q 0
重要的代码
StreamTcpPacket() 函数中会调用 StreamTcpCheckFlowDrops 函数,如果 flow->flags 有 FLOW_ACTION_DROP ,那么就会丢包处理。
static inline int StreamTcpCheckFlowDrops(Packet *p)
{
/* If we are on IPS mode, and got a drop action triggered from
* the IP only module, or from a reassembled msg and/or from an
* applayer detection, then drop the rest of the packets of the
* same stream and avoid inspecting it any further */
if (EngineModeIsIPS() && (p->flow->flags & FLOW_ACTION_DROP))
return 1;
return 0;
}
在应用层处理中,增加规则判断,如果想阻断,增加标识即可阻断
比如对 url 进行阻断,在 app-layer-htp.c 中间中 HTPHandleRequestData()函数增加如下代码
if (input != NULL && strstr(input , "tom.html") != NULL)
{
f->flags |= FLOW_ACTION_DROP;
}
用 curl 命令进行测试,验证了 tom.html 无法打开
host:~$ curl http://40.0.0.2/jacky.html
<h1> my name is jacky</h1>
host:~$ curl http://40.0.0.2/tom.html
^C
host:~$
在服务器 40.0.0.2 设备上抓包 ,会发现头三个握手包都到服务器了,客户端的请求包未到服务器,被网关丢弃了,一分钟后,服务器主动 Fin 了。
需要说一下的是,直接DROP包客户端体验不是太好,suricata 似乎不支持主动发 rst 包,有点遗憾,回头研究一下看,能不能自己构造 rst 包发给客户端和服务端
[root@localhost html]# tcpdump -i any port 80 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
14:23:23.024196 IP 30.0.0.2.43336 > 40.0.0.2.http: Flags [S], seq 3649125777, win 29200, options [mss 1460,sackOK,TS val 371008070 ecr 0,nop,wscale 7], length 0
14:23:23.024226 IP 40.0.0.2.http > 30.0.0.2.43336: Flags [S.], seq 3484780862, ack 3649125778, win 28960, options [mss 1460,sackOK,TS val 1484005215 ecr 371008070,nop,wscale 7], length 0
14:23:23.024414 IP 30.0.0.2.43336 > 40.0.0.2.http: Flags [.], ack 1, win 229, options [nop,nop,TS val 371008070 ecr 1484005215], length 0
14:24:23.084548 IP 40.0.0.2.http > 30.0.0.2.43336: Flags [F.], seq 1, ack 1, win 227, options [nop,nop,TS val 1484065275 ecr 371008070], length 0