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.
[stextbox id=“warning“ caption=“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.
[/stextbox]
Inhalt
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[stextbox id=“note“ caption=“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.
[/stextbox]
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.
[stextbox id=“note“ caption=“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])