AllgemeinBlogLinuxPythonShell

IPs mittels iptables automatisch blockieren lassen

Eigentlich wollte ich meinen vServer noch sicherer machen, indem ich von Seiten wie myip.ms eine vorgefertigte Blackliste herunterlade. Die gezogenen IPs sollten mittels iptables blockiert werden. Ich hatte auch schon soweit alles fertig geschrieben, ein Python3 Script, ein Shell Script und die SQL Scripte zum Anlegen der Datenbank.
Allerdings habe ich nach ein „paar“ Einträgen folgende Nachricht von iptables bekommen: iptables: Memory allocation problem.

Nach ein bisschen Internet Recherche fand ich heraus, dass mich mein Hoster auf eine bestimmte Anzahl an iptable Einträgen begrenzt.

Die maximal Möglichen Einträge findet ihr mit diesem Befehl.

egrep "failcnt|numiptent" /proc/user_beancounters

numiptent steht dabei für die NETFILTER Einträge. In diesem Beitrag steht, dass diese dem Schwellenwert von 200 – 300 Einträgen ebenfalls aufgrund eventueller Performanzprobleme nicht überschreiten sollte. Da ich von myip.ms über 500 Einträge übertragen habe, war ich auch schon weit über das Limit. Ebenfalls habe ich noch diesen Artikel gefunden: https://www.danami.com/clients/knowledgebase/92/How-can-I-fix-the-error-The-VPS-iptables-rule-limit-numiptent-is-too-low.html

Naja, ganz abschreiben wollte ich es noch nicht und vielleicht fällt mir auch noch ein automatischer Prozess ein, den ich integrieren kann. Ich möchte jedoch schon den bis dato geschriebenen Code veröffentlichen.

Da ist zum einen der Shell Code, der die beiden SQL Dateien CREATE_FIREWALL.sql und FIREWALL_SCHEMA.sql aufruft.

#!/bin/bash

if [ -f "CREATE_FIREWALL.sql" ] && [ -f "FIREWALL_SCHEMA.sql" ]
then
                mysql -uuser -ppassword < "CREATE_FIREWALL.sql"
                mysql -uuser -ppassword < "FIREWALL_SCHEMA.sql"
else
        echo "[C] CREATE_FIREWALL.sql OR FIREWALL SCHEMA.sql missing"
fi

in der CREATE_FIREWALL steht die Einrichtung der Datenbank,

CREATE Database IF NOT EXISTS Firewall;
GRANT ALL PRIVILEGES ON Firewall.* TO 'user'@'localhost' IDENTIFIED BY 'password';

während in der FIREWALL_SCHEMA das Schema der Tabelle, der gerade erzeugten DB, beinhaltet.

use Firewall;

SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS FW_IPS;

SET FOREIGN_KEY_CHECKS = 1;

CREATE TABLE IF NOT EXISTS FW_IPS(
IP varchar(16) NOT NULL,
ORIGIN varchar(100),
SOURCE varchar(100),
STATUS boolean not null default 0,
NEW boolean not null default 0,
PRIMARY KEY(IP)
);

Kurz eine kleine Beschreibung der einzelnen Spalten:

IP zu blockierende IP
ORIGIN Herkunftsland der IP
SOURCE Resource wo diese IP steht, in diesem Fall myip.ms
STATUS Soll dieser Block aktiv sein?(True/False)
NEW Ist diese IP neu dazu gekommen?(True/False)
Python Code

Kommen wir hier zu dem Python Code, der eine Verbindung zu myip.ms aufbaut. Der Aufbau wird mit dem urllib.request Module vollzogen. Als Response bekomme ich eine Byte String, den ich mit .data.read().decode() umkonvertiere. In der Schleife benutze ich regex und entferne bloße Kommentare(gekennzeichnet mit #) oder IPv6 Einträge. Somit bleiben nur die von mir erforderlichen IPv4 Einträge. Diese verbleibenden Einträge kippe ich dann in die Firewall Datenbank. Das PDB ist ein von mir geschriebenes Modul. Die QueryInsert Methode benötigt die SQL-Abfrage, die Werte als Tupel und als letztes ein FLAG, das bestimmt ob ein Update versucht wird, wenn der Private Schlüssel schon existiert. Jenes ist jedoch nicht der Fall, daher habe ich ihn auf False gesetzt. Der NEW FLAG wird bei den Werten als 1 übergeben, da es sich hier immer um eine neue IP handeln wird, wenn die IP in die DB geschrieben wird.

try:
        import urllib.request as req
        import re
        import PDB
except:
        print("could not load modules")
        exit(1)
try:
        #try to open URL
        data = req.urlopen("https://myip.ms/files/blacklist/csf/latest_blacklist.txt")
except urllib.error.URLError as e: print(e.reason)

#decode received data from URL
ips=data.read().decode()

#Open DB Connection
DB= PDB.databaseconnection('localhost','user','password','Firewall')
DB.Connect()

#for each line in ip collected from URL
for line in ips.splitlines():
        #reject lines with a # and a total length of < 15
        if  not re.findall("\A#", line) and len(line) < 15:
                #also check if not empty
                if not line == '':
                        #DB Insert without update process
                        DB.QueryInsert("insert into FW_IPS(%s) VALUES(%s)",{"IP":line,"SOURCE":"myip.ms","STATUS":0,"NEW":1},False)
#Close DB
DB.Disconnect()
Shell Code

In dem Shell Script lege ich zuerst die BLACKLIST Chain an und platziere die bei der INPUT Chain an erster Stelle. Danach geht die Loop über die Einträge in der DB, die das FLAG NEW noch auf 1 stehen haben. Sobald es die IP erfolgreich mit iptables registriert hat, kümmern sich 2 weitere SQL Befehle darum, die FLAGS STATUS und NEW auf jeweils 1 bzw. 0 zu setzen.

 

iptables -N BLACKLIST 
iptables -I INPUT 1 -j BLACKLIST 
for ip in `mysql -s -N -e "select IP from Firewall.FW_IPS where NEW = 1"` 
do 
  iptables -A BLACKLIST -s $ip -j DROP 
  if [ $? -eq 0 ] 
  then 
    mysql -s -N -e "update Firewall.FW_IPS set STATUS = 1 where IP = '${ip}'" 
    mysql -s -N -e "update Firewall.FW_IPS set NEW = 0 where IP = '${ip}'" 
  fi 
done

Der komplette Prozess hat wie gesagt funktioniert, jedoch durch den Lock mehr oder weniger unbrauchbar. Zusätzlich verwende ich nämlich noch fail2ban, der auch Einträge in die iptables tätigt. Alles in allem muss ich mir etwas neues überlegen, damit ich die IPs von bestimmten Herkunftsländern blockieren kann.

 

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.