All posts by cdstealer

Splunk tweaks

Set the default search time period.
$SPLUNK_HOME/etc/system/local/ui-prefs.conf file includes:

[search]
 dispatch.earliest_time = -15m
 dispatch.latest_time = now

----------------------

Handy Exim Filters

For a while now I'd been thinking about how to block spam that is mascarading as myself. Today, I fixed it :)
In my exim.conf, I have a section for spam filtering under the "acl_check_data" section.

warn  message = X-Antivirus-Scanned: Clean
warn  message = X-Spam-Score: $spam_score
      spam = mail:true
      condition = ${if <{$message_size}{80k}{true}{false}}
warn  message = X-SA-Status: No
      spam = mail:true
      condition = ${if <{$spam_score_int}{29}{true}{false}}
warn  message = X-Spam-Report: $spam_report
      spam = mail:true
      condition = ${if >{$spam_score_int}{40}{true}{false}}
warn  message = Subject: [SPAM] $h_Subject
      spam = mail:true
      condition = ${if >{$spam_score_int}{30}{true}{false}
warn  message = X-SA-Status: Yes
      spam = mail:true
      condition = ${if >{$spam_score_int}{30}{true}{false}}
warn message = X-Spam-Score: 80
spam = mail:true
condition = ${if !match{$sender_host_address:}{"(127.0.0.1|198.16.181.0/24)"}{true}{false}}}}
condition = ${if match{$sender_address:}{"(me|other.me)"@}{true}{false}}
condition = ${if match{$h_to:}{"(me|other.me)"@}{true}{false}}
deny message = This message scored $spam_score spam points. spam = mail:true condition = ${if >{$spam_score_int}{44}{true}{false}}

I also added the following under the acl_check_helo:

# If the HELO pretend to be this host
deny    condition = ${if or { \
                     {eq {${lc:$sender_helo_name}}{cdstealer.com}} \
                     {eq {${lc:$sender_helo_name}}{127.0.0.1}} \
                     {eq {${lc:$sender_helo_name}}{198.16.181.0/24}} \
                     } {true}{false} }

Create Bootable Windows Installation USB

So, a situation may arise where you are required to install Windows where the only media you have is a USB drive. Now if you're already running Windows, you can do this with the media creation tool.
You can download a Windows 10 image direct from M$ here.

Obviously, we're running Linux ;) So download the ISO image and let's get cracking ;)

USB Prepare

You will need a drive >4Gb (I used a 2Gb :\)

Insert your USB and execute (where 'X' is the drive eg /dev/sda):

fdisk /dev/sdX

I'm assuming there are no partitions defined. So we'll start from scratch.

# fdisk /dev/sdj

