Post

Iptables/Firewalld + ipset Basics and Blocking Malicious Ips from Abuseip Dataset

Iptables/Firewalld + ipset Basics and Blocking Malicious Ips from Abuseip Dataset

FIREWALLD

Install firewalld

Basic commands

1
2
3
4
5
6
7
# Start / Enable / Check status
sudo systemctl start firewalld
sudo systemctl enable firewalld
sudo systemctl status firewalld

# Reload config (after changes)
sudo firewall-cmd --reload

Zones

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# List all zones
firewall-cmd --get-zones

# List zones for a specific interfact
firewall-cmd --get-zone-of-interface=eth0

# Show default zone
firewall-cmd --get-default-zone

# List all zone configs
firewall-cmd --list-all-zones

# List active zones
firewall-cmd --get-active-zones

# Set default zone
sudo firewall-cmd --set-default-zone=home

# Assign interface to a zone
sudo firewall-cmd --zone=home --change-interface=eth0 --permanent

Services

1
2
3
4
5
6
7
8
9
10
11
12
# List all available services
firewall-cmd --get-services

# List services in a specific zone
firewall-cmd --list-services --zone=public

# Allow a service
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=http --permanent

# Remove a service
sudo firewall-cmd --zone=public --remove-service=http --permanent

Ports

1
2
3
4
5
6
7
8
9
# Open a TCP or UDP port
sudo firewall-cmd --zone=public --add-port=8080/tcp
sudo firewall-cmd --zone=public --add-port=53/udp --permanent

# Close a port
sudo firewall-cmd --zone=public --remove-port=8080/tcp --permanent

# List open ports in current zone
firewall-cmd --list-ports

Interfaces

1
2
3
4
5
# List interfaces and their zones
firewall-cmd --get-active-zones

# Change zone for an interface
sudo firewall-cmd --zone=internal --change-interface=eth0 --permanent

Immediate vs Permanent

--permanent → Saves rule across reboots

No --permanent → Temporary until next reload or reboot

Masquerading / NAT

1
2
# Enable masquerading (useful for routers)
sudo firewall-cmd --zone=external --add-masquerade --permanent

Useful Combinations

1
2
3
4
5
# Allow a service and limit to a single IP
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="1.2.3.4" service name="ssh" accept'

# Drop all traffic from a subnet
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" drop'

List Services Running locally

1
2
ss -tuln
netstat -tuln

Blocking IPs

1
2
3
4
5
6
7
8
9
10
11
12
# Block a single IP (rich rule)
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=1.2.3.4 reject'

# Drop instead of reject
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=1.2.3.4 drop'

# Remove rich rule
sudo firewall-cmd --permanent --remove-rich-rule='rule family=ipv4 source address=1.2.3.4 reject'

# View all rich rules
firewall-cmd --list-rich-rules

Automation

Script to scan open ports and auto-whitelist them in firewalld

1
2
3
4
for port in $(ss -tuln | awk 'NR>1 {print $5}' | cut -d: -f2 | sort -n | uniq); do
  firewall-cmd --permanent --add-port=${port}/tcp
done
firewall-cmd --reload

To list specific services that are already running

1
sudo ss -tulnp | awk -F '[",]' 'NR>1 && /users/ { print $2 }' | sort -u

Now automating adding them to the whitelist:

1
2
3
for i in $(sudo ss -tulnp | awk -F '[",]' 'NR>1 && /users/ { print $2 }' | sort -u); do
 firewall-cmd --permanent --zone=public --add-service=$i;
 done

For Custom services running on dynamic IPs like mosh (for ssh)

Create firewalld services in xml formats in the path /etc/firewalld/services/service.xml Using a mosh example, it dynamically assigns ip addresses between 60000 and 61000

1
nano /etc/firewalld/services/mosh.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>Mosh service</short>
  <description>mosh service</description>
  <port protocol="udp" port="60000-61000"/>
</service>
1
2
firewall-cmd --permanent --add-service=mosh
firewall-cmd --reload

Now your mosh connection should work well.

Blocking Large datasets

Not advicable. Will slow down the server since its not optimized for that. Consider using iptables and ipset.

|
|
|
|
|
|
\/

IPTABLES and IPSET

Installation

1
2
sudo apt install ipset 
sudo apt install iptables

Allow a service e.g ssh

1
2
iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

Allow HTTP and HTTPS

1
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT

Block an ip

1
iptables -A INPUT -s 111.1.111.1 -j DROP

