Ausgehende Mails von Webseiten/PHP limitieren

Bei einem Hosting-Webserver besteht ein grosses Problem wenn eine Webseite, beispielsweise ein CMS wie Drupal, Joomla, usw. „gehackt“ wird: Durch Sicherheitslücken gelingt es den hackern einen PHP-Mailclient auf der Wesbeite zu installieren und Zehntausende Spam-Mails über den Mailserver auf dem Webserver zu verschicken.
Auch Web-Benutzer. welche ein CRM-CMS installieren und über die Marketting Funktion „Newsletter“ Mails zu tausenden sendet sind eine Gefahr.
Denn verschickt ein Server zu viele „Spam-Mails“ wird dieser schnell auf diverse blacklists kommen. Und das will man nicht.

Es liegt hier also nahe die ausgehenden Mails zu limitieren, beispielsweise auf „100 Mails pro Stunde“. Dies geht beispielsweise mit postfwd.

Ein Problem gibt es dabei jedoch noch zu überwinden.

Als ich bei einem Web-Server den postfwd implementiert hatte war ich verwundert, dass dieser zwar funktionierte, wenn man die Mail via telnet/smtp einlieferte, jedoch nicht wenn die mail über die PHP mail() Funktion verschickt wurde.
Der Blog-Post: Rate limit outgoing emails from PHP web applications using postfix and policyd führte mich dann auf die richtige Spur: Mails welche via lokalem Benutzer oder einer Applikation wie PHP verschickt werden, passieren den SMTP daemon gar nie, da sie via lokalem mail transport direkt an den Mailserver übergeben werden.

Damit greifen die smtpd_*_restrictions von postfix gar nie und es ist leider auch keine Möglichkeit vorgesehen „lokal“ eingelieferte Mails an einen externen Service zu übergeben.

Mittels einer zweiten Postfix-Instanz, welche als Smarthost (smtp relay) fungiert lässt sich das Problem jedoch elegant lösen: Auf der primären Instanz wird mittels dem relayhost Parameter jede Mail an die zweite Instanz relayed, was immer per SMTP Übermittlung geschieht. Somit greifen auch die smtpd_*_restrictions wieder und selbst „lokale“ Mails passieren den postfwd.

Und die Funktion um mehrere unabhängige Postfix-Instanzen (mit eigenem main.cf/master.cf) auf dem slevben System laufen zu lassen hat Postfix seit 2.6 sehr komfortabel eingebaut!

Hier werde ich das Setup von postfwd und postfix via zweiter smtp-relay Instanz auf CentOS beschreiben.

Ein Wort zum PolicyD

Nebst postfwd gibt es noch den policyd, welcher was ähnliches macht (oder machen sollte).
Dieser ist jedoch hoffnungslos veraltet und wird auch nicht mehr weiter entwickelt.
Schon bei der Version musste ich den „Snapshot-Release“ (2.1.x) nehmen, da „Stable“ (2.0.14) kein IPv6 Support hat und daher heutzutage eigentlich nicht mehr benutzt werden kann (ausser man deaktiviert ipv6 komplett auf dem System).
Ich habe es aber auch mit viel debugging nie geschafft den policyd fürs rate limitting richtig zum laufen zu kriegen und vermute es gibt mittlerweile eine Inkompatibilität zu postfix.

Installieren von postfwd

postfwd wie beschrieben installieren und konfigurieren.

Postfix konfigurieren

Primäre Instanz

Zunächst wird aus der primären Instanz ein sogenannter „null-client“ gemacht, welcher keine (auch keine lokalen) Mails mehr versendet.
Dazu wird die Datei: /etc/postfix/main.cf mit folgendem Inhalt ersetzt:

# Postfix 2.6+, disable inet services, specifically disable smtpd(8)
master_service_disable = inet

# No local delivery
mydestination =
local_transport = error:5.1.1 Mailbox unavailable
alias_database =
alias_maps =
local_recipient_maps =

