mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-27 11:13:24 +02:00
Merge branch 'master' into next
This commit is contained in:
@@ -54,6 +54,86 @@ bin2ip() {
|
||||
echo "${address[*]}"
|
||||
}
|
||||
|
||||
network_get_intfs() {
|
||||
local zone="${1}"
|
||||
|
||||
case "${zone^^}" in
|
||||
RED)
|
||||
# For PPPoE, the RED interface is called ppp0 (unless we use QMI)
|
||||
if [ "${RED_TYPE}" = "PPPOE" ] && [ "${RED_DRIVER}" != "qmi_wwan" ]; then
|
||||
echo "ppp0"
|
||||
return 0
|
||||
|
||||
# Otherwise we return RED_DEV
|
||||
elif [ -n "${RED_DEV}" ]; then
|
||||
echo "${RED_DEV}"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
GREEN)
|
||||
if [ -n "${GREEN_DEV}" ]; then
|
||||
echo "${GREEN_DEV}"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
ORANGE)
|
||||
if [ -n "${ORANGE_DEV}" ]; then
|
||||
echo "${ORANGE_DEV}"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
BLUE)
|
||||
if [ -n "${BLUE_DEV}" ]; then
|
||||
echo "${BLUE_DEV}"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
IPSEC)
|
||||
local VARS=(
|
||||
id status x1 x2 type x3 x4 x5 x6 x7 x8 x9 x10
|
||||
x11 x12 x13 x14 x15 x16 x17 x18 x19 x20
|
||||
x21 x22 x23 x24 x25 x26 x27 x28 x29 x30
|
||||
x31 x32 x33 x34 interface_mode rest
|
||||
)
|
||||
|
||||
while IFS="," read -r "${VARS[@]}"; do
|
||||
# Check if the connection is enabled
|
||||
[ "${status}" = "on" ] || continue
|
||||
|
||||
# Check if this a net-to-net connection
|
||||
[ "${type}" = "net" ] || continue
|
||||
|
||||
# Determine the interface name
|
||||
case "${interface_mode}" in
|
||||
gre|vti)
|
||||
echo "${interface_mode}${id}"
|
||||
;;
|
||||
esac
|
||||
done < /var/ipfire/vpn/config
|
||||
|
||||
return 0
|
||||
;;
|
||||
|
||||
WIREGUARD|WG)
|
||||
echo "wg+"
|
||||
return 0
|
||||
;;
|
||||
|
||||
OPENVPN|OVPN)
|
||||
# OpenVPN is using all tun devices
|
||||
echo "tun+"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Not found
|
||||
return 1
|
||||
}
|
||||
|
||||
network_get_address() {
|
||||
local network="${1}"
|
||||
|
||||
|
||||
@@ -39,11 +39,6 @@ fi
|
||||
|
||||
NAT_MASK="0x0f000000"
|
||||
|
||||
IPS_REPEAT_MARK="0x80000000"
|
||||
IPS_REPEAT_MASK="0x80000000"
|
||||
IPS_BYPASS_MARK="0x40000000"
|
||||
IPS_BYPASS_MASK="0x40000000"
|
||||
|
||||
IPSET_DB_DIR="/var/lib/location/ipset"
|
||||
|
||||
SYNPROXY_OPTIONS=(
|
||||
@@ -84,16 +79,6 @@ iptables_init() {
|
||||
modprobe nf_log_ipv4
|
||||
sysctl -q -w net.netfilter.nf_log.2=nf_log_ipv4
|
||||
|
||||
# IPS Bypass Chain which stores the BYPASS bit in connection tracking
|
||||
iptables -N IPSBYPASS
|
||||
iptables -A IPSBYPASS -j CONNMARK --save-mark --mask "$(( ~IPS_REPEAT_MASK & 0xffffffff ))"
|
||||
|
||||
# Jump into bypass chain when the BYPASS bit is set
|
||||
for chain in INPUT FORWARD OUTPUT; do
|
||||
iptables -A "${chain}" -m mark \
|
||||
--mark "$(( IPS_REPEAT_MARK | IPS_BYPASS_MARK ))/$(( IPS_REPEAT_MASK | IPS_BYPASS_MASK ))" -j IPSBYPASS
|
||||
done
|
||||
|
||||
# Empty LOG_DROP and LOG_REJECT chains
|
||||
iptables -N LOG_DROP
|
||||
iptables -A LOG_DROP -m limit --limit 10/second -j LOG
|
||||
@@ -175,7 +160,7 @@ iptables_init() {
|
||||
iptables -A CTOUTPUT -p icmp -m conntrack --ctstate RELATED -j ACCEPT
|
||||
|
||||
# Restore any connection marks
|
||||
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
|
||||
iptables -t mangle -A PREROUTING -m mark --mark 0 -j CONNMARK --restore-mark
|
||||
|
||||
# Fix for braindead ISPs
|
||||
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
|
||||
@@ -236,15 +221,6 @@ iptables_init() {
|
||||
iptables -A FORWARD -i tun+ -j OVPNBLOCK
|
||||
iptables -A FORWARD -o tun+ -j OVPNBLOCK
|
||||
|
||||
# IPS (Suricata) chains
|
||||
iptables -N IPS_INPUT
|
||||
iptables -N IPS_FORWARD
|
||||
iptables -N IPS_OUTPUT
|
||||
|
||||
for chain in INPUT FORWARD OUTPUT; do
|
||||
iptables -A "${chain}" -m mark --mark "0x0/$(( IPS_REPEAT_MASK | IPS_BYPASS_MASK ))" -j "IPS_${chain}"
|
||||
done
|
||||
|
||||
# OpenVPN transfer network translation
|
||||
iptables -t nat -N OVPNNAT
|
||||
iptables -t nat -A POSTROUTING -j OVPNNAT
|
||||
@@ -399,6 +375,22 @@ iptables_init() {
|
||||
-m mark --mark "0x04000000/${NAT_MASK}" -j SNAT --to-source "${ORANGE_ADDRESS}"
|
||||
fi
|
||||
|
||||
# IPS (Suricata) chains
|
||||
iptables -t mangle -N IPS
|
||||
iptables -t mangle -N IPS_CLEAR
|
||||
iptables -t mangle -N IPS_SCAN_IN
|
||||
iptables -t mangle -N IPS_SCAN_OUT
|
||||
|
||||
iptables -t mangle -A INPUT -j IPS_SCAN_IN
|
||||
iptables -t mangle -A FORWARD -j IPS_SCAN_IN
|
||||
iptables -t mangle -A FORWARD -j IPS_SCAN_OUT
|
||||
iptables -t mangle -A OUTPUT -j IPS_SCAN_OUT
|
||||
|
||||
for chain in INPUT FORWARD OUTPUT; do
|
||||
iptables -t mangle -A "${chain}" -j IPS
|
||||
iptables -t mangle -A "${chain}" -j IPS_CLEAR
|
||||
done
|
||||
|
||||
# RED chain, used for the red interface
|
||||
iptables -N REDINPUT
|
||||
iptables -A INPUT -j REDINPUT
|
||||
|
||||
@@ -21,140 +21,150 @@
|
||||
|
||||
. /etc/sysconfig/rc
|
||||
. ${rc_functions}
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin; export PATH
|
||||
. /etc/init.d/networking/functions.network
|
||||
|
||||
eval $(/usr/local/bin/readhash /var/ipfire/suricata/settings)
|
||||
eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
|
||||
|
||||
# Name of the firewall chains.
|
||||
IPS_INPUT_CHAIN="IPS_INPUT"
|
||||
IPS_FORWARD_CHAIN="IPS_FORWARD"
|
||||
IPS_OUTPUT_CHAIN="IPS_OUTPUT"
|
||||
IPS_REPEAT_MARK="0x80000000"
|
||||
IPS_REPEAT_MASK="0x80000000"
|
||||
|
||||
# The IPS requested that this connection is being bypassed
|
||||
IPS_BYPASS_REQUESTED_MARK="0x40000000"
|
||||
IPS_BYPASS_REQUESTED_MASK="0x40000000"
|
||||
|
||||
# Marks a connection to be bypassed
|
||||
IPS_BYPASS_MARK="0x20000000"
|
||||
IPS_BYPASS_MASK="0x20000000"
|
||||
|
||||
# Set if we request to scan this packet
|
||||
IPS_SCAN_MARK="0x10000000"
|
||||
IPS_SCAN_MASK="0x10000000"
|
||||
|
||||
# Set if a packet has been whitelisted
|
||||
IPS_WHITELISTED_MARK="0x08000000"
|
||||
IPS_WHITELISTED_MASK="0x08000000"
|
||||
|
||||
# Supported network zones
|
||||
NETWORK_ZONES=( "RED" "GREEN" "ORANGE" "BLUE" "IPSEC" "WG" "OVPN" )
|
||||
|
||||
# Optional options for the Netfilter queue.
|
||||
NFQ_OPTS="--queue-bypass "
|
||||
|
||||
# Array containing the 4 possible network zones.
|
||||
network_zones=( red green blue orange ovpn )
|
||||
|
||||
# Array to store the network zones weather the IPS is enabled for.
|
||||
enabled_ips_zones=()
|
||||
|
||||
# PID file of suricata.
|
||||
PID_FILE="/var/run/suricata.pid"
|
||||
|
||||
# Function to get the amount of CPU cores of the system.
|
||||
function get_cpu_count {
|
||||
CPUCOUNT=0
|
||||
|
||||
# Loop through "/proc/cpuinfo" and count the amount of CPU cores.
|
||||
while read line; do
|
||||
[ "$line" ] && [ -z "${line%processor*}" ] && ((CPUCOUNT++))
|
||||
done </proc/cpuinfo
|
||||
|
||||
# Limit to a maximum of 16 cores, because suricata does not support more than
|
||||
# 16 netfilter queues at the moment.
|
||||
if [ $CPUCOUNT -gt "16" ]; then
|
||||
echo "16"
|
||||
else
|
||||
echo $CPUCOUNT
|
||||
fi
|
||||
}
|
||||
NFQ_OPTS=(
|
||||
"--queue-bypass"
|
||||
)
|
||||
|
||||
# Function to flush the firewall chains.
|
||||
function flush_fw_chain {
|
||||
# Call iptables and flush the chains
|
||||
iptables -w -F "$IPS_INPUT_CHAIN"
|
||||
iptables -w -F "$IPS_FORWARD_CHAIN"
|
||||
iptables -w -F "$IPS_OUTPUT_CHAIN"
|
||||
flush_fw_chain() {
|
||||
iptables -w -t mangle -F IPS
|
||||
iptables -w -t mangle -F IPS_CLEAR
|
||||
iptables -w -t mangle -F IPS_SCAN_IN
|
||||
iptables -w -t mangle -F IPS_SCAN_OUT
|
||||
}
|
||||
|
||||
# Function to create the firewall rules to pass the traffic to suricata.
|
||||
function generate_fw_rules {
|
||||
cpu_count=$(get_cpu_count)
|
||||
|
||||
# Loop through the array of network zones.
|
||||
for zone in "${network_zones[@]}"; do
|
||||
# Convert zone into upper case.
|
||||
zone_upper=${zone^^}
|
||||
|
||||
# Generate variable name for checking if the IDS is
|
||||
# enabled on the zone.
|
||||
enable_ids_zone="ENABLE_IDS_$zone_upper"
|
||||
|
||||
# Check if the IDS is enabled for this network zone.
|
||||
if [ "${!enable_ids_zone}" == "on" ]; then
|
||||
# Check if the current processed zone is "red" and the configured type is PPPoE dialin.
|
||||
if [ "$zone" == "red" ] && [ "$RED_TYPE" == "PPPOE" ] && [ "$RED_DRIVER" != "qmi_wwan" ]; then
|
||||
# Set device name to ppp0.
|
||||
network_device="ppp0"
|
||||
elif [ "$zone" == "ovpn" ]; then
|
||||
# Get all virtual net devices because the RW server and each
|
||||
# N2N connection creates it's own tun device.
|
||||
for virt_dev in /sys/devices/virtual/net/*; do
|
||||
# Cut-off the directory.
|
||||
dev="${virt_dev##*/}"
|
||||
|
||||
# Only process tun devices.
|
||||
if [[ $dev =~ "tun" ]]; then
|
||||
# Add the network device to the array of enabled zones.
|
||||
enabled_ips_zones+=( "$dev" )
|
||||
fi
|
||||
done
|
||||
|
||||
# Process next zone.
|
||||
continue
|
||||
else
|
||||
# Generate variable name which contains the device name.
|
||||
zone_name="$zone_upper"
|
||||
zone_name+="_DEV"
|
||||
|
||||
# Grab device name.
|
||||
network_device=${!zone_name}
|
||||
fi
|
||||
|
||||
# Add the network device to the array of enabled zones.
|
||||
enabled_ips_zones+=( "$network_device" )
|
||||
fi
|
||||
done
|
||||
|
||||
generate_fw_rules() {
|
||||
# Assign NFQ_OPTS
|
||||
NFQ_OPTIONS=$NFQ_OPTS
|
||||
local NFQ_OPTIONS=( "${NFQ_OPTS[@]}" )
|
||||
|
||||
local cpu_count="$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
# Check if there are multiple cpu cores available.
|
||||
if [ "$cpu_count" -gt "1" ]; then
|
||||
# Balance beetween all queues.
|
||||
NFQ_OPTIONS+="--queue-balance 0:$(($cpu_count-1))"
|
||||
NFQ_OPTIONS+=" --queue-cpu-fanout"
|
||||
# Balance beetween all queues
|
||||
NFQ_OPTIONS+=(
|
||||
"--queue-balance" "0:$(($cpu_count-1))"
|
||||
"--queue-cpu-fanout"
|
||||
)
|
||||
else
|
||||
# Send all packets to queue 0.
|
||||
NFQ_OPTIONS+="--queue-num 0"
|
||||
# Send all packets to queue 0
|
||||
NFQ_OPTIONS+=(
|
||||
"--queue-num" "0"
|
||||
)
|
||||
fi
|
||||
|
||||
# Flush the firewall chains.
|
||||
flush_fw_chain
|
||||
|
||||
# Check if the array of enabled_ips_zones contains any elements.
|
||||
if [[ ${enabled_ips_zones[@]} ]]; then
|
||||
# Loop through the array and create firewall rules.
|
||||
for enabled_ips_zone in "${enabled_ips_zones[@]}"; do
|
||||
# Create rules queue input and output related traffic and pass it to the IPS.
|
||||
iptables -w -A "$IPS_INPUT_CHAIN" -i "$enabled_ips_zone" -j NFQUEUE $NFQ_OPTIONS
|
||||
iptables -w -A "$IPS_OUTPUT_CHAIN" -o "$enabled_ips_zone" -j NFQUEUE $NFQ_OPTIONS
|
||||
# Don't process packets where the IPS has requested to bypass the stream
|
||||
iptables -w -t mangle -A IPS \
|
||||
-m comment --comment "BYPASSED" \
|
||||
-m mark --mark "$(( IPS_BYPASS_MARK ))/$(( IPS_BYPASS_MASK ))" -j RETURN
|
||||
|
||||
# Create rules which are required to handle forwarded traffic.
|
||||
for enabled_ips_zone_forward in "${enabled_ips_zones[@]}"; do
|
||||
iptables -w -A "$IPS_FORWARD_CHAIN" -i "$enabled_ips_zone" -o "$enabled_ips_zone_forward" -j NFQUEUE $NFQ_OPTIONS
|
||||
# If suricata decided to bypass a stream, we will store the mark in the connection tracking table
|
||||
iptables -w -t mangle -A IPS \
|
||||
-m mark --mark "$(( IPS_BYPASS_REQUESTED_MARK ))/$(( IPS_BYPASS_REQUESTED_MASK ))" \
|
||||
-j CONNMARK --set-mark "$(( IPS_BYPASS_MARK ))/$(( IPS_BYPASS_MASK ))"
|
||||
|
||||
# Don't process packets that have already been seen by the IPS
|
||||
for chain in IPS IPS_SCAN_IN IPS_SCAN_OUT; do
|
||||
iptables -w -t mangle -A "${chain}" \
|
||||
-m mark --mark "$(( IPS_REPEAT_MARK ))/$(( IPS_REPEAT_MASK ))" -j RETURN
|
||||
done
|
||||
|
||||
local zone
|
||||
local status
|
||||
local intf
|
||||
|
||||
# Mark packets for all zones that we want to scan
|
||||
for zone in "${NETWORK_ZONES[@]}"; do
|
||||
status="ENABLE_IDS_${zone}"
|
||||
|
||||
if [ "${!status}" = "on" ]; then
|
||||
# Handle IPsec packets
|
||||
case "${zone}" in
|
||||
IPSEC)
|
||||
iptables -w -t mangle -A IPS_SCAN_IN \
|
||||
-m policy --pol ipsec --dir in -j MARK --set-mark "$(( IPS_SCAN_MARK ))/$(( IPS_SCAN_MASK ))"
|
||||
iptables -w -t mangle -A IPS_SCAN_OUT \
|
||||
-m policy --pol ipsec --dir out -j MARK --set-mark "$(( IPS_SCAN_MARK ))/$(( IPS_SCAN_MASK ))"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Add interfaces
|
||||
for intf in $(network_get_intfs "${zone}"); do
|
||||
iptables -w -t mangle -A IPS_SCAN_IN \
|
||||
-i "${intf}" -j MARK --set-mark "$(( IPS_SCAN_MARK ))/$(( IPS_SCAN_MASK ))"
|
||||
iptables -w -t mangle -A IPS_SCAN_OUT \
|
||||
-o "${intf}" -j MARK --set-mark "$(( IPS_SCAN_MARK ))/$(( IPS_SCAN_MASK ))"
|
||||
done
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# Don't keep processing packets we don't want to scan
|
||||
iptables -w -t mangle -A IPS -m mark ! --mark "$(( IPS_SCAN_MARK ))/$(( IPS_SCAN_MASK ))" -j RETURN
|
||||
|
||||
# Never send any whitelisted packets to the IPS
|
||||
if [ -r "/var/ipfire/suricata/ignored" ]; then
|
||||
local id network remark enabled rest
|
||||
|
||||
while IFS=',' read -r id network remark enabled rest; do
|
||||
# Skip disabled entries
|
||||
[ "${enabled}" = "enabled" ] || continue
|
||||
|
||||
iptables -w -t mangle -A IPS -s "${network}" -j MARK --set-mark "$(( IPS_WHITELISTED_MARK ))/$(( IPS_WHITELISTED_MASK ))"
|
||||
iptables -w -t mangle -A IPS -d "${network}" -j MARK --set-mark "$(( IPS_WHITELISTED_MARK ))/$(( IPS_WHITELISTED_MASK ))"
|
||||
done < "/var/ipfire/suricata/ignored"
|
||||
fi
|
||||
|
||||
# Count and skip the whitelisted packets
|
||||
iptables -w -t mangle -A IPS \
|
||||
-m comment --comment "WHITELISTED" \
|
||||
-m mark --mark "$(( IPS_WHITELISTED_MARK ))/$(( IPS_WHITELISTED_MASK ))" -j RETURN
|
||||
|
||||
# Send packets to suricata
|
||||
iptables -w -t mangle -A IPS -m comment --comment "SCANNED" -j NFQUEUE "${NFQ_OPTIONS[@]}"
|
||||
|
||||
# Clear all bits again after packets have been sent to the IPS
|
||||
# This is required so that encapsulated packets can't inherit any set bits here and won't be scanned.
|
||||
iptables -w -t mangle -A IPS_CLEAR \
|
||||
-j MARK --set-mark "0/$(( IPS_BYPASS_MASK | IPS_BYPASS_REQUESTED_MASK | IPS_REPEAT_MASK | IPS_SCAN_MASK ))"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
# Get amount of CPU cores.
|
||||
cpu_count=$(get_cpu_count)
|
||||
# Get amount of CPU cores
|
||||
cpu_count="$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
# Numer of NFQUES.
|
||||
NFQUEUES="-q 0"
|
||||
@@ -167,11 +177,7 @@ case "$1" in
|
||||
if [ "$ENABLE_IDS" == "on" ]; then
|
||||
# Start the IDS.
|
||||
boot_mesg "Starting Intrusion Detection System..."
|
||||
/usr/bin/suricata -c /etc/suricata/suricata.yaml -D $NFQUEUES >/dev/null 2>/dev/null
|
||||
evaluate_retval
|
||||
|
||||
# Allow reading the pidfile.
|
||||
chmod 644 $PID_FILE
|
||||
loadproc -b /usr/bin/suricata-watcher -c /etc/suricata/suricata.yaml $NFQUEUES
|
||||
|
||||
# Flush the firewall chain
|
||||
flush_fw_chain
|
||||
@@ -183,32 +189,24 @@ case "$1" in
|
||||
|
||||
stop)
|
||||
boot_mesg "Stopping Intrusion Detection System..."
|
||||
killproc -p $PID_FILE /var/run
|
||||
killproc -p /var/run/suricata.pid /usr/bin/suricata
|
||||
|
||||
# Flush firewall chain.
|
||||
flush_fw_chain
|
||||
|
||||
# Sometimes suricata not correct shutdown. So killall.
|
||||
killall -KILL /usr/bin/suricata 2>/dev/null
|
||||
|
||||
# Remove suricata control socket.
|
||||
rm /var/run/suricata/* >/dev/null 2>/dev/null
|
||||
|
||||
# Trash remain pid file if still exists.
|
||||
rm -f $PID_FILE >/dev/null 2>/dev/null
|
||||
|
||||
# Don't report returncode of rm if suricata was not started
|
||||
exit 0
|
||||
;;
|
||||
|
||||
status)
|
||||
statusproc /usr/bin/suricata
|
||||
PIDFILE="/var/run/suricata.pid" statusproc /usr/bin/suricata
|
||||
;;
|
||||
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
|
||||
reload)
|
||||
# Send SIGUSR2 to the suricata process to perform a reload
|
||||
# of the ruleset.
|
||||
@@ -226,5 +224,3 @@ case "$1" in
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
chmod 644 /var/log/suricata/* 2>/dev/null
|
||||
|
||||
Reference in New Issue
Block a user