Archive

Kategorien

mysql und postfix cidr blacklisten

Postfix kennt eine nette Art von Tabellen um schnell auch grössere Netzwerke zu sperren. Das erreicht man durch eine cidr Blacklist z.B. so

1
192.168.205.0/27 reject

welche dann z.B. so in die Postfix Konfig (main.cf) eingebaut werden kann

1
smtpd_client_restrictions = ... cidr:/etc/postfix/blacklist ...

Nur wird es nicht ganz so einfach diese Tabelle auch in eine mysql-Datenbank zu überführen. Denn IPs/Netze in der Form 192.168.205.0/27 in eine DB zu schreiben wäre eine ganz schlechte (weil sehr ineffiziente) Idee 🙂 Die Datenbank würde diese Form als String-Type speichern (text oder varchar bei mysql). Leider sind v.a. Suchoperationen bei Strings eher langsam. Gerade bei einer grossen Tabelle und einem (oder mehreren) beschäftigten postfix Server wird die Performance sehr leidlich sein.

Zum Glück kann man jede IP auch als Integer darstellen. Das sind dann reine Zahlentypen für mysql und damit sind viel schnellere Operationen möglich. Zudem kann man problemlos Schlüssel darauf legen (z.B. UNIQUE um doppelte Einträge in der Blacklist zu verhindern).

Zuerst erstellt man sich eine entsprechende mysql Tabelle

CREATE TABLE `blacklist` (
  `network` INT(10) UNSIGNED NOT NULL,
  `broadcast` INT(10) UNSIGNED NOT NULL,
  UNIQUE KEY `network` (`network`),
  UNIQUE KEY `broadcast` (`broadcast`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

wichtig ist beim Typ int, dass man unsigned verwendet. Sonst können IPs, deren erstes Oktett grösser als 127 ist, nicht mehr korrekt gespeichert werden.

Um diese mit dem Inhalt der cidr-Map des Postfix zu füttern, braucht es ein Tool, welches aus einer cidr Notation einer IP/Netz die erste (network) und letzte (broadcast) IP-Adresse ermitteln kann. Ich habe bei mir ipcalc und ein paar Zeilen Bash verwendet. Entstanden ist folgendes Script, welches die entsprechenden Mysql-Statments (insert) generiert

#!/bin/bash
 
for i in $(cat /etc/postfix/block.cidr | awk '{print $1}') ; do
 IP=$(ipcalc $i | grep 'Address' | awk '{print $2}')
 SUB=$(ipcalc $i | grep 'Broadcast' | awk '{print $2}')
# dieser Test ist nötig weil ein Host in der Notation
# 192.168.205.16/32 keine Broadcast Adresse hat
# in dem Fall werden network und broadcast (mysql Spalten) mit identischen Werten abgefüllt
 [ "x$SUB" = 'x' ] && SUB=$IP
 echo "INSERT INTO \`blacklist\` (\`network\`,\`broadcast\` ) VALUES (INET_ATON('$IP'),INET_ATON('$SUB')) ON DUPLICATE KEY UPDATE \`active\`=1;" >> /root/cidr2mysql.sql
done

in /root/cidr2mysql sollten sich nun die INSERT befinden

INSERT INTO `blacklist` (`network`,`broadcast` ) VALUES (INET_ATON('192.168.205.0'),INET_ATON('192.168.205.31')) ON DUPLICATE KEY UPDATE `active`=1;

hier habe ich die mysql-Funktion INET_ATON verwendet um die IPs schnell in Integers zu verwandeln. Kann man sich auch direkt auf der mysql-Konsole angucken

mysql> select inet_aton('192.168.205.0'),inet_aton('192.168.205.31');
+----------------------------+-----------------------------+
| inet_aton('192.168.205.0') | inet_aton('192.168.205.31') |
+----------------------------+-----------------------------+
| 3232288000 | 3232288031 |
+----------------------------+-----------------------------+
1 row in set (0.01 sec)

Diese Funktion erstellt den Wert nach folgender Regel:
192×256^3 + 168×256^2 + 205×256 + 0
192×256^3 + 168×256^2 + 205×256 + 31

Dann kann man sich dann die entsprechende Postfix-Konfig schreiben um von der cidr Map auf mysql umzustellen. Das ist ein kleines Konfigfile, welches dem Postfix mitteilt, wie er an die mysql-DB kommt und was er abfragen muss. Der Name ist egal, am besten aber im Konfigverzeichnis des Postfix (bei mir /etc/postfix/mysql-blacklist.cf)

1
2
3
4
5
hosts = MYSQL_HOST
user = MYSQL_USER
password = MYSQL_PASSWORT
dbname = MYSQL_DATENBANK
query = SELECT 'reject' FROM `blacklist` WHERE inet_aton('%s') BETWEEN `network` AND `broadcast`

Dann noch

1
smtpd_client_restrictions = ... mysql:/etc/postfix/mysql-blacklist.cf ...

in main.cf anpassen und postfix restart.

Zum Schluss: diese Lösung hat einen Pferdefuss mit Potential :-) Geht die mysql-DB runter (aus welchem Grund auch immer), dann wird postfix alle Mails verweigern. Wenn man also mit dem Plan spielt dies in einem grösseren Umfeld zu machen, würde ich dringend zu einer mysql-Cluster Lösung raten. Entweder durch eine Master-Slave-Replikation oder noch besser mit einem ndb Cluster (http://dev.mysql.com/doc/refman/5.1/de/ndbcluster.html)

Der Hauptunterschied zwischen einer Master-Slave-Replikation und einem ndb-mysql-Cluster? Kurz: ein Cluster garantiert einen konsistenten Zustand zu jeder Zeit. Bei einer Replikation kann es sein, dass ein langsamer Slave einen COMMIT noch nicht abgeschlossen hat. Wenn dann der Master abstürzt sind Master und Slave nicht mehr konsistent zueinander und das bedeutet bei DBs oft Ärger :-)
Einen ndb-Cluster kann man sich vereinfacht gesagt als RAID1 für mysql vorstellen. Dabei werden die beiden "Disks" des RAID über ein TCP Protokoll miteinander verbunden.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

  

  

  

fifteen − three =

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahren Sie mehr darüber, wie Ihre Kommentardaten verarbeitet werden .