Various problems with ipset

1 - You can’t add a very large list because you can’t configure hashsize and maxelem in the panel.

  • Solution: Modify /usr/local/hestia/bin/v-add-firewall-ipset to your liking.

I used a hack with the filename ‘list1-2’ ‘list2-1’ and the

suffix “${ip_name##*-}”
case…

But come on, the original script could at least check the file size and generate appropriate values, if it’s so difficult to give us the option in the panel.

2 - If you get tired of having to modify Hestia scripts

  • create your lists via the command line:
    Surprise: the panel doesn’t care; you can no longer use iptables from the GUI to create rules against that list.

Wasn’t the panel meant to make things easier for those who already have advanced command-line knowledge?

It would be great if there was a button to destroy my server or burn down my house, and they let me press it.

Don’t take it the wrong way! I’m just leaving ideas for improvement.
:wink:

You can simply edit the script and raise the hardcoded maxelem to suit your needs.

You are free to create a PR with the proposed changes.

Sarcasm and a mocking tone are not a good way to “make sure” your comments aren’t taken the wrong way. Even if you say you’re just sharing ideas for improvement, that tone makes it much easier for people to feel attacked or belittled instead of actually focusing on the feedback.

3 Likes

And I’d like to take this opportunity to comment on your great contribution (to the general public):

Be careful about changing the default hashsize and maxelem values ​​to excessively high numbers. hashsize reserves memory for each set, whether you fill it or not.


Setting an incorrect value for hashsize can create duplicate hashes, while a correct value can speed up ipset IP lookup by over 500%.

And I’ll explain my contribution, which you took as an insult.

#------------------------> init <------------------------

Get name suffix (last characters before the hyphen) example: “2” for “yourListXX-2”

suffix=“${ip_name##*-}”

Configure hashsize and maxelem according to suffix

case $suffix in
2)
hashsize=4194304 # It must be a power of 2
maxelem=4194304  # Choose any number you like; I don’t complicate things, I just copy and paste the value from above.
;;
1)
hashsize=16384  # It must be a power of 2
maxelem=16384  # Choose any number you like; I don’t complicate things, I just copy and paste the value from above.
;;
*)
# Hestia default values
hashsize=“”
maxelem=1048576
;;
esac

Create sets with parameters

if [ -n “$hashsize” ]; then
$IPSET_BIN -quiet create -exist “$ip_name” hash:net family $inet_ver hashsize $hashsize maxelem $maxelem
$IPSET_BIN -quiet destroy “${ip_name}-tmp”
$IPSET_BIN create “${ip_name}-tmp” -exist hash:net family $inet_ver hashsize $hashsize maxelem $maxelem
else
$IPSET_BIN -quiet create -exist “$ip_name” hash:net family $inet_ver
$IPSET_BIN -quiet destroy “${ip_name}-tmp”
$IPSET_BIN create “${ip_name}-tmp” -exist hash:net family $inet_ver maxelem $maxelem
fi

$IPSET_BIN flush “${ip_name}-tmp”

#------------------------> end <------------------------

#$IPSET_BIN -quiet create -exist “$ip_name” hash:net family $inet_ver

#$IPSET_BIN -quiet destroy “${ip_name}-tmp”

#$IPSET_BIN create “${ip_name}-tmp” -exist hash:net family $inet_ver maxelem 1048576

#$IPSET_BIN flush “${ip_name}-tmp”

I do not recommend trusting the modification of the Hestia script, it will fly off without warning in any update, and I don’t know how to clip its wings.

If you need something professional, as I said in my first comment, you can read the file and determine the number of entries or weight to automatically generate the most appropriate values.

To use manually created sets from the Hestia firewall (which I recommend), avoid surprises that you’ll soon encounter with daily firewall updates and large sets. I recommend using a custom.sh template and managing your rules from there.

Could you please test this new v-add-firewall-ipset version and provide feedback?

It removes the hardcoded maxelem and It automatically sets maxelem to the number of entries in the list and chooses a reasonable hashsize. It also works correctly when the list grows or shrinks.

cd /usr/local/hestia/bin/
mv v-add-firewall-ipset v-add-firewall-ipset.backup
curl -fsSLm10 https://7j.gg/vafi -o v-add-firewall-ipset
chmod +x v-add-firewall-ipset

Then you can use v-add-firewall-ipset to add new ipsets, or v-update-firewall-ipset yes to update current ones.

