Dovecot, Quotas - Speicherquoten für User und Maildomains
Wer kennt es nicht, das Dilemma mit dem Bewerten der eMails, nach wichtigen, sprich aufbewahrungswürdigen Nachrichten und unwichtigen Nachrichten, die gelöscht werden können. Werden viele Mailinglisten abboniert, wird die Sache nicht wesentlich einfacher.
Tja, und in den allermeisten Fällen ist man auf einem Multiusersystem, wie einem Mail-/IMAP-Server nicht alleine unterwegs, sondern teilt sich die Ressource mit vielen Usern. Da dieses Ressource, sprich die Speicherkapazität des IMAP-Servers i.d.R. physikalische Grenzen gesetzt sind, stellt sich die Frage, wie nun mit dieser Herausforderung umgehen.
Im Idealfall hat man neben einigen Powerusern, die weit über die Stränge schlagen, eine große Masse an Anwendern, die weit ab der imaginären Meßlatte liegen. Selbst bei einem System mit 2.700 Nutzern und zig Powerusern hält sich der durchschnittliche Datenverbrauch in Grenzen und das ganz ohne Quotaregelung!
Auf der anderen Seite habe ich da ein System mit nur knapp 400 Nutzer, die regelmäßig an die Speichergrenzen des verfügbaren Servers stoßen und das trotz großzügigster 5 GB pro Nutzer. Trotz regelmäßig einlaufender Klagen der Absender, die vergeblich versuchen ihre geschäftliche Korrespondenz einzuliefern, verweigert der Mailserverr die Annahme weiterer Nachrichten.
Der Einsatz einer Quotaregelung kann helfen, muss es aber nicht. Eine entsprechende Regelung sollte wohl durchdacht und mit den Nutzern abgestimmt werden, ansonsten ist man mitunter mehr mit Supportanfragen beschäftigt, als einem lieb und teuer ist.
Rahmenbedingungen
Beim Einsatz von Speicherquoten bei unserem Dovecot-Server können wir verschiedenste Speicherbereiche zusammenfassen; man bezeichnet diese Bereiche auch als Quota-Roots. So ist es möglich den Platzbedarf einer ganzen Maildomain zu überwachen. Da wir unsere Maildomains mit Postfixadmin verwalten, können wir bei der Definition einer Domain den zugewiesenen Speicherplatz dort auch definieren.
Auf der einen Seite können wir den Speicherbedarf des jeweiligen Postfachs eines Endbenutzers überwachen. Hier können wir für alle User einer Domain den gleichen Platzbedarf festlegen, oder natürlich auch unterschiedliche Speichergrößen definieren - für das gemeine Fußvolk weniger und für die VIPs entsprechend mehr. Auch diese Festlegung können wir mit hilfe von Postfixadmin treffen.
Mit der Definition des möglichen Speicherbedarfs ist es natürlich noch nicht getan. Unser Dovecot-Server muss natürlich auch den aktuellen und tatsächlichen Platzbedarf eines jeden Quota-Roots ermitteln und sich merken. Hierzu kommen die verschiedenen Quota-Backends zum Einsatz. Von den zur Verfügung stehenden Quota-Backends werden wir uns die beiden Verfahren maildir und dict kurz ansehen.
- maildir Im Homeverzeichnis legt der Dovecot-Server eine Datei Namens maildirsize an. In dieser Datei vermerkt der Dovecot-Server den tatsächlichen Speicherverbrauch, der sowohl beim Eingang und beim Löschen von Nachrichten aktualisiert wird und darüber hinaus noch in regelmäßigen Abständen.
- dict Man kann zu Verwaltung der Quotas natürlich auch auf sog. Dictionary zurückgreifen. In der Regel macht man das, wenn man auf ein SQL-Backend zurückgreifen will oder muss.
Je Verzeichnis in einem Namensraum auf dem Dovecot-Server können eigene Quota-Rules definiert werden, die festlegen, was genau dort gelten soll. So können einzelne Verzeichnisse ganz von der Bewertung ausgenommen werden, andere dagegen auch mit einem zusätzlichem mehr an Platz ausgestattet werden.
Mit Quota-Warnings erfolgt die Benachrichtigung an den Postfachinhaber bei Überschreitung der Quota-Rules.
Seit Version 2.2 von Dovecot kann mit der Definition von Quota-Grace festgelegt werden, ob und ggf. wie hoch das gesetzte Speicherlimit beim Speichern einer Nachricht überschritten werden kann.
Konfiguration
Quota-Limits
Die Definition der Quota-Limits erfolgt mit Hilfe der Quota-Rules in der Konfigurationsdatei /etc/dovecot/conf.d/90-quota.conf.
# vim /etc/dovecot/conf.d/90-quota.conf
... ## ## Quota limits ## # Quota limits are set using "quota_rule" parameters. To get per-user quota # limits, you can set/override them by returning "quota_rule" extra field # from userdb. It's also possible to give mailbox-specific limits, for example # to give additional 100 MB when saving to Trash: plugin { #quota_rule = *:storage=1G #quota_rule2 = Trash:storage=+100M # LDA/LMTP allows saving the last mail to bring user from under quota to # over quota, if the quota doesn't grow too high. Default is to allow as # long as quota will stay under 10% above the limit. Also allowed e.g. 10M. #quota_grace = 10%% # Django : 2014-08-06 # default: unset # jeder User bekommt im 1GB Mailstorage zugewiesen quota_rule = *:storage=100K # jeder User kann 10k = 10.240 Nachrichten in der INBOX liegen haben quota_rule2 = INBOX:messages=+10K # jeder User darf im Unterverzeichnis Trash zusätzlich 125 MB haben quota_rule3 = INBOX/Trash:storage=+125M # der Sent-Ordner wird bei der Bewertung ausgenommen quota_rule4 = INBOX/Sent:ignore # das Verzeichnis Order darf 25% overquota liegen quota_rule5 = INBOX/Archiv:storage=+25%% # # jeder user darf die Quota-Limitierung einmalig um 10% überschreiten quota_grace = 10%% }
Die einzelnen Konfigurationsparameter sind in dem Konfigurationsbeispiel ausreichend beschrieben.
Quota-Backend
Beim Backend verwenden wir das äußerst robuste und stabile Maildir-Backend. Dovecot wird dabei die Quota-Definition und die aktuellen Werte in eine Datei maildirsize im Userverzeichnis verwenden.
Die Auswahl des Quota-Backends erfolgt am Ende der Konfigurationsdatei /etc/dovecot/conf.d/90-quota.conf.
# vim /etc/dovecot/conf.d/90-quota.conf
## ## Quota backends ## # Multiple backends are supported: # dirsize: Find and sum all the files found from mail directory. # Extremely SLOW with Maildir. It'll eat your CPU and disk I/O. # dict: Keep quota stored in dictionary (eg. SQL) # maildir: Maildir++ quota # fs: Read-only support for filesystem quota plugin { #quota = dirsize:User quota #quota = maildir:User quota #quota = dict:User quota::proxy::quota #quota = fs:User quota # Django : 2014-08-06 # default: unset # Quota-Backend Maildir++ aktiviert # ignoreunlimited : Für Benutzer ohne einem Quota-Limit wird keine Quota-Berechnung durchgeführt # noenforcing : Es wird zwar eine Quota-Berechnung durchgeführt und protokolliert, bei einer # Überschreitung des Quota-Schwelle aber keine Sperrung vorgenommen. # ns=<namspace> : Quota-Berechnung und -Protokollierung nur für der Namespace "namespace" # vornehmen quota = maildir:User quota:ignoreunlimited:noenforcing }
Quota-Plugin
Da unsere Quota-Überprüfung nicht nur beim Eingang via LMTP, sondern auch beim Kopieren oder Verschieben durch den Endnutzer via POP3 oder IMAP, stattfinden soll, aktivieren wir das Plugin quota in der Konfigurationsdatei /etc/dovecot/conf.d/10-mail.conf. Hierzu ergänzen wir den Eintrag beim globalen Parameter mail_plugins.
# vim /etc/dovecot/conf.d/10-mail.conf
... # Space separated list of plugins to load for all services. Plugins specific to # IMAP, LDA, etc. are added to this list in their own .conf files. # Django : 2014-08-05 # default: #mail_plugins = mail_plugins = zlib # Django : 2014-08-06 - Quotaregelung aktiviert # last : mail_plugins = zlib mail_plugins = zlib quota ...
Somit ergibt sich folgendes komplettes Konfigurationsdatei 10-mail.conf.
- /etc/dovecot/conf.d/10-mail.conf
## ## Mailbox locations and namespaces ## # Location for users' mailboxes. The default is empty, which means that Dovecot # tries to find the mailboxes automatically. This won't work if the user # doesn't yet have any mail, so you should explicitly tell Dovecot the full # location. # # If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u) # isn't enough. You'll also need to tell Dovecot where the other mailboxes are # kept. This is called the "root mail directory", and it must be the first # path given in the mail_location setting. # # There are a few special variables you can use, eg.: # # %u - username # %n - user part in user@domain, same as %u if there's no domain # %d - domain part in user@domain, empty if there's no domain # %h - home directory # # See doc/wiki/Variables.txt for full list. Some examples: # # mail_location = maildir:~/Maildir # mail_location = mbox:~/mail:INBOX=/var/mail/%u # mail_location = mbox:/var/mail/%d/%1n/%n:INDEX=/var/indexes/%d/%1n/%n # # <doc/wiki/MailLocation.txt> # # Django : 2014-07-30 # default: unset # %d = domain, Domain-Part der eMailadresse # %n = username, alles was links vom Domainpart @domain.tld steht mail_location = maildir:/srv/vmail/%d/%n/Maildir # If you need to set multiple mailbox locations or want to change default # namespace settings, you can do it by defining namespace sections. # # You can have private, shared and public namespaces. Private namespaces # are for user's personal mails. Shared namespaces are for accessing other # users' mailboxes that have been shared. Public namespaces are for shared # mailboxes that are managed by sysadmin. If you create any shared or public # namespaces you'll typically want to enable ACL plugin also, otherwise all # users can access all the shared mailboxes, assuming they have permissions # on filesystem level to do so. namespace inbox { # Namespace type: private, shared or public # Django : 2014-07-30 # default: unset type = private # Hierarchy separator to use. You should use the same separator for all # namespaces or some clients get confused. '/' is usually a good one. # The default however depends on the underlying mail storage format. # Django : 2014-07-30 # default: unset separator = / # Prefix required to access this namespace. This needs to be different for # all namespaces. For example "Public/". # Django : 2014-07-30 # default: unset prefix = INBOX/ # Physical location of the mailbox. This is in same format as # mail_location, which is also the default for it. #location = # There can be only one INBOX, and this setting defines which namespace # has it. inbox = yes # If namespace is hidden, it's not advertised to clients via NAMESPACE # extension. You'll most likely also want to set list=no. This is mostly # useful when converting from another server with different namespaces which # you want to deprecate but still keep working. For example you can create # hidden namespaces with prefixes "~/mail/", "~%u/mail/" and "mail/". #hidden = no # Show the mailboxes under this namespace with LIST command. This makes the # namespace visible for clients that don't support NAMESPACE extension. # "children" value lists child mailboxes, but hides the namespace prefix. #list = yes # Namespace handles its own subscriptions. If set to "no", the parent # namespace handles them (empty prefix should always have this as "yes") # Django : 2014-07-30 # default: unset subscriptions = yes } # Example shared namespace configuration #namespace { #type = shared #separator = / # Mailboxes are visible under "shared/user@domain/" # %%n, %%d and %%u are expanded to the destination user. #prefix = shared/%%u/ # Mail location for other users' mailboxes. Note that %variables and ~/ # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the # destination user's data. #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u # Use the default namespace for saving subscriptions. #subscriptions = no # List the shared/ namespace only if there are visible shared mailboxes. #list = children #} # Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"? #mail_shared_explicit_inbox = no # System user and group used to access mails. If you use multiple, userdb # can override these by returning uid or gid fields. You can use either numbers # or names. <doc/wiki/UserIds.txt> #mail_uid = #mail_gid = # Group to enable temporarily for privileged operations. Currently this is # used only with INBOX when either its initial creation or dotlocking fails. # Typically this is set to "mail" to give access to /var/mail. #mail_privileged_group = # Grant access to these supplementary groups for mail processes. Typically # these are used to set up access to shared mailboxes. Note that it may be # dangerous to set these if users can create symlinks (e.g. if "mail" group is # set here, ln -s /var/mail ~/mail/var could allow a user to delete others' # mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). #mail_access_groups = # Allow full filesystem access to clients. There's no access checks other than # what the operating system does for the active UID/GID. It works with both # maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ # or ~user/. #mail_full_filesystem_access = no # Dictionary for key=value mailbox attributes. Currently used by URLAUTH, but # soon intended to be used by METADATA as well. #mail_attribute_dict = ## ## Mail processes ## # Don't use mmap() at all. This is required if you store indexes to shared # filesystems (NFS or clustered filesystem). #mmap_disable = no # Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL # since version 3, so this should be safe to use nowadays by default. #dotlock_use_excl = yes # When to use fsync() or fdatasync() calls: # optimized (default): Whenever necessary to avoid losing important data # always: Useful with e.g. NFS when write()s are delayed # never: Never use it (best performance, but crashes can lose data) #mail_fsync = optimized # Locking method for index files. Alternatives are fcntl, flock and dotlock. # Dotlocking uses some tricks which may create more disk I/O than other locking # methods. NFS users: flock doesn't work, remember to change mmap_disable. #lock_method = fcntl # Directory in which LDA/LMTP temporarily stores incoming mails >128 kB. #mail_temp_dir = /tmp # Valid UID range for users, defaults to 500 and above. This is mostly # to make sure that users can't log in as daemons or other system users. # Note that denying root logins is hardcoded to dovecot binary and can't # be done even if first_valid_uid is set to 0. #first_valid_uid = 500 #last_valid_uid = 0 # Valid GID range for users, defaults to non-root/wheel. Users having # non-valid GID as primary group ID aren't allowed to log in. If user # belongs to supplementary groups with non-valid GIDs, those groups are # not set. #first_valid_gid = 1 #last_valid_gid = 0 # Maximum allowed length for mail keyword name. It's only forced when trying # to create new keywords. #mail_max_keyword_length = 50 # ':' separated list of directories under which chrooting is allowed for mail # processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). # This setting doesn't affect login_chroot, mail_chroot or auth chroot # settings. If this setting is empty, "/./" in home dirs are ignored. # WARNING: Never add directories here which local users can modify, that # may lead to root exploit. Usually this should be done only if you don't # allow shell access for users. <doc/wiki/Chrooting.txt> #valid_chroot_dirs = # Default chroot directory for mail processes. This can be overridden for # specific users in user database by giving /./ in user's home directory # (eg. /home/./user chroots into /home). Note that usually there is no real # need to do chrooting, Dovecot doesn't allow users to access files outside # their mail directory anyway. If your home directories are prefixed with # the chroot directory, append "/." to mail_chroot. <doc/wiki/Chrooting.txt> #mail_chroot = # UNIX socket path to master authentication server to find users. # This is used by imap (for shared users) and lda. #auth_socket_path = /var/run/dovecot/auth-userdb # Directory where to look up mail plugins. # Django : 2014-07-30 # Auswahl des Verzeichnisses mit dem Mail-Plugins # default: #mail_plugin_dir = /usr/lib/dovecot mail_plugin_dir = /usr/lib64/dovecot # Space separated list of plugins to load for all services. Plugins specific to # IMAP, LDA, etc. are added to this list in their own .conf files. # Django : 2014-08-05 # default: #mail_plugins = mail_plugins = zlib # Django : 2014-08-06 - Quotaregelung aktiviert # last : mail_plugins = zlib mail_plugins = zlib quota ## ## Mailbox handling optimizations ## # Mailbox list indexes can be used to optimize IMAP STATUS commands. They are # also required for IMAP NOTIFY extension to be enabled. #mailbox_list_index = no # The minimum number of mails in a mailbox before updates are done to cache # file. This allows optimizing Dovecot's behavior to do less disk writes at # the cost of more disk reads. #mail_cache_min_mail_count = 0 # When IDLE command is running, mailbox is checked once in a while to see if # there are any new mails or other changes. This setting defines the minimum # time to wait between those checks. Dovecot can also use dnotify, inotify and # kqueue to find out immediately when changes occur. #mailbox_idle_check_interval = 30 secs # Save mails with CR+LF instead of plain LF. This makes sending those mails # take less CPU, especially with sendfile() syscall with Linux and FreeBSD. # But it also creates a bit more disk I/O which may just make it slower. # Also note that if other software reads the mboxes/maildirs, they may handle # the extra CRs wrong and cause problems. #mail_save_crlf = no # Max number of mails to keep open and prefetch to memory. This only works with # some mailbox formats and/or operating systems. #mail_prefetch_count = 0 # How often to scan for stale temporary files and delete them (0 = never). # These should exist only after Dovecot dies in the middle of saving mails. #mail_temp_scan_interval = 1w ## ## Maildir-specific settings ## # By default LIST command returns all entries in maildir beginning with a dot. # Enabling this option makes Dovecot return only entries which are directories. # This is done by stat()ing each entry, so it causes more disk I/O. # (For systems setting struct dirent->d_type, this check is free and it's # done always regardless of this setting) #maildir_stat_dirs = no # When copying a message, do it with hard links whenever possible. This makes # the performance much better, and it's unlikely to have any side effects. #maildir_copy_with_hardlinks = yes # Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only # when its mtime changes unexpectedly or when we can't find the mail otherwise. #maildir_very_dirty_syncs = no # If enabled, Dovecot doesn't use the S=<size> in the Maildir filenames for # getting the mail's physical size, except when recalculating Maildir++ quota. # This can be useful in systems where a lot of the Maildir filenames have a # broken size. The performance hit for enabling this is very small. #maildir_broken_filename_sizes = no # Always move mails from new/ directory to cur/, even when the \Recent flags # aren't being reset. #maildir_empty_new = no ## ## mbox-specific settings ## # Which locking methods to use for locking mbox. There are four available: # dotlock: Create <mailbox>.lock file. This is the oldest and most NFS-safe # solution. If you want to use /var/mail/ like directory, the users # will need write access to that directory. # dotlock_try: Same as dotlock, but if it fails because of permissions or # because there isn't enough disk space, just skip it. # fcntl : Use this if possible. Works with NFS too if lockd is used. # flock : May not exist in all systems. Doesn't work with NFS. # lockf : May not exist in all systems. Doesn't work with NFS. # # You can use multiple locking methods; if you do the order they're declared # in is important to avoid deadlocks if other MTAs/MUAs are using multiple # locking methods as well. Some operating systems don't allow using some of # them simultaneously. #mbox_read_locks = fcntl #mbox_write_locks = dotlock fcntl mbox_write_locks = fcntl # Maximum time to wait for lock (all of them) before aborting. #mbox_lock_timeout = 5 mins # If dotlock exists but the mailbox isn't modified in any way, override the # lock file after this much time. #mbox_dotlock_change_timeout = 2 mins # When mbox changes unexpectedly we have to fully read it to find out what # changed. If the mbox is large this can take a long time. Since the change # is usually just a newly appended mail, it'd be faster to simply read the # new mails. If this setting is enabled, Dovecot does this but still safely # fallbacks to re-reading the whole mbox file whenever something in mbox isn't # how it's expected to be. The only real downside to this setting is that if # some other MUA changes message flags, Dovecot doesn't notice it immediately. # Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK # commands. #mbox_dirty_syncs = yes # Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE, # EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored. #mbox_very_dirty_syncs = no # Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK # commands and when closing the mailbox). This is especially useful for POP3 # where clients often delete all mails. The downside is that our changes # aren't immediately visible to other MUAs. #mbox_lazy_writes = yes # If mbox size is smaller than this (e.g. 100k), don't write index files. # If an index file already exists it's still read, just not updated. #mbox_min_index_size = 0 # Mail header selection algorithm to use for MD5 POP3 UIDLs when # pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired # algorithm, but it fails if the first Received: header isn't unique in all # mails. An alternative algorithm is "all" that selects all headers. #mbox_md5 = apop3d ## ## mdbox-specific settings ## # Maximum dbox file size until it's rotated. #mdbox_rotate_size = 2M # Maximum dbox file age until it's rotated. Typically in days. Day begins # from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled. #mdbox_rotate_interval = 0 # When creating new mdbox files, immediately preallocate their size to # mdbox_rotate_size. This setting currently works only in Linux with some # filesystems (ext4, xfs). #mdbox_preallocate_space = no ## ## Mail attachments ## # sdbox and mdbox support saving mail attachments to external files, which # also allows single instance storage for them. Other backends don't support # this for now. # Directory root where to store mail attachments. Disabled, if empty. #mail_attachment_dir = # Attachments smaller than this aren't saved externally. It's also possible to # write a plugin to disable saving specific attachments externally. #mail_attachment_min_size = 128k # Filesystem backend to use for saving attachments: # posix : No SiS done by Dovecot (but this might help FS's own deduplication) # sis posix : SiS with immediate byte-by-byte comparison during saving # sis-queue posix : SiS with delayed comparison and deduplication #mail_attachment_fs = sis posix # Hash format to use in attachment filenames. You can add any text and # variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}. # Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits #mail_attachment_hash = %{sha1}
Quota-Warnung via eMail
Zur Information der Endnutzer bei Überschreiten der Quota-Warnschwellen, kann der Service quota-warning verwendet werden. Bei Überschreitung der definierten Warnschwellen wird einmalig der Service quota-warning mit dem definierten Wert für die Warnschwellen und dem Usernamen angesprochen. quota-warning startet ein Shell-Script mit den Ausführungsrechten unseres virtuellen Users vmail, damit das scripot in die jeweiligen Postfächer der Benutzer schreiben kann.
Die Definition der Warnschwellen quota_warning und des Service quota-warning erfolgt über die Konfigurationsdatei /etc/dovecot/conf.d/90-quota.conf
# vim /etc/dovecot/conf.d/90-quota.conf
... # You can execute a given command when user exceeds a specified quota limit. # Each quota root has separate limits. Only the command for the first # exceeded limit is excecuted, so put the highest limit first. # The commands are executed via script service by connecting to the named # UNIX socket (quota-warning below). # Note that % needs to be escaped as %%, otherwise "% " expands to empty. plugin { #quota_warning = storage=95%% quota-warning 95 %u #quota_warning2 = storage=80%% quota-warning 80 %u # Django : 2014-08-06 # default: unset # Quotawarnings aktiviert quota_warning = storage=100%% quota-warning 100 %u quota_warning2 = storage=95%% quota-warning 95 %u quota_warning3 = storage=80%% quota-warning 80 %u } # Example quota-warning service. The unix listener's permissions should be # set in a way that mail processes can connect to it. Below example assumes # that mail processes run as vmail user. If you use mode=0666, all system users # can generate quota warnings to anyone. # Django : 2014-08-06 # default: unset service quota-warning { executable = script /usr/local/bin/quota-warning.sh user = vmail unix_listener quota-warning { user = vmail } } ...
Beim Shell-Script orientieren wir uns an dem Musterbeispiel aus dem Dovecot Wiki.
# vim /usr/local/bin/quota-warning.sh
- /usr/local/bin/quota-warning.sh
#!/bin/sh PERCENT=$1 USER=$2 cat << EOF | /usr/libexec/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing" From: postmaster@nausch.org To: $USER Date: `date +"%a, %d %b %Y %H:%M:%S %z"` Subject: Quota Warnung zu Ihrem eMailkonto! Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit Ihr Postkorb $USER ist aktuell zu $PERCENT% gefüllt. Bitte archivieren oder löschen Sie alte, nicht mehr benötige Nachrichten. Andernfalls könnte es vorkommen, dass Sie keine neuen Nachrichten mehr empfangen könnten! Ihr Postmaster! EOF
Damit der Service quota-warning das Shell-Script als User vmail starten kann, setzen wir noch die Datei-(ausführungs)-Rechte des Scripts.
# chmod 700 /usr/local/bin/quota-warning.sh
# chown vmail:vmail /usr/local/bin/quota-warning.sh
Quota-Informationen via IMAP
Den Konfigurationsparameter imap_quota haben wir bereits in der Konfigurationsdatei /etc/dovecot/conf.d/20-imap.conf beim Protokoll imap gesetzt.
# vim /etc/dovecot/conf.d/20-imap.conf
... protocol imap { # Space separated list of plugins to load (default is global mail_plugins). #mail_plugins = $mail_plugins # Django : 2014-08-06 Quota-Informationen via IMAP zur Verfügung stellen mail_plugins = $mail_plugins imap_quota # Maximum number of IMAP connections allowed for a user from each IP address. # NOTE: The username is compared case-sensitively. #mail_max_userip_connections = 10 } ...
Somit werden dem Client während seiner Verbindung zu unserem IMAP-Server Informationen bei Quota-Überschreitungen zur Verfügung gestellt.
Diese Meldung können wir noch individualisieren, um so z.B. auf eine Hilfeseite zu verweisen, wo der Endanwender nähere Informationen zu der Meldung und dem Thema Quota finden kann.
# vim /etc/dovecot/conf.d/90-quota.conf
... # Django : 2014-08-07 # default: quota_exceeded_message = # Individuelle Over-Quota-Nachricht mit link zum Wiki mit weiteren Informationen, die # der Client während der IMAP-Sitzung bei Überschreitung der gesetzten Schwellen erhält. plugin { quota_exceeded_message = Quota überschritten, weitere Infos finden Sie hier: http://wiki.mailserver.guru/doku.php/centos:mail_c7:dovecot_8 } ...
Zum Aktivieren der individualisierten Meldung muss noch ein Reload des Dovecot-Daemon durchgeführt werden.
# systemctl reload dovecot
90-quota.conf Gesamtkonfigurationsdatei
In Summe ergibt sich also folgende Gesamtkonfigurationsdatei unserer Quota-Einstellungen.
# vim /etc/dovecot/conf.d/90-quota.conf
- /etc/dovecot/conf.d/90-quota.conf
## ## Quota configuration. ## # Note that you also have to enable quota plugin in mail_plugins setting. # <doc/wiki/Quota.txt> ## ## Quota limits ## # Quota limits are set using "quota_rule" parameters. To get per-user quota # limits, you can set/override them by returning "quota_rule" extra field # from userdb. It's also possible to give mailbox-specific limits, for example # to give additional 100 MB when saving to Trash: plugin { #quota_rule = *:storage=1G #quota_rule2 = Trash:storage=+100M # LDA/LMTP allows saving the last mail to bring user from under quota to # over quota, if the quota doesn't grow too high. Default is to allow as # long as quota will stay under 10% above the limit. Also allowed e.g. 10M. #quota_grace = 10%% # Django : 2014-08-06 # default: unset # jeder User bekommt im 1GB Mailstorage zugewiesen quota_rule = *:storage=100K # jeder User kann 10k = 10.240 Nachrichten in der INBOX liegen haben quota_rule2 = INBOX:messages=+10K # jeder User darf im Unterverzeichnis Trash zusätzlich 125 MB haben quota_rule3 = INBOX/Trash:storage=+125M # der Sent-Ordner wird bei der Bewertung ausgenommen quota_rule4 = INBOX/Sent:ignore # das Verzeichnis Order darf 25% overquota liegen quota_rule5 = INBOX/Archiv:storage=+25%% # # jeder user darf die Quota-Limitierung einmalig um 10% überschreiten quota_grace = 10%% } ## ## Quota warnings ## # You can execute a given command when user exceeds a specified quota limit. # Each quota root has separate limits. Only the command for the first # exceeded limit is excecuted, so put the highest limit first. # The commands are executed via script service by connecting to the named # UNIX socket (quota-warning below). # Note that % needs to be escaped as %%, otherwise "% " expands to empty. plugin { #quota_warning = storage=95%% quota-warning 95 %u #quota_warning2 = storage=80%% quota-warning 80 %u # Django : 2014-08-06 # default: unset # Quotawarnings aktiviert quota_warning = storage=100%% quota-warning 100 %u quota_warning2 = storage=95%% quota-warning 95 %u quota_warning3 = storage=80%% quota-warning 80 %u } # Example quota-warning service. The unix listener's permissions should be # set in a way that mail processes can connect to it. Below example assumes # that mail processes run as vmail user. If you use mode=0666, all system users # can generate quota warnings to anyone. # Django : 2014-08-06 # default: unset service quota-warning { executable = script /usr/local/bin/quota-warning.sh user = vmail unix_listener quota-warning { user = vmail } } # Django : 2014-08-07 # default: quota_exceeded_message = # Individuelle Over-Quota-Nachricht mit link zum Wiki mit weiteren Informationen, die # der Client während der IMAP-Sitzung bei Überschreitung der gesetzten Schwellen erhält. plugin { quota_exceeded_message = Quota überschritten, weitere Infos finden Sie hier: http://wiki.mailserver.guru/doku.php/centos:mail_c7:dovecot_8 } ## ## Quota backends ## # Multiple backends are supported: # dirsize: Find and sum all the files found from mail directory. # Extremely SLOW with Maildir. It'll eat your CPU and disk I/O. # dict: Keep quota stored in dictionary (eg. SQL) # maildir: Maildir++ quota # fs: Read-only support for filesystem quota plugin { #quota = dirsize:User quota #quota = maildir:User quota #quota = dict:User quota::proxy::quota #quota = fs:User quota # Django : 2014-08-06 # default: unset # Quota-Backend Maildir++ aktiviert # ignoreunlimited : Für Benutzer ohne einem Quota-Limit wird keine Quota-Berechnung durchgeführt # noenforcing : Es wird zwar eine Quota-Berechnung durchgeführt und protokolliert, bei einer # Überschreitung des Quota-Schwelle aber keine Sperrung vorgenommen. # ns=<namspace> : Quota-Berechnung und -Protokollierung nur für der Namespace "namespace" # vornehmen quota = maildir:User quota:ignoreunlimited:noenforcing } # Multiple quota roots are also possible, for example this gives each user # their own 100MB quota and one shared 1GB quota within the domain: plugin { #quota = dict:user::proxy::quota #quota2 = dict:domain:%d:proxy::quota_domain #quota_rule = *:storage=102400 #quota2_rule = *:storage=1048576 }
Individuelle Quotas
In den seltensten Fällen werden wir nicht alle Mailbox-Inhalber über einen Kamm scheren können, wenn es um Speicherkontingenzte geht. So werden diejenigen, die mehr bezahlen mehr Speicherplatz zur Verfügung gestellt bekommen. Auch ist klar dass der BOfH1) unlimitierten Platz bekommt, wie der Datenmessie eben weniger.
Abhängig von unserem Authentifizierungs-Mechanismus können wir auch benutzerspezifische Quotas setzen.
passwd
Nutzen wir die passwd-Datei /etc/dovecot/users nutzen wir die UserDB Extra Fields um die Benutzer individuellen Quotas festzulegen. Haben wir bereits Definitionen bei den UserDB Extra Fields trennen wir unsere Quota-Definition mit einem Leerzeichen " " ab.
# vim /etc/dovecot/users
- /etc/dovecot/users
# Django : 2014-07-30 # Musterbeispiel eines Eintrages für den passdb-file Authentifizierungsmechanismus # # Beispiel für einen Nutzer "django@nausch.org" und dem (Klartext)Passwort "Dj4n90_d3r_G33k!" : # django@nausch.org:{PLAIN}Dj4n90_d3r_G33k!:10000:10000::/srv/vmail/nausch.org/django::userdb_quota_rule=*:storage=42G
SQL
Bei Nutzung eines MySQL-Backends zur Authentifikation können wir die Quota-Definition der eMail-Konten aus der Userverwaltung von postfixadmin verwenden. Die SQL-Statements aus der auth-sql.conf.ext passen wir, falls wir das noch nicht bei der Erstkonfiguration erledigt haben an.
user_query = SELECT CONCAT('/var/spool/mail/vmail/', maildir) AS home, 10000 AS uid, 10000 AS gid, \ CONCAT('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active='1';
password_query = SELECT username AS user, password, 10000 AS userdb_uid, 10000 AS userdb_gid, \ CONCAT('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' \ AND active = '1';
Zum Testen, ob unser SQL-Statement das gewünschte Ergebnis liefert, verbinden wir uns von unserem Dovecot-Host aus mit unserer Datenbank. Die Eingaben am Client sind in der Farbe blau und die Rückmeldungen des MySQL-Servers in der Farbe hellgrau gekennzeichnet.
$ mysql -h mysql.dmz.nausch.org -D postfix -u dovecot_user -p
Enter password:D45_157_5uP3r57r3n9_g3h31m!
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 58334
Server version: 5.1.73 Source distribution
Copyright (c) 2000, 2014, Oracle, Monty Program Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [postfix]> SELECT CONCAT('/var/spool/mail/vmail/', maildir) AS home, 10000 AS uid, 10000 AS gid, CONCAT('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = 'django@nausch.org' AND active='1';
+-------------------------------+-------+-------+--------------------+
| home | uid | gid | quota_rule |
+-------------------------------+-------+-------+--------------------+
| /srv/vmail/nausch.org/django/ | 10000 | 10000 | *:bytes=5120000000 |
+-------------------------------+-------+-------+--------------------+
1 row in set (0.00 sec)
MySQL [postfix]> SELECT username AS user, password, 10000 AS userdb_uid, 10000 AS userdb_gid, CONCAT('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = 'django@nausch.org' AND active = '1';
+-------------------+------------------+------------+------------+--------------------+
| user | password | userdb_uid | userdb_gid | quota_rule |
+-------------------+------------------+------------+------------+--------------------+
| django@nausch.org | Dj4n90_d3r_G33k! | 10000 | 10000 | *:bytes=5120000000 |
+-------------------+------------------+------------+------------+--------------------+
1 row in set (0.00 sec)
MySQL [postfix]> quit;
Bye
Quotas und doveadm
Neuberechnung mit recalc
Hat man größere Migrationsarbeiten an einem Dovecot-Server hinter sich, oder musste auf ein Backup mehrerer User oder Maildomänen zurückgreifen, kann es notwendig und hilfreich sein, die Berechnung der Quotas neu anzustoßen. Hierzu benutzt man die option quota beim Befehl doveadm. Die Quota-Neuberechnung des User-Kontos django@nausch.org erreicht man mit folgendem Aufruf.
# doveadm quota recalc -u django@nausch.org
Die Neuberechung aller Nutzrkonten würde dann mit folgendem Aufruf erfolgen:
# doveadm quota recalc -A
Abfrage mit get
Genau so, wie wir die Neuberechnung eines einzelnen Nutzerkontos anstossen können, ist auch die Abfrage der Quota-Daten eines Nutzers möglich.
# doveadm quota get -u django@nausch.org
Quota name Type Value Limit % User quota STORAGE 117 500 23 User quota MESSAGE 4 - 0
Einen Überblick über alle Nutzerkonten erhält man mit der Option -A.
# doveadm quota get -A
Username Quota name Type Value Limit % django@nausch.org User quota STORAGE 117 500 23 django@nausch.org User quota MESSAGE 4 - 0 ...
Quota-Status für den Frontend-Mailserver (Postfix-SMTP-Server)
Unser Prontend-Mailserver nimmt eMails entgegen, sofern der postfix-Mailserver sich für die Maildomain zuständig, ein gültiges Userpostfach vorhanden sowie die ganzen SPAM- und AV2)-Maßnahmen überstanden wurden. Wird das Frontend-System anschließend die Post nicht beim Backendsystem los, bleibt ihm nichts anderes übrig, als die Nachricht zum Absender zurückzuschicken - ein sog. late-bounce. War die Absender-Adresse gefälscht, würde dieser Bounce zurück an die Adresse eines unbeteiligten Dritten geschickt - BACKSCATTER! Sowas kann natürlich bei entsprechener Masse sehr schnell unangenehm für den betreiber des Mailservers werden und ist daher dringendst zu vermeiden.
Dovecot kann Quota-Stati unserem Frontend-Mailserver Postfix über einen Policy-Server zur Verfügung stellen.
Dovecot Konfiguration
Die Konfiguration des Quota-Policy-Daemon nehmen wir am Ende der Konfigurationsdatei /etc/dovecot/conf.d/90-quota.conf vor.
# vim /etc/dovecot/conf.d/90-quota.conf
... ## ## Quota Policy Daemon ## # Django : 2014-08-07 # Policy-Daemon für Postfix Frontend-Mailserver (MTA) # Kapitel 11.11 "Der Quota-Policy-Server für Postfix" # Dovecotbuch (ISBN 978-3-95539-74-7) Seite 219 ff. # service quota-status { executable = quota-status -p postfix inet_listener { address = 10.0.0.70 port = 10000 } client_limit = 1 } # # quota_status_success : Ist das Speicher noch möglich, nichts machen "DUNNO" # quota_status_nouser : Ist der Nutzer unbekannt, auch nichts machen "DUNNO". Ein anderes Backend # könnte dafür ev. zuständig sein. # quota_status_overquota : Errorcode und Fehlermeldung, die bei Erreichen der gesetzte Quota an den # Frontend-Mailserver (MTA) Postfix zurückgemeldet wird. plugin { quota_status_success = DUNNO quota_status_nouser = DUNNO quota_status_overquota = "552 5.2.2 Mailbox is over quota / Mailbox ist voll" }
Zur Aktivierung der Konfigurationsänderung führen wir einen Reload unseres Dovecot-Servers durch.
# systemctl reload dovecot
Überprüfen wir nun die offenen Ports unseres Dovecot-Servers, sehen wir auch den Port 10000 unseres Quota-Status-Policy-Daemon.
# netstat -tulpen | grep dovecot
tcp 0 0 10.0.0.70:4190 0.0.0.0:* LISTEN 0 97758 10151/dovecot tcp 0 0 10.0.0.70:993 0.0.0.0:* LISTEN 0 97821 10151/dovecot tcp 0 0 10.0.0.70:995 0.0.0.0:* LISTEN 0 97784 10151/dovecot tcp 0 0 10.0.0.70:110 0.0.0.0:* LISTEN 0 97783 10151/dovecot tcp 0 0 10.0.0.70:143 0.0.0.0:* LISTEN 0 97820 10151/dovecot tcp 0 0 10.0.0.70:10000 0.0.0.0:* LISTEN 0 98545 10151/dovecot tcp 0 0 10.0.0.70:24 0.0.0.0:* LISTEN 0 97793 10151/dovecot
Paketfilter Konfiguration
Damit nun nicht jeder fremde Host sich mit dem Port 10000 verbinden kann, regeln wir den Zugriff über eine Firewall-Regel so, dass nur der vorgeschaltete MTA Postfix-Mailserver sich mit unserem Dovecot-Server auf Port 10000 verbinden kann.
Unter CentOS 7 wird als Standard-Firewall die dynamische firewalld verwendet. Ein großer Vorteil der dynamischen Paketfilterregeln ist unter anderem, dass zur Aktivierung der neuen Firewall-Regel(n) nicht der Daemon durchgestartet werden muss und somit alle aktiven Verbiundungen kurz getrennt werden. Sondern unsere Änderungen können on-the-fly aktiviert oder auch wieder deaktiviert werden.
In unserem Konfigurationsbeispiel hat unser Postfix-Server die IP-Adresse 10.0.0.80 und unser Dovecot-Server die 10.0.0.70. Wir brauchen also eine Firewall-Definition, die ausschließlich Verbindungen von der Source-IP 10.0.0.80 auf die Destination-IP 10.0.0.70 auf Port 10000 gestattet.
Mit Hilfe des Programms firewall-cmd legen wir nun eine permanente Regel in der Zone public, dies entspricht in unserem Beispiel das Netzwerk-Interface eth0 mit der IP 10.0.0.70 an. Als Source-IP geben wir die IP-Adresse unseres Postfix-Servers also die 10.0.0.80 an. Genug der Vorrede, mit nachfolgendem Befehl wird diese restriktive Regel angelegt.
# firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="10.0.0.80/32" port protocol="tcp" port="10000" destination address="10.0.0.70/32" accept"
Zum Aktivieren brauchen wir nun nur einen reload des Firewall-Daemon vornehmen.
# firewall-cmd --reload
Fragen wir nun den Regelsatz unserer iptables-basieten Firewall ab, finden wir in der Chain IN_public_allow unsere aktive Regel.
# iptables -nvL IN_public_allow
Chain IN_public_allow (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- * * 10.0.0.80 10.0.0.70 tcp dpt:10000 ctstate NEW
0 0 ACCEPT tcp -- * * 10.0.0.80 10.0.0.70 tcp dpt:24 ctstate NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:110 ctstate NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:143 ctstate NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:4190 ctstate NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:993 ctstate NEW
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:995 ctstate NEW
Postfix Konfiguration
Wie schon angesprochen, wollen wir den Dovecot-Policy-Daemon erst ansprechen, wenn alle Vorprüfungen in Sachen Antivirus und SPAM erfolgt sind und bevor die Nachricht vom Postfix-Server mit einem permit angenommen wird.
Bei der Installation und Grundabsicherung von Postfix haben wir uns bereits ein umfangreiches Restriction-Regelwerk angelegt. Dort tragen wir nun vor dem abschließendem permit den Quota-Status-Check am Dovecot Backend-System ein.
# vim /etc/postfix/main.cf
... # # Django : 2012-02-06 # Schutz durch Restrictions für unseren Postfix-Mailserver # smtpd_recipient_restrictions = # Postmaster, abuse und andere aufgaben- oder funktionsgebundene E-Mail-Adressen (Role-Accounts) whitelisten check_recipient_access btree:/etc/postfix/access_recipient-rfc, # Black- und Whitelisting (Kapitel 8.2.3 White- und Blacklisting) check_client_access cidr:/etc/postfix/access_client, check_helo_access btree:/etc/postfix/access_helo, check_sender_access btree:/etc/postfix/access_sender, check_recipient_access btree:/etc/postfix/access_recipient, # Unsauberer eMails nicht annehmen (Kapitel 8.2.4 Anforderungen an Mailadressen) reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, # Unsere eigenen Nutzer zulassen-/erlauben (Kapitel 8.2.2 Relaying erlauben und verbieten) permit_sasl_authenticated, permit_mynetworks, # RBL überprüfen (Kapitel 10.11 Realtime Blackhole Lists) reject_rbl_client zen.spamhaus.org, reject_rbl_client ix.dnsbl.manitu.net, reject_rbl_client bl.spamcop.net, reject_rhsbl_client multi.uribl.com, # Greylisting via postgrey checken via Unix-Socket (Kapitel 9.2.5 postgrey installieren) check_policy_service unix:postgrey/socket, # Policyd-Weight check over TCP-Connection (Kapitel 9.3 policyd-weight installieren) check_client_access btree:/etc/postfix/policyd_weight_client_whitelist, check_policy_service inet:127.0.0.1:12525, # Dynamische Prüfung auf existente Relay-Empfänger (Kapitel 12.2.2 Dynamische Empfänger-Verifizierung) reject_unverified_recipient, # Backupserver (MX) erlauben permit_mx_backup, # alles andere an relaying verbieten (Kapitel 8.2.2 Relaying erlauben und verbieten) reject_unauth_destination, # Quota-Status-Policy-Daemon am Dovecot-Backend-System # Dovecotbuch (ISBN 978-3-95539-74-7) Seite 219 ff. (Kapitel 11.11 "Der Quota-Policy-Server für Postfix") check_policy_service inet:10.0.0.70:10000, # Zu guter Letzt alles durchlassen, was bis jetzt noch nicht beanstandet wurde permit ...
Zum Aktivieren der Konfigurationsänderung starten wir den Postfix-Mailserver einmal durch.
# systemctl restart postfix
Mailclient auf Benutzerseite
Mailclients haben bisweilen sehr unterschiedliche Arten, die Informationen aus dem imap_quota Plugin darzustellen.
kMail
kMail stelle die Informationen des imap_quota-Plugins zur Verfügung, wenn der Mauszeiger über den betreffenden Mailkonto plaziert wird.
Wir die Quota überschritten wir zusätzlich der Ordner-Name mit der Schriftfarbe rot dargestellt.
Thunderbird
Im Gegensatz zum Mailclient kMail, stellt Thunderbird keine Informationen zur Quota-Auslastung in der Statuszeile an, wenn der Schwellwert noch nicht errreicht wurde.
Ist die Quota-Informationsgrenze noch nicht erreicht, wird in der Statusleiste keine Information zum aktuellen Stand der Quota zur Verfügung gestellt.
Erst bei erreichen der Schwelle wir eine Information angezeigt.
Abhilfe schafft da das Display-Quota Plugin.
Wird es installiert, werden immer aktuelle Quota-Status-Informationen in der Startleiste angezeigt.
Entsprechend ist hier die Anzeige bei Überschreitung der Quota-Einstellungen.
Horde Framework/Webmail
Auch die Nutzer des Webmailers IMP unserer Horde-Framework-Installation kommen natürlich in den Genuß der Quota-Anzeige. Wir müssen diese lediglich in der Konfig aktivieren. Hierzu öffnen wir die backends.local.php und tragen nachfolgende Zeilen nach.
# vim /var/www/horde/imp/config/backends.local.php
... $servers['advanced'] = array( // Disabled by default // Django : 2013-02-01 'disabled' => false, 'name' => 'mein Advanced Dovecot-IMAP Server', 'hostspec' => '10.0.0.70', 'hordeauth' => 'full', 'protocol' => 'imap', 'port' => 143, 'secure' => 'tls', 'maildomain' => 'nausch.org', 'smtp' => array( 'localhost' => 'vml000090.dmz.nausch.org', 'host' => 'vml000080.dmz.nausch.org', 'port' => 25, ), // Django : 2014-08-11 'quota' => array( 'driver' => 'imap', 'params' => array( 'hide_when_unlimited' => false, 'unit' => 'MB', 'format' => array('short' => 'Quota Status: %.0f%% bei einer Belegung von %.0f %s') ) ), // 'acl' => true, 'cache' => false, ); ...
Sobald wir uns bei unserem Horde-Webmailer anmelden und die Applikation IMP auswählen, wird der aktuelle Status zur Quota-Belegung in der Statuszeile angezeigt.