mirror of https://github.com/OISF/suricata
qa: add nfq based firewall test with live reload
parent
7ac32910c9
commit
49b1382a8b
@ -0,0 +1,13 @@
|
||||
# allow session setup
|
||||
accept:hook tcp:all any any <> any 80 (flow:not_established; alert; sid:1021;)
|
||||
|
||||
# pass rest of the flow to
|
||||
accept:hook tcp:all any any <> any 80 (flow:established; alert; sid:1023;)
|
||||
#accept:hook ip:all any any <> any any (alert; sid:1024;)
|
||||
|
||||
# default drop
|
||||
|
||||
accept:hook http1:request_started any any -> any any (alert; sid:100;)
|
||||
accept:hook http1:request_line any any -> any any (http.method; content:"GET"; http.uri; content:"/"; alert; sid:101;)
|
||||
accept:tx http1:request_headers any any -> any any (http.user_agent; content:"wget"; nocase; alert; sid:102;)
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
# allow session setup
|
||||
accept:hook tcp:all any any <> any 80 (flow:not_established; alert; sid:1021;)
|
||||
|
||||
# pass rest of the flow to
|
||||
accept:hook tcp:all any any <> any 80 (flow:established; alert; sid:1023;)
|
||||
#accept:hook ip:all any any <> any any (alert; sid:1024;)
|
||||
|
||||
# default drop
|
||||
|
||||
accept:hook http1:request_started any any -> any any (alert; sid:100;)
|
||||
accept:hook http1:request_line any any -> any any (http.method; bsize:3; urilen:>1; sid:201; alert;)
|
||||
accept:tx http1:request_headers any any -> any any (http.user_agent; pcre:"/wget/i"; sid:202; alert;)
|
||||
|
||||
@ -0,0 +1,324 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to test live Firewall capabilities for NFQ.
|
||||
#
|
||||
# Uses 3 network namespaces:
|
||||
# - client
|
||||
# - server
|
||||
# - dut
|
||||
#
|
||||
# Dut is where Suricata will run:
|
||||
#
|
||||
# [ client ]$clientif - $dutclientif[ dut ]$dutserverif - $serverif[ server ]
|
||||
#
|
||||
# By routing packets between the dut interfaces, Suricata becomes the router.
|
||||
# Packets will be forwarded by the kernel, sent to Suricata via iptables NFQUEUE
|
||||
# which can then verdict them.
|
||||
|
||||
# Call with following arguments:
|
||||
# 1st: runmode string (single/autofp/workers)
|
||||
# 2nd: suricata yaml to use
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ $# -ne "2" ]; then
|
||||
echo "ERROR call with 2 args: runmode (single/autofp/workers) and yaml"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
RUNMODE=$1
|
||||
YAML=$2
|
||||
|
||||
# dump some info
|
||||
echo "* printing some diagnostics..."
|
||||
ip netns list
|
||||
uname -a
|
||||
ip r
|
||||
echo "* printing some diagnostics... done"
|
||||
|
||||
clientns=client
|
||||
serverns=server
|
||||
dutns=dut
|
||||
clientip="10.10.10.2/24"
|
||||
clientnet="10.10.10.0/24"
|
||||
serverip='10.10.20.2/24'
|
||||
servernet="10.10.20.0/24"
|
||||
dutclientip="10.10.10.1/24"
|
||||
dutserverip='10.10.20.1/24'
|
||||
clientif=client
|
||||
serverif=server
|
||||
dutclientif=dut_client
|
||||
dutserverif=dut_server
|
||||
|
||||
echo "* removing old namespaces..."
|
||||
NAMESPACES=$(ip netns list|cut -d' ' -f1)
|
||||
for NS in $NAMESPACES; do
|
||||
if [ $NS = $dutns ] || [ $NS = $clientns ] || [ $NS = $serverns ]; then
|
||||
ip netns delete $NS
|
||||
fi
|
||||
done
|
||||
echo "* removing old namespaces... done"
|
||||
|
||||
# remove eve.json from previous run
|
||||
if [ -f eve.json ]; then
|
||||
rm eve.json
|
||||
fi
|
||||
|
||||
if [ -e ./rust/target/release/suricatasc ]; then
|
||||
SURICATASC=./rust/target/release/suricatasc
|
||||
else
|
||||
SURICATASC=./rust/target/debug/suricatasc
|
||||
fi
|
||||
|
||||
RES=0
|
||||
|
||||
# adding namespaces
|
||||
echo "* creating namespaces..."
|
||||
ip netns add $clientns
|
||||
ip netns add $serverns
|
||||
ip netns add $dutns
|
||||
echo "* creating namespaces... done"
|
||||
|
||||
#diagnostics output
|
||||
echo "* list namespaces..."
|
||||
ip netns list
|
||||
ip netns exec $clientns ip ad
|
||||
ip netns exec $serverns ip ad
|
||||
ip netns exec $dutns ip ad
|
||||
echo "* list namespaces... done"
|
||||
|
||||
# create virtual ethernet link between client-dut and server-dut
|
||||
# These are not yet mapped to a namespace
|
||||
echo "* creating virtual ethernet devices..."
|
||||
ip link add ptp-$clientif type veth peer name ptp-$dutclientif
|
||||
ip link add ptp-$serverif type veth peer name ptp-$dutserverif
|
||||
echo "* creating virtual ethernet devices...done"
|
||||
|
||||
echo "* list interface in global namespace..."
|
||||
ip link
|
||||
echo "* list interface in global namespace... done"
|
||||
|
||||
echo "* map virtual ethernet interfaces to their namespaces..."
|
||||
ip link set ptp-$clientif netns $clientns
|
||||
ip link set ptp-$serverif netns $serverns
|
||||
ip link set ptp-$dutclientif netns $dutns
|
||||
ip link set ptp-$dutserverif netns $dutns
|
||||
echo "* map virtual ethernet interfaces to their namespaces... done"
|
||||
|
||||
echo "* list namespaces and interfaces within them..."
|
||||
ip netns list
|
||||
ip netns exec $clientns ip ad
|
||||
ip netns exec $serverns ip ad
|
||||
ip netns exec $dutns ip ad
|
||||
echo "* list namespaces and interfaces within them... done"
|
||||
|
||||
# bring up interfaces. Client and server get IP's.
|
||||
# Disable rx and tx csum offload on all sides.
|
||||
|
||||
echo "* setup client interface..."
|
||||
iface=ptp-$clientif
|
||||
ip netns exec $clientns ip addr add $clientip dev $iface
|
||||
ip netns exec $clientns ip link set $iface up
|
||||
echo "* setup client interface... done"
|
||||
|
||||
echo "* setup server interface..."
|
||||
iface=ptp-$serverif
|
||||
ip netns exec $serverns ip addr add $serverip dev $iface
|
||||
ip netns exec $serverns ip link set $iface up
|
||||
echo "* setup server interface... done"
|
||||
|
||||
echo "* setup dut interfaces..."
|
||||
ip netns exec $dutns ip addr add $dutclientip dev ptp-$dutclientif
|
||||
ip netns exec $dutns ip addr add $dutserverip dev ptp-$dutserverif
|
||||
ip netns exec $dutns ip link set ptp-$dutclientif up
|
||||
ip netns exec $dutns ip link set ptp-$dutserverif up
|
||||
echo "* setup dut interfaces... done"
|
||||
|
||||
echo "* setup client/server routes..."
|
||||
# routes:
|
||||
#
|
||||
# client can reach servernet through the client side ip of the dut
|
||||
via_ip=$(echo $dutclientip|cut -f1 -d'/')
|
||||
ip netns exec $clientns ip route add $servernet via $via_ip dev ptp-$clientif
|
||||
#
|
||||
# server can reach clientnet through the server side ip of the dut
|
||||
via_ip=$(echo $dutserverip|cut -f1 -d'/')
|
||||
ip netns exec $serverns ip route add $clientnet via $via_ip dev ptp-$serverif
|
||||
echo "* setup client/server routes... done"
|
||||
|
||||
echo "* enabling forwarding in the dut..."
|
||||
# forward all
|
||||
ip netns exec $dutns sysctl net.ipv4.ip_forward=1
|
||||
ip netns exec $dutns iptables -I FORWARD 1 -j NFQUEUE
|
||||
echo "* enabling forwarding in the dut... done"
|
||||
|
||||
# set first rule file
|
||||
cp qa/live/netns/firewall1-l3.rules firewall.rules
|
||||
RULES="firewall.rules"
|
||||
cat firewall.rules
|
||||
|
||||
echo "* starting Suricata in the \"dut\" namespace..."
|
||||
# Start Suricata in the dut namespace, then SIGINT after 240 secords. Will
|
||||
# close it earlier through the unix socket.
|
||||
timeout --kill-after=300 --preserve-status 240 \
|
||||
ip netns exec $dutns \
|
||||
./src/suricata -c $YAML -l ./ -q 0 -v \
|
||||
--set firewall.rule-path=. \
|
||||
--set default-rule-path=. --runmode=$RUNMODE &
|
||||
SURIPID=$!
|
||||
sleep 10
|
||||
echo "* starting Suricata... done"
|
||||
|
||||
echo "* starting tshark on in the server namespace..."
|
||||
timeout --kill-after=240 --preserve-status 180 \
|
||||
ip netns exec $serverns \
|
||||
tshark -i ptp-$serverif -T json > tshark-server.json &
|
||||
TSHARKSERVERPID=$!
|
||||
sleep 5
|
||||
echo "* starting tshark on in the server namespace... done, pid $TSHARKSERVERPID"
|
||||
|
||||
echo "* starting Caddy..."
|
||||
# Start Caddy in the server namespace
|
||||
timeout --kill-after=480 --preserve-status 240 \
|
||||
ip netns exec $serverns \
|
||||
caddy file-server --browse &
|
||||
CADDYPID=$!
|
||||
sleep 10
|
||||
echo "* starting Caddy in the \"server\" namespace... done"
|
||||
|
||||
echo "* running curl in the \"client\" namespace..."
|
||||
set +e
|
||||
timeout --kill-after=30 --preserve-status 15 \
|
||||
ip netns exec $clientns \
|
||||
curl -O http://10.10.20.2/index.html
|
||||
CURLRES=$?
|
||||
set -e
|
||||
echo "* running curl in the \"client\" namespace... done: $CURLRES"
|
||||
|
||||
echo "* running wget in the \"client\" namespace..."
|
||||
set +e
|
||||
timeout --kill-after=30 --preserve-status 15 \
|
||||
ip netns exec $clientns \
|
||||
wget http://10.10.20.2/index.html
|
||||
WGETRES=$?
|
||||
set -e
|
||||
echo "* running wget in the \"client\" namespace... done"
|
||||
|
||||
ping_ip=$(echo $serverip|cut -f1 -d'/')
|
||||
echo "* running ping $ping_ip in the \"client\" namespace..."
|
||||
set +e
|
||||
ip netns exec $clientns \
|
||||
ping -c 10 $ping_ip
|
||||
PINGRES=$?
|
||||
set -e
|
||||
echo "* running ping in the \"client\" namespace... done"
|
||||
|
||||
# pings should have been dropped, so ping reports error
|
||||
if [ $PINGRES != 1 ]; then
|
||||
echo "ERROR ping should have failed"
|
||||
RES=1
|
||||
fi
|
||||
|
||||
# first rulefile:
|
||||
# expecting 2 of 101 because of the curl and wget requests
|
||||
# expecting 1 of 102 because only wget is accepted
|
||||
SID101=$(jq -c 'select(.alert.signature_id==101)' ./eve.json | wc -l)
|
||||
SID102=$(jq -c 'select(.alert.signature_id==102)' ./eve.json | wc -l)
|
||||
echo "SID101 $SID101 SID102 $SID102"
|
||||
if [ $SID101 -ne 2 ]; then
|
||||
echo "ERROR wrong alert count for sid 101: $SID101"
|
||||
RES=1
|
||||
fi
|
||||
if [ $SID102 -ne 1 ]; then
|
||||
echo "ERROR wrong alert count for sid 102: $SID102"
|
||||
RES=1
|
||||
fi
|
||||
|
||||
echo "* installing new firewall rules..."
|
||||
cp qa/live/netns/firewall2-l3.rules firewall.rules
|
||||
cat firewall.rules
|
||||
echo "* installing new firewall rules... done"
|
||||
|
||||
echo "* issuing rule reload..."
|
||||
ip netns exec $dutns \
|
||||
${SURICATASC} -c "reload-rules" /var/run/suricata/suricata-command.socket
|
||||
# give suricata time to reload
|
||||
sleep 10
|
||||
echo "* issuing rule reload... done"
|
||||
|
||||
echo "* running wget in the \"client\" namespace..."
|
||||
set +e
|
||||
timeout --kill-after=30 --preserve-status 15 \
|
||||
ip netns exec $clientns \
|
||||
wget http://10.10.20.2/index.html
|
||||
WGETRES=$?
|
||||
set -e
|
||||
echo "* running wget in the \"client\" namespace... done"
|
||||
|
||||
sleep 10
|
||||
|
||||
echo "* shutting down tshark..."
|
||||
kill -INT $TSHARKSERVERPID
|
||||
wait $TSHARKSERVERPID
|
||||
echo "* shutting down tshark... done"
|
||||
|
||||
# second rulefile (after reload)
|
||||
SID201=$(jq -c 'select(.alert.signature_id==201)' ./eve.json | wc -l)
|
||||
SID202=$(jq -c 'select(.alert.signature_id==202)' ./eve.json | wc -l)
|
||||
echo "SID201 $SID201 SID202 $SID202"
|
||||
|
||||
ACCEPTED=$(jq -c 'select(.event_type == "stats")' ./eve.json | tail -n1 | jq '.stats.ips.accepted')
|
||||
BLOCKED=$(jq -c 'select(.event_type == "stats")' ./eve.json | tail -n1 | jq '.stats.ips.blocked')
|
||||
echo "ACCEPTED $ACCEPTED BLOCKED $BLOCKED"
|
||||
|
||||
if [ $ACCEPTED -eq 0 ]; then
|
||||
echo "ERROR should have seen non-0 accepted"
|
||||
RES=1
|
||||
fi
|
||||
if [ $BLOCKED -lt 10 ]; then
|
||||
echo "ERROR should have seen 10+ blocked"
|
||||
RES=1
|
||||
fi
|
||||
if [ $SID201 -ne 1 ]; then
|
||||
echo "ERROR wrong alert count for sid 201: $SID201"
|
||||
RES=1
|
||||
fi
|
||||
if [ $SID202 -ne 1 ]; then
|
||||
echo "ERROR wrong alert count for sid 202: $SID202"
|
||||
RES=1
|
||||
fi
|
||||
|
||||
# validate that we didn't receive pings
|
||||
SERVER_RECV_PING=$(jq -c '.[]' ./tshark-server.json|jq 'select(._source.layers.icmp."icmp.type"=="8")'|wc -l)
|
||||
echo "* server pings received check (should be 0): $SERVER_RECV_PING"
|
||||
if [ $SERVER_RECV_PING -ne 0 ]; then
|
||||
jq '.[]' ./tshark-server.json | jq 'select(._source.layers.icmp)'
|
||||
RES=1
|
||||
fi
|
||||
echo "* server pings received check... done"
|
||||
|
||||
echo "* shutting down..."
|
||||
set +e
|
||||
kill -INT $CADDYPID
|
||||
wait $CADDYPID
|
||||
CADDYRES=$?
|
||||
set -e
|
||||
ip netns exec $dutns \
|
||||
${SURICATASC} -c "shutdown" /var/run/suricata/suricata-command.socket
|
||||
wait $SURIPID
|
||||
echo "* shutting down... done"
|
||||
|
||||
# Caddy sometimes exits uncleanly. Warn about it but otherwise
|
||||
# it can be ignored.
|
||||
if [ $CADDYRES -ne 0 ]; then
|
||||
echo "WARNING Caddy exited with error $CADDYRES"
|
||||
fi
|
||||
|
||||
echo "* dumping some stats..."
|
||||
cat ./eve.json | jq -c 'select(.http)'|tail -n1|jq
|
||||
cat ./eve.json | jq -c 'select(.stats)|.stats.ips'|tail -n1|jq
|
||||
echo "* dumping some stats... done"
|
||||
|
||||
echo "* done: $RES"
|
||||
exit $RES
|
||||
Loading…
Reference in New Issue