Lokale Firewall für meinen Webserver


Wenn man einen eigenen Webserver betreibt, hat man ganz schnell "Freunde" aus aller Welt. Mit ein paar Zeilen Code ist es dann auf einmal viel ruhiger im Log. Das hier basiert noch auf der guten alten Iptables, wird also unter Umständen schwierig auf neuen Systemen, da dort nftables verwendet wird. Geht aber auch da, die nftables Syntax ist ganz brauchbar (siehe Beispiel), ein Äquivalent zu ipset gibt's wohl auch, also mit ein bisschen Einlesen und Experimentieren kriegt man das hin.

# example configuration file for ferm
#
# requires:
# - ferm: http://ferm.foo-projects.org/
# - ipset: http://ipset.netfilter.org/
# - fail2ban: https://github.com/fail2ban/fail2ban/releases
#
# firewall for a web server with geoblocking and blocking of
# some known malware sites (example!)
# for the ipsets to work, the blacklist.sh script is needed
# restart fail2ban after ferm restart / configuration change!
#
# check http://opendbl.net/ for additional lists.
#
# dj0Nz 2018

@def $HOME = ( 192.168.xxx.yyy );
@def $BROADCAST = ( 192.168.xxx.255 255.255.255.255 );

table filter {
    chain INPUT {
        policy DROP;

        # connection tracking
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;

	# include blacklist ipsets
	mod set set dshield src DROP;
	mod set set talos src DROP;
	mod set set tor src DROP;

        # geoblocking ... handle with care ;)
	mod set set geoblock src DROP;

	# respond to ping
        proto icmp icmp-type echo-request ACCEPT;

	# allow ssh connections from home network
        saddr $HOME proto tcp dport 22 {
		LOG log-prefix 'SSH Login: ';
			ACCEPT;
		}

	# allow http from home network
        saddr $HOME proto tcp dport ( 80 443 ) ACCEPT;

	# allow http
	# for the de-set ipset see blacklist.sh
        mod set set de-set src {
		proto tcp dport ( 80 443 ) {
			mod connlimit connlimit-above 50 REJECT;
			mod connlimit connlimit-above 250 connlimit-mask 0 REJECT;
			ACCEPT;
	        }
	}

	# allow local packets
        interface lo ACCEPT;
    }

    chain OUTPUT {
	    policy ACCEPT;
	    mod state state (ESTABLISHED RELATED) ACCEPT;
    }

    chain FORWARD {
        policy DROP;
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;
    }
}

Die Service Unit für ipset legst Du an mit

vi /etc/systemd/system/ipset.service
Dann den Text unten da rein ballern und mit der Sequenz
systemctl daemon-reload
systemctl enable ipset
aktivieren. Starten kannst Du das erst, wenn Du die ipsets mit Inhalt gefüllt hast.

[Unit]
Description=ipset persistent rules
Before=ferm.service
ConditionFileNotEmpty=/etc/ipset.conf

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

[Install]
WantedBy=multi-user.target

Das Blacklist Download Skript kannst Du mit cron einmal am Tag laufen lassen. Es muss einmal laufen, bevor Du ipset starten kannst.

#!/bin/bash

# create blacklist ipsets to use with
# local firewall input chain
#
# dj0Nz 05/2018

# recreate existing sets
ipset -q create dshield hash:net
ipset -q create talos hash:ip
ipset -q create geoblock hash:net
ipset -q create tor hash:ip
ipset -q create de-set hash:net
ipset flush dshield
ipset flush talos
ipset flush geoblock
ipset flush tor
ipset flush de-set

# country list for geoblocking
countries="cn ru kr pk tw sg hk"

# my external ip address
ext_ip=`dig @resolver1.opendns.com A myip.opendns.com +short -4`

# dshield first
input=/tmp/dshield.tmp
output=/tmp/dshield.list

if [ -f $input ]; then
   rm $input
fi
if [ -f $output ]; then
   rm $output
fi

wget -q https://isc.sans.edu/block.txt -O $input
grep '^[0-9]' $input | awk '{print $1"/"$3}' >> $output
while read -r line
do
   ipset add dshield $line
done < $output

# cisco talos
input=/tmp/talos.tmp
wget -q https://talosintelligence.com/documents/ip-blacklist -O $input
while read -r line
do
   ipset add talos $line
done < $input

# geoblocking
input=/tmp/countries.tmp

if [ -f $input ]; then
   rm $input
fi

for country in $countries
do
   if [ -f /tmp/$country.zone ]; then
      rm /tmp/$country.zone
   fi
   wget -q http://www.ipdeny.com/ipblocks/data/countries/$country.zone -O /tmp/$country.zone
   cat /tmp/$country.zone >> $input
done
while read -r line
do
   ipset add geoblock $line
done < $input

# german networks
if [ -f /tmp/de.zone ]; then
   rm /tmp/de.zone
fi
wget -q http://www.ipdeny.com/ipblocks/data/countries/de.zone -O /tmp/de.zone
while read -r line
do
   ipset add de-set $line
done < /tmp/de.zone

# get a list of Tor exit nodes that can access $ext_ip, skip the comments and read line by line
# copied from http://mikhailian.mova.org/node/194
wget -q https://check.torproject.org/cgi-bin/TorBulkExitList.py?ip=$ext_ip -O -|sed '/^#/d' |while read ip
do
  ipset -q -A tor $ip
done

# save for later use (system reboot etc)
ipset save -file /etc/ipset.conf