From df160801c88a10e878b01a48a49b19f7ab2805fc Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 12 Aug 2026 17:55:46 +0000 Subject: [PATCH] =?UTF-8?q?deploy:=20iptables=20hashlimit=20flood-mitigati?= =?UTF-8?q?on=20script=20(per-source-IP=20rate=20limit=20on=20the=20IRC=20?= =?UTF-8?q?client=20ports;=20policy-accept,=20loopback-exempt,=20idempoten?= =?UTF-8?q?t=20add/del)=20=E2=80=94=20kernel-layer=20defense-in-depth,=20n?= =?UTF-8?q?ot=20applied=20automatically?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/iptables-echoircd.sh | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 deploy/iptables-echoircd.sh diff --git a/deploy/iptables-echoircd.sh b/deploy/iptables-echoircd.sh new file mode 100755 index 0000000..9c56d0b --- /dev/null +++ b/deploy/iptables-echoircd.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# echoIRCd kernel-layer flood mitigation — defense-in-depth on top of the ircd's own +# per-IP `accept_rate` limiter. Drops excess NEW connections to the IRC client ports +# (per source IP) in the kernel, before they ever reach the daemon. +# +# SAFE BY DESIGN: it only appends DROP rules for the rate-limited *excess* to ports +# 6767/6770. The INPUT policy stays ACCEPT, so SSH, established connections, Docker, +# and every other service are untouched. Loopback is exempt (the liveness probe + local +# tools live there). Easy to remove, and it never changes any default policy. +# +# Apply: sudo deploy/iptables-echoircd.sh add +# Remove: sudo deploy/iptables-echoircd.sh del +# Show: sudo iptables -L INPUT -n -v | grep -iE '6767|6770' +# +# NOT persistent across reboot on its own — persist with `netfilter-persistent save` +# (iptables-persistent) or re-run from a boot unit. Review interaction with Docker's +# own rules first if you persist it. +# +# nftables equivalent (if you ever install nft), per-source-IP: +# tcp dport { 6767, 6770 } ct state new \ +# meter ircrate { ip saddr limit rate over 30/second burst 60 packets } drop +set -u + +PORTS="6767,6770" # IRC client ports (plaintext + TLS); S2S 7700 intentionally left alone +RATE="30/second" # sustained NEW connections/sec per source IP +BURST="60" # instantaneous burst allowed per source IP +ACTION="${1:-}" + +# iptables for IPv4, plus ip6tables only when it's installed (this host is v4-only). +IPT_CMDS=(iptables) +command -v ip6tables >/dev/null 2>&1 && IPT_CMDS+=(ip6tables) + +# Apply the same rule to each available table. $1 is the op: -I (insert) or -D (delete). +rule() { + local op="$1" ipt + for ipt in "${IPT_CMDS[@]}"; do + "$ipt" "$op" INPUT ! -i lo -p tcp -m multiport --dports "$PORTS" \ + -m conntrack --ctstate NEW \ + -m hashlimit --hashlimit-name ircrate --hashlimit-mode srcip \ + --hashlimit-above "$RATE" --hashlimit-burst "$BURST" \ + -j DROP 2>/dev/null || true + done +} + +case "$ACTION" in + add) + rule -D # remove any prior copy first, so re-running never stacks duplicates + rule -I + echo "echoircd rate-limit installed: ports ${PORTS}, ${RATE} burst ${BURST} per source IP (v4+v6)" + ;; + del) + rule -D + echo "echoircd rate-limit removed" + ;; + *) + echo "usage: $0 {add|del}" >&2 + exit 1 + ;; +esac