Welcome to fdisk (util-linux 2.30.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sdj: 1.9 GiB, 2058354688 bytes, 4020224 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0003dd8d
-------------------------------------------------------------------------------
Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 
First sector (2048-4020223, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-4020223, default 4020223): 

Created a new partition 1 of type 'Linux' and of size 1.9 GiB.
-------------------------------------------------------------------------------
Command (m for help): t
Selected partition 1
Hex code (type L to list all codes): 7
Changed type of partition 'Linux' to 'HPFS/NTFS/exFAT'.
-------------------------------------------------------------------------------
Command (m for help): a
Selected partition 1
The bootable flag on partition 1 is enabled now.
-------------------------------------------------------------------------------
Command (m for help): p
Disk /dev/sdj: 1.9 GiB, 2058354688 bytes, 4020224 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0003dd8d

Device     Boot Start     End Sectors  Size Id Type
/dev/sdj1  *     2048 4020223 4018176  1.9G  7 HPFS/NTFS/exFAT
-------------------------------------------------------------------------------
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Make sure you have sys-fs/ntfs3g installed.

Execute

mkntfs -Q /dev/sdj1
# mkntfs -Q /dev/sdj1
Cluster size has been automatically set to 4096 bytes.
Creating NTFS volume structures.
mkntfs completed successfully. Have a nice day.
Install boot sector

Make sure you have ms-sys installed.

Execute ms-sys -n /dev/sdj1

# ms-sys -n /dev/sdj1
NTFS Windows 7 boot record successfully written to /dev/sdj1

Now for the installation files.

Mount the downloaded ISO and the USB drive.

mount -o loop /$HOME/Downloads/Win10_1709_English_x64.iso /mnt/THIS
mount /dev/sdj1 /mnt/THIS

Now copy the files.

cp -Rv /mnt/THIS/* /run/media///

And that's it. Unmount the ISO/USB and go install.

Simple Python HTTP server

Knocking up a simple and quick web server can be extremely useful without the need to install and configure a full fat web server. However, this isn't secure and will expose ALL files from the current directory executed in!

The official doc can be found here (v2) and here (v3).

NOTE: You will need to generate the certs for HTTPS version.

Python2:

HTTP

HTTPS

#!/usr/bin/env python2

import BaseHTTPServer, SimpleHTTPServer

httpd = BaseHTTPServer.HTTPServer(('<listen IP>', <port>),
        SimpleHTTPServer.SimpleHTTPRequestHandler)

httpd.serve_forever()

Or a simple one liner:

python -m SimpleHTTPServer
#!/usr/bin/env python2

import BaseHTTPServer, SimpleHTTPServer
import ssl

httpd = BaseHTTPServer.HTTPServer(('<listen IP>', <port>),
        SimpleHTTPServer.SimpleHTTPRequestHandler)

httpd.socket = ssl.wrap_socket (httpd.socket,
        keyfile="key.pem",
        certfile='cert.pem', server_side=True)

httpd.serve_forever()

Python3:

HTTP

HTTPS

#!/usr/bin/env python3

import http.server, socketserver

Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(('<listen ip>', <port>), Handler)

httpd.serve_forever()

Or a simple one liner:

python -m http.server <port> --bind '<listen ip>'
#!/usr/bin/env python3

import http.server, socketserver
import ssl

Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(('<listen ip>', <port>), Handler)

httpd.socket = ssl.wrap_socket (httpd.socket,
        keyfile="key.pem",
        certfile='cert.pem', server_side=True)

httpd.serve_forever()

Creating and applying a patch.

Just because! So sometimes you may need to patch a kernel source file, or a script, or maybe even your life who knows. But whatever the reason, creating a patch file and applying it are extremely easy.

Let's take the config from this post as an example.

Copy the file /etc/grub.d/10_linux to /etc/grub.d/10_linux_patched.

Open /etc/grub.d/10_linux_patched in your favourite editor and make the required changes (line 204), then save.

Original:

initrd=
  for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
           "initrd-${version}" "initramfs-${version}.img" \
           "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
           "initrd-${alt_version}" "initramfs-${alt_version}.img" \
           "initramfs-genkernel-${version}" \
           "initramfs-genkernel-${alt_version}" \
           "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
           "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
 if test -e "${dirname}/${i}" ; then
           initrd="$i"
           break
 fi
 done

Patched:

 initrd=
 for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
            "initrd-${version}" "initramfs-${version}.img" \
            "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
            "initrd-${alt_version}" "initramfs-${alt_version}.img" \
            "initramfs-genkernel-${version}" \
            "initramfs-genkernel-${alt_version}" \
            "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
            "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
 if test -e "${dirname}/${i}" ; then
            initrd="early_ucode.cpio ${rel_dirname}/$i"
            break
 else
            initrd="early_ucode.cpio"
 fi
 done

If you just run a diff on the files, you see the changes.

# diff 10_linux_unpatched 10_linux_patched
204c204
<            initrd="$i"
---
>            initrd="early_ucode.cpio ${rel_dirname}/${i}"
205a206,207
>     else
>            initrd="early_ucode.cpio"

But this output we can't use to patch with. Now we rerun the diff command, but with a 'u' switch.

# diff -u 10_linux_unpatched 10_linux_patched
--- 10_linux_unpatched	2017-06-18 14:38:05.204929981 +0100
+++ 10_linux_patched	2017-06-18 14:38:26.540589618 +0100
@@ -201,8 +201,10 @@
 	   "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
 	   "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
     if test -e "${dirname}/${i}" ; then
-           initrd="$i"
+           initrd="early_ucode.cpio ${rel_dirname}/${i}"
            break
+    else
+           initrd="early_ucode.cpio"
     fi
   done

Hopefully the above output will make sense. Basically, '-' is old and '+' is new. So all we need to do is direct this output to a file.

diff -u 10_linux_unpatched 10_linux_patched > 10_linux.patch

The filenames that precede the '---' & '+++' are the files to be read '+++' and the file to be changed '---'. These 2 lines may also have a full or partial path to the files.

To apply the patch, just execute:
patch < 10_linux.patch

If you now diff the 2 files, they should match ;)

So if not applying in the root of the files, we need to inform patch to omit the preceding segments.

example:

If the first 2 lines of the patch have a relative path.

--- a/10_linux_unpatched 2017-06-18 14:38:05.204929981 +0100
+++ b/10_linux_patched 2017-06-18 14:38:26.540589618 +0100

We would use the 'p' switch.

patch -p1 < 10_linux.patch

This will ignore the 'a' & 'b' path segments.

Gentoo CPU Microcode

Here we will get the firmware for the CPU loaded at boot.  This helps the stability and efficiency of the processor.

Let's make sure we have the required options enabled in the kernel and rebuild if not.

General setup  --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
Processor type and features  --->
    <*> CPU microcode loading support
    [*]   Intel microcode loading support

If these were not enabled, enable them as above (not modules!) and recompile and install your kernel.

Next we'll install 2 packages required for this to work.

# emerge -av intel-microcode iucode_tool

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

Calculating dependencies... done!
[ebuild R ] sys-firmware/intel-microcode-20161104::gentoo USE="split-ucode -initramfs -monolithic" 0 KiB
[ebuild R ] sys-apps/iucode_tool-2.1.1::gentoo 0 KiB

Mount your /boot filesystem and execute the following to generate the cpio archive.

iucode_tool -S --write-earlyfw=/boot/early_ucode.cpio /lib/firmware/intel-ucode/*

You should see output like this with a different HEX value.

iucode_tool: system has processor(s) with signature 0x000206a7
iucode_tool: Writing selected microcodes to: /boot/early_ucode.cpio

Now we'll configure grub2 and install the new boot config.

Open the file /etc/grub.d/10_linux in your favourite editor and (at line ~204) make the following change.

From this:

initrd=
  for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
           "initrd-${version}" "initramfs-${version}.img" \
           "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
           "initrd-${alt_version}" "initramfs-${alt_version}.img" \
           "initramfs-genkernel-${version}" \
           "initramfs-genkernel-${alt_version}" \
           "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
           "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
    if test -e "${dirname}/${i}" ; then
      initrd="$i"
      break
    fi
  done

To:

 initrd=
 for i in "initrd.img-${version}" "initrd-${version}.img" "initrd-${version}.gz" \
  	   "initrd-${version}" "initramfs-${version}.img" \
  	   "initrd.img-${alt_version}" "initrd-${alt_version}.img" \
  	   "initrd-${alt_version}" "initramfs-${alt_version}.img" \
  	   "initramfs-genkernel-${version}" \
  	   "initramfs-genkernel-${alt_version}" \
  	   "initramfs-genkernel-${GENKERNEL_ARCH}-${version}" \
  	   "initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}"; do
 if test -e "${dirname}/${i}" ; then
 initrd="early_ucode.cpio ${rel_dirname}/$i"
 break
 else
 initrd="early_ucode.cpio"
 fi
 done

Edit /etc/default/grub (at line ~59)

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to kernel
GRUB_DISABLE_LINUX_UUID=true

And last off, install the new grub config:

grub-mkconfig -o /boot/grub/grub.cfg

And that is it!

If there is an update to sys-firmware/intel-microcode from this point that contains firmware for your CPU, just mount your boot FS and execute the iucode_tool command to generate a new cpio archive.

Expand VirtualBox VDI drive

This process is very easy.  Obviously I'll be using  a Linux host, but the process should be the same for a windows host.

Windows Guest:

First thing is to detach the vdi from the VM we wish to expand.

Open the VirtualBox Manager GUI.

Open the settings for the VM to modify.

Click Storage on the left

Right click the VDI to change and select Remove Attachment.

Now open a terminal and enter the following command (change for your details)

VBoxManage modifymedium "/path/to/win10/win10.vdi" --resize 50000

(The resize value is in MB)

As soon as you press enter, the vdi will be expanded.

Now re-attach the vdi to the VM

Open the settings for the VM to modify.

Click Storage on the left

Right click on the Controller and select Add Hard Disk

Then Choose existing disk

Browse to the VDI file.

Now we need to configure windows to use the new space.

Start up the VM.

Right click on the Start button and select Disk Management

Right click on the drive you want to expand and select Extend Volume.

Click Next a few times.

Done.  That's it!

NFS server/client setup

As always, first thing is first ;)  Make sure you have the correct kernel options enabled.

File systems --->
  [*] Dnotify support
  [*] Network File Systems --->
        <*>   NFS client support
        <*>     NFS client support for NFS version 4
        [*]   NFS client support for NFSv4.1
        <*>   NFS server support
        [*]     NFS server support for NFS version 4
        [*]       NFSv4.1 server support for Parallel NFS (pNFS)

Depending on other options enabled in the kernel, version 3 may be auto selected and non-optional.

Once compiled and built.  We can install the required apps.  Make sure you enable to required USE flags.

emerge -av net-fs/nfs-utils

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

Calculating dependencies... done!
[ebuild R ~] net-fs/nfs-utils-2.1.1::gentoo USE="libmount nfsidmap nfsv4 nfsv41 tcpd uuid -caps -ipv6 -kerberos -nfsdcld (-selinux)"

SERVER:

Now we'll create the directory structure.  The first line is the virtual root followed by each filesystem.

mkdir /export
mkdir /export/videos
mkdir /export/music

Now to add the fstab entries.  I've set the ro option as I only want the local server to write.

/NAS/Videos /export/videos none bind,ro 0 0
/NAS/Music /export/music none bind,ro 0 0

Then mount them.

mount -a

On to the exports config.

vi /etc/exports
# /etc/exports: NFS file systems being exported. See exports(5).

/export <client hostname>(insecure,rw,sync,no_subtree_check,crossmnt,fsid=0)
/export/videos <client hostname>(insecure,ro,sync,no_subtree_check,crossmnt)
/export/music <client hostname>(insecure,ro,sync,no_subtree_check,crossmnt)

Just one more little tweak.  Cat the following file.

# cat /proc/fs/nfsd/nfsv4recoverydir
/var/lib/nfs/v4recovery

Now create the directory.

mkdir /var/lib/nfs/v4recovery

Without this, there will be errors in the syslog and potentially erratic performance on the client(s).

We're using systemd for this, so let's start the daemons.

systemctl start rpcbind nfs-server

And enable at boot.

systemctl enable rpcbind nfs-server

CLIENT:

For systemd, the nfs-client service will be started automatically when systemd detects that exported directories are being mounted.

So let's mount!

mount <server hostname>:/music /music
mount <server hostname>:/videos /videos

To make persistent, add the following:

<server hostname>:/videos /mnt/videos nfs4 ro,auto,_netdev,noexec 0 0
<server hostname>:/music/ /mnt/music nfs4 ro,auto,_netdev,noexec 0 0

 

Systemd & Syslog-ng

Systemd although very good, has caused problems for logging (for me).  Systemd does not play nice with logging applications such as Splunk.  The reason for this is that systemd takes over syslog and stores all log data in its journald system which uses tmpfs (RAM) until flushed to disk in its proprietary  format.  This is to make log data more secure.

Here is how I got around that so that I could analyse my logs.  It's pretty straight forward and isn't too involved.

Configure journald.conf

I have set the following options in my config and everything else is commented out.

# cat /etc/systemd/journald.conf 

[Journal]
Storage=volatile
ForwardToSyslog=yes
ForwardToKMsg=no
ForwardToConsole=no
ForwardToWall=no

Configure Syslog-ng

The journald config above will now send everything to syslog which by default will store in /var/log/messages.  To split out specific logs, you'll need to tell syslog what to do with them.  Below is a basic config to split a few logs that I'm interested in.  Some apps may use their own logger, so be aware of this.

# cat /etc/syslog-ng/syslog-ng.conf 
@version: 3.7
# $Id$
#
# Syslog-ng default configuration file for Gentoo Linux

# https://bugs.gentoo.org/show_bug.cgi?id=426814
@include "scl.conf"

options { 
 threaded(yes);
 chain_hostnames(no); 

 # The default action of syslog-ng is to log a STATS line
 # to the file every 10 minutes. That's pretty ugly after a while.
 # Change it to every 12 hours so you get a nice daily update of
 # how many messages syslog-ng missed (0).
 stats_freq(43200); 
 # The default action of syslog-ng is to log a MARK line
 # to the file every 20 minutes. That's seems high for most
 # people so turn it down to once an hour. Set it to zero
 # if you don't want the functionality at all.
 mark_freq(3600); 
};

source src {
 system();
 internal();
};

destination messages { file("/var/log/messages"); };

# By default messages are logged to tty12...
destination console_all { file("/dev/tty12"); };
# ...if you intend to use /dev/console for programs like xconsole
# you can comment out the destination line above that references /dev/tty12
# and uncomment the line below.
#destination console_all { file("/dev/console"); };

# iptables log
destination firewall { file("/var/log/firewall.log"); };
filter f_firewall { program("iptables") or match("Dropped" value(MESSAGE)); };
log { source(src); filter(f_firewall); destination(firewall); flags(final); };

# ssh log
destination sshd { file("/var/log/sshd.log"); };
filter f_sshd { program("^sshd$"); }; 
log { source(src); filter(f_sshd); destination(sshd); flags(final); };

# named log
destination named { file("/var/log/named.log" owner(named) group(named) perm(0600) dir_perm(0700)); };
filter f_named { program("^named$"); }; 
log { source(src); filter(f_named); destination(named); flags(final); };

# dhcp log
destination dhcpd { file("/var/log/dhcpd.log"); };
filter f_dhcpd { program("^dhcpd$"); }; 
log { source(src); filter(f_dhcpd); destination(dhcpd); flags(final); };

# spamd log
destination spamd { file("/var/log/spamd.log"); };
filter f_spamd { program("^spamassassin$") or program("^/usr/sbin/spamd$"); }; 
log { source(src); filter(f_spamd); destination(spamd); flags(final); };

# ALWAYS AT THE END
log { source(src); destination(messages); };
log { source(src); destination(console_all); };

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