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:
- ipset-blacklist: conf
- Project Honey Pot: Threat List
- TOR Exit Nodes: Exit List
- MaxMind GeoIP Anonymous Proxies: Sample Feed
- BruteForceBlocker: List Feed
- Spamhaus DROP: List
- C.I. Army Malicious IPs: List
- blocklist.de: List
- GreenSnow: List Feed
- Firehol Level 1: Feed
- Stopforumspam: Feed
5) Register the list in HestiaCP#
Add the IPset feed to the HestiaCP system visually:
- Open the HestiaCP dashboard and click the gear icon (Server Settings).
- Click the Firewall tab.
- Open IP Set Lists.
- Click Add IP list.
Configure fields as follows:
- Name:
BLOCK-LIST - Data source: Select "Block malicious IPs" or provide the script path:
script:/usr/local/hestia/install/common/firewall/ipset/blacklist.sh
- IP version:
IPv4 - Auto update:
Yes
6) Create firewall rules#
Ipset + HestiaCP blocking flow#
Map the registered ipset:BLOCK-LIST to the corresponding blocking rules in the panel:
Rule 1: Deny inbound TCP#
- Action:
REJECT - Protocol:
TCP - Port:
0(denies all ports) - Address:
ipset:BLOCK-LIST - Comment:
DROP_BAD_TCP
Rule 2: Deny inbound/outbound UDP#
- Action:
DROP - Protocolo:
UDP - Porta:
0 - Address:
ipset:BLOCK-LIST - Comment:
DROP_BAD_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)#
- Action:
DROP - Protocolo:
ICMP - Porta:
0 - Address:
ipset:BLOCK-LIST - Comment:
DROP_BAD_ICMP
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:
- IPset/Systemd Events:
journalctl --since "1 hour ago" | grep -i "ipset"
- Iptables Block Logs (Kernel messages):
tail -100 /var/log/messages | grep -i "DROP\|REJECT"
- HestiaCP System logs:
tail -100 /usr/local/hestia/log/system.log
Checklist: HestiaCP ipset firewall validation#
Use this audit checklist when provisioning or validating IPsets on HestiaCP:
- [ ] Prerequisites: Validated IPset, Fail2ban, and
blacklist.shpresence. - [ ] Backups: Completed timestamped iptables rule backup and IPset structure backups.
- [ ] Whitelisting: Provisioned a WHITELIST set and set a priority INPUT ACCEPT rule.
- [ ] Execution: Ran syntax test (
bash -n) and verified manual blacklist execution. - [ ] Registration: Registered
BLOCK-LISTin HestiaCP with auto-updates enabled. - [ ] Rules: Enforced TCP, UDP, and ICMP REJECT rules in the firewall panel.
- [ ] Automation: Enabled cron updates at 3:00 AM.
- [ ] Monitoring: Configured tail checks and deployed the size audit script (
monitor-ipset.sh). - [ ] Contingency: Verified rollback plan is documented and working.
Was this article helpful?
Leave a quick reaction to help prioritize future technical guides:
This post is licensed under CC BY-NC.



Comments
Join the discussion below.
0 comments