From 2527ebfaf2b3eee11b11c4ee589010e8c7a61a99 Mon Sep 17 00:00:00 2001 From: youtous Date: Fri, 24 Apr 2020 14:55:32 +0200 Subject: [PATCH] added dovecot quota feature add postfix service quota check check-for-changes on quotas setquota command fix checkforchanges quota addquota verify user exists add setquota in setup.sh merging addquota into setquota test quota commands add ldap tests for dovecot quota fix smtp only quota postfix rules test postfix conf add quota test integration add quota exceeded test add wait analyze fix tests fix setup typo add test fixes fix error output wip update startup rules fix setup fix setup tests fix output commands remove quota on remove user try to fix sync limit mails check if file exists fix path change used quota user fix post size check if quota file exists update tests configure virtualmailbox limit for dovecot last fix fix quota expr relax dovecot tests auto create dovecot-quotas fix dovecot apply quota test wip quota warning trying to fix get dovadm quota dovecot applies fix fix quota warning lda path test count mail on quota fix quota warning permissiosn fix test --- Dockerfile | 1 + Makefile | 2 +- config/dovecot-quotas.cf | 3 + setup.sh | 25 +- target/bin/delmailuser | 6 + target/bin/delquota | 28 ++ target/bin/setquota | 49 +++ target/check-for-changes.sh | 16 +- target/dovecot/10-mail.conf | 2 +- target/dovecot/20-imap.conf | 4 + target/dovecot/90-quota.conf | 50 +++ target/dovecot/scripts/quota-warning.sh | 14 + target/start-mailserver.sh | 59 +++- test/mail_smtponly.bats | 7 +- test/mail_with_ldap.bats | 11 + .../email-templates/quota-exceeded.txt | 25 ++ test/tests.bats | 290 +++++++++++++++++- 17 files changed, 581 insertions(+), 11 deletions(-) create mode 100644 config/dovecot-quotas.cf create mode 100755 target/bin/delquota create mode 100755 target/bin/setquota create mode 100644 target/dovecot/20-imap.conf create mode 100644 target/dovecot/90-quota.conf create mode 100644 target/dovecot/scripts/quota-warning.sh create mode 100644 test/test-files/email-templates/quota-exceeded.txt diff --git a/Dockerfile b/Dockerfile index 0fcd4f24..af6c4151 100644 --- a/Dockerfile +++ b/Dockerfile @@ -124,6 +124,7 @@ RUN echo "0 */6 * * * clamav /usr/bin/freshclam --quiet" > /etc/cron.d/clamav-fr # Configures Dovecot COPY target/dovecot/auth-passwdfile.inc target/dovecot/??-*.conf /etc/dovecot/conf.d/ +COPY target/dovecot/scripts/quota-warning.sh /usr/local/bin/quota-warning.sh WORKDIR /usr/share/dovecot # hadolint ignore=SC2016,SC2086 RUN sed -i -e 's/include_try \/usr\/share\/dovecot\/protocols\.d/include_try \/etc\/dovecot\/protocols\.d/g' /etc/dovecot/dovecot.conf && \ diff --git a/Makefile b/Makefile index 8b618459..77fbb165 100644 --- a/Makefile +++ b/Makefile @@ -155,4 +155,4 @@ clean: sudo rm -rf test/config ;\ mv testconfig.bak test/config ;\ fi - -sudo rm -rf test/onedir test/alias test/relay test/config/dovecot-lmtp/userdb test/config/key* test/config/opendkim/keys/domain.tld/ test/config/opendkim/keys/example.com/ test/config/opendkim/keys/localdomain2.com/ test/config/postfix-aliases.cf test/config/postfix-receive-access.cf test/config/postfix-receive-access.cfe test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf + -sudo rm -rf test/onedir test/alias test/quota test/relay test/config/dovecot-lmtp/userdb test/config/key* test/config/opendkim/keys/domain.tld/ test/config/opendkim/keys/example.com/ test/config/opendkim/keys/localdomain2.com/ test/config/postfix-aliases.cf test/config/postfix-receive-access.cf test/config/postfix-receive-access.cfe test/config/dovecot-quotas.cf test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf diff --git a/config/dovecot-quotas.cf b/config/dovecot-quotas.cf new file mode 100644 index 00000000..155328aa --- /dev/null +++ b/config/dovecot-quotas.cf @@ -0,0 +1,3 @@ +# define user's quota in this file +# E.g; +# user@domain:50M diff --git a/setup.sh b/setup.sh index 84a5d4fd..029813ec 100755 --- a/setup.sh +++ b/setup.sh @@ -8,7 +8,7 @@ CRI= _check_root() { if [[ $EUID -ne 0 ]]; then - echo "Curently docker-mailserver doesn't support podman's rootless mode, please run this script as root user." + echo "Curently docker-mailserver doesn't support podman's rootless mode, please run this script as root user." exit 1 fi } @@ -75,7 +75,7 @@ _usage() { OPTIONS: -i IMAGE_NAME The name of the docker-mailserver image, by default - 'tvial/docker-mailserver:latest' for docker, and + 'tvial/docker-mailserver:latest' for docker, and 'docker.io/tvial/docker-mailserver:latest' for podman. -c CONTAINER_NAME The name of the running container. @@ -97,6 +97,10 @@ SUBCOMMANDS: $0 alias del $0 alias list + quota: + $0 quota set [] + $0 quota del + config: $0 config dkim (default: 2048) @@ -252,6 +256,23 @@ case $1 in esac ;; + quota) + shift + case $1 in + set) + shift + _docker_image setquota $@ + ;; + del) + shift + _docker_image delquota $@ + ;; + *) + _usage + ;; + esac + ;; + config) shift case $1 in diff --git a/target/bin/delmailuser b/target/bin/delmailuser index 138ca75a..65198e69 100755 --- a/target/bin/delmailuser +++ b/target/bin/delmailuser @@ -2,6 +2,7 @@ DATABASE=${DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} ALIAS_DATABASE="/tmp/docker-mailserver/postfix-virtual.cf" +QUOTA_DATABASE="/tmp/docker-mailserver/dovecot-quotas.cf" usage() { echo "Usage: delmailuser <-y> ..." @@ -51,6 +52,11 @@ shift $((OPTIND-1)) -e "s/,"$USER"//g" \ -e "s/"$USER",//g" $ALIAS_DATABASE [ $? = 0 ] && echo "$USER and potential aliases deleted." || errex "Aliases for $USER couldn't be deleted in $ALIAS_DATABASE. $?" + # remove quota directives + if [ -f "$QUOTA_DATABASE" ]; then + sed -i -e "/^$USER:.*$/d" $QUOTA_DATABASE || errex "Quota for $USER couldn't be deleted in $QUOTA_DATABASE. $?" + fi + if [ "$MAILDEL" != "y" ]; then read -p "Do you want to delete the mailbox as well(all mails will be removed)?(y/n) " MAILDEL echo diff --git a/target/bin/delquota b/target/bin/delquota new file mode 100755 index 00000000..737e641a --- /dev/null +++ b/target/bin/delquota @@ -0,0 +1,28 @@ +#! /bin/bash + +DATABASE=${DATABASE:-/tmp/docker-mailserver/dovecot-quotas.cf} +USER_DATABASE=${USER_DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} + +USER="$1" + +usage() { + echo "Usage: delquota " +} + +errex() { + echo "$@" 1>&2 + exit 1 +} + +escape() { + echo "${1//./\\.}" +} + +[ -z "$USER" ] && { usage; errex "No username specified"; } +expr index "$USER" "@" >/dev/null || { usage; errex "username must include the domain"; } +if ! grep -qE "^$USER\|" "$USER_DATABASE"; then + usage; errex "user $USER does not exist" +fi +[ -s "$DATABASE" ] || exit 0 + +sed -i -e "/^$USER:.*$/d" $DATABASE diff --git a/target/bin/setquota b/target/bin/setquota new file mode 100755 index 00000000..48c6f18e --- /dev/null +++ b/target/bin/setquota @@ -0,0 +1,49 @@ +#! /bin/bash + +DATABASE=${DATABASE:-/tmp/docker-mailserver/dovecot-quotas.cf} +USER_DATABASE=${USER_DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} + +USER="$1" +shift +QUOTA="$@" + +usage() { + echo "Usage: setquota []" +} + +errex() { + echo "$@" 1>&2 + exit 1 +} + +[ -z "$USER" ] && { usage; errex "no username specified"; } +expr index "$USER" "@" >/dev/null || { usage; errex "username must include the domain"; } +if ! grep -qE "^$USER\|" "$USER_DATABASE"; then + usage; errex "user $USER does not exist" +fi + +# check quota +if [ -n "$QUOTA" ] && ! echo "$QUOTA" | grep -qE "^([0-9]+(B|k|M|G|T)|0)\$"; then + usage; errex "invalid quota format. e.g. 302M (B (byte), k (kilobyte), M (megabyte), G (gigabyte) or T (terabyte))" +fi + +# Protect config file with lock to avoid race conditions +touch $DATABASE +( + flock -e 200 + + if [ -z "$QUOTA" ]; then + read -s "Enter quota (e.g. 10M): " QUOTA + echo + [ -z "$QUOTA" ] && errex "Quota must not be empty. Use 0 for unlimited quota" + fi + + # check quota + if [ -n "$QUOTA" ] && ! echo "$QUOTA" | grep -qE "^([0-9]+(B|k|M|G|T)|0)\$"; then + usage; errex "invalid quota format. e.g. 302M (B (byte), k (kilobyte), M (megabyte), G (gigabyte) or T (terabyte))" + fi + + delquota "$USER" + echo "$USER:$QUOTA" >> $DATABASE +) 200<$DATABASE + diff --git a/target/check-for-changes.sh b/target/check-for-changes.sh index 2109e1d9..f11f8f7d 100755 --- a/target/check-for-changes.sh +++ b/target/check-for-changes.sh @@ -32,7 +32,7 @@ echo "${log_date} Using postmaster address ${PM_ADDRESS}" # Create an array of files to monitor, must be the same as in start-mailserver.sh declare -a cf_files=() -for file in postfix-accounts.cf postfix-virtual.cf postfix-aliases.cf; do +for file in postfix-accounts.cf postfix-virtual.cf postfix-aliases.cf dovecot-quotas.cf; do [ -f "$file" ] && cf_files+=("$file") done @@ -119,14 +119,26 @@ if [[ $chksum == *"FAIL"* ]]; then # Setting variables for better readability user=$(echo ${login} | cut -d @ -f1) domain=$(echo ${login} | cut -d @ -f2) + + user_attributes="" + # test if user has a defined quota + if [ -f /tmp/docker-mailserver/dovecot-quotas.cf ]; then + user_quota=($(grep "${user}@${domain}:" -i /tmp/docker-mailserver/dovecot-quotas.cf | tr ':' '\n')) + + if [ ${#user_quota[@]} -eq 2 ]; then + user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}" + fi + fi + # Let's go! echo "${login} ${domain}/${user}/" >> /etc/postfix/vmailbox # User database for dovecot has the following format: # user:password:uid:gid:(gecos):home:(shell):extra_fields # Example : # ${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::userdb_mail=maildir:/var/mail/${domain}/${user} - echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::" >> /etc/dovecot/userdb + echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::${user_attributes}" >> /etc/dovecot/userdb mkdir -p /var/mail/${domain}/${user} + # Copy user provided sieve file, if present test -e /tmp/docker-mailserver/${login}.dovecot.sieve && cp /tmp/docker-mailserver/${login}.dovecot.sieve /var/mail/${domain}/${user}/.dovecot.sieve echo ${domain} >> /tmp/vhost.tmp diff --git a/target/dovecot/10-mail.conf b/target/dovecot/10-mail.conf index da0a13e9..13ba82a2 100644 --- a/target/dovecot/10-mail.conf +++ b/target/dovecot/10-mail.conf @@ -205,7 +205,7 @@ mail_privileged_group = docker # 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. -#mail_plugins = +mail_plugins = $mail_plugins quota ## ## Mailbox handling optimizations diff --git a/target/dovecot/20-imap.conf b/target/dovecot/20-imap.conf new file mode 100644 index 00000000..161c2150 --- /dev/null +++ b/target/dovecot/20-imap.conf @@ -0,0 +1,4 @@ +protocol imap { + # allow IMAP clients to ask quota usage + mail_plugins = $mail_plugins imap_quota +} diff --git a/target/dovecot/90-quota.conf b/target/dovecot/90-quota.conf new file mode 100644 index 00000000..b2753a97 --- /dev/null +++ b/target/dovecot/90-quota.conf @@ -0,0 +1,50 @@ +plugin { + # debug quota using `doveadm quota get -u user@domain` + # recompute quota using `doveadm quota recalc -u user@domain` + + # Track the current quota usage in Dovecot’s index files. + quota = count:User quota + + # Use virtual sizes for count + quota_vsizes = yes + + # define the maximum message size to be saved, sync with postfix + quota_max_mail_size = 10000M + + # default quota per mailbox, sync with postfix + quota_rule = *:storage=128M + + # allow user to delete messages + quota_rule2 = Trash:storage=+50M + + quota_grace = 10%% + # 10% is the default + quota_warning = storage=95%% quota-warning 95 %u %d + quota_warning2 = storage=80%% quota-warning 80 %u %d + quota_warning3 = -storage=100%% quota-warning below %u %d # user is no longer over quota + + + quota_status_success=DUNNO + quota_status_nouser=DUNNO + quota_status_overquota = "552 5.2.2 Mailbox is full" +} + +service quota-warning { + executable = script /usr/local/bin/quota-warning.sh + unix_listener quota-warning { + user = dovecot + group = dovecot + mode = 0660 + } +} + +# allow postfix to query quota +service quota-status { + executable = quota-status -p postfix + inet_listener { + port = 65265 + # You can choose any port you want + } + client_limit = 1 +} + diff --git a/target/dovecot/scripts/quota-warning.sh b/target/dovecot/scripts/quota-warning.sh new file mode 100644 index 00000000..fd4de980 --- /dev/null +++ b/target/dovecot/scripts/quota-warning.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env sh + +# Report a quota usage warning to an user + +PERCENT=$1 +USER=$2 +DOMAIN=$3 + +cat << EOF | /usr/lib/dovecot/dovecot-lda -d "$USER" -o "plugin/quota=maildir:User quota:noenforcing" +From: postmaster@$DOMAIN +Subject: quota warning + +Your mailbox is now $PERCENT% full. +EOF diff --git a/target/start-mailserver.sh b/target/start-mailserver.sh index ece25c41..913ddc6f 100644 --- a/target/start-mailserver.sh +++ b/target/start-mailserver.sh @@ -99,6 +99,7 @@ function register_functions() { if [ "$SMTP_ONLY" != 1 ]; then _register_setup_function "_setup_dovecot" _register_setup_function "_setup_dovecot_dhparam" + _register_setup_function "_setup_dovecot_quota" _register_setup_function "_setup_dovecot_local_user" fi @@ -501,7 +502,7 @@ function _setup_chksum_file() { pushd /tmp/docker-mailserver declare -a cf_files=() - for file in postfix-accounts.cf postfix-virtual.cf postfix-aliases.cf; do + for file in postfix-accounts.cf postfix-virtual.cf postfix-aliases.cf dovecot-quotas.cf; do [ -f "$file" ] && cf_files+=("$file") done @@ -630,6 +631,43 @@ function _setup_dovecot() { chmod -f +x /usr/lib/dovecot/sieve-pipe/* } +function _setup_dovecot_quota() { + notify 'task' 'Setting up Dovecot quota' + + if [ "$ENABLE_LDAP" = 1 ]; then + notify 'inf' "Dovecot quota is not implemented with LDAP." + + if [ -f /etc/dovecot/conf.d/90-quota.conf ]; then + mv /etc/dovecot/conf.d/90-quota.conf /etc/dovecot/conf.d/90-quota.conf.disab + sed -i "s/mail_plugins = \$mail_plugins quota/mail_plugins = \$mail_plugins/g" /etc/dovecot/conf.d/10-mail.conf + sed -i "s/mail_plugins = \$mail_plugins imap_quota/mail_plugins = \$mail_plugins/g" /etc/dovecot/conf.d/20-imap.conf + fi + else + if [ -f /etc/dovecot/conf.d/90-quota.conf.disab ]; then + mv /etc/dovecot/conf.d/90-quota.conf.disab /etc/dovecot/conf.d/90-quota.conf + sed -i "s/mail_plugins = \$mail_plugins/mail_plugins = \$mail_plugins quota/g" /etc/dovecot/conf.d/10-mail.conf + sed -i "s/mail_plugins = \$mail_plugin/mail_plugins = \$mail_plugins imap_quota/g" /etc/dovecot/conf.d/20-imap.conf + fi + + message_size_limit_mb=$((DEFAULT_VARS["POSTFIX_MESSAGE_SIZE_LIMIT"] / 1000000)) + mailbox_limit_mb=$((DEFAULT_VARS["POSTFIX_MAILBOX_SIZE_LIMIT"] / 1000000)) + + sed -i "s/quota_max_mail_size =.*/quota_max_mail_size = ${message_size_limit_mb}$([ "$message_size_limit_mb" == 0 ] && echo "" || echo "M")/g" /etc/dovecot/conf.d/90-quota.conf + sed -i "s/quota_rule = \*:storage=.*/quota_rule = *:storage=${mailbox_limit_mb}$([ "$mailbox_limit_mb" == 0 ] && echo "" || echo "M")/g" /etc/dovecot/conf.d/90-quota.conf + + if [ ! -f /tmp/docker-mailserver/dovecot-quotas.cf ]; then + notify 'inf' "'config/docker-mailserver/dovecot-quotas.cf' is not provided. Using default quotas." + echo -n >/tmp/docker-mailserver/dovecot-quotas.cf + fi + fi + + if [ "$SMTP_ONLY" = 1 ]; then + sed -i "s/check_policy_service inet:localhost:65265//g" /etc/postfix/main.cf + else + sed -i "s/reject_unknown_recipient_domain, reject_rbl_client zen.spamhaus.org/reject_unknown_recipient_domain, check_policy_service inet:localhost:65265, reject_rbl_client zen.spamhaus.org/g" /etc/postfix/main.cf + fi +} + function _setup_dovecot_local_user() { notify 'task' 'Setting up Dovecot Local User' echo -n > /etc/postfix/vmailbox @@ -657,15 +695,28 @@ function _setup_dovecot_local_user() { # Setting variables for better readability user=$(echo ${login} | cut -d @ -f1) domain=$(echo ${login} | cut -d @ -f2) + + user_attributes="" + # test if user has a defined quota + if [ -f /tmp/docker-mailserver/dovecot-quotas.cf ]; then + user_quota=($(grep "${user}@${domain}:" -i /tmp/docker-mailserver/dovecot-quotas.cf | tr ':' '\n')) + + if [ ${#user_quota[@]} -eq 2 ]; then + user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}" + fi + fi + # Let's go! - notify 'inf' "user '${user}' for domain '${domain}' with password '********'" + notify 'inf' "user '${user}' for domain '${domain}' with password '********', attr=${user_attributes}" + echo "${login} ${domain}/${user}/" >> /etc/postfix/vmailbox # User database for dovecot has the following format: # user:password:uid:gid:(gecos):home:(shell):extra_fields # Example : # ${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::userdb_mail=maildir:/var/mail/${domain}/${user} - echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::" >> /etc/dovecot/userdb + echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::${user_attributes}" >> /etc/dovecot/userdb mkdir -p /var/mail/${domain}/${user} + # Copy user provided sieve file, if present test -e /tmp/docker-mailserver/${login}.dovecot.sieve && cp /tmp/docker-mailserver/${login}.dovecot.sieve /var/mail/${domain}/${user}/.dovecot.sieve echo ${domain} >> /tmp/vhost.tmp @@ -775,6 +826,8 @@ function _setup_postfix_sizelimits() { postconf -e "message_size_limit = ${DEFAULT_VARS["POSTFIX_MESSAGE_SIZE_LIMIT"]}" notify 'inf' "Configuring postfix mailbox size limit" postconf -e "mailbox_size_limit = ${DEFAULT_VARS["POSTFIX_MAILBOX_SIZE_LIMIT"]}" + notify 'inf' "Configuring postfix virtual mailbox size limit" + postconf -e "virtual_mailbox_limit = ${DEFAULT_VARS["POSTFIX_MAILBOX_SIZE_LIMIT"]}" } function _setup_postfix_smtputf8() { diff --git a/test/mail_smtponly.bats b/test/mail_smtponly.bats index fbbca9ee..e0cc8080 100644 --- a/test/mail_smtponly.bats +++ b/test/mail_smtponly.bats @@ -47,6 +47,11 @@ function teardown_file() { assert_failure } +@test "checking configuration: dovecot quota absent in postconf (disabled using SMTP_ONLY)" { + run docker exec mail_smtponly /bin/bash -c "postconf | grep 'check_policy_service inet:localhost:65265'" + assert_failure +} + # # smtp # @@ -75,4 +80,4 @@ function teardown_file() { @test "last" { skip 'this test is only there to reliably mark the end for the teardown_file' -} \ No newline at end of file +} diff --git a/test/mail_with_ldap.bats b/test/mail_with_ldap.bats index b1d5f151..10e430fb 100644 --- a/test/mail_with_ldap.bats +++ b/test/mail_with_ldap.bats @@ -168,6 +168,17 @@ function teardown_file() { assert_success } +@test "checking dovecot: quota plugin is disabled" { + run docker exec mail_with_ldap /bin/sh -c "grep '\$mail_plugins quota' /etc/dovecot/conf.d/10-mail.conf" + assert_failure + run docker exec mail_with_ldap /bin/sh -c "grep '\$mail_plugins imap_quota' /etc/dovecot/conf.d/20-imap.conf" + assert_failure + run docker exec mail_with_ldap ls /etc/dovecot/conf.d/90-quota.conf + assert_failure + run docker exec mail_with_ldap ls /etc/dovecot/conf.d/90-quota.conf.disab + assert_success +} + @test "checking spoofing: rejects sender forging" { run docker exec mail_with_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/ldap-smtp-auth-spoofed.txt | grep 'Sender address rejected: not owned by user'" assert_success diff --git a/test/test-files/email-templates/quota-exceeded.txt b/test/test-files/email-templates/quota-exceeded.txt new file mode 100644 index 00000000..71d221a1 --- /dev/null +++ b/test/test-files/email-templates/quota-exceeded.txt @@ -0,0 +1,25 @@ +HELO mail.external.tld +MAIL FROM: user@external.tld +RCPT TO: quotauser@otherdomain.tld +DATA +From: Docker Mail Server +To: Existing Local User +Date: Sat, 22 May 2010 07:43:25 -0400 +Subject: Test Message saturating your mailbox +This is a test mail with a lot of data +Lorem ipsum dolor sit amet. Et quidem architecto id Quis saepe vel corporis odit aut nesciunt nisi. Et voluptatem exercitationem ut enim veritatis et nostrum optio aut magni eius et perspiciatis magnam et ipsam illo. 33 inventore unde est voluptatem exercitationem aut dolores nesciunt aut porro facere voluptatem quisquam hic corrupti culpa. 33 dicta sapiente eum rerum harum aut facere nobis in expedita debitis. Hic velit provident et laborum debitis eos tempore quaerat et iste tempora ut perferendis enim rem maxime incidunt qui quas sequi. Et voluptate enim qui explicabo tempora quo sint sequi sit enim quam aut eaque placeat At maiores doloribus. Qui doloremque consequatur eos voluptates ipsa eum placeat internos est eius internos ut aperiam necessitatibus et excepturi neque a nobis praesentium. Non alias omnis qui eaque dignissimos ea voluptates maiores aut inventore nihil. Ut quaerat voluptatem qui assumenda labore et magni harum sed possimus sint et minus voluptatibus eum veritatis error eos minus eaque! Qui commodi explicabo eos repellat enim hic nisi commodi qui nulla rerum hic debitis tempore. Qui autem praesentium ab cupiditate accusantium velit voluptatibus. Aut tempore molestiae qui doloribus molestias aut ipsum obcaecati At laborum asperiores qui sint architecto. Et voluptatem porro ut repellendus odio eum voluptatem facere cum mollitia obcaecati aut tempore adipisci hic quisquam aliquam. Ad mollitia amet a accusantium magni At provident obcaecati id laudantium odit et dolorum voluptatum! + +Cum accusamus corporis ut nemo dolores ut porro odio ipsam quibusdam et culpa esse et dolor fugit. Aut illo et laudantium voluptate et asperiores nobis sit autem atque est eius amet. A quasi quam eum galisum quia ea tempora veniam non incidunt ratione in natus culpa quo Quis doloremque ut quia reiciendis. Aut sunt nihil et nihil quas fuga laborum et assumenda esse. Et quisquam quod qui necessitatibus doloremque sit dicta sapiente sed vero laborum aut magnam omnis et eveniet incidunt non asperiores repudiandae! Aut consectetur galisum ab laboriosam quas eos omnis necessitatibus. Sed dolores distinctio hic maiores voluptatem rem facilis soluta a labore dolorum. Non voluptas commodi non aperiam voluptatem aut blanditiis illum et commodi ipsum in sint voluptatem sit corrupti architecto et quia quia. Qui possimus voluptatem eos repellat veritatis velit quia eos fugit repellat qui reprehenderit dolorem 33 animi dolorem. In deserunt repellat ea totam impedit et sint temporibus vel explicabo expedita id earum assumenda ex eaque enim et sint incidunt. Aut totam eveniet et aspernatur maiores est cumque dolorem sit officiis doloribus. Hic nesciunt rerum ut sint sint ad veritatis omnis ut maxime exercitationem. Sit natus voluptatum 33 dolorem consequatur ab perspiciatis enim et voluptas molestias sed iste perferendis eum tempore eligendi. Et enim ratione ut autem atque eum dolore neque sit necessitatibus quibusdam ab illum explicabo eum excepturi debitis! + +Aut galisum Quis ab neque ducimus sed laboriosam veniam et tempora ratione. Vel molestiae blanditiis et molestiae Quis ut eius dolorum aut voluptatem obcaecati quo alias iusto est fugiat galisum. In doloremque eligendi qui similique facere aut quibusdam consequatur est perferendis voluptate. Quo molestiae consequatur ut autem praesentium sit perspiciatis vitae sed voluptate dolores. Et velit modi vel consequuntur accusamus et rerum dolor! In voluptas libero rem sunt dolorem ut officiis exercitationem sit dignissimos quia ut aliquid quos ex iste distinctio est molestias cumque. Qui repellendus voluptates sit totam sequi aut deserunt ipsum et dolores possimus et corporis possimus qui praesentium autem. Ea eaque veritatis vel quis voluptas ut voluptas dicta non minus suscipit. Sed culpa tempore necessitatibus omnis eum assumenda modi non iusto assumenda 33 sint molestiae ut consequatur libero. Eos accusantium similique ut natus eaque non omnis repellat est maxime quibusdam! Est accusantium facilis sit voluptas et voluptas officia ab repellat veritatis eum commodi animi ut omnis veniam. Est ratione Quis aut velit vitae ut eaque dignissimos qui perspiciatis voluptate est optio voluptatem sit cumque quia. Aut ratione eligendi et sequi nihil ut sunt quasi sed beatae dolore. Et iusto reprehenderit ab Quis voluptas eum suscipit dolor aut autem sint! + +Et illo molestiae eum impedit voluptatum aut dignissimos numquam qui culpa quia ut delectus delectus. Qui voluptatem amet et quia velit ut debitis possimus. In similique molestiae ut minima error et placeat molestiae ab quasi fugiat qui minima quibusdam. Sit porro aperiam aut fugiat quis ut ullam dolores quo maiores voluptas aut reiciendis quibusdam id eaque modi! Cum minima dicta eos molestiae veritatis est excepturi neque non magni galisum. Sit reiciendis aspernatur ut amet doloribus aut velit tempora quo quasi veritatis est voluptatem illum eos repellendus eius et beatae iure. Est voluptates totam et mollitia reprehenderit est sequi doloremque et eius odit At officia dolorem. Sed magnam quidem eum ipsa consectetur quo consequatur ipsa ad internos rerum. + +Et voluptatum nobis ut odio voluptatem et quibusdam fugit ut libero sapiente vel quia voluptatem. Et voluptas quaerat vel quasi commodi At minus itaque? A similique voluptatem ut porro dolor cum voluptatem facere et provident odit. Et odio eligendi rem necessitatibus adipisci ut distinctio rerum sed accusantium nemo. In ipsum nostrum aut praesentium voluptates eum velit quis At rerum consequatur cum reprehenderit assumenda aut quibusdam delectus et aliquam voluptates. Ut possimus harum aut molestiae consequatur in fugiat voluptate non temporibus galisum qui ullam distinctio. Qui quia sequi non nobis veniam est sint facere qui enim laudantium. Qui repellendus sunt id soluta illum qui voluptatem enim. Ut praesentium vero cum fuga architecto est velit pariatur. Ut necessitatibus doloremque At delectus voluptatem ab dignissimos dolorem. Ut maxime repudiandae et repellat suscipit et delectus quia rem corrupti temporibus est doloribus veritatis et facilis beatae et harum alias. Quo reiciendis vero cum dolor natus ut nisi tenetur non quia quia est iste internos. Qui earum dignissimos qui earum vero eos dignissimos expedita id officiis architecto sit corporis inventore et quisquam nesciunt. A dolorem doloribus et quia unde hic minus voluptatem. + +Sit sint obcaecati et reiciendis tenetur aut dolorum culpa. Ab veritatis maxime qui necessitatibus facilis eum voluptate asperiores non totam omnis. Nam modi officia in reiciendis odit sit rerum laudantium est rerum voluptatem ut fugit cupiditate! Sit atque sint aut delectus omnis ut asperiores enim quo reprehenderit quae! In quasi nemo ut error totam ut quia harum ut commodi tenetur? Non quod dolorum eum explicabo labore vel asperiores quas est perferendis nulla eum nemo tenetur. Ut libero blanditiis ex voluptatibus repudiandae ab reiciendis nemo id debitis impedit hic quia incidunt sed quam excepturi ut magnam odit. Qui dolor deleniti aut sunt voluptas aut blanditiis distinctio nam omnis deleniti hic omnis rerum eum magni voluptatem. Nam labore facere eum molestiae dolorum ea consectetur praesentium ut cupiditate iste ad magnam aut neque maiores! Et excepturi ducimus ut nemo voluptas eum voluptas nihil hic perferendis quos vel quasi nesciunt est praesentium dolore hic quia quis. Et maxime ducimus ea cupiditate voluptatem ad quia dolores! + +Sed quos quaerat vel aperiam minus non sapiente quia ut ratione dolore eum officiis rerum. Non dolor vitae qui facilis dignissimos aut voluptate odit et ullam consequuntur. Et laudantium perspiciatis sit nisi temporibus a temporibus itaque ut iure dolor a voluptatum mollitia eos officia nobis et quibusdam voluptas. Amet eligendi eos nulla corporis et blanditiis nihil vel eveniet veritatis et sunt perferendis id molestiae eius! Quo harum quod aut nemo autem ut adipisci sint sed quia sunt. Aut voluptas error ut quae perferendis eos adipisci internos. Nam rerum fugiat aut minima nostrum quo repellendus quas exercitationem tenetur. Et molestiae architecto id quibusdam reprehenderit et magnam aliquam! Quo tempora veritatis At dolorem sint ex nulla blanditiis At voluptas laudantium est molestiae exercitationem et sequi voluptates aut ipsa atque. Et animi ipsum aut atque recusandae ea nemo ullam non quisquam quos sit libero sint vel libero delectus. Eos labore quidem a velit obcaecati nam explicabo consequatur eos maxime blanditiis? Et ipsam molestiae non quia explicabo ex galisum repudiandae et tempora veniam. Sed optio repellendus ut consequatur temporibus et harum quas hic ipsa officia? Aut dolores ipsum sit nulla dignissimos id quia perferendis aut dolores dolor et quibusdam porro aut Quis consequatur. + +. +QUIT diff --git a/test/tests.bats b/test/tests.bats index 7d21f509..67ff4463 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -1,6 +1,6 @@ load 'test_helper/bats-support/load' load 'test_helper/bats-assert/load' - +load 'test_helper/common' # # shared functions @@ -934,6 +934,240 @@ EOF [ ! -z "$output" ] } + +@test "checking quota: setquota user must be existing" { + run docker exec mail /bin/sh -c "addmailuser quota_user@domain.tld mypassword" + assert_success + + run docker exec mail /bin/sh -c "setquota quota_user 50M" + assert_failure + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 50M" + assert_success + + run docker exec mail /bin/sh -c "setquota username@fulldomain 50M" + assert_failure + + run docker exec mail /bin/sh -c "delmailuser -y quota_user@domain.tld" + assert_success +} +@test "checking quota: setquota must be well formatted" { + run docker exec mail /bin/sh -c "addmailuser quota_user@domain.tld mypassword" + assert_success + + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 26GIGOTS" + assert_failure + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 123" + assert_failure + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld M" + assert_failure + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld -60M" + assert_failure + + + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 10B" + assert_success + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 10k" + assert_success + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 10M" + assert_success + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 10G" + assert_success + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 10T" + assert_success + + + run docker exec mail /bin/sh -c "delmailuser -y quota_user@domain.tld" + assert_success +} + + +@test "checking quota: delquota user must be existing" { + run docker exec mail /bin/sh -c "addmailuser quota_user@domain.tld mypassword" + assert_success + + run docker exec mail /bin/sh -c "delquota uota_user@domain.tld" + assert_failure + run docker exec mail /bin/sh -c "delquota quota_user" + assert_failure + run docker exec mail /bin/sh -c "delquota dontknowyou@domain.tld" + assert_failure + + run docker exec mail /bin/sh -c "setquota quota_user@domain.tld 10T" + assert_success + run docker exec mail /bin/sh -c "delquota quota_user@domain.tld" + assert_success + run docker exec mail /bin/sh -c "grep -i 'quota_user@domain.tld' /tmp/docker-mailserver/dovecot-quotas.cf" + assert_failure + + run docker exec mail /bin/sh -c "delmailuser -y quota_user@domain.tld" + assert_success +} +@test "checking quota: delquota allow when no quota for existing user" { + run docker exec mail /bin/sh -c "addmailuser quota_user@domain.tld mypassword" + assert_success + + run docker exec mail /bin/sh -c "grep -i 'quota_user@domain.tld' /tmp/docker-mailserver/dovecot-quotas.cf" + assert_failure + + run docker exec mail /bin/sh -c "delquota quota_user@domain.tld" + assert_success + run docker exec mail /bin/sh -c "delquota quota_user@domain.tld" + assert_success + + run docker exec mail /bin/sh -c "delmailuser -y quota_user@domain.tld" + assert_success +} + +@test "checking quota: dovecot quota present in postconf" { + run docker exec mail /bin/bash -c "postconf | grep 'check_policy_service inet:localhost:65265'" + assert_success +} + + +@test "checking quota: dovecot mailbox max size must be equal to postfix mailbox max size" { + postfix_mailbox_size=$(docker exec mail sh -c "postconf | grep -Po '(?<=mailbox_size_limit = )[0-9]+'") + run echo "$postfix_mailbox_size" + refute_output "" + + # dovecot relies on virtual_mailbox_size by default + postfix_virtual_mailbox_size=$(docker exec mail sh -c "postconf | grep -Po '(?<=virtual_mailbox_limit = )[0-9]+'") + assert_equal "$postfix_virtual_mailbox_size" "$postfix_mailbox_size" + + postfix_mailbox_size_mb=$(($postfix_mailbox_size / 1000000)) + + dovecot_mailbox_size_mb=$(docker exec mail sh -c "doveconf | grep -oP '(?<=quota_rule \= \*\:storage=)[0-9]+'") + run echo "$dovecot_mailbox_size_mb" + refute_output "" + + assert_equal "$postfix_mailbox_size_mb" "$dovecot_mailbox_size_mb" +} + + +@test "checking quota: dovecot message max size must be equal to postfix messsage max size" { + postfix_message_size=$(docker exec mail sh -c "postconf | grep -Po '(?<=message_size_limit = )[0-9]+'") + run echo "$postfix_message_size" + refute_output "" + + postfix_message_size_mb=$(($postfix_message_size / 1000000)) + + dovecot_message_size_mb=$(docker exec mail sh -c "doveconf | grep -oP '(?<=quota_max_mail_size = )[0-9]+'") + run echo "$dovecot_message_size_mb" + refute_output "" + + assert_equal "$postfix_message_size_mb" "$dovecot_message_size_mb" +} + +@test "checking quota: quota directive is removed when mailbox is removed" { + run docker exec mail /bin/sh -c "addmailuser quserremoved@domain.tld mypassword" + assert_success + + run docker exec mail /bin/sh -c "setquota quserremoved@domain.tld 12M" + assert_success + + run docker exec mail /bin/sh -c 'cat /tmp/docker-mailserver/dovecot-quotas.cf | grep -E "^quserremoved@domain.tld\:12M\$" | wc -l | grep 1' + assert_success + + run docker exec mail /bin/sh -c "delmailuser -y quserremoved@domain.tld" + assert_success + + run docker exec mail /bin/sh -c 'cat /tmp/docker-mailserver/dovecot-quotas.cf | grep -E "^quserremoved@domain.tld\:12M\$"' + assert_failure +} + +@test "checking quota: dovecot applies user quota" { + sleep 15 # wait until any other change has finished + run docker exec mail /bin/sh -c "doveadm quota get -u 'user1@localhost.localdomain' | grep 'User quota STORAGE'" + assert_output --partial "- 0" + + # set a quota + originalChangesProcessed=$(count_processed_changes mail) + run docker exec mail /bin/sh -c "setquota user1@localhost.localdomain 50M" + assert_success + + # wait until change detector has processed the change + count=0 + while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] + do + ((count++)) && ((count==60)) && break + sleep 1 + done + [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] + assert_success + + # let dovecot breathe + sleep 5 + + run docker exec mail /bin/sh -c 'doveadm quota get -u user1@localhost.localdomain | grep "User quota STORAGE"' + assert_output --partial '51200' + assert_success + + # remove the quota + originalChangesProcessed=$(count_processed_changes mail) + run docker exec mail /bin/sh -c "delquota user1@localhost.localdomain" + assert_success + + # wait until change detector has processed the change + count=0 + while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] + do + ((count++)) && ((count==60)) && break + sleep 1 + done + [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] + assert_success + + # let dovecot breathe + sleep 5 + + run docker exec mail /bin/sh -c 'doveadm quota get -u user1@localhost.localdomain | grep "User quota STORAGE"' + assert_output --partial '- 0' + assert_success +} + +@test "checking quota: warn message received when quota exceeded" { + sleep 15 # wait until any other change has finished + + originalChangesProcessed=$(count_processed_changes mail) + + # create user + run docker exec mail /bin/sh -c "addmailuser quotauser@otherdomain.tld mypassword && setquota quotauser@otherdomain.tld 10k" + assert_success + + count=0 + while [ "${originalChangesProcessed}" = "$(count_processed_changes mail)" ] + do + ((count++)) && ((count==60)) && break + sleep 1 + done + [ "${originalChangesProcessed}" != "$(count_processed_changes mail)" ] + assert_success + + # let dovecot breathe + sleep 5 + + run docker exec mail /bin/sh -c 'doveadm quota get -u quotauser@otherdomain.tld | grep "User quota STORAGE"' + assert_output --partial '10' + assert_success + + # send some big emails + run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/quota-exceeded.txt" + run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/quota-exceeded.txt" + run docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/quota-exceeded.txt" + assert_success + + # check for quota warn message existence + run repeat_until_success_or_timeout 20 sh -c "docker exec mail sh -c 'grep \"Subject: quota warning\" /var/mail/otherdomain.tld/quotauser/new/ -R'" + assert_success + + # ensure only the first big message and the warn message are present (other messages are rejected: mailbox is full) + run docker exec mail sh -c 'ls /var/mail/otherdomain.tld/quotauser/new/ | wc -l' + assert_success + assert_output "2" + + run docker exec mail /bin/sh -c "delmailuser -y quotauser@otherdomain.tld" + assert_success +} + # # PERMIT_DOCKER mynetworks # @@ -1119,6 +1353,60 @@ EOF assert_failure } +# quota +@test "checking setup.sh: setup.sh setquota" { + mkdir -p ./test/quota/config && echo "" > ./test/quota/config/dovecot-quotas.cf + + run ./setup.sh -p ./test/quota/config email add quota_user@example.com test_password + run ./setup.sh -p ./test/quota/config email add quota_user2@example.com test_password + + run ./setup.sh -p ./test/quota/config quota set quota_user@example.com 12M + assert_success + run ./setup.sh -p ./test/quota/config quota set 51M quota_user@example.com + assert_failure + run ./setup.sh -p ./test/quota/config quota set unknown@domain.com 150M + assert_failure + + run ./setup.sh -p ./test/quota/config quota set quota_user2 51M + assert_failure + + run /bin/sh -c 'cat ./test/quota/config/dovecot-quotas.cf | grep -E "^quota_user@example.com\:12M\$" | wc -l | grep 1' + assert_success + + run ./setup.sh -p ./test/quota/config quota set quota_user@example.com 26M + assert_success + run /bin/sh -c 'cat ./test/quota/config/dovecot-quotas.cf | grep -E "^quota_user@example.com\:26M\$" | wc -l | grep 1' + assert_success + + run grep "quota_user2@example.com" ./test/alias/config/dovecot-quotas.cf + assert_failure +} + +@test "checking setup.sh: setup.sh delquota" { + mkdir -p ./test/quota/config && echo "" > ./test/quota/config/dovecot-quotas.cf + + run ./setup.sh -p ./test/quota/config email add quota_user@example.com test_password + run ./setup.sh -p ./test/quota/config email add quota_user2@example.com test_password + + run ./setup.sh -p ./test/quota/config quota set quota_user@example.com 12M + assert_success + run /bin/sh -c 'cat ./test/quota/config/dovecot-quotas.cf | grep -E "^quota_user@example.com\:12M\$" | wc -l | grep 1' + assert_success + + + run ./setup.sh -p ./test/quota/config quota del unknown@domain.com + assert_failure + run /bin/sh -c 'cat ./test/quota/config/dovecot-quotas.cf | grep -E "^quota_user@example.com\:12M\$" | wc -l | grep 1' + assert_success + + run ./setup.sh -p ./test/quota/config quota del quota_user@example.com + assert_success + run grep "quota_user@example.com" ./test/alias/config/dovecot-quotas.cf + assert_failure +} + + + # config @test "checking setup.sh: setup.sh config dkim" { run ./setup.sh -c mail config dkim