diff --git a/RHEL-171167-portblock-add-IPv6-support.patch b/RHEL-171167-portblock-add-IPv6-support.patch new file mode 100644 index 0000000..a9dd0ca --- /dev/null +++ b/RHEL-171167-portblock-add-IPv6-support.patch @@ -0,0 +1,125 @@ +From 269264e5a9859f9d2d31207d5067fc7673025db0 Mon Sep 17 00:00:00 2001 +From: Yusuf Yildiz +Date: Wed, 22 Apr 2026 16:09:23 +0200 +Subject: [PATCH] portblock: add IPv6 support to iptables and nft backends + +Previously, portblock rejected IPv6 service IPs because: + +- The iptables path fed the address to $IPTABLES (=iptables), + which only accepts IPv4. +- The nft path emitted `ip daddr`/`ip saddr`, which match IPv4 + only even inside an `inet` table; IPv6 requires + `ip6 daddr`/`ip6 saddr`. + +Detect the IP family from $OCF_RESKEY_ip once (a colon indicates +IPv6) and route both backends through a single ${ip_family} +variable: + +- iptables path: IPTABLES=${ip_family}tables selects `iptables` + or `ip6tables` as appropriate. +- nft path: rule templates emit `${ip_family} daddr`/ + `${ip_family} saddr`; the same keyword is used in the + active_grep_pat regex generator so monitor matches what write + emits. +- The `iptables -V` capability probe now uses $IPTABLES. + +Verified on RHEL 9.7 across all four combinations of +(firewall=iptables|nft) x (ip=IPv4|IPv6): + +- iptables + IPv4: installs iptables DROP rule (regression ok). +- nft + IPv4: emits `ip daddr ...` rule (regression ok). +- iptables + IPv6: installs ip6tables DROP rule (new). +- nft + IPv6: emits `ip6 daddr ...` rule (new). + +stop cleans up fully in every case (no leftover rules in +`nft list ruleset`, `iptables -S`, or `ip6tables -S` after the +run). + +tickle_tcp/tickle_local paths and documentation updates are not +covered by this change; they can be handled in follow-up PRs. + +Closes #2151 + +Signed-off-by: Yusuf Yildiz +--- + heartbeat/portblock | 26 +++++++++++++++++--------- + 1 file changed, 17 insertions(+), 9 deletions(-) + +diff --git a/heartbeat/portblock b/heartbeat/portblock +index 803eea55f6..8fef3beec8 100755 +--- a/heartbeat/portblock ++++ b/heartbeat/portblock +@@ -49,6 +49,14 @@ fi + : ${OCF_RESKEY_method=${OCF_RESKEY_method_default}} + : ${OCF_RESKEY_status_check=${OCF_RESKEY_status_check_default}} + : ${OCF_RESKEY_ip=${OCF_RESKEY_ip_default}} ++ ++# IPv6 support: detect family from service IP and parameterise both backends. ++case "$OCF_RESKEY_ip" in ++ *:*) ip_family=ip6 ;; ++ *) ip_family=ip ;; ++esac ++IPTABLES=${ip_family}tables ++ + : ${OCF_RESKEY_reset_local_on_unblock_stop=${OCF_RESKEY_reset_local_on_unblock_stop_default}} + : ${OCF_RESKEY_tickle_dir=${OCF_RESKEY_tickle_dir_default}} + : ${OCF_RESKEY_sync_script=${OCF_RESKEY_sync_script_default}} +@@ -332,9 +340,9 @@ active_grep_pat() + local ip + [ "$4" = "s" ] && ip=$src || ip=$dst + if [ "$method" = "DROP" ]; then +- echo "^\s\+ip${w}$4addr${w}${ip}${w}$1${w}$4port${w}$2${w}ct${w}state${w}{${w}established,${w}related,${w}new${w}}${w}drop$" ++ echo "^\s\+${ip_family}${w}$4addr${w}${ip}${w}$1${w}$4port${w}$2${w}ct${w}state${w}{${w}established,${w}related,${w}new${w}}${w}drop$" + else +- echo "^\s\+ip${w}$4addr${w}${ip}${w}$1${w}$4port${w}$2${w}ct${w}state${w}{${w}established,${w}related,${w}new${w}}${w}reject${w}with${w}tcp${w}reset$" ++ echo "^\s\+${ip_family}${w}$4addr${w}${ip}${w}$1${w}$4port${w}$2${w}ct${w}state${w}{${w}established,${w}related,${w}new${w}}${w}reject${w}with${w}tcp${w}reset$" + fi + else + if [ "$method" = "DROP" ]; then +@@ -545,7 +553,7 @@ NftDelete() + { + local chain=$1 proto=$2 ds=$3 ip=$(echo "$4" | sed "s#/#\\\/#") ports=$5 + # Try both single port and multi-port patterns for handle search +- local handles=$($NFTABLES -a list chain inet $TABLE $chain 2>/dev/null | awk "/\s+ip ${ds}addr $ip $proto ${ds}port $ports/"' {printf "%d ", $NF}') ++ local handles=$($NFTABLES -a list chain inet $TABLE $chain 2>/dev/null | awk "/\s+${ip_family} ${ds}addr $ip $proto ${ds}port $ports/"' {printf "%d ", $NF}') + for handle in $handles; do + ocf_log debug "NftDelete: Deleting $chain rule with handle $handle" + nft delete rule inet $TABLE $chain handle $handle || { +@@ -570,9 +578,9 @@ DoPort() + nft) + if [ "$op" = "insert" ]; then + if [ "$method" = "DROP" ]; then +- $NFTABLES $op rule inet $TABLE $chain ip ${ds}addr $ip $proto ${ds}port $ports ct state { established, related, new } drop ++ $NFTABLES $op rule inet $TABLE $chain ${ip_family} ${ds}addr $ip $proto ${ds}port $ports ct state { established, related, new } drop + else +- $NFTABLES $op rule inet $TABLE $chain ip ${ds}addr $ip $proto ${ds}port $ports ct state { established, related, new } reject with tcp reset ++ $NFTABLES $op rule inet $TABLE $chain ${ip_family} ${ds}addr $ip $proto ${ds}port $ports ct state { established, related, new } reject with tcp reset + fi + elif [ "$op" = "delete" ]; then + NftDelete "$chain" "$proto" "$ds" "$ip" "$ports" +@@ -609,13 +617,13 @@ PortBLOCK() + else + if [ "$FIREWALL" = "nft" ]; then + if $try_reset ; then +- $NFTABLES insert rule inet $TABLE OUTPUT ip saddr $3 $1 sport $2 ct state { established, related, new } reject with tcp reset ++ $NFTABLES insert rule inet $TABLE OUTPUT ${ip_family} saddr $3 $1 sport $2 ct state { established, related, new } reject with tcp reset + tickle_local + fi + if [ "$method" = "DROP" ]; then +- $NFTABLES insert rule inet $TABLE INPUT ip daddr $3 $1 dport $2 ct state { established, related, new } drop ++ $NFTABLES insert rule inet $TABLE INPUT ${ip_family} daddr $3 $1 dport $2 ct state { established, related, new } drop + else +- $NFTABLES insert rule inet $TABLE INPUT ip daddr $3 $1 dport $2 ct state { established, related, new } reject with tcp reset ++ $NFTABLES insert rule inet $TABLE INPUT ${ip_family} daddr $3 $1 dport $2 ct state { established, related, new } reject with tcp reset + fi + rc_in=$? + if $try_reset ; then +@@ -937,7 +945,7 @@ if [ "$FIREWALL" = "nft" ]; then + echo "$portno" | grep -q "," && portno="{ $(echo $portno | sed 's/,/, /g') }" + elif [ "$FIREWALL" = "iptables" ]; then + # iptables v1.4.20+ is required to use -w (wait) +- version=$(iptables -V | grep -oE '[0-9]+[\.0-9]+') ++ version=$($IPTABLES -V | grep -oE '[0-9]+[\.0-9]+') + ocf_version_cmp "$version" "1.4.19.1" + if [ "$?" -eq "2" ]; then + wait="-w" diff --git a/RHEL-183131-portblock-fix-IPv6-rule-detection-in-the-iptables-backend.patch b/RHEL-183131-portblock-fix-IPv6-rule-detection-in-the-iptables-backend.patch new file mode 100644 index 0000000..ce0a6d1 --- /dev/null +++ b/RHEL-183131-portblock-fix-IPv6-rule-detection-in-the-iptables-backend.patch @@ -0,0 +1,75 @@ +From 81386a499106053f658aad336865cc36f67ab168 Mon Sep 17 00:00:00 2001 +From: Yusuf Yildiz +Date: Fri, 5 Jun 2026 12:42:42 +0200 +Subject: [PATCH] portblock: fix IPv6 rule detection in the iptables backend + +active_grep_pat() hardcoded the IPv4 "anywhere" address 0.0.0.0/0 when +building the regex used to detect existing DROP/REJECT rules from +"iptables -L" output. ip6tables prints ::/0 in the source/destination +"anywhere" column, so for an IPv6 service IP the grep never matched an +existing rule and chain_isactive() always reported the rule as absent. + +Because rule handling is gated on that detection, an IPv6 OCF_RESKEY_ip +caused: + + - "unblock start" to never remove the DROP rule (DoIptables() sees the + rule as already absent and skips the -D), so the port stayed blocked + after the protected service was up; + - "block stop" to likewise leave the DROP rule behind; + - monitor/status to always report inactive; + - duplicate rules to potentially accumulate across restarts. + +"block start" appeared to work only because IptablesBLOCK() inserts the +rule when detection reports it absent, which masked the problem. + +Select the "anywhere" literal with a case on $ip_family (::/0 for +ip6tables, 0.0.0.0/0 otherwise), matching the family-aware handling the +nft backend already had. + +A second variant of the same detection failure remained: the "opt" column +("--") is printed by legacy iptables but omitted by nft-backed ip6tables, +so the mandatory "--" in the regex still failed to match on those builds +and the DROP rule was again never removed on unblock. Make the opt column +optional so the pattern matches both legacy iptables (prints "--") and +nft-backed ip6tables (omits it); IPv4 with legacy "--" is unchanged. + +Tested on a 3-node AlmaLinux 9.7 cluster (iptables/ip6tables v1.8.10, +nf_tables) driving an IPv6 service IP end-to-end through LINSTOR Gateway +kernel NFS (portblock action=block + action=unblock): the DROP rule is now +inserted on block and removed on unblock with no leftover rule, monitor +reports correctly, and the IPv4 path is unchanged. + +Co-authored-by: Claude +Signed-off-by: Yusuf Yildiz +--- + heartbeat/portblock | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/heartbeat/portblock b/heartbeat/portblock +index 8fef3beec8..f922ee0db6 100755 +--- a/heartbeat/portblock ++++ b/heartbeat/portblock +@@ -323,7 +323,10 @@ END + active_grep_pat() + { + w="[ ][ ]*" +- any="0\\.0\\.0\\.0/0" ++ case $ip_family in ++ ip6) any="::/0" ;; ++ *) any="0\\.0\\.0\\.0/0" ;; ++ esac + src=$any dst=$3 + if [ "$4" = "s" ]; then + local src=$3 +@@ -346,9 +349,9 @@ active_grep_pat() + fi + else + if [ "$method" = "DROP" ]; then +- echo "^DROP${w}${prot}${w}--${w}${src}${w}${dst}${w}multiport${w}${4}ports${w}${2}$" ++ echo "^DROP${w}${prot}${w}\(--${w}\)\?${src}${w}${dst}${w}multiport${w}${4}ports${w}${2}$" + else +- echo "^REJECT${w}${prot}${w}--${w}${src}${w}${dst}${w}multiport${w}${4}ports${w}${2}${w}ctstate${w}NEW,RELATED,ESTABLISHED${w}reject-with${w}tcp-reset$" ++ echo "^REJECT${w}${prot}${w}\(--${w}\)\?${src}${w}${dst}${w}multiport${w}${4}ports${w}${2}${w}ctstate${w}NEW,RELATED,ESTABLISHED${w}reject-with${w}tcp-reset$" + fi + fi + } diff --git a/resource-agents.spec b/resource-agents.spec index 20a1f85..f0e15eb 100644 --- a/resource-agents.spec +++ b/resource-agents.spec @@ -45,7 +45,7 @@ Name: resource-agents Summary: Open Source HA Reusable Cluster Resource Scripts Version: 4.16.0 -Release: 70%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist} +Release: 71%{?rcver:%{rcver}}%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist} License: GPL-2.0-or-later AND LGPL-2.1-or-later URL: https://github.com/ClusterLabs/resource-agents Source0: %{upstream_prefix}-%{upstream_version}.tar.gz @@ -141,6 +141,8 @@ Patch88: RHEL-180765-2-nfsserver-fixes-incorrect-indentation.patch Patch89: RHEL-156729-sybaseASE-fix-for-missing-sybaseASE-env-during-probe-action.patch Patch90: RHEL-180765-3-nfsserver-do-not-try-to-stop-fsidd-when-not-present.patch Patch91: RHEL-182592-nfsserver-monitor-nfsdcld-and-nfs-mountd-services-to-trigger-recovery-on-failure.patch +Patch92: RHEL-171167-portblock-add-IPv6-support.patch +Patch93: RHEL-183131-portblock-fix-IPv6-rule-detection-in-the-iptables-backend.patch # bundled ha-cloud-support libs Patch500: ha-cloud-support-aliyun.patch @@ -403,6 +405,8 @@ exit 1 %patch -p1 -P 89 %patch -p1 -P 90 %patch -p1 -P 91 +%patch -p1 -P 92 +%patch -p1 -P 93 # bundled ha-cloud-support libs %patch -p1 -P 500 @@ -735,6 +739,12 @@ rm -rf %{buildroot}/usr/share/doc/resource-agents %{_usr}/lib/ocf/lib/heartbeat/OCF_*.pm %changelog +* Fri Jun 19 2026 Oyvind Albrigtsen - 4.16.0-71 +- portblock: add IPv6 support +- portblock: fix IPv6 rule detection in the iptables backend + + Resolves: RHEL-171167, RHEL-183131 + * Thu Jun 11 2026 Arslan Ahmad - 4.16.0-70 - nfsserver: monitor nfsdcld and nfs-mountd services to trigger recovery on failure