# Send everything to the internal mailhub
relayhost = 127.0.0.1:10025

Sekundäre Instanz

Nun wird mittels dem postfix tool: postmulti die zweite Instanz erstellt:

postmulti -e init postmulti -I postfix-postfwd -G mta -e create postmulti -i postfix-postfwd -x postconf -e "master_service_disable =" "authorized_submit_users = root" postmulti -i postfix-postfwd -e enable 

Damit wird nun im Verzeichnis: /etc/postfix-postfwd/ eine zweite Instanz mit einem „Standard“ main.cf und master.cf erstellt.

Nun wird zuerst in der Datei: /etc/postfix-postfwd/main.cf die folgenden Werte am Ende eingefügt:

# postfwd smtpd_sender_restrictions      = check_policy_service inet:127.0.0.1:10040 

Und in /etc/postfix-postfwd/master.cf eine neue smtpd Instanz auf Port 10025 hinzugefügt:

127.0.0.1:10025     inet  n       -       n       -       -       smtpd
Lokale Mails
Damit das lokale Mail System noch funktioniert (beispielsweise wenn ein Dienst ein Mail an: root schickt), muss der Wert des Parameters: myorigin auf der primären Instanz (postfix) zum Wert des Parameters mydestination auf der Sekundären Instanz (postfix-postfwd) passen.

Nun noch postfix neustarten:

systemctl restart postfix

Nun sollten zwei Postfix Instanzen laufen: Die Primäre, welche auf keinem Port hört und alle mails and die Sekundäre auf Port 10025 relayed und die Sekundäre, welche Mails auf Port 10025 annimmt, an postfwd auf Port 10040 übergibt und dann schlussendlich via Port 25 raus sendet.

postfix Kommandos ausführen bei mehren Insanzen
Bei mehreren Instanzen gehen alle Postfix-Kommandos wie z.B: mailq oder postsuper auf die Haupt-Instanz. Will man diese bei den anderen Instanzen ausführen muss man postmulti -i INSTANCE -x COMMAND benutzen, z.B.:
postmulti -i postfix-postfwd -x mailq

Policy anlegen

Nun geht es darum in postfwd eine policy anzulegen.

Dazu wird die Datei: postfwd.cf editiert.

# Mail throttling
id=RATE_DAILY;  action=rate(sender/100/86400/REJECT only 100 messages per day for s$sender [$$ratecount hits])
id=RATE_HOURLY; action=rate(sender/50/3600/REJECT only 50 messages per hour for $$sender [$$ratecount hits])

# For Debugging
#id=RATE_MINUTE;  action=rate(sender/3/180/REJECT only 3 messages per 3 minutes for $$sender [$$ratecount hits])

Anstelle von REJECT kann als Aktion auch ein SMTP Status Code verwendet werden; postfix reagiert dann entsprechend mit REJECT oder wie in diesem Beispiel DEFER:

id=RATE_MINUTE;  action=rate(sender/3/180/450 4.7.1 sorry, max 3 requests per 3 minutes for $$sender [$$ratecount hits])

Als weitere Alternative lassen sich die Mails auch in die HOLD queue verschieben. Damit erfolgt keine automatische auslieferung mehr der Mails in der Queue, bis ein administrator diese freigibt.

Dies kann nützlich sein, wenn man verhindern will, dass man bei einem „Spam-Einruch“ Mails verliert:

id=RATE_DAILY;  action=rate(sender/100/86400/HOLD only 100 messages per day for $$sender [$$ratecount hits])

Quellen

Published by

Steven Varco

Steven ist ein Redhat RHCE-Zertifizierter Linux-Crack und ist seit über 18 Jahren sowohl beruflich wie auch privat auf Linux spezialisiert. In seinem Keller steht ein Server Rack mit diversen ESX und Linux Servern.

Schreibe einen Kommentar

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