Mail-Quotas mit Dovecot

Aus Debacher-Wiki
Wechseln zu: Navigation, Suche

Will man die Speicherbelegung durch die Mailaccounts beschränken, oder auch nur den Überblick über den Verbrauch behalten, so bietet sich das Quota-Plugin von Dovecot an.

Für die folgende Beschreibung habe ich nicht die Datei /etc/dovecot/conf.d/90-quota.conf erweitert, sondern meine lokale Konfigurationsdatei /etc/dovecot/local.conf. Dazu habe ich Ergänzungen an der Datei vorgenommen und zusätzlich noch ein paar weitere Dateien erstellt, deren Dateinamen sich aus der Erweiterung der Konfiguration ergeben.

/etc/dovecot/local.conf

Am Anfang der Datei habe ich folgende Zeile ergänzt:

mail_plugins = $mail_plugins quota

Damit wird das Quota-Plugin zusätzlich aktiviert.

Im Bereich für das Imap-Protokoll erfolgt dann noch die spezifische Einbindung für das Plugin imap_quota, das einem Imap-Client die Quota-Werte zustellen kann und keine weitere Konfiguration benötigt.

protocol imap {
 mail_max_userip_connections = 8
 mail_plugins = $mail_plugins imap_quota
}

Dann habe ich folgende Zeilen ergänzt, die genaue Position sollte keine Rolle spielen:

dict {
   sqluserquota = mysql:/etc/dovecot/dovecot-dict-sql-user.conf
}

plugin {
 #quota_rule = *:storage=1G
 #quota_rule2 = Trash:storage=+100M

 quota_grace = 10%%

 quota_warning = storage=66%% quota-warning 66 %u
 quota_warning2 = storage=80%% quota-warning 80 %u
 quota_warning3 = storage=95%% quota-warning 95 %u
#  quota = maildir:User quota:noenforcing
 quota = dict:User Quota::noenforcing:proxy::sqluserquota
}
 
service quota-warning {
  executable = script /usr/local/bin/quota-warning.sh
  user = vmail
  unix_listener quota-warning {
       group = vmail
       mode = 0660  
       user = vmail 
  }
}   
   
service dict {
   unix_listener dict {
       mode = 0600
       user = vmail
   }
}

Die Zeilen der Art

 #quota_rule = *:storage=1G
 #quota_rule2 = Trash:storage=+100M

 quota_grace = 10%%

die Quota-Rules beschreiben die gültigen Speicherplatzbeschränkungen. Es kann mehrere Regeln geben, die dann am Ende durchnummeriert werden. Hier sind diese Regeln auskommentiert, sie werden ja jeweils aus der Datenbank bezogen. Gültig ist nur die quota_grace, die ein einmaliges Überschreiten des Grenzwertes erlaubt. Dadurch soll mit der letzten Mail die Grenze möglichst ausgeschöpft werden. So kann man vermeiden, dass große Mails schon abgelehnt werden, kleinere aber noch durchkommen, was den Nutzer verwirren könnte.

Mit den Zeilen

#  quota = maildir:User quota:noenforcing
 quota = dict:User Quota::noenforcing:proxy::sqluserquota

wird das Quota-Backend beschrieben. Die auskommentierte Zeile würde den aktuellen Verbrauch im Dateisystem speichern und zwar in einer Datei maildirsize im Mail-Verzeichnis des jeweiligen Benutzers. Ich habe diese Version nicht weiter verfolgt, da Postfixadmin auf diese Informationen nicht zugreifen kann. Postfixadmin benötigt die Speicherung in einer bestimmten Datenbank-Tabelle. Dazu benötigt man eine Quota-Root vom Typ dict, über die auf die Datenbank zugegriffen werden kann.

Der frei festlegbare Bezeichner sqluserquota verweist auf die dict-Konfiguration am Anfang des neuen Bereiches. Hier ist dann die Datei angegeben, die die Kommunikation mit der Datenbank beschreibt, hier die Datei /etc/dovecot/dovecot-dict-sql-user.conf.

Generell gilt folgende Syntax für die Backend Definition

quota = dict:<quota root name>:<username>[:<option>[...]]:<dictionary URI>

Wenn das Feld Benutzername leer bleibt, dann wird der aktuelle Benutzer genommen, es ginge auch %d für Domainquotas. Die übliche Syntax ist also

quota = dict:<quota root name>:[:<option>[...]]:<dictionary URI>

Der zweite doppelte Doppelpunkt ergibt sich aus der Syntax-Regel für proxy

proxy:[<dict path>]:<destination dict>