This is the diff:

❯ diff -u v-add-firewall-ipset.ori v-add-firewall-ipset
--- v-add-firewall-ipset.ori    2025-12-21 23:04:34.319528731 +0100
+++ v-add-firewall-ipset        2025-12-22 01:42:56.229690834 +0100
@@ -133,8 +133,8 @@
        fi

        # Validate iplist file size
-       iplist_size=$(sed -r -e '/^#|^$/d' "$iplist_tempfile" | wc -l)
-       [[ "$iplist_size" -le "$IPSET_MIN_SIZE" ]] && check_result "$E_INVALID" "IP list file too small (<${IPSET_MIN_SIZE}), ignoring"
+       iplist_size="$(grep -cEv '^(#|$)' "$iplist_tempfile")"
+       [[ "$iplist_size" -lt "$IPSET_MIN_SIZE" ]] && check_result "$E_INVALID" "IP list ${IPSET_FILE} is too small (<${IPSET_MIN_SIZE}), ignoring"
        mv -f "$iplist_tempfile" "${IPSET_PATH}/${IPSET_FILE}.iplist"

 fi
@@ -143,14 +143,47 @@
 inet_ver="inet"
 [ "$ip_version" == "v6" ] && inet_ver="inet6"

-$IPSET_BIN -quiet create -exist "$ip_name" hash:net family $inet_ver
+# Set default value if $iplist_size is not defined or empty
+if [[ -z $iplist_size ]]; then
+       if [[ -f "${IPSET_PATH}/${IPSET_FILE}.iplist" ]]; then
+               iplist_size="$(grep -cEv '^(#|$)' < "${IPSET_PATH}/${IPSET_FILE}.iplist")"
+               [[ "$iplist_size" -lt "$IPSET_MIN_SIZE" ]] && check_result "$E_INVALID" "IP list file ${IPSET_PATH}/${IPSET_FILE}.iplist is too small (<${IPSET_MIN_SIZE}), ignoring"
+       else
+               check_result "$E_NOTEXIST" "iplist file not found: ${IPSET_PATH}/${IPSET_FILE}.iplist"
+       fi
+fi
+
+# Ensure iplist_size is a valid positive integer
+if ! [[ "$iplist_size" =~ ^[0-9]+$ ]] || [[ "$iplist_size" -le 0 ]]; then
+       check_result "$E_INVALID" "Invalid or empty iplist: iplist_size=$iplist_size"
+fi
+
+# Set maxelem equal to the number of entries
+maxelem="$iplist_size"
+
+# Calculate hashsize as a power of 2 based on iplist_size/4
+# This balances memory usage and lookup performance
+min_buckets=$((iplist_size / 4))
+[[ "$min_buckets" -lt 64 ]] && min_buckets=64
+
+hashsize=64
+while [[ "$hashsize" -lt "$min_buckets" ]]; do
+       hashsize=$((hashsize * 2))
+done
+
+# Create the main set with calculated parameters (if it doesn't exist)
+$IPSET_BIN -quiet create -exist "$ip_name" hash:net family "$inet_ver" maxelem "$maxelem" hashsize "$hashsize"
+
+# Recreate the temporary set with the same parameters
 $IPSET_BIN -quiet destroy "${ip_name}-tmp"
-$IPSET_BIN create "${ip_name}-tmp" -exist hash:net family $inet_ver maxelem 1048576
-$IPSET_BIN flush "${ip_name}-tmp"
+$IPSET_BIN -quiet create "${ip_name}-tmp" hash:net family "$inet_ver" maxelem "$maxelem" hashsize "$hashsize"
+$IPSET_BIN -quiet flush "${ip_name}-tmp"

+# Populate the temporary set from the iplist file
 sed -rn -e '/^#|^$/d' -e "s/^(.*)/add ${ip_name}-tmp \\1/p" "${IPSET_PATH}/${IPSET_FILE}.iplist" | $IPSET_BIN -quiet restore
-check_result $? "Populating ipset table"
+check_result $? "Populating ipset table ${ip_name}"

+# Atomically swap the temporary set with the main set
 $IPSET_BIN swap "${ip_name}-tmp" "${ip_name}"
 $IPSET_BIN -quiet destroy "${ip_name}-tmp"

I’ve been testing it for 5 days and it works fine, I’ve created this PR: