Protect HestiaCP servers with an ipset list
Back to blog

Protect HestiaCP servers with an ipset list

6/7/2026 · 4 min · Infrastructure

HestiaCP, in combination with the Fail2ban and Iptables daemons, provides a robust security baseline for Linux servers. However, managing thousands of individual firewall rules in Iptables degrades CPU performance and introduces network latency. IPset addresses this issue by storing dynamic list sets directly in kernel memory using high-speed hash structures. This allows for low-overhead, volumetric blocks at scale.

This guide covers validating system prerequisites, executing mandatory iptables backups, configuring IP whitelists to prevent accidental lockouts, validating script syntax, and detailing log auditing and rollback routines.


1) System prerequisites verification#

Before deploying any automated scripts or IPset firewalls on HestiaCP, ensure all backend dependencies are present and that the control panel service is running:

# 1. Verify if the IPset utility is installed and output its version
which ipset
ipset --version

# 2. Check Fail2ban installation and command line response
which fail2ban-client
fail2ban-client --version

# 3. Check if HestiaCP daemon is active and running (service names may vary: hestia, hestia-web, or hestiacp)
systemctl list-units --type=service | grep -i hestia
# And query the specific service status (e.g., hestia)
systemctl status hestia

# 4. Verify the path and permissions of the native blacklist script
ls -la /usr/local/hestia/install/common/firewall/ipset/blacklist.sh

2) Mandatory preventive firewall backup#

Editing network routing rules or iptables configurations carries an inherent risk of administrative lockouts. Always perform a timestamped backup of the active Iptables ruleset and IPset tables before editing:

# 1. Back up all active Iptables rules to a backup file
iptables-save > /root/iptables-backup-$(date +%Y%m%d).rules

# 2. Back up the active IPset tables structure
ipset list > /root/ipset-backup-$(date +%Y%m%d).txt

# 3. Optional: Back up CSF configuration directories if running in parallel
tar czf /root/csf-backup-$(date +%Y%m%d).tar.gz /etc/csf/

3) IP whitelisting (lockout prevention)#

To prevent administrators, external monitors, or development tools from getting blocked by public spam/abuse feeds, provision a dedicated whitelist set and apply a priority input rule before the block/reject statements:

# 1. Create a whitelist set structure in kernel memory
ipset create WHITELIST hash:ip

# 2. Append trusted IP addresses or CIDR blocks to the set
ipset add WHITELIST 10.0.0.1
ipset add WHITELIST 192.168.1.0/24
# Optional: Add your own static administrative public IP
# ipset add WHITELIST 203.0.113.50

> [!IMPORTANT]
> **LOCKOUT WARNING:** Ensure you add your own current public IP address (or VPN/Monitoring IPs) to the `WHITELIST` before enabling blocking rules. Keep a separate, active SSH session open as a backup while executing firewall updates to avoid lockouts.

# 3. Create a priority ACCEPT rule in Iptables (inserted at the top of the chain)
iptables -I INPUT -m set --match-set WHITELIST src -j ACCEPT

(If managing firewall rules via the HestiaCP GUI, add an ACCEPT rule mapping the whitelist IPset before any drop or reject rules).


4) Blacklist script validation#

Ensure the HestiaCP blacklist script is properly formatted, executable, and runs cleanly:

# 1. Grant execution rights to the root user
chmod +x /usr/local/hestia/install/common/firewall/ipset/blacklist.sh

# 2. Perform a dry run syntax check on the bash script
bash -n /usr/local/hestia/install/common/firewall/ipset/blacklist.sh

# 3. Execute in verbose mode to diagnose connection issues or processing errors
sudo bash -x /usr/local/hestia/install/common/firewall/ipset/blacklist.sh 2>&1 | head -n 50

# 4. Manually run the script in the foreground to fetch the initial block list
sudo /usr/local/hestia/install/common/firewall/ipset/blacklist.sh

The script consolidates multiple verified public security feeds, without placeholders:


5) Register the list in HestiaCP#

Add the IPset feed to the HestiaCP system visually:

  1. Open the HestiaCP dashboard and click the gear icon (Server Settings).
  2. Click the Firewall tab.
  3. Open IP Set Lists.
  4. Click Add IP list.

Configure fields as follows:

script:/usr/local/hestia/install/common/firewall/ipset/blacklist.sh


6) Create firewall rules#

Ipset + HestiaCP blocking flow#

flowchart LR subgraph Kernel IS["IPset BLOCK-LIST<br/>hash:ip"] IPT["iptables INPUT<br/>match set src"] end subgraph Userspace BL[blacklist.sh] F2B[Fail2ban] CRON[Daily Cron 3:00] HCP[HestiaCP Firewall UI] end BL -->|feeds to| IS F2B -->|dynamic ban| IS CRON -->|triggers| BL HCP -->|DROP/REJECT rules| IPT IS -->|match| IPT IPT -->|DROP/REJECT| PKT[Malicious Packet] style IS fill:#f59e0b,color:#000 style IPT fill:#ef4444,color:#fff

Map the registered ipset:BLOCK-LIST to the corresponding blocking rules in the panel:

Rule 1: Deny inbound TCP#

Rule 2: Deny inbound/outbound UDP#

(Note: Using DROP instead of REJECT is recommended for UDP and ICMP protocols. REJECT sends ICMP Port Unreachable messages back to the sender, which confirms that the host is active and listening).

Rule 3: Deny ICMP packets (ping)#


7) Post-configuration and verification#

Run operational checks to confirm the IPset rules are loaded correctly:

# 1. Verify that the BLOCK-LIST set is active in the kernel
ipset list BLOCK-LIST | head -n 10

# 2. Count the active IP count inside the set (skipping header lines)
ipset save BLOCK-LIST | grep -c "^add "
# Or
ipset list BLOCK-LIST -o save | grep "^add" | wc -l

# 3. Verify that the set is bound to active Iptables rules
iptables -L -n | grep -i "ipset"

# 4. Inject a test IP to verify the set accepts manual entry
ipset add BLOCK-LIST 192.0.2.1

# 5. Query the test IP in the active list
ipset test BLOCK-LIST 192.0.2.1

8) Automating ipset updates via cron#

To keep the blocklist current, schedule automatic script runs via cron:

# Test manual update script execution
sudo /usr/local/hestia/install/common/firewall/ipset/blacklist.sh

# Edit root crontab to update automatically every day at 3:00 AM
# We use 'flock' to prevent concurrent executions if list downloads hang or lag
# 0 3 * * * /usr/bin/flock -n /var/run/blacklist-ipset.lock /usr/local/hestia/install/common/firewall/ipset/blacklist.sh > /dev/null 2>&1

### 8-b) ipset persistence across reboots

By default, IPsets are stored in volatile kernel memory and are cleared on reboots. To make them persistent, dump active sets to a rules file and restore them on boot using a systemd service:

Dump active sets to rules file#

ipset save > /etc/ipset.rules


Create `/etc/systemd/system/ipset-restore.service`:

[Unit] Description=Restore IPset Rules Before=network.target

[Service] Type=oneshot ExecStart=/sbin/ipset restore -f /etc/ipset.rules ExecStop=/sbin/ipset save -f /etc/ipset.rules RemainAfterExit=yes

[Install] WantedBy=multi-user.target


Enable the service:

systemctl daemon-reload systemctl enable ipset-restore.service


9) Monitoring ipset and Fail2ban#

Implement health monitoring to ensure the threat list is populated. System network faults or drive failures may interrupt updates:

# 1. Check general Fail2ban jail status
fail2ban-client status
fail2ban-client status sshd

# 2. List currently banned IPs on the sshd jail
fail2ban-client status sshd | grep -i "banned"

# 3. Monitor Fail2ban logs in real-time
tail -n 100 /var/log/fail2ban.log

Health audit script (monitor-ipset.sh)#

Save this monitoring script to /usr/local/sbin/monitor-ipset.sh, ; it checks for list existence and accurately counts IP members:

#!/bin/bash
# monitor-ipset.sh - Verifies minimum threat list size
SET_NAME="${1:-BLOCK-LIST}"
MIN_IPS="${2:-100}"

if ! ipset list "$SET_NAME" >/dev/null 2>&1; then
    echo "CRITICAL: IPset $SET_NAME does not exist!" | mail -s "Security Alert: IPset Size" [email protected]
    exit 2
fi

# Count only 'add' entries (skipping headers)
COUNT=$(ipset save "$SET_NAME" | grep -c "^add ")

if [ "$COUNT" -lt "$MIN_IPS" ]; then
    echo "ALERT: The IPset list $SET_NAME is critically small: $COUNT IPs loaded." | mail -s "Security Alert: IPset Size" [email protected]
    exit 1
else
    echo "OK: IPset $SET_NAME running with $COUNT IPs."
    exit 0
fi

10) Rollback and contingency plan#

If legitimate access is blocked, execute these rollback commands to flush IPset rules and restore your iptables state:

# 1. Delete list references from active iptables chains
iptables -D INPUT -m set --match-set BLOCK-LIST src -j DROP 2>/dev/null
iptables -D INPUT -m set --match-set BLOCK-LIST src -j REJECT 2>/dev/null

# 2. Flush and destroy kernel IPset sets
ipset destroy BLOCK-LIST
ipset destroy WHITELIST

# 3. Restore original iptables state from backup (replace date with your actual backup rules file)
ls -la /root/iptables-backup-*.rules
iptables-restore < /root/iptables-backup-20260618.rules

11) Diagnostic logs#

Inspect these logs to debug firewall problems or panel update failures:

journalctl --since "1 hour ago" | grep -i "ipset"

tail -100 /var/log/messages | grep -i "DROP\|REJECT"

tail -100 /usr/local/hestia/log/system.log


Checklist: HestiaCP ipset firewall validation#

Use this audit checklist when provisioning or validating IPsets on HestiaCP:

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments