Compare commits
5 commits
137565ca70
...
510be4f4be
| Author | SHA1 | Date | |
|---|---|---|---|
| 510be4f4be | |||
| 534ee6a1d2 | |||
| 804c311b02 | |||
| c8d9f03587 | |||
| 4d38ab8f1b |
3 changed files with 15 additions and 314 deletions
|
|
@ -5,8 +5,11 @@
|
|||
#
|
||||
# License: Creative Commons BY-NC-SA license
|
||||
#
|
||||
# Version 0.1
|
||||
# 2025-12-14
|
||||
# Version 0.1 / 2025-12-14
|
||||
# First release.
|
||||
#
|
||||
# Version 0.2 / 2025-12-28
|
||||
# Enable plugins (managesieve, password) in Roundcube
|
||||
#
|
||||
|
||||
usage() {
|
||||
|
|
@ -434,6 +437,13 @@ EOF
|
|||
sed -i "s|^\s*\$config\['imap_host'\]\s*=.*|\$config['imap_host'] = 'tls://$FQDN:143';|" /etc/roundcube/config.inc.php
|
||||
sed -i "s|^\s*\$config\['smtp_host'\]\s*=.*|\$config['smtp_host'] = 'tls://$FQDN:587';|" /etc/roundcube/config.inc.php
|
||||
|
||||
# Enable plugins
|
||||
sed -i "/\$config\['plugins'\] = \[/,/\];/c\\
|
||||
\$config['plugins'] = [\\
|
||||
'managesieve',\\
|
||||
'password',\\
|
||||
];" /etc/roundcube/config.inc.php
|
||||
|
||||
# Check if web interface is loading
|
||||
curl -s https://$FQDN | grep title | grep -q "Roundcube Webmail"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ that file for your own domain and selector:
|
|||
|
||||
```sh title="Run this on your server"
|
||||
# Add the mapping of domain/selector (use your own domain here)
|
||||
echo example.org 20251000901 > /etc/rspamd/dkim_selectors.map
|
||||
echo example.org 2025100901 > /etc/rspamd/dkim_selectors.map
|
||||
```
|
||||
|
||||
That’s all. rspamd now knows that whenever it sees an outgoing email from `anyone@example.org` it will get the DKIM
|
||||
|
|
|
|||
|
|
@ -8,315 +8,6 @@ sidebar:
|
|||
|
||||
import { Aside } from "@astrojs/starlight/components";
|
||||
|
||||
<Aside type="tip" title="Optional feature">
|
||||
This feature is completely optional. If you are eager to get finished then skip this page and maybe come back later.
|
||||
<Aside type="tip" title="Work in progress">
|
||||
This page is currently being rewritten. Please be patient.
|
||||
</Aside>
|
||||
|
||||
Quotas are size limits for users. You can make sure that users do not waste arbitrary amounts of disk space but are
|
||||
forced to clean up old emails every now and then.
|
||||
|
||||
The magic happens in two places:
|
||||
|
||||
1. Postfix needs to reject new emails if the user’s mailbox is over quota.
|
||||
2. Dovecot needs to keep track of the quota and how much the user has already used up of it.
|
||||
|
||||
---
|
||||
|
||||
https://doc.dovecot.org/2.4.2/core/plugins/quota.html#quota-service
|
||||
|
||||
https://sys4.de/en/blog/postfix-dovecot-mailbox-quota/
|
||||
|
||||
---
|
||||
|
||||
https://www.postfix.org/SMTPD_POLICY_README.html
|
||||
|
||||
telnet 127.0.0.1 13373 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'.
|
||||
recipient=chris@auenland.workaround.org
|
||||
|
||||
action=554 5.2.2 Quota exceeded (mailbox for user is full)
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
swaks --from nonexistens@example.org --to chris@auenland.workaround.org
|
||||
|
||||
=== Trying auenland.workaround.org:25...
|
||||
=== Connected to auenland.workaround.org.
|
||||
<- 220 auenland ESMTP Postfix (Debian)
|
||||
-> EHLO minty
|
||||
<- 250-auenland
|
||||
<- 250-PIPELINING
|
||||
<- 250-SIZE 10240000
|
||||
<- 250-VRFY
|
||||
<- 250-ETRN
|
||||
<- 250-STARTTLS
|
||||
<- 250-ENHANCEDSTATUSCODES
|
||||
<- 250-8BITMIME
|
||||
<- 250-DSN
|
||||
<- 250-SMTPUTF8
|
||||
<- 250 CHUNKING
|
||||
-> MAIL FROM:<nonexistens@example.org>
|
||||
<- 250 2.1.0 Ok
|
||||
-> RCPT TO:<chris@auenland.workaround.org>
|
||||
<** 554 5.2.2 <chris@auenland.workaround.org>: Recipient address rejected: Quota exceeded (mailbox for user is full)
|
||||
-> QUIT
|
||||
<- 221 2.0.0 Bye
|
||||
=== Connection closed with remote host.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
cat 99-ispmail-quota.conf
|
||||
mail_plugins {
|
||||
quota = yes
|
||||
}
|
||||
|
||||
quota "User quota" {
|
||||
storage_size = 100K
|
||||
storage_grace = 0
|
||||
|
||||
warning warn-95 {
|
||||
quota_storage_percentage = 95
|
||||
execute quota-warning {
|
||||
args = 95 %{user}
|
||||
}
|
||||
}
|
||||
|
||||
warning warn-80 {
|
||||
quota_storage_percentage = 80
|
||||
execute quota-warning {
|
||||
args = 80 %{user}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service quota-status {
|
||||
executable = quota-status -p postfix
|
||||
inet_listener quota-status {
|
||||
port = 13373
|
||||
}
|
||||
client_limit = 1
|
||||
}
|
||||
|
||||
# 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.
|
||||
service quota-warning {
|
||||
executable = script /usr/local/bin/ispmail-quota-warning.sh
|
||||
user = dovecot
|
||||
unix_listener quota-warning {
|
||||
user = vmail
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
## Quota backends
|
||||
##
|
||||
|
||||
# Multiple backends are supported:
|
||||
# count: Default and recommended, quota driver tracks the quota internally within Dovecot's index files.
|
||||
# maildir: Maildir++ quota
|
||||
# fs: Read-only support for filesystem quota
|
||||
#quota "User quota" {
|
||||
# driver = count
|
||||
#}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```
|
||||
cat /usr/local/bin/ispmail-quota-warning.sh
|
||||
#!/bin/sh
|
||||
PERCENT=$1
|
||||
USER=$2
|
||||
cat << EOF | /usr/lib/dovecot/dovecot-lda -d $USER -o quota_enforce=no
|
||||
From: postmaster@domain.com
|
||||
Subject: quota warning
|
||||
|
||||
Your mailbox is now $PERCENT% full.
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Dovecot quota policy service
|
||||
|
||||
Let’s start with Dovecot. Find the file `/etc/dovecot/conf.d/90-quota.conf` and edit it. There are several `plugin {}`
|
||||
sections. Take one and make it look like:
|
||||
|
||||
```
|
||||
plugin {
|
||||
quota = count:User quota
|
||||
quota_vsizes = yes
|
||||
|
||||
quota_status_success = DUNNO
|
||||
quota_status_nouser = DUNNO
|
||||
quota_status_overquota = "452 4.2.2 Mailbox is full and cannot receive any more emails"
|
||||
}
|
||||
```
|
||||
|
||||
The first line defines that you want to calculate the used space in a user’s _maildir_. There are several
|
||||
[backends](https://doc.dovecot.org/configuration_manual/quota_plugin/) like that but the
|
||||
_[count](https://doc.dovecot.org/configuration_manual/quota/quota_count/#quota-backend-count)_ is the best choice in
|
||||
this context. (Previous guides used _maildir_ here.) The string “User quota” is just an arbitrary string that may be
|
||||
queried from a mail user agent.
|
||||
|
||||
The lines starting with “`quota_status_…`” set return values for the service that you will set up in a minute. It will
|
||||
tell Postfix that it will not interfere (_DUNNO_ – colloquial way to say “I don’t know”). And it will return a string
|
||||
with a return code 452 if the user is over quota. Codes starting with “4” mean temporary errors. It will tell the
|
||||
sending party that it is worth retrying at a later time. However if the user does not resolve the issue it will lead to
|
||||
a _bounce_ error email after three days.
|
||||
|
||||
In the same file (_90-quota.conf_) add another section:
|
||||
|
||||
```
|
||||
service quota-status {
|
||||
executable = /usr/lib/dovecot/quota-status -p postfix
|
||||
unix_listener /var/spool/postfix/private/quota-status {
|
||||
user = postfix
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This creates a new [Dovecot service](https://doc.dovecot.org/configuration_manual/service_configuration/) responding to
|
||||
requests from other processes. You surely recognize that we put it into the jail that Postfix runs in
|
||||
(_/var/spool/postfix_), so that Postfix can access it.
|
||||
|
||||
Time to restart Dovecot:
|
||||
|
||||
```
|
||||
systemctl restart dovecot
|
||||
```
|
||||
|
||||
Take a look at the /var/spool/postfix/private directory. If all went as intended you will find a socket file called
|
||||
`quota-status` there. Otherwise please check the `/var/log/mail.log` file for errors.
|
||||
|
||||
### Postfix recipient restrictions
|
||||
|
||||
If we stopped here, then Dovecot would reject emails for users who have no space left. However Postfix would still
|
||||
happily receive new emails and attempt to forward them to Dovecot via LMTP. Dovecot however will deny that. It will then
|
||||
keep the email in its queue and retry for a while. In the end it will send a _bounce_ back to the sender telling them
|
||||
about the problem. So why is this bad?
|
||||
|
||||
1. The sender will assume that the email was delivered while it is stuck in the queue for up to three days.
|
||||
2. Spam emails use forged senders. So at the time that Postfix generates the _bounce email_ it will likely send it to an
|
||||
innocent person. This is called _backscatter_ and considered a mail server misconfiguration. Such a problem may get
|
||||
your mail server blacklisted. You don’t want that.
|
||||
|
||||
So the next logical step is to make Postfix check whether a mailbox is over quota whenever a new email arrives. Let’s
|
||||
hook up into the “RCPT TO” phase of the SMTP dialog when a new email comes in. Postfix checks its
|
||||
_smtpd_recipient_restrictions_ configuration at this stage. Run this command in the shell:
|
||||
|
||||
```
|
||||
postconf smtpd_recipient_restrictions=reject_unauth_destination, \
|
||||
"check_policy_service unix:private/quota-status"
|
||||
```
|
||||
|
||||
This adds two checks:
|
||||
|
||||
1. `reject_unauth_destination` checks whether the mail server is the final destination for the recipient’s email
|
||||
address. This is pretty much the default behavior if you do not define any restrictions.
|
||||
2. `check_policy_service` connects to the socket file at `/var/spool/postfix/private/quota-status` that was put there by
|
||||
Dovecot. It will use it to ask Dovecot whether the user is over quota in which case the email would get rejected.
|
||||
|
||||
### Test it
|
||||
|
||||
If you are curious to see this working, then set John’s mailbox quota to 5 KB:
|
||||
|
||||
```sql
|
||||
# mariadb mailserver
|
||||
mysql> update virtual_users set quota=4000 where email='john@example.org';
|
||||
```
|
||||
|
||||
Send him a few emails using the ‘swaks’ tool:
|
||||
|
||||
```
|
||||
swaks --server localhost --to john@example.org
|
||||
```
|
||||
|
||||
After a few emails you will see the rejection message:
|
||||
|
||||
```
|
||||
-> RCPT TO:john@example.org
|
||||
<** 452 4.2.2 john@example.org: Recipient address rejected: Mailbox is full and cannot receive any more emails
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
These are things you should consider if quotas do not seem to work properly:
|
||||
|
||||
- Check if you have enabled “quota” in the “mail_plugins” in the 10-mail.conf file.
|
||||
- Your users may complain that they have deleted many emails but are still over quota. Let them check if they actually
|
||||
emptied the _Trash_ folder. Of course emails in that folder also contribute to the disk space usage. Once the Trash
|
||||
folder is expunged the problem should be gone. You may also allow your users more space in the Trash folder. That’s
|
||||
explained in the [Dovecot documentation](https://doc.dovecot.org/configuration_manual/quota/#quota-rules).
|
||||
- If you directly remove files from a user’s Maildir instead of properly accessing the mailbox using IMAP then you will
|
||||
screw up the quota calculation. In that case let Dovecot recalculate the quota:
|
||||
`doveadm quota recalc -u john@example.org`
|
||||
|
||||
### Automatic warning emails
|
||||
|
||||
The last step is to inform the poor users if they accidentally went over quota. After all they do not necessarily
|
||||
recognize that on their own. Let’s do that by sending them an email with a warning. Yes, we will make sure that the
|
||||
email gets through even if the quota is reached.
|
||||
|
||||
Edit the `90-quota.conf` file again. Add this section to the file (derived from the
|
||||
[Dovecot documentation](https://doc.dovecot.org/configuration_manual/quota/#quota-warning-scripts)):
|
||||
|
||||
```
|
||||
plugin {
|
||||
quota_warning = storage=95%% quota-warning 95 %u
|
||||
quota_warning2 = storage=80%% quota-warning 80 %u
|
||||
}
|
||||
service quota-warning {
|
||||
executable = script /usr/local/bin/quota-warning.sh
|
||||
unix_listener quota-warning {
|
||||
user = vmail
|
||||
group = vmail
|
||||
mode = 0660
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This section defines two automatic quota warnings. The first (quota_warning) is triggered if the user reaches 95% of the
|
||||
quota. The second (quota_warning2) at 80%. These lines follow this schema:
|
||||
|
||||
- **Trigger** (e.g. “storage=95%”). The “%” sign needs to be used twice if you want to emit a literal percent sign. So
|
||||
this is not a typo.
|
||||
- The **socket** you want to call in that case. Our socket is the “service quota-warning” that calls a shell script.
|
||||
- Additional **parameters** that are passed to the shell script in our case. They tell the script the percentage that
|
||||
has been reached (e.g. 95) and the address of the user who should get the warning.
|
||||
|
||||
Apparently we need the script to run. So please create a new file at `/usr/local/bin/quota-warning.sh` and put these
|
||||
lines into it:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
PERCENT=$1
|
||||
USER=$2
|
||||
cat << EOF | /usr/lib/dovecot/dovecot-lda -d $USER -o "plugin/quota=maildir:User quota:noenforcing"
|
||||
From: postmaster@webmail.example.org
|
||||
Subject: Quota warning - $PERCENT% reached
|
||||
|
||||
Your mailbox can only store a limited amount of emails.
|
||||
Currently it is $PERCENT% full. If you reach 100% then
|
||||
new emails cannot be stored. Thanks for your understanding.
|
||||
EOF
|
||||
```
|
||||
|
||||
Make this file executable:
|
||||
|
||||
```
|
||||
chmod +x /usr/local/bin/quota-warning.sh
|
||||
```
|
||||
|
||||
Time to restart Dovecot again:
|
||||
|
||||
```
|
||||
systemctl restart dovecot
|
||||
```
|
||||
|
||||
Dovecot’s quota limits can be configured in many ways. If you have special needs then give
|
||||
[their documentation](https://doc.dovecot.org/configuration_manual/quota/) a look.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue