From patchwork Tue Sep 10 14:37:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Tremer X-Patchwork-Id: 8085 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) client-signature RSA-PSS (4096 bits)) (Client CN "mail01.haj.ipfire.org", Issuer "R10" (verified OK)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4X35sV1Th2z3wyW for ; Tue, 10 Sep 2024 14:38:06 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) client-signature ECDSA (secp384r1)) (Client CN "mail02.haj.ipfire.org", Issuer "E6" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4X35sN54SJz2BQ; Tue, 10 Sep 2024 14:38:00 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [127.0.0.1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4X35sN4Zv4z34Gr; Tue, 10 Sep 2024 14:38:00 +0000 (UTC) Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) client-signature RSA-PSS (4096 bits)) (Client CN "mail01.haj.ipfire.org", Issuer "R10" (verified OK)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4X35sH5cC8z340Q for ; Tue, 10 Sep 2024 14:37:55 +0000 (UTC) Received: from michael.haj.ipfire.org (michael.haj.ipfire.org [172.28.1.242]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature ECDSA (secp384r1) client-digest SHA384) (Client CN "michael.haj.ipfire.org", Issuer "E6" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4X35sH1dgnzsY; Tue, 10 Sep 2024 14:37:55 +0000 (UTC) Received: by michael.haj.ipfire.org (Postfix, from userid 0) id 4X35sH0FMxzTgkm; Tue, 10 Sep 2024 14:37:55 +0000 (UTC) From: Michael Tremer To: development@lists.ipfire.org Subject: [PATCH 01/20] suricata: Move the IPS into the mangle table Date: Tue, 10 Sep 2024 14:37:14 +0000 Message-Id: <20240910143748.3469271-2-michael.tremer@ipfire.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240910143748.3469271-1-michael.tremer@ipfire.org> References: <20240910143748.3469271-1-michael.tremer@ipfire.org> MIME-Version: 1.0 Message-ID-Hash: U3KMXY4UCLVMEZ3WUNR7REY3QPSIQ7FT X-Message-ID-Hash: U3KMXY4UCLVMEZ3WUNR7REY3QPSIQ7FT X-MailFrom: root@michael.haj.ipfire.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Michael Tremer X-Mailman-Version: 3.3.8 Precedence: list List-Id: IPFire development talk Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This should make the IPS more efficient, we should have fewer rules and the IPS will now sit at the edge of the networking stack as it will see packets immediately when they come and and just before they leave. Signed-off-by: Michael Tremer --- src/initscripts/system/firewall | 23 +------ src/initscripts/system/suricata | 108 +++++++++++--------------------- 2 files changed, 39 insertions(+), 92 deletions(-) diff --git a/src/initscripts/system/firewall b/src/initscripts/system/firewall index 6727e4a20..39d9c0f23 100644 --- a/src/initscripts/system/firewall +++ b/src/initscripts/system/firewall @@ -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 @@ -237,12 +222,10 @@ iptables_init() { iptables -A FORWARD -o tun+ -j OVPNBLOCK # IPS (Suricata) chains - iptables -N IPS_INPUT - iptables -N IPS_FORWARD - iptables -N IPS_OUTPUT + iptables -t mangle -N IPS - for chain in INPUT FORWARD OUTPUT; do - iptables -A "${chain}" -m mark --mark "0x0/$(( IPS_REPEAT_MASK | IPS_BYPASS_MASK ))" -j "IPS_${chain}" + for chain in PREROUTING POSTROUTING; do + iptables -t mangle -A "${chain}" -j IPS done # OpenVPN transfer network translation diff --git a/src/initscripts/system/suricata b/src/initscripts/system/suricata index 79f9478c3..253ece117 100644 --- a/src/initscripts/system/suricata +++ b/src/initscripts/system/suricata @@ -27,13 +27,20 @@ PATH=/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin; export PATH eval $(/usr/local/bin/readhash /var/ipfire/suricata/settings) eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings) +IPS_REPEAT_MARK="0x80000000" +IPS_REPEAT_MASK="0x80000000" +IPS_BYPASS_MARK="0x40000000" +IPS_BYPASS_MASK="0x40000000" + # Name of the firewall chains. IPS_INPUT_CHAIN="IPS_INPUT" IPS_FORWARD_CHAIN="IPS_FORWARD" IPS_OUTPUT_CHAIN="IPS_OUTPUT" # Optional options for the Netfilter queue. -NFQ_OPTS="--queue-bypass " +NFQ_OPTS=( + "--queue-bypass" +) # Array containing the 4 possible network zones. network_zones=( red green blue orange ovpn ) @@ -64,91 +71,48 @@ function get_cpu_count { # 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" + iptables -w -t mangle -F IPS } # 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 - # Assign NFQ_OPTS - NFQ_OPTIONS=$NFQ_OPTS + local NFQ_OPTIONS=( "${NFQ_OPTS[@]}" ) + + local cpu_count="$(get_cpu_count)" # 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 - - # 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 - done - done - fi + # Don't process packets where the IPS has requested to bypass the stream + iptables -w -t mangle -A IPS -m mark --mark "$(( IPS_BYPASS_MARK ))/$(( IPS_BYPASS_MASK ))" -j RETURN + + # Don't process packets that have already been seen by the IPS + iptables -w -t mangle -A IPS -m mark --mark "$(( IPS_REPEAT_MARK ))/$(( IPS_REPEAT_MASK ))" -j RETURN + + # Send packets to suricata + iptables -w -t mangle -A IPS -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_MARK ))/$(( IPS_BYPASS_MASK ))" \ + -j CONNMARK --save-mark --mask "$(( IPS_BYPASS_MASK ))" + + return 0 } case "$1" in