Tag Archives: firewall

IPTables with IPSets

First thing is to ensure the correct options are set in the kernel.

IP sets support the following type of sets:

bitmap:ip The bitmap:ip set type uses a memory range, where each bit represents one IP address and can store up to 65535 (B-class network) entries. You can store same size network addresses in this kind of sets as well and an IP address will be in the set if the network address it belongs to can be found in the set.
bitmap:ip,mac The bitmap:ip,mac set type uses a memory range, where each 8 bytes represents one IP and a MAC addresses. A bitmap:ip,mac set type can store up to 65535 (B-class network) IP addresses with MAC.
bitmap:port The bitmap:port set type uses a memory range, where each bit represents one TCP/UDP port. A bitmap:port type of set can store up to 65535 ports.
hash:ip The hash:ip set type uses a hash to store IP addresses where clashing is resolved by storing the clashing elements in an array and, as a last resort, by dynamically growing the hash. Same size network addresses can be stored in an hash:ip type of set as well.
hash:net The hash:net set type also uses a hash to store CIDR netblocks, which may be of different sizes. The same techique is used to avoid clashes as at the hash:ip set type.
hash:ip,port The hash:ip,port is similar to hash:ip but you can store IP address and protocol-port pairs in it. TCP, SCTP, UDP, UDPLITE, ICMP and ICMPv6 are supported with port numbers/ICMP(v6) types and other protocol numbers without port information.
hash:ip,port,ip You can store IP address, port number, and IP address triples in an hash:ip,port,ip type of set.
hash:ip,port,net You can store IP address, port number and network address triples in this kind of set.
hash:net,port The set type supports to store network address and port number pairs.
hash:net,iface In this kind of set one can store network address and interface name pairs.
list:set In a list:set kind of set you can store other sets; it is like an ordered union of different sets.
-*- Networking support --->
    Networking options --->
        [*] Network packet filtering framework (Netfilter) --->
            <*> IP set support --->
                (256) Maximum number of IP sets  
                <*> bitmap:ip set support
                <*> bitmap:ip,mac set support
                <*> bitmap:port set support
                <*> hash:ip set support
                <*> hash:ip,mark set support
                <*> hash:ip,port set support
                <*> hash:ip,port,ip set support
                <*> hash:ip,port,net set support
                <*> hash:ip,mac set support
                <*> hash:mac set support
                <*> hash:net,port,net set support
                <*> hash:net set support
                <*> hash:net,net set support
                <*> hash:net,port set support
                <*> hash:net,iface set support
                <*> list:set set support

If you're having to now add this, you'll obviously need to reboot once you have built and installed the updated kernel.

Now let's install  ipset.

# emerge -av ipset

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild R ] net-firewall/ipset-6.29::gentoo USE="-modules"

Ipset has the modules flag set by default.  With the above kernel config, the build will fail as I haven't used modules DUH!  So just disable the flag.  If you've build the kernel with modules instead, just leave as is.

So on to the fun stuff.  Ipset has a pretty good help section and the manpage is detailed :)

I use systemd, so there is no service to restart for iptables or ipset.

Obviously you'll have iptables configured and running :)  By default I block everything and only have specific ports open to the world.  But that doesn't mean we don't need ipsets.  I'm going to use ipset to block spiders (crawlers) that I don't want or that bypass my robots.txt.  I could limit the blocking to specific ports eg 80 & 443, but I'm just going to block everything.  Just because I can ;) I've also set a timeout (optional) so the IP's can be rotated and the sets don't grow too big.

So let's create an ipset.  I only want to block CIDR, so I don't need to add port, mac etc etc.

ipset create netSpiders hash:net timeout 86400

You can add IPs in here also, but I like to keep things clean so I also have:

ipset create ipSpiders hash:ip timeout 86400

NOTE:  If you haven't rebooted into the ipset enabled kernel, you'll get the error:

ipset v6.29: Kernel error received: set type not supported

You can list the set and any rules within by executing:

# ipset list
Name: spiders
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536 timeout 86400
Size in memory: 368
References: 0
Number of entries: 0
Members:

As you can see, we have nothing defined yet.  So let's add some spiders.  Unfortunately it's per CIDR.

# ipset add netSpiders 123.151.148.0/22
# ipset add netSpiders 157.54.0.0/15
# ipset add netSpiders 157.56.0.0/14
# ipset add netSpiders 157.60.0.0/16
# ipset add netSpiders 65.52.0.0/14
# ipset add netSpiders 5.10.83.0/25
# ipset add netSpiders 208.115.113.80/28
# ipset add netSpiders 208.115.111.64/28
# ipset add netSpiders 198.27.64.0/18
# ipset add netSpiders 54.160.0.0/12
# ipset add netSpiders 54.224.0.0/12

We can then list our set to see the rules.

# ipset list netSpiders
Name: netSpiders
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536 timeout 86400
Size in memory: 1072
References: 0
Number of entries: 11
Members:
54.224.0.0/12
208.115.113.80/28
54.160.0.0/12
123.151.148.0/22
198.27.64.0/18
5.10.83.0/25
157.60.0.0/16
208.115.111.64/28
157.56.0.0/14
65.52.0.0/14
157.54.0.0/15

You can remove rules by executing:

# ipset del netSpiders 54.224.0.0/12

We then add the ipset to iptables.  I log dropped connections, but if you are just dropping, then just use DROP as the target instead.

iptables -A INPUT -m set --match-set netSpiders src -j LOGGING

Looking at my firewall log, I can see the ipset already working.

kernel: Incoming Dropped: IN=enp2s0 OUT= MAC=xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:08:00 SRC=54.227.98.207 DST=xxx.xxx.xxx.xxx LEN=60 TOS=0x00 PREC=0x00 TTL=43 ID=27451 DF PROTO=TCP SPT=56227 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0

If you check iptables, this is what you'll see for the ipset entry.

# iptables -L -n
...
LOGGING all -- 0.0.0.0/0 0.0.0.0/0 match-set netSpiders src
...

You can remove ipsets completely by executing:

ipset destroy netSpiders

not specifying the set will delete ALL sets.

If a set is in use by iptables, you will get the following error.

ipset v6.29: Set cannot be destroyed: it is in use by a kernel component

Also ipsets, like iptables, are not persistent from reboots.  You can save the sets by executing:

ipset save > ipset.save
# cat ipset.save 
create netSpiders hash:net family inet hashsize 1024 maxelem 65536 timeout 86400
add netSpiders 157.60.0.0/16
add netSpiders 65.52.0.0/14
add netSpiders 208.115.113.80/28
add netSpiders 123.151.148.0/22
add netSpiders 5.10.83.0/25
add netSpiders 157.56.0.0/14
add netSpiders 208.115.111.64/28
add netSpiders 54.144.0.0/12
add netSpiders 198.27.64.0/18
add netSpiders 54.224.0.0/12
add netSpiders 54.160.0.0/12
add netSpiders 157.54.0.0/15

Which will store all sets in the save file.  To restore, it's just the reverse.

# ipset restore < ipset.save

One thing to bare in mind is that iptables may fail to start if you restore rules that reference any ipsets that don't exist.

iptables v1.4.21: Set netSpiders doesn't exist.

Startup/Shutdown.

At the time of writing this, ipset does not support systemd.  So I knocked up a script and 2 systemd unit files which seem to do the job very nicely.

Script #### Ipset Startup #### Ipset Shutdown

Please save the script into /usr/sbin/ and both the unit files into /etc/systemd/system/.  Then enable them.

 # systemctl enable ipset-save
 # systemctl enable ipset