From affa35def4d6520f0e93378dae6b186a989cac3e Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 11 Nov 2026 18:50:58 +0100 Subject: [PATCH] Limit of new TCP connections per source IP: configurable rate, drop only above the limit. - default_settings.conf: new default_limit_new_tcp_connections_per_seconds_rate=30. - main_ipv{4,6}.conf.sample: new limit_new_tcp_connections_per_seconds_rate (burst is twice the rate; invalid or empty values fall back to the default). - ipt-firewall-server, ip6t-firewall-server: the rule no longer ACCEPTs SYNs below the limit (which bypassed the service rules, destination IPs and the HTTP limits); like the HTTP limits it now only logs and drops SYNs above the limit (--hashlimit-above). Fallback without xt_hashlimit: chain syn-multi-limit with a global limit. - ip6t-firewall-server: fix --hashlimit-srcmask (128 instead of 32), drop only the given ports instead of all new TCP connections, hashlimit names syn_multi_v6*, add the can_use_hashlimit check. Co-Authored-By: Claude Opus 5.5 --- conf/default_settings.conf | 7 ++++ conf/main_ipv4.conf.sample | 7 ++++ conf/main_ipv6.conf.sample | 7 ++++ ip6t-firewall-server | 61 ++++++++++++++++++++++++---------- ipt-firewall-server | 68 +++++++++++++++++++++++--------------- 5 files changed, 106 insertions(+), 44 deletions(-) diff --git a/conf/default_settings.conf b/conf/default_settings.conf index c6a0f24..d8f86bd 100644 --- a/conf/default_settings.conf +++ b/conf/default_settings.conf @@ -6,6 +6,13 @@ default_per_IP_connection_limit=250 +# ip6t-firewall-server,ipt-firewall-server: +# New (syn) TCP connections per second per source IP on the ports in +# 'limit_new_tcp_connections_per_seconds_ports'. The burst is twice +# this value. +# +default_limit_new_tcp_connections_per_seconds_rate=30 + # ------------- # --- Default Ports for Services out diff --git a/conf/main_ipv4.conf.sample b/conf/main_ipv4.conf.sample index 9512669..a9ff7b7 100644 --- a/conf/main_ipv4.conf.sample +++ b/conf/main_ipv4.conf.sample @@ -820,6 +820,13 @@ limit_new_tcp_connections_per_seconds_per_source_IP=true # limit_new_tcp_connections_per_seconds_ports="" +# - limit_new_tcp_connections_per_seconds_rate +# - +# - New (syn) TCP connections per second per source IP; the burst is twice +# - this value. Defaults to '$default_limit_new_tcp_connections_per_seconds_rate'. +# - +limit_new_tcp_connections_per_seconds_rate=${default_limit_new_tcp_connections_per_seconds_rate} + # ------------- # --- Router ? diff --git a/conf/main_ipv6.conf.sample b/conf/main_ipv6.conf.sample index fa36602..1c2e3f3 100644 --- a/conf/main_ipv6.conf.sample +++ b/conf/main_ipv6.conf.sample @@ -839,6 +839,13 @@ limit_new_tcp_connections_per_seconds_per_source_IP=true # limit_new_tcp_connections_per_seconds_ports="" +# - limit_new_tcp_connections_per_seconds_rate +# - +# - New (syn) TCP connections per second per source IP; the burst is twice +# - this value. Defaults to '$default_limit_new_tcp_connections_per_seconds_rate'. +# - +limit_new_tcp_connections_per_seconds_rate=${default_limit_new_tcp_connections_per_seconds_rate} + # ------------- # --- Kernel related - Adjust Kernel Parameters (Security/Tuning) diff --git a/ip6t-firewall-server b/ip6t-firewall-server index 9ddd69d..15a0baa 100755 --- a/ip6t-firewall-server +++ b/ip6t-firewall-server @@ -1162,29 +1162,56 @@ echononl "\tLimit new (syn) TCP connections per second per source IP (multiport) if $limit_new_tcp_connections_per_seconds_per_source_IP \ && [[ ${#limit_new_tcp_connections_per_seconds_ports} -gt 0 ]]; then - #$ip6t -A INPUT -p tcp -m conntrack --ctstate NEW -m limit --limit 60/s --limit-burst 20 -j ACCEPT + if [[ -z "$limit_new_tcp_connections_per_seconds_rate" \ + || ! "$limit_new_tcp_connections_per_seconds_rate" =~ ^[0-9]+$ ]]; then + limit_new_tcp_connections_per_seconds_rate=$default_limit_new_tcp_connections_per_seconds_rate + fi + limit_new_tcp_connections_per_seconds_burst=$((limit_new_tcp_connections_per_seconds_rate * 2)) - # Rate-Limit für neue SYNs auf 443 pro IP - $ip6t -A INPUT -p tcp --syn \ - -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ - -m hashlimit --hashlimit-name syn_multi_v4 \ - --hashlimit 30/second --hashlimit-burst 60 \ - --hashlimit-mode srcip --hashlimit-srcmask 32 \ - -j ACCEPT + # Only (syn) packets above the limit are dropped here. Packets below the + # limit are not accepted but pass on to the rules of the services, so this + # does not open any port and does not bypass later limits. + if can_use_hashlimit ; then - if $log_rejected || $log_all ; then + if $log_rejected || $log_all ; then + $ip6t -A INPUT -p tcp --syn \ + -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ + -m hashlimit --hashlimit-name syn_multi_v6_log --hashlimit-mode srcip \ + --hashlimit-srcmask 128 \ + --hashlimit-above ${limit_new_tcp_connections_per_seconds_rate}/second \ + --hashlimit-burst $limit_new_tcp_connections_per_seconds_burst \ + -m limit --limit 2/second --limit-burst 10 \ + -j $LOG_TARGET $tag_log_prefix "$log_prefix SYN over limit (multiport):" + fi - #$ip6t -A INPUT -p tcp -m conntrack --ctstate NEW -j $LOG_TARGET $tag_log_prefix "$log_prefix Limit new TCP conn's: " - - # rate-limited logging für Überschreiter $ip6t -A INPUT -p tcp --syn \ -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ - -m hashlimit --hashlimit-name syn_multi_v4_log \ - --hashlimit 2/second --hashlimit-burst 10 \ - --hashlimit-mode srcip --hashlimit-srcmask 32 \ - -j $LOG_TARGET $tag_log_prefix "$log_prefix SYN over limit (multiport):" + -m hashlimit --hashlimit-name syn_multi_v6 --hashlimit-mode srcip \ + --hashlimit-srcmask 128 \ + --hashlimit-above ${limit_new_tcp_connections_per_seconds_rate}/second \ + --hashlimit-burst $limit_new_tcp_connections_per_seconds_burst \ + -j DROP + + else + + warn "xt_hashlimit not available; using simple global limit fallback for SYN rate limiting (multiport)." + + $ip6t -N syn-multi-limit + $ip6t -A syn-multi-limit \ + -m limit --limit ${limit_new_tcp_connections_per_seconds_rate}/second \ + --limit-burst $limit_new_tcp_connections_per_seconds_burst \ + -j RETURN + if $log_rejected || $log_all ; then + $ip6t -A syn-multi-limit -m limit --limit 2/second --limit-burst 10 \ + -j $LOG_TARGET $tag_log_prefix "$log_prefix SYN over limit (multiport):" + fi + $ip6t -A syn-multi-limit -j DROP + + $ip6t -A INPUT -p tcp --syn \ + -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ + -j syn-multi-limit fi - $ip6t -A INPUT -p tcp -m conntrack --ctstate NEW -j DROP + echo_done else echo_skipped diff --git a/ipt-firewall-server b/ipt-firewall-server index 57b311e..9693fef 100755 --- a/ipt-firewall-server +++ b/ipt-firewall-server @@ -1210,41 +1210,55 @@ echononl "\tLimit new (syn) TCP connections per second per source IP (multiport) if $limit_new_tcp_connections_per_seconds_per_source_IP \ && [[ ${#limit_new_tcp_connections_per_seconds_ports} -gt 0 ]]; then - #$ipt -A INPUT -p tcp -m conntrack --ctstate NEW -m limit --limit 60/s --limit-burst 20 -j ACCEPT + if [[ -z "$limit_new_tcp_connections_per_seconds_rate" \ + || ! "$limit_new_tcp_connections_per_seconds_rate" =~ ^[0-9]+$ ]]; then + limit_new_tcp_connections_per_seconds_rate=$default_limit_new_tcp_connections_per_seconds_rate + fi + limit_new_tcp_connections_per_seconds_burst=$((limit_new_tcp_connections_per_seconds_rate * 2)) - # Rate-Limit für neue SYNs auf 443 pro IP + # Only (syn) packets above the limit are dropped here. Packets below the + # limit are not accepted but pass on to the rules of the services, so this + # does not open any port and does not bypass later limits. if can_use_hashlimit ; then - $ipt -A INPUT -p tcp --syn \ - -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ - -m hashlimit --hashlimit-name syn_multi_v4 \ - --hashlimit 30/second --hashlimit-burst 60 \ - --hashlimit-mode srcip --hashlimit-srcmask 32 \ - -j ACCEPT - if $log_rejected || $log_all ; then + if $log_rejected || $log_all ; then + $ipt -A INPUT -p tcp --syn \ + -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ + -m hashlimit --hashlimit-name syn_multi_v4_log --hashlimit-mode srcip \ + --hashlimit-srcmask 32 \ + --hashlimit-above ${limit_new_tcp_connections_per_seconds_rate}/second \ + --hashlimit-burst $limit_new_tcp_connections_per_seconds_burst \ + -m limit --limit 2/second --limit-burst 10 \ + -j $LOG_TARGET $tag_log_prefix "$log_prefix SYN over limit (multiport):" + fi - # rate-limited logging für Überschreiter $ipt -A INPUT -p tcp --syn \ -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ - -m hashlimit --hashlimit-name syn_multi_v4_log \ - --hashlimit 2/second --hashlimit-burst 10 \ - --hashlimit-mode srcip --hashlimit-srcmask 32 \ - -j $LOG_TARGET $tag_log_prefix "$log_prefix SYN over limit (multiport):" + -m hashlimit --hashlimit-name syn_multi_v4 --hashlimit-mode srcip \ + --hashlimit-srcmask 32 \ + --hashlimit-above ${limit_new_tcp_connections_per_seconds_rate}/second \ + --hashlimit-burst $limit_new_tcp_connections_per_seconds_burst \ + -j DROP + else + + warn "xt_hashlimit not available; using simple global limit fallback for SYN rate limiting (multiport)." + + $ipt -N syn-multi-limit + $ipt -A syn-multi-limit \ + -m limit --limit ${limit_new_tcp_connections_per_seconds_rate}/second \ + --limit-burst $limit_new_tcp_connections_per_seconds_burst \ + -j RETURN + if $log_rejected || $log_all ; then + $ipt -A syn-multi-limit -m limit --limit 2/second --limit-burst 10 \ + -j $LOG_TARGET $tag_log_prefix "$log_prefix SYN over limit (multiport):" + fi + $ipt -A syn-multi-limit -j DROP + + $ipt -A INPUT -p tcp --syn \ + -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ + -j syn-multi-limit fi -else - warn "xt_hashlimit not available; using simple global limit fallback for SYN rate limiting (multiport)." - $ipt -A INPUT -p tcp --syn \ - -m multiport --dports $limit_new_tcp_connections_per_seconds_ports \ - -m limit --limit 30/second --limit-burst 60 \ - -j ACCEPT -fi - - - #$ipt -A INPUT -p tcp -m conntrack --ctstate NEW -j DROP - - $ipt -A INPUT -p tcp --syn -m multiport --dports $limit_new_tcp_connections_per_seconds_ports -j DROP - echo_done else