2022-01-16 / Bartłomiej Kurek
Server setup: #3 - firewall preparation

In this article we cover the installation of the tools and creation of the services that will be used by the firewall.
For the firewall we are going to use iptables-persistent and ipset.
The former allows for saving and restoring the rules, the latter allows to maintain the lists of ip addresses. ipset will be used here for blacklisting individual IPs and whole networks.

Series link

Install

We need to install both packages:

# apt install iptables-persistent ipset

ipset service

For ipset we will create a service. Have a look at this awesome guide at linux-audit.com.

File: /etc/systemd/system/ipset.service

[Unit]
Description=ipset persistent rule service
Before=netfilter-persistent.service
ConditionFileNotEmpty=/etc/iptables/ipset

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/ipset -exist -file /etc/iptables/ipset restore
ExecStop=/usr/sbin/ipset -file /etc/iptables/ipset save

[Install]
WantedBy=multi-user.target

Enable the service:

# systemctl enable ipset.service

ipset quick how-to

Create the blacklists before first use. They will be automatically saved and reloaded afterwards:

# ipset create blacklist hash:ip hashsize 4096 
# ipset create blacklist6 hash:net hashsize 4096 family inet6
# ipset -file /etc/iptables/ipset save
# systemctl restart ipset.service

Now we can use them.

Listing:

# ipset list
Name: blacklist
Type: hash:ip
Revision: 4
Header: family inet hashsize 4096 maxelem 65536
Size in memory: 272
References: 3
Number of entries: 0
Members:

Name: blacklist6
Type: hash:net
Revision: 6
Header: family inet6 hashsize 4096 maxelem 65536
Size in memory: 1304
References: 3
Number of entries: 0
Members:

Blacklisting an address/a range:

# ipset add blacklist 89.161.255.58
# ipset add blacklist 8.8.8.0/24

Viewing a specific list:

# ipset list blacklist | grep 89
89.161.255.58

After restarting the service the lists are restored.
They are saved in /etc/iptables/ipset and restored from there (vide: ipset.service above).

# systemctl restart ipset.service
# ipset list blacklist | grep 89
89.161.255.58

iptables: rules.v4

The following rules constitute the basic firewall. There is no forwarding/nat included yet. This will be covered in future articles. For now, please treat it as a very usable firewall skeleton.
What's included here: the blacklists, basic services, basic outgoing ports, logging. I also integrated the rules for logging and slowing down known attacks. For more information please visit this excellent resource at blog.sevagas.com.

Warnings:

  • If your interface has a different name, adapt these rules. I'm using eth0.
  • If you uncomment anti-spoof rules, make sure to use correct interface and ip address.
  • For the SYN trap here I'm using telnet and Microsoft CIFS ports. In reality I'm using a trap on multiple ports (including default SSH port), but vpn is not covered here yet and - for the sake of clarity - ssh in this guide uses the standard port. This might be discussed further in another article.

Firewall rules:

  • The default policy is DROP.
  • First, the blacklist are checked. If ip is blacklisted, the packets are dropped.
  • If packets were not dropped - the tcp flags are inspected in order to detect known scans. The scans are logged (to syslog) and packets are dropped.
  • Incoming ping requests are allowed (change to DROP if needed), outgoing icmp packets are allowed.
  • Localhost connections (lo interface) are allowed.
  • Outgoing connections are allowed only to these ports/protocols: ssh, smtp, dns, dhcp, http, ntp, https, submission, vpn.
  • Incoming connections are allowed to these services: ssh, smtp, http, https, submission.
  • Any other packets (not matched by current rules) are logged and then dropped.

File: /etc/iptables/rules.v4

*filter
# Default policy: DROP
:INPUT DROP []
:FORWARD DROP []
:OUTPUT DROP []

## Blacklist
-I INPUT -m set --match-set blacklist src -j DROP
-I FORWARD -m set --match-set blacklist src -j DROP
-I OUTPUT -m set --match-set blacklist src -j DROP

## Blocking example: cidr/single-ip
## cidr
-A INPUT -s 34.128.0.0/10 -j DROP
## ***** ***
-A INPUT -s 89.161.255.58 -j DROP

########################################################################
# Scanners, attacks
########################################################################
## Invalid packets (attacks, scans, etc.)
## Please read:
## [1] http://blog.sevagas.com/?Iptables-firewall-versus-nmap-and,31
##     # Creative Commons Attribution 4.0 International License.
##     # https://creativecommons.org/licenses/by/4.0/
##     # Changed:
##     # - the strings used for logging (grepping/processing)
##     # - ports in a SYN trap
##     # - blacklist names

## SYN scan (read [1])
-A INPUT -p tcp -m multiport --dports 23,445 --tcp-flags ALL SYN -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: SYN trap>" 
## Blacklist for 60 seconds, drop
-A INPUT -p tcp -m multiport --dports 23,445 --tcp-flags ALL SYN -m recent --name blacklist_180 --set -j DROP

## ACK scan
## Log
-A INPUT -p tcp ! --syn -m state --state NEW -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: ACK>"
## Blacklist for 60 seconds, drop
-A INPUT -p tcp ! --syn -m state --state NEW -m recent --name blacklist_60 --set -m comment --comment "Firewall: ACK scan" -j DROP

## XMAS scan
## Log
-A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: XMAS>"
-A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: XMAS-PSH>"
-A INPUT -p tcp --tcp-flags ALL ALL -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall XMAS-ALL>"
## Blacklist for 60 seconds, drop
-A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -m recent --name blacklist_60 --set -m comment --comment "Drop/Blacklist Xmas/PSH scan" -j DROP
-A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -m recent --name blacklist_60 --set -m comment --comment "Drop/Blacklist Xmas scan" -j DROP
-A INPUT -p tcp --tcp-flags ALL ALL -m recent --name blacklist_60 --set -m comment --comment "Drop/Blacklist Xmas/All scan" -j DROP

## NULL scan
## Log
-A INPUT -p tcp --tcp-flags ALL NONE -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: NULL>"
## Blacklist for 60 seconds, drop
-A INPUT -p tcp --tcp-flags ALL NONE -m recent --name blacklist_60 --set -m comment --comment "Firewall: NULL scan" -j DROP
# -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

## FIN scan
## Log attack
-A INPUT -p tcp --tcp-flags ALL FIN -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall FIN> "
## Blacklist for 60 seconds, drop
-A INPUT -p tcp --tcp-flags ALL FIN -m recent --name blacklist_60 --set  -m comment --comment "Drop/Blacklist FIN scan" -j DROP

## fragments
-A INPUT -f -j DROP

## Anti spoof: you need to enter your iface and address here
# -A INPUT -i eth0 -s 10.0.0.219 -j DROP
# -A OUTPUT -o eth0 ! -s 10.0.0.219 -j DROP


## UDP scan
-A INPUT -p udp -m limit --limit 6/h --limit-burst 1 -m length --length 0:28 -j LOG --log-prefix "<Firewall: 0 length udp>"
-A INPUT -p udp -m length --length 0:28 -m comment --comment "Drop UDP packet with no content" -j DROP

########################################################################

## Localhost
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT


## Allow packets returning (for connections initiated by us)
-A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

## Allow outgoing packets to properly connected clients
-A OUTPUT -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

## Allow ping?
-A INPUT -p icmp -m state --state NEW -m icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp -m state --state RELATED,ESTABLISHED -m icmp --icmp-type echo-reply -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT

## Allow outgoing connections to specific services:
## ssh
-A OUTPUT -p tcp --dport 22 -j ACCEPT
## smtp
-A OUTPUT -p tcp --dport 25 -j ACCEPT
## dns
-A OUTPUT -p udp --dport 53 -j ACCEPT
## dhcp
-A OUTPUT -p udp --dport 67 -j ACCEPT
## http
-A OUTPUT -p tcp --dport 80 -j ACCEPT
## ntp
-A OUTPUT -p udp --dport 123 -j ACCEPT
## https
-A OUTPUT -p tcp --dport 443 -j ACCEPT
-A OUTPUT -p udp --dport 443 -j ACCEPT
## smtps
-A OUTPUT -p tcp --dport 587 -j ACCEPT
## openvpn
-A OUTPUT -p tcp --dport 1194 -j ACCEPT
-A OUTPUT -p udp --dport 1194 -j ACCEPT


## Allow all output? 
# -A OUTPUT -p tcp -j ACCEPT


########################################################################
## OUR SERVICES
##----------------------------------------------------------------------
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 25 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -i eth0 -p udp -m udp --dport 443 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 587 -j ACCEPT
##----------------------------------------------------------------------


########################################################################
## FORWARDING
##----------------------------------------------------------------------
## To be done
##----------------------------------------------------------------------


# Logging
-A INPUT -m limit --limit 3/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 4
-A FORWARD -m limit --limit 3/min -j LOG --log-prefix "iptables_FORWARD_denied: " --log-level 4
-A OUTPUT -m limit --limit 3/min -j LOG --log-prefix "iptables_OUTPUT_denied: " --log-level 4


## Drop everything else
-A INPUT -j DROP
-P INPUT DROP
-A FORWARD -j DROP
-P FORWARD DROP 
-A OUTPUT -j DROP
-P OUTPUT DROP

COMMIT

If you want to load these rules manually, use the following command:

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

iptables: rules.v6

This is an ipv6 equivalent of the ipv4 rules above . The difference is that these rules are being loaded by ip6tables command and there are slight differences in protocol names, blacklist names and fragmented packet matching. Otherwise it's just an elaborate copy-paste.

File: /etc/iptables/rules.v6

*filter
# Default policy: DROP
:INPUT DROP []
:FORWARD DROP []
:OUTPUT DROP []


## Blacklist
-I INPUT -m set --match-set blacklist6 src -j DROP
-I FORWARD -m set --match-set blacklist6 src -j DROP
-I OUTPUT -m set --match-set blacklist6 src -j DROP

########################################################################
# Scanners, attacks
########################################################################
## Invalid packets (attacks, scans, etc.)
## Please read:
## [1] http://blog.sevagas.com/?Iptables-firewall-versus-nmap-and,31
##     # Creative Commons Attribution 4.0 International License.
##     # https://creativecommons.org/licenses/by/4.0/
##     # Changed:
##     # - the strings used for logging (grepping/processing)
##     # - ports in a SYN trap
##     # - blacklist names

## SYN scan (read [1])
-A INPUT -p tcp -m multiport --dports 23,445 --tcp-flags ALL SYN -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: SYN trap>" 
## Blacklist for 60 seconds, drop
-A INPUT -p tcp -m multiport --dports 23,445 --tcp-flags ALL SYN -m recent --name blacklist6_180 --set -j DROP

## ACK scan
## Log
-A INPUT -p tcp ! --syn -m state --state NEW -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: ACK>"
## Blacklist for 60 seconds, drop
-A INPUT -p tcp ! --syn -m state --state NEW -m recent --name blacklist6_60 --set -m comment --comment "Firewall: ACK scan" -j DROP

## XMAS scan
## Log
-A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: XMAS>"
-A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: XMAS-PSH>"
-A INPUT -p tcp --tcp-flags ALL ALL -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall XMAS-ALL>"
## Blacklist for 60 seconds, drop
-A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -m recent --name blacklist6_60 --set -m comment --comment "Drop/Blacklist Xmas/PSH scan" -j DROP
-A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -m recent --name blacklist6_60 --set -m comment --comment "Drop/Blacklist Xmas scan" -j DROP
-A INPUT -p tcp --tcp-flags ALL ALL -m recent --name blacklist6_60 --set -m comment --comment "Drop/Blacklist Xmas/All scan" -j DROP

## NULL scan
## Log
-A INPUT -p tcp --tcp-flags ALL NONE -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall: NULL>"
## Blacklist for 60 seconds, drop
-A INPUT -p tcp --tcp-flags ALL NONE -m recent --name blacklist6_60 --set -m comment --comment "Firewall: NULL scan" -j DROP
# -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

## FIN scan
## Log attack
-A INPUT -p tcp --tcp-flags ALL FIN -m limit --limit 3/m --limit-burst 5 -j LOG --log-prefix "<Firewall FIN> "
## Blacklist for 60 seconds, drop
-A INPUT -p tcp --tcp-flags ALL FIN -m recent --name blacklist6_60 --set  -m comment --comment "Drop/Blacklist FIN scan" -j DROP

## fragments
-A INPUT -m frag -j DROP

## Anti spoof: you need to enter your iface and address here
## -A INPUT -i eth0 -s ... -j DROP
## -A OUTPUT -o eth0 ! -s ... -j DROP


## UDP scan
-A INPUT -p udp -m limit --limit 6/h --limit-burst 1 -m length --length 0:28 -j LOG --log-prefix "<Firewall: 0 length udp>"
-A INPUT -p udp -m length --length 0:28 -m comment --comment "Drop UDP packet with no content" -j DROP

########################################################################

## Localhost
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT


## Allow packets returning (for connections initiated by us)
-A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

## Allow outgoing packets to properly connected clients
-A OUTPUT -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

## Allow ping?

-A INPUT -p ipv6-icmp -m state --state NEW -m icmp6 --icmpv6-type echo-request -j ACCEPT
-A INPUT -p ipv6-icmp -m state --state RELATED,ESTABLISHED -m icmp6 --icmpv6-type echo-reply -j ACCEPT
-A OUTPUT -p ipv6-icmp -j ACCEPT

## Allow outgoing connections to specific services:
## ssh
-A OUTPUT -p tcp --dport 22 -j ACCEPT
## smtp
-A OUTPUT -p tcp --dport 25 -j ACCEPT
## dns
-A OUTPUT -p udp --dport 53 -j ACCEPT
## dhcp
-A OUTPUT -p udp --dport 67 -j ACCEPT
## http
-A OUTPUT -p tcp --dport 80 -j ACCEPT
## ntp
-A OUTPUT -p udp --dport 123 -j ACCEPT
## https
-A OUTPUT -p tcp --dport 443 -j ACCEPT
-A OUTPUT -p udp --dport 443 -j ACCEPT
## smtps
-A OUTPUT -p tcp --dport 587 -j ACCEPT
## openvpn
-A OUTPUT -p tcp --dport 1194 -j ACCEPT
-A OUTPUT -p udp --dport 1194 -j ACCEPT


## Allow all output? 
# -A OUTPUT -p tcp -j ACCEPT


########################################################################
## OUR SERVICES
##----------------------------------------------------------------------
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 25 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -i eth0 -p udp -m udp --dport 443 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 587 -j ACCEPT
##----------------------------------------------------------------------



########################################################################
## FORWARDING
##----------------------------------------------------------------------
## to be done...
##----------------------------------------------------------------------



# Logging
-A INPUT -m limit --limit 3/min -j LOG --log-prefix "ip6tables_INPUT_denied: " --log-level 4
-A FORWARD -m limit --limit 3/min -j LOG --log-prefix "ip6tables_FORWARD_denied: " --log-level 4
-A OUTPUT -m limit --limit 3/min -j LOG --log-prefix "ip6tables_OUTPUT_denied: " --log-level 4


## Drop everything else
-A INPUT -j DROP
-P INPUT DROP
-A FORWARD -j DROP
-P FORWARD DROP 
-A OUTPUT -j DROP
-P OUTPUT DROP

COMMIT

If you want to load these rules manually, use the following command:

# ip6tables-restore < /etc/iptables/rules.v6

Start netfilter

When basic firewall configuration is in place: restart netfilter service and do some Trinity-like testing!

# /etc/init.d/netfilter-persistent restart