Block All Incoming Traffic (after allowing specific ports)

1
2
iptables -P INPUT DROP
iptables -P FORWARD DROP

Allow Established & Related Connections

1
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Allow localhost traffic

1
2
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT

Persistant saving of rules

1
2
apt install iptables-persistent
netfilter-persistent save

Manually save rules

1
iptables-save > /etc/iptables/rules.v4

Restore rules

1
iptables-restore < /etc/iptables/rules.v4

Limit SSH to 3 connections per minute

1
2
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 3 -j DROP

Block Specific Countries with ipset + geoip

1
iptables -A INPUT -m geoip --src-cc CN,RU -j DROP

Enable NAT (masquerading) for internal network

1
2
3
4
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE

# Also enable ip forwarding 
echo 1 > /proc/sys/net/ipv4/ip_forward

Redirect(proxy) HTTP traffic to local Squid on port 3128

1
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128

Block IP Range or CIDR

1
iptables -A INPUT -s 203.0.113.0/24 -j DROP

Use iptables-restore in safe test mode via at (just so kasinuke ile serious)

1
echo "iptables-restore < ~/rules.v4" | at now + 2 minutes


Case Study to block 100+ ips from abuseip database

Doing this prcess using a bash script to import an IP at a time is consuming and may end up caking up to an hour of our time.

Best bet would be doing a restore using ipset. Here is how: But before, Lets look at the currently existing iptable rules,

1
iptables -L -n -v

Set Default Policies to ACCEPT (safe baseline)

1
2
3
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

Save the current state (optional but smart)

Before making changes:

1
iptables-save > ~/iptables-backup.rules

You can restore this if something goes wrong:

1
iptables-restore < ~/iptables-backup.rules

Allow SSH (critical for remote access):

1
2
iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

Allow Localhost traffic:

1
2
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

Allow Established connections:

1
2
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT

/————————————————————————

Important: DO NOT set default DROP until SSH (or other admin ports) is allowed, or you’ll lock yourself out.

/————————————————————————

Ipset magic

While iptables -A INPUT -s 111.1.111.1 -j DROP works, its not feasible for large datasets. Also ipset has a default cap of just over 65k ips. We need to increase this to handle our many ips.

1
2
# create a large capacity ipset to handle max 200k ips
ipset create blacklist hash:ip maxelem 200000 hashsize 65536

Create a file blacklist-malips.txt with one IP per line:

1
curl -s  https://raw.githubusercontent.com/borestad/blocklist-abuseipdb/refs/heads/main/abuseipdb-s100-1d.ipv4 |sed /^#/d | awk '{print $1}' > abuseips.txt

Script to create an ipset and export the ips from abuseip db to a restore file. If doing this, ignore the step above.

1
2
#create a ipset called blacklist-malip with a max of 200k ips to it and genetate an export file
(   echo "create blacklist-malip hash:ip family inet hashsize 65536 maxelem 200000";   grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' abuseips.txt | awk '{ print "add blacklist-malip -exist", $1 }'; ) > ipset-batch.conf

Perform ipset restore

1
ipset restore < ipset-batch.conf

Check if the ips have been ingested.

1
ipset list blacklist-malip | wc -l

Use iptables to block traffic from the blacklist

1
iptables -I INPUT -m set --match-set blacklist-malip src -j DROP

Test and Persist the Rules

1
2
sudo apt install iptables-persistent
sudo netfilter-persistent save

Automating the ipset process

1
2
3
4
5
6
7
8
9
#!/bin/bash
# get ips from abuseips
curl -s  https://raw.githubusercontent.com/borestad/blocklist-abuseipdb/refs/heads/main/abuseipdb-s100-1d.ipv4 |sed /^#/d | awk '{print $1}' > abuseips.txt

# export the ips to a restore file
( grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' abuseips.txt | awk '{ print "add blacklist-malip -exist", $1 }'; ) > ipset-batch.conf

#update the ipset
ipset restore < ipset-batch.conf

Save for a file block_the_abuser.sh chmod +x block_the_abuser.sh

Cron the process to update every 2 days

Edit crontab

1
crontab -e

Add entry

1
0 2 */2 * * /path/to/your/block_the_abuser.sh

Save and exit.

With this simple process, we have effectively used Iptables + ipset to block malicious ips from accessing out servers. This is not all the protection in the world, but a good ground for blocking known attackers scanning the internet for vulnerabilities.

This post is licensed under CC BY 4.0 by the author.