und der Tatsache, dass der Pfad in der Regel nicht angegeben wird (siehe auch http://wiki2.dovecot.org/Dictionary ).

Für die dict-Konfiguration muss auch ein Service definiert werden, vor allem der zugehörige Benutzer. Über diesen Service wird dann kommuniziert. Für jeden definierten Service findet sich dann eine entsprechende Datei in /var/run/dovecot/.

Der Parameter noenforcing in der Backend-Definition bewirkt, dass die Quotas berechnet werden, aber keine Sperrung erfolgt, Mails werden auch über die konfigurierte Grenze hinaus angenommen.

Aber Warnungen werden generiert, dazu dienen die Zeilen:

 quota_warning = storage=66%% quota-warning 66 %u
 quota_warning2 = storage=80%% quota-warning 80 %u
 quota_warning3 = storage=95%% quota-warning 95 %u

Beim Überschreiten der jeweiligen Grenzen bekommt der Benutzer eine entsprechende Meldung. Auch dies ist wieder über einen Service geregelt, nämlich quota-warning. Es wird dort das angegebene Script /usr/local/bin/quota-warning.sh mit den Parametern Auslastung in % und Postfach aufgerufen, also z.B.

/usr/local/bin/quota-warning.sh 80 test@netthelp.de

Mit dem direkten Aufruf kann man das Script auch testen.

dovecot-mysql.conf

# Database driver: mysql, pgsql
driver = mysql

# Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, and CRYPT.
default_pass_scheme = CRYPT

# Database options
#db_unix_socket = /var/lib/mysql/mysql.sock

connect = host=/var/run/mysql/mysql.sock dbname=postfix user=postfix password=assword

password_query = SELECT password FROM mailbox WHERE username = '%u' AND active = '1'

user_query = SELECT concat('maildir:/var/vmail/',maildir) as mail, \ 
                   303 AS uid, \ 
                   303 AS gid, \ 
                   CONCAT('*:bytes=', \
                    IF(mailbox.quota = 0, domain.maxquota*1024000, mailbox.quota)) \
                   as quota_rule \
            FROM mailbox, domain \
            WHERE username = '%u' AND mailbox.active = '1' AND \
                  domain.domain = '%d' AND domain.active = '1'

iterate_query = SELECT username as user FROM mailbox WHERE active ='1'

Gegenüber der ursprünglichen Version ist hier die user_query erweitert, so dass auch die Quotas abgefragt werden. Die Logik ist recht aufwendig gestaltet. Wenn für einen Benutzer keine Quotas angegeben sind, also der Wert auf 0 steht, dann wird der Wert maxquota aus der Domaintabelle benutzt.

Zusätzlich habe ich noch die iterate_query eingeführt, die benötigt man, wenn man z.B. mittels

doveadm quota recalc -u *@netthelp.de

die Belegung für alle Benutzer der angegebenen Domain erneut berechnen will, oder sie für alle Benutzer mittels

doveadm quota get -u *@netthelp.de

abfragen möchte.

Auch für die Neuberechnung aller Belegungen

doveadm quota recalc -A

oder deren Abfrage

doveadm quota get -A

braucht man die Iteration.

dovecot-dict-sql-user.conf

Diese Datei beschreibt welche Informationen Dovecot in welches Feld der Datenbank schreibt.

connect = host=/var/run/mysql/mysql.sock dbname=postfix user=postfix password=assword

map {
   pattern = priv/quota/storage
   table = quota2
   username_field = username
   value_field = bytes
}

map {
   pattern = priv/quota/messages
   table = quota2
   username_field = username
   value_field = messages
}

Die Daten kommen in die Tabelle quota2 und zwar der Speicherplatzbedarf in das Feld bytes und die Anzahl der Mail in das Feld messages, jeweils beim Benutzer username.

Hinweis: Hier gibt es eine große Gefahr. Dovecot löscht erst die Zeile für den Benutzer, bevor es sie neu einträgt. Wenn noch andere Daten in der gleichen Tabellenzeile stehen sind die damit eventuell verloren. Ich hatte eine entsprechendes Problem, als ich Domain-Quotas in die Tabelle domain eintragen lassen wollte. Bis ich das Problem erkannt hatte waren zwei Maildomains gelöscht.

Ich verzichte daher momentan auf die Nutzung von Domain-Quotas, obwohl man eventuell mit dem Parameter no-unset in der Backend-Definition dieses Verhalten unterbinden kann (ab Version 2.2.20 siehe http://wiki2.dovecot.org/Quota/Dict http://dovecot.2317879.n4.nabble.com/More-information-about-Dovecot-2-2-x-quota-mysql-and-dict-td54077.html http://code.metager.de/source/history/dovecot/2.2/)

/usr/local/bin/quota-warning.sh

Dieses Script erstellt die Warnmeldungen, wenn die angegebenen Auslastungswerte überschritten werden.

#!/bin/sh
PERCENT=$1
USER=$2
ADMIN="verwalter@domain.de"
FROM="postmaster@domain.de"

msg="From: $FROM
To: $USER
Bcc: $ADMIN
Subject: Quota-Warnung $PERCENT% 

Lieber Nutzer,
    
das Postfach $USER ist derzeit zu 

$PERCENT% gefuellt.

Bitte einige Mails loeschen.
       
Herzlichen Dank
Das Netthelp Mail-System."

echo -e "$msg" | /usr/sbin/sendmail -t -f $FROM "$USER"

exit 0

In vielen Beschreibungen findet man folgende Version für das Script:

#!/bin/sh
PERCENT=$1
USER=$2
cat << EOF | /usr/lib/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing"
From: postmaster@domain.com
Subject: quota warning
 
Your mailbox is now $PERCENT% full.
EOF

Das bewirkt auf alle Fälle, dass zusätzlich im Verzeichnis des Benutzers die Auslastungsdatei erzeugt wird. Meiner Ansicht nach sollte hier nicht ein Maildir-Quota benutzt werden (denkbar -d $USER -o "plugin/quota=dict:User quota::noenforcing:proxy::sqluserquota" analog zu http://www.tech-island.com/tutorials/webserver/mail ). Diese Regel soll auf alle Fälle die Zustellung der Warnmeldung ermöglichen. Da ich aber nur Warnungen unterhalb von 100% erzeuge sollte das kein Problem sein.

/srv/www/htdocs/postfixadmin/config.inc.php

Damit sich die Quota-Konfiguration mit Postfixadmin verwalten lässt müssen in der Konfigurationsdatei ein paar Einstellungen angepasst werden.

$CONF['maxquota'] = '10240';
$CONF['domain_quota_default'] = '20480';
$CONF['quota'] = 'YES';
$CONF['used_quotas'] = 'YES';
$CONF['domain_quota'] = 'YES'; 
$CONF['quota_multiplier'] = '1024000'; 
$CONF['new_quota_table'] = 'YES';


Ablehnung durch Postfix, wenn Postfach voll

Für einen Benutzer, der seine Quotas überzogen hat soll möglichst schon Postfix die Mail gar nicht erst annehmen. Sonst müssen später bei der fehlgeschlagenen Zustellung Fehlermeldungen generiert und verschickt werden. Damit Postfix von Dovecot erfährt, dass das Postfach voll ist muss man die Konfiguration um einen weiteren Service ergänzen.

service quota-status {
 executable = quota-status -p postfix
 inet_listener {
#    address = 127.0.0.1
   port = 12345
 }
 client_limit = 1
}

Die Portnummer hier kann man nahezu beliebig wählen, sie muss nur frei sein.

Nun muss man die Meldungen konfigurieren:

plugin {
 quota_status_success = DUNNO
 quota_status_nouser = DUNNO
 quota_status_overquota = "552 5.2.2 Mailbox is over quota / Mailbox ist voll"
}

Wenn der Benutzer über seinem Limit liegt, dann kommt eine Fehlermeldung zurück, ansonsten DUNNO, was soviel bedeutet wie "noch keine Entscheidung getroffen", dann muss Postfix anhand anderer Regeln ermitteln, ob die Mail angenommen wird.

Die Postfix-Konfiguration muss dann im Bereich

smtpd_recipient_restrictions =

erweitert werden um eine Zeile wie:

check_policy_service inet:127.0.0.1:12345,

Diesen Teil habe ich bisher noch nicht getestet, da ich momentan auch noch keine Mails ablehnen möchte.

Werkzeuge für Dovecot

doveadm who

zeigt an, wer mit welcher IP angemeldet ist

doveadm kick <user>

beendet die Verbindung des angegebenen Users.

doveadm search -u <user> mailbox Trash savedbefore 60d

Zeigt alle Mail im Ordner Trash, die vor mehr als 60 Tagen gespeichert wurden.

Vergleichsoperatoren können sein SAVEDBEFORE, SAVEDON, SAVEDSINCE , gültige Zeiteinheiten sind weeks (abbr: w), days (abbr: d), hours (abbr: h), mins (abbr: m) and secs (abbr: s).

doveadm expunge -u <user> mailbox Junk savedbefore 60d

Löscht alle Mail im Ordner Junk, die vor mehr als 60 Tagen gespeichert wurden.

Weitere Informationen unter http://wiki2.dovecot.org/Tools/Doveadm/SearchQuery und http://wiki2.dovecot.org/Tools/Doveadm/Search .

Statt für einen User kann man mit dem Parameter -A die Operationen auch für alle Nutzer ausführen. Das ist auch die Idee hinter folgendem kleinen Script:

#!/bin/bash
#
DOVEADM="/usr/bin/doveadm";

$DOVEADM expunge -A mailbox Trash savedbefore 90d
$DOVEADM expunge -A mailbox Junk  savedbefore 60d


doveadm mailbox list -u <user>

Zeigt die Imap-Ordner des angegebenen Benutzers:

Sent
Drafts
Trash
Junk
INBOX

Neue Ordner anlegen kann man mittels:

doveadm mailbox create -u <user> Papierkorb

Löschen kann man dann Ordner mittels:

doveadm mailbox delete -u <user> Papierkorb

Zu klären

Folgende Punkte sind aus meiner Sicht noch zu klären bzw. müssen ausprobiert werden:

  • Abgleich der Quota-Warnungen mit den Farbwechseln (Gelb-Rot) bei PostfixAdmin (anscheinend bei 45% und 75%)
  • Vermeiden des Löschens der MySQL-Domain bei Domainquotas mit dict
  • Ablehnung der Mails durch Postfix