mirror of https://github.com/OISF/suricata
qa: add script to test firewall bridge mode
parent
2e2132a16f
commit
1c66eec656
@ -0,0 +1,268 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to test live Firewall capabilities for AF_PACKET.
|
||||||
|
#
|
||||||
|
# Uses 3 network namespaces:
|
||||||
|
# - client
|
||||||
|
# - server
|
||||||
|
# - dut
|
||||||
|
#
|
||||||
|
# Dut is where Suricata will run:
|
||||||
|
#
|
||||||
|
# [ client ]$clientif - $dutclientif[ dut ]$dutserverif - $serverif[ server ]
|
||||||
|
#
|
||||||
|
# By copying packets between the dut interfaces, Suricata becomes the bridge.
|
||||||
|
|
||||||
|
# Call with following arguments:
|
||||||
|
# 1st: "2" or "3" to indicate the tpacket version.
|
||||||
|
# 2nd: runmode string (single/autofp/workers)
|
||||||
|
# 3rd: suricata yaml to use
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [ $# -ne "3" ]; then
|
||||||
|
echo "ERROR call with 3 args: tpacket version (2/3), runmode (single/autofp/workers) and yaml"
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
TPACKET=$1
|
||||||
|
RUNMODE=$2
|
||||||
|
YAML=$3
|
||||||
|
|
||||||
|
# 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.10/24"
|
||||||
|
serverip='10.10.10.20/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 ethtool -K $iface rx off tx off
|
||||||
|
ip netns exec $clientns sysctl -w net.ipv6.conf.$iface.disable_ipv6=1
|
||||||
|
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 ethtool -K $iface rx off tx off
|
||||||
|
ip netns exec $serverns sysctl -w net.ipv6.conf.$iface.disable_ipv6=1
|
||||||
|
ip netns exec $serverns ip link set $iface up
|
||||||
|
echo "* setup server interface... done"
|
||||||
|
|
||||||
|
echo "* setup dut interfaces..."
|
||||||
|
ip netns exec $dutns ethtool -K ptp-$dutclientif rx off tx off
|
||||||
|
ip netns exec $dutns ethtool -K ptp-$dutserverif rx off tx off
|
||||||
|
ip netns exec $dutns sysctl -w net.ipv6.conf.ptp-$dutclientif.disable_ipv6=1
|
||||||
|
ip netns exec $dutns sysctl -w net.ipv6.conf.ptp-$dutserverif.disable_ipv6=1
|
||||||
|
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"
|
||||||
|
|
||||||
|
# set first rule file
|
||||||
|
cp qa/live/netns/firewall1.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 ./ --af-packet -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=240 --preserve-status 120 \
|
||||||
|
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.10.20/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.10.20/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
|
||||||
|
|
||||||
|
# give stats time to get updated
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
echo "* shutting down tshark..."
|
||||||
|
kill -INT $TSHARKSERVERPID
|
||||||
|
wait $TSHARKSERVERPID
|
||||||
|
echo "* shutting down tshark... done"
|
||||||
|
|
||||||
|
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')
|
||||||
|
KERNEL_PACKETS=$(jq -c 'select(.event_type == "stats")' ./eve.json | tail -n1 | jq '.stats.capture.kernel_packets')
|
||||||
|
echo "ACCEPTED $ACCEPTED BLOCKED $BLOCKED KERNEL_PACKETS $KERNEL_PACKETS"
|
||||||
|
|
||||||
|
if [ $KERNEL_PACKETS -eq 0 ]; then
|
||||||
|
echo "ERROR no packets captured"
|
||||||
|
RES=1
|
||||||
|
fi
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
cat ./eve.json | jq -c 'select(.stats)|.stats.capture'|tail -n1|jq
|
||||||
|
echo "* dumping some stats... done"
|
||||||
|
|
||||||
|
|
||||||
|
echo "* done: $RES"
|
||||||
|
exit $RES
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
accept:packet arp:all any any -> any any (alert; sid:200;)
|
||||||
|
|
||||||
|
# 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,131 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Suricata configuration file. In addition to the comments describing all
|
||||||
|
# options in this file, full documentation can be found at:
|
||||||
|
# https://docs.suricata.io/en/latest/configuration/suricata-yaml.html
|
||||||
|
|
||||||
|
# This configuration file was generated by Suricata 9.0.0-dev.
|
||||||
|
suricata-version: "9.0"
|
||||||
|
|
||||||
|
##
|
||||||
|
## Step 1: Inform Suricata about your network
|
||||||
|
##
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# more specific is better for alert accuracy and performance
|
||||||
|
address-groups:
|
||||||
|
HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"
|
||||||
|
#HOME_NET: "[192.168.0.0/16]"
|
||||||
|
#HOME_NET: "[10.0.0.0/8]"
|
||||||
|
#HOME_NET: "[172.16.0.0/12]"
|
||||||
|
#HOME_NET: "any"
|
||||||
|
|
||||||
|
EXTERNAL_NET: "!$HOME_NET"
|
||||||
|
#EXTERNAL_NET: "any"
|
||||||
|
|
||||||
|
HTTP_SERVERS: "$HOME_NET"
|
||||||
|
SMTP_SERVERS: "$HOME_NET"
|
||||||
|
SQL_SERVERS: "$HOME_NET"
|
||||||
|
DNS_SERVERS: "$HOME_NET"
|
||||||
|
TELNET_SERVERS: "$HOME_NET"
|
||||||
|
AIM_SERVERS: "$EXTERNAL_NET"
|
||||||
|
DC_SERVERS: "$HOME_NET"
|
||||||
|
DNP3_SERVER: "$HOME_NET"
|
||||||
|
DNP3_CLIENT: "$HOME_NET"
|
||||||
|
MODBUS_CLIENT: "$HOME_NET"
|
||||||
|
MODBUS_SERVER: "$HOME_NET"
|
||||||
|
ENIP_CLIENT: "$HOME_NET"
|
||||||
|
ENIP_SERVER: "$HOME_NET"
|
||||||
|
|
||||||
|
port-groups:
|
||||||
|
HTTP_PORTS: "80"
|
||||||
|
SHELLCODE_PORTS: "!80"
|
||||||
|
ORACLE_PORTS: 1521
|
||||||
|
SSH_PORTS: 22
|
||||||
|
DNP3_PORTS: 20000
|
||||||
|
MODBUS_PORTS: 502
|
||||||
|
FILE_DATA_PORTS: "[$HTTP_PORTS,110,143]"
|
||||||
|
FTP_PORTS: 21
|
||||||
|
GENEVE_PORTS: 6081
|
||||||
|
VXLAN_PORTS: 4789
|
||||||
|
TEREDO_PORTS: 3544
|
||||||
|
SIP_PORTS: "[5060, 5061]"
|
||||||
|
|
||||||
|
##
|
||||||
|
## Step 2: Select outputs to enable
|
||||||
|
##
|
||||||
|
|
||||||
|
# Global stats configuration
|
||||||
|
stats:
|
||||||
|
enabled: yes
|
||||||
|
# The interval field (in seconds) controls the interval at
|
||||||
|
# which stats are updated in the log.
|
||||||
|
interval: 8
|
||||||
|
# Add decode events to stats.
|
||||||
|
decoder-events: true
|
||||||
|
# Decoder event prefix in stats. Has been 'decoder' before, but that leads
|
||||||
|
# to missing events in the eve.stats records. See issue #2225.
|
||||||
|
#decoder-events-prefix: "decoder.event"
|
||||||
|
# Add stream events as stats.
|
||||||
|
stream-events: true
|
||||||
|
exception-policy:
|
||||||
|
per-app-proto-errors: true # default: false. True will log errors for
|
||||||
|
# each app-proto. Warning: VERY verbose
|
||||||
|
outputs:
|
||||||
|
- eve-log:
|
||||||
|
enabled: yes
|
||||||
|
filetype: regular #regular|syslog|unix_dgram|unix_stream|redis
|
||||||
|
filename: eve.json
|
||||||
|
ethernet: true # log ethernet header in events when available
|
||||||
|
types:
|
||||||
|
- alert
|
||||||
|
- http:
|
||||||
|
extended: yes # enable this for extended logging information
|
||||||
|
- dns
|
||||||
|
- tls:
|
||||||
|
extended: yes # enable this for extended logging information
|
||||||
|
- files:
|
||||||
|
force-magic: no # force logging magic on all logged files
|
||||||
|
# force logging of checksums, available hash functions are md5,
|
||||||
|
# sha1 and sha256
|
||||||
|
#force-hash: [md5]
|
||||||
|
- drop:
|
||||||
|
alerts: yes # log alerts that caused drops
|
||||||
|
flows: all # start or all: 'start' logs only a single drop
|
||||||
|
verdict: yes
|
||||||
|
- stats:
|
||||||
|
totals: yes # stats for all threads merged together
|
||||||
|
threads: no # per thread stats
|
||||||
|
deltas: no # include delta values
|
||||||
|
# Don't log stats counters that are zero. Default: true
|
||||||
|
#null-values: false # False will NOT log stats counters: 0
|
||||||
|
- flow
|
||||||
|
|
||||||
|
unix-command:
|
||||||
|
enabled: auto
|
||||||
|
|
||||||
|
af-packet:
|
||||||
|
- interface: ptp-dut_client
|
||||||
|
cluster-id: 1
|
||||||
|
copy-iface: ptp-dut_server
|
||||||
|
- interface: ptp-dut_server
|
||||||
|
cluster-id: 2
|
||||||
|
copy-iface: ptp-dut_client
|
||||||
|
- interface: default
|
||||||
|
threads: 2
|
||||||
|
cluster-type: cluster_flow
|
||||||
|
copy-mode: ips
|
||||||
|
|
||||||
|
firewall:
|
||||||
|
# toggle to enable firewall mode
|
||||||
|
enabled: yes
|
||||||
|
|
||||||
|
# Firewall rule file are in their own path and are not managed
|
||||||
|
# by Suricata-Update.
|
||||||
|
rule-path: /etc/suricata/firewall/
|
||||||
|
|
||||||
|
# List of files with firewall rules. Order matters, files are loaded
|
||||||
|
# in order and rules are applied in that order (per state, see docs)
|
||||||
|
rule-files:
|
||||||
|
- firewall.rules
|
||||||
Loading…
Reference in New Issue