#!/bin/sh ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2007-2022 IPFire Team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ############################################################################### . /etc/sysconfig/rc . ${rc_functions} . /etc/init.d/networking/functions.network eval $(/usr/local/bin/readhash /var/ipfire/suricata/settings) 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" ) # Function to flush the firewall chains. 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. generate_fw_rules() { # Assign 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))" "--queue-cpu-fanout" ) else # Send all packets to queue 0 NFQ_OPTIONS+=( "--queue-num" "0" ) fi # Flush the firewall chains. flush_fw_chain # 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 # 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 RED) # If IPsec is not enabled, skip everything that is IPsec traffic if [ "${ENABLE_IDS_IPSEC}" != "on" ]; then for intf in $(network_get_intfs "${zone}"); do iptables -w -t mangle -A IPS_SCAN_IN \ -i "${intf}" -m policy --pol ipsec --dir in -j RETURN iptables -w -t mangle -A IPS_SCAN_OUT \ -o "${intf}" -m policy --pol ipsec --dir out -j RETURN done fi ;; 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 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="$(getconf _NPROCESSORS_ONLN)" # Numer of NFQUES. NFQUEUES="-q 0" if [ $cpu_count -gt "1" ]; then NFQUEUES+=":$(($cpu_count-1))" fi # Check if the IDS should be started. if [ "$ENABLE_IDS" == "on" ]; then # Start the IDS. boot_mesg "Starting Intrusion Detection System..." loadproc -b /usr/bin/suricata-watcher -c /etc/suricata/suricata.yaml $NFQUEUES # Flush the firewall chain flush_fw_chain # Generate firewall rules generate_fw_rules fi ;; stop) boot_mesg "Stopping Intrusion Detection System..." killproc -p /var/run/suricata.pid /usr/bin/suricata # Flush firewall chain. flush_fw_chain # Don't report returncode of rm if suricata was not started exit 0 ;; status) 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. kill -USR2 $(pidof suricata) # Flush the firewall chain. flush_fw_chain # Generate firewall rules. generate_fw_rules ;; *) echo "Usage: $0 {start|stop|restart|reload|status}" exit 1 ;; esac