#!/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