funwithlinux guide

Dynamic iptables Rules with IP Sets

In the world of Linux network security, `iptables` is a powerful tool for managing firewall rules. However, as networks grow and security requirements become more complex—such as blocking thousands of malicious IPs or whitelisting hundreds of trusted addresses—traditional `iptables` rules can become unwieldy. Adding or removing individual IPs with `iptables` often requires reloading rules, leading to downtime, and large rule sets can degrade performance. Enter **IP sets**—a kernel-based framework that allows you to group IP addresses, networks, ports, or other network identifiers into dynamic, manageable sets. When paired with `iptables`, IP sets enable efficient, real-time updates to firewall rules without reloading the entire rule set. This blog will guide you through everything you need to know about using IP sets with `iptables`, from basics to advanced configurations.

Table of Contents

  1. What Are IP Sets?
  2. Why Use IP Sets with iptables?
  3. Installation & Prerequisites
  4. Creating and Managing IP Sets
  5. Integrating IP Sets with iptables
  6. Advanced Use Cases
  7. Persisting IP Sets Across Reboots
  8. Troubleshooting Common Issues
  9. Best Practices
  10. References

What Are IP Sets?

IP sets are kernel-level data structures designed to store collections of network-related entries (IP addresses, networks, ports, etc.) for efficient lookup. Unlike individual iptables rules, which check each packet against a linear list of rules, IP sets use hash tables or trees, allowing for O(1) or O(log n) lookup times—even for large datasets.

IP sets are managed via the ipset userspace tool, which communicates with the kernel’s ip_set module. They support various types, each optimized for specific use cases (e.g., single IPs, CIDR ranges, IP-port pairs).

Why Use IP Sets with iptables?

Traditional iptables rules suffer from two major drawbacks when handling large numbers of IPs:

  • Performance: A rule list with thousands of entries forces iptables to check each packet against every rule until a match is found, leading to latency.
  • Manageability: Adding/removing IPs requires modifying the rule set, which often involves flushing and reloading rules (disruptive) or appending new rules (which bloats the list further).

IP sets solve these issues by:

  • Centralizing IP management: Group IPs into sets, and reference the set in a single iptables rule.
  • Dynamic updates: Add/remove IPs from a set in real time without modifying or reloading iptables rules.
  • Efficiency: Kernel-level hash tables ensure fast lookups, even for large sets.

Installation & Prerequisites

Before using IP sets, ensure your system has the required tools and kernel support.

Step 1: Check Kernel Support

IP sets require the ip_set kernel module. Verify support with:

lsmod | grep ip_set

If no output, load the module manually:

sudo modprobe ip_set

(Most modern Linux kernels include ip_set by default.)

Step 2: Install ipset Tool

The ipset userspace tool manages sets. Install it via your package manager:

  • Debian/Ubuntu:

    sudo apt update && sudo apt install ipset -y
  • RHEL/CentOS/Rocky Linux:

    sudo dnf install ipset -y
  • Arch Linux:

    sudo pacman -S ipset

Step 3: Verify Installation

Confirm ipset is installed:

ipset --version

Creating and Managing IP Sets

IP sets are defined by a type (e.g., hash:ip, hash:net) and optional parameters (e.g., maximum size, timeout). Below are common operations.

IP Set Types

The most useful types include:

TypePurposeExample Entry
hash:ipSingle IPv4/IPv6 addresses192.168.1.100
hash:netIPv4/IPv6 networks (CIDR ranges)10.0.0.0/24
hash:ip,portIP + port pairs (e.g., 1.2.3.4:80)192.168.1.1:22
hash:net,portNetwork + port pairs (e.g., 10.0.0.0/24:443)172.16.0.0/16:8080

Basic IP Set Commands

1. Create a Set

Use ipset create <set-name> <type> [parameters].

Example 1: Blocklist for single IPv4 addresses
Create a hash:ip set named blocklist-ips with a maximum of 10,000 entries:

sudo ipset create blocklist-ips hash:ip family inet hashsize 1024 maxelem 10000
  • family inet: Use IPv4 (omit or use inet6 for IPv6).
  • hashsize 1024: Initial hash table size (kernel auto-scales).
  • maxelem 10000: Maximum number of entries.

Example 2: Allowlist for IPv4 networks
Create a hash:net set named allowlist-nets for CIDR ranges:

sudo ipset create allowlist-nets hash:net family inet maxelem 500

2. Add Entries to a Set

Use ipset add <set-name> <entry> [timeout <seconds>].

Add an IP to blocklist-ips:

sudo ipset add blocklist-ips 203.0.113.45

Add a network to allowlist-nets:

sudo ipset add allowlist-nets 192.168.5.0/24

Add a temporary IP (auto-expires after 1 hour):

sudo ipset add blocklist-ips 198.51.100.10 timeout 3600  # 3600 seconds = 1 hour

3. List Sets and Entries

  • List all sets:
    sudo ipset list
  • List details for a specific set:
    sudo ipset list blocklist-ips

4. Remove Entries from a Set

sudo ipset del blocklist-ips 203.0.113.45  # Remove a single IP

5. Flush (Clear) a Set

Remove all entries from a set (but keep the set itself):

sudo ipset flush blocklist-ips

6. Destroy a Set

Delete a set entirely:

sudo ipset destroy blocklist-ips

Integrating IP Sets with iptables

Once a set is created, reference it in iptables rules using the -m set match extension.

Matching IP Sets in iptables Rules

The syntax for referencing a set in iptables is:

sudo iptables -A <chain> -m set --match-set <set-name> <direction> -j <target>
  • <chain>: The iptables chain (e.g., INPUT, OUTPUT, FORWARD).
  • --match-set <set-name>: The name of the IP set.
  • <direction>: src (source IP) or dst (destination IP).
  • <target>: Action (e.g., DROP, ACCEPT, LOG).

Example 1: Block All IPs in blocklist-ips

Add a rule to the INPUT chain to drop traffic from any IP in blocklist-ips:

sudo iptables -A INPUT -m set --match-set blocklist-ips src -j DROP

Example 2: Allow Traffic from allowlist-nets

Allow SSH (port 22) only from networks in allowlist-nets:

sudo iptables -A INPUT -p tcp --dport 22 -m set --match-set allowlist-nets src -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROP  # Deny all others

Example 3: Block IP-Port Pairs

Create a hash:ip,port set named blocklist-ip-port and block traffic to those pairs:

sudo ipset create blocklist-ip-port hash:ip,port family inet
sudo ipset add blocklist-ip-port 198.51.100.20:80  # Block 198.51.100.20:80

# Block incoming traffic to these IP:port pairs
sudo iptables -A INPUT -m set --match-set blocklist-ip-port dst -j DROP

Dynamic Updates: Adding/Removing IPs Without Reloading iptables

The biggest advantage of IP sets is dynamic updates. For example, to block a new IP:

sudo ipset add blocklist-ips 192.0.2.1  # No need to modify iptables!

The iptables rule referencing blocklist-ips will immediately drop traffic from 192.0.2.1.

Advanced Use Cases

Time-Based IP Sets

Use the timeout parameter to auto-expire entries. For example, block an IP for 10 minutes:

sudo ipset add blocklist-ips 203.0.113.50 timeout 600  # 600 seconds = 10 minutes

Verify with ipset list blocklist-ips (check the timeout column).

Port-Specific IP Sets

Block IPs only on specific ports using hash:ip,port sets. For example, block SSH brute-force attackers:

# Create a set for IP:port pairs (SSH is port 22)
sudo ipset create blocklist-ssh hash:ip,port family inet

# Add a rule to drop SSH traffic from these IP:port pairs
sudo iptables -A INPUT -p tcp --dport 22 -m set --match-set blocklist-ssh src -j DROP

# Block an attacker's IP on SSH (port 22)
sudo ipset add blocklist-ssh 198.51.100.30:22

Persisting IP Sets Across Reboots

IP sets are not persistent by default—they are lost on reboot. To persist them:

Step 1: Save Sets to a File

Save all sets to /etc/ipset.conf:

sudo ipset save > /etc/ipset.conf

Step 2: Restore Sets on Boot

Restore sets at startup using a systemd service.

For Debian/Ubuntu

Create a systemd service file:

sudo nano /etc/systemd/system/ipset-persistent.service

Add the following content:

[Unit]
Description=Restore IP sets on boot
Before=netfilter-persistent.service  # Ensure sets load before iptables rules

[Service]
Type=oneshot
ExecStart=/usr/sbin/ipset restore -f /etc/ipset.conf
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl enable ipset-persistent.service
sudo systemctl start ipset-persistent.service

For RHEL/CentOS

RHEL-based systems can use ipset-service (install with sudo dnf install ipset-service). Enable persistence with:

sudo systemctl enable ipset
sudo systemctl start ipset

This automatically saves sets to /etc/sysconfig/ipset and restores them on boot.

Troubleshooting Common Issues

Issue: “Set does not exist” when adding iptables rules

The IP set must exist before referencing it in iptables. Create the set first:

sudo ipset create blocklist-ips hash:ip  # Then add the iptables rule

Issue: “Operation not permitted”

Ensure you’re running commands as root (use sudo).

Issue: ipset command not found

Reinstall the ipset package (see Installation).

Issue: Kernel module missing

If modprobe ip_set fails, your kernel may not support IP sets. Upgrade to a newer kernel or enable ip_set in kernel config.

Best Practices

  1. Name Sets Clearly: Use descriptive names like blocklist-ssh or allowlist-vpn.
  2. Limit Set Size: Even with hash tables, very large sets (100k+ entries) can impact performance. Split into smaller sets if needed.
  3. Audit Regularly: Prune expired or unused entries with ipset flush <set-name> or manual deletions.
  4. Combine with Fail2ban: Use Fail2ban to automatically add malicious IPs to IP sets. Configure Fail2ban’s action to run ipset add instead of iptables commands.
  5. Backup Sets: Periodically save sets with ipset save > backup-ipset.conf to avoid data loss.

References