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 limittieren, beispielsweise auf „100 Mails pro Stunde“. Dies geht beispielsweise mit dem policyd.

Ein Problem gibt es dabei jedoch noch zu überwinden.

Als ich bei einem Web-Server den policyd 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 policy.

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 policyd und postfix via zweiter smtp-relay Instanz auf CentOS beschreiben.

Installieren von policyd

PolicyD wie beschrieben installieren und konfigurieren.

ACHTUNG: Version 2.1.x verwenden

Bei der Wahl der Version ist unbedingt drauf zu achten das „Snapshot-Release“ (2.1.x) zu 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).

In /etc/cbpolicyd/cluebringer.conf kann man alle Module deaktivieren, ausser das „Quotas module“ (enable=1).
Ausserdem sollte man beim loging noch: log_mail=mail@syslog:native eintragen.

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-policyd -G mta -e create
postmulti -i postfix-policyd -x postconf -e "master_service_disable =" "authorized_submit_users = root"
postmulti -i postfix-policyd -e enable

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

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

# policyd
smtpd_sender_restrictions      = check_policy_service inet:127.0.0.1:10031

Und in /etc/postfix-policyd/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ärfen Instanz (postfix-policyd) 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 policyd auf Port 10031 ü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-policyd -x mailq

Policy anlegen

Nun geht es darum in policyd eine policy anzulegen.
Hat man das Webinterface (policyd-webui) installiert, kann man sich darauf verbinden und alles raus löschen.

Dann wird zuerst eine Grund-Policy unter: Policies > Main Action: Add erstellt:
Name: Default
Priority: 0
Description: Default System Policy
Disabled: no

Policies > Main Action: Members / Action: Add
Source: any
Destination: any
Comment:
Disabled: no

Nun unter: Quotas > Configure Action: Add
Name: limit outgoing mail
Link to policy: Default
Track: Sender:user@domain
Period: 3600
Verdict: DEFER
Data: Too many mails in too short time. Mail throttling enabled.
Comment: Maximum outbound mails per sender.
Disabled: no

Quotas > Configure Action: Limits / Action: Add
Type: MessageCount
Counter Limit: 100
Comment: Maximum messages
Disabled: no

Zum schluss sollten alle Tabellen in der policyd Datenabnk leer sein, ausser:

mysql> select * from policies;
+----+---------+----------+-----------------------+----------+
| ID | Name    | Priority | Description           | Disabled |
+----+---------+----------+-----------------------+----------+
|  1 | Default |        0 | Default System Policy |        0 |
+----+---------+----------+-----------------------+----------+
1 row in set (0.00 sec)
 
 
mysql> select * from policy_members;
+----+----------+--------+-------------+---------+----------+
| ID | PolicyID | Source | Destination | Comment | Disabled |
+----+----------+--------+-------------+---------+----------+
|  1 |        1 | NULL   | NULL        | NULL    |        0 |
+----+----------+--------+-------------+---------+----------+
1 row in set (0.00 sec)
 
 
mysql> select * from quotas;
+----+----------+---------------------+--------------------+--------+---------+------------------------------------------------------------+-----------+------------------------------------+----------+
| ID | PolicyID | Name                | Track              | Period | Verdict | Data                                                       | LastQuota | Comment                            | Disabled |
+----+----------+---------------------+--------------------+--------+---------+------------------------------------------------------------+-----------+------------------------------------+----------+
|  3 |        1 | limit outgoing mail | Sender:user@domain |   3600 | DEFER   | Too many mails in too short time. Mail throttling enabled. |         0 | Maximum outbound mails per sender. |        0 |
+----+----------+---------------------+--------------------+--------+---------+------------------------------------------------------------+-----------+------------------------------------+----------+
1 row in set (0.00 sec)
 
 
mysql> select * from quotas_limits;
+----+----------+--------------+--------------+------------------+----------+
| ID | QuotasID | Type         | CounterLimit | Comment          | Disabled |
+----+----------+--------------+--------------+------------------+----------+
|  4 |        3 | MessageCount |          100 | Maximum messages |        0 |
+----+----------+--------------+--------------+------------------+----------+
1 row in set (0.00 sec)

In der Tabelle quotas_tracking sieht man dann, welche Absender schon welches Quota verbruacht haben:

mysql> select * from quotas_tracking;
+----------------+------------------------------------------------+------------+---------+
| QuotasLimitsID | TrackKey                                       | LastUpdate | Counter |
+----------------+------------------------------------------------+------------+---------+
|              4 | Sender:web-user@example.com                    | 1543231729 |  3.9373 |
+----------------+------------------------------------------------+------------+---------+
1 rows in set (0.00 sec)

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.