Kaynağa Gözat

Exim add custom ratelimits by user (#2225)

* Exim add custom ratelimits by user

* Update Custom Limits by domain/user or default (limit.conf)

* Update 1.5.9.sh

* Update upgrade version to 1.6.0

* Include copy of limits.conf to

* Adjust filename to limit.conf

* Correct folder

* Add support for Debian 11 and Ubuntu 22.04

* Add new keys to configs

* Prevent mail.domain.com being removed if used as alias for webmail

* Add support for CLI and WEB UI

Priority:
- "Account" 
- "Domain"
- "Server"

Admin is only allowed to edit / change value for security reasons

Co-authored-by: Arturo Blanco <ablanco@ablanco.es>
Co-authored-by: Jaap Marcus <9754650+jaapmarcus@users.noreply.github.com>
Co-authored-by: Raphael <rs@scit.ch>
Arturo Blanco 4 yıl önce
ebeveyn
işleme
8faa6b8ba9

+ 9 - 0
bin/v-add-mail-account

@@ -24,6 +24,8 @@ source /etc/hestiacp/hestia.conf
 source $HESTIA/func/main.sh
 # shellcheck source=/usr/local/hestia/func/domain.sh
 source $HESTIA/func/domain.sh
+# shellcheck source=/usr/local/hestia/func/syshealth.sh
+source $HESTIA/func/syshealth.sh
 # load config file
 source_conf "$HESTIA/conf/hestia.conf"
 
@@ -101,6 +103,13 @@ str="$str TIME='$time' DATE='$date'"
 echo "$str" >> $USER_DATA/mail/$domain.conf
 chmod 660 $USER_DATA/mail/$domain.conf
 
+syshealth_repair_mail_account_config
+
+user_rate_limit=$(get_object_value 'mail' 'DOMAIN' "$domain" '$RATE_LIMIT');
+if [ -n "$user_rate_limit" ]; then
+    echo "$user_rate_limit" > $HOMEDIR/$user/conf/mail/$domain/limits/$account
+fi
+
 # Increase mail accounts counter
 accounts=$(wc -l $USER_DATA/mail/$domain.conf | cut -f 1 -d ' ')
 increase_user_value "$user" '$U_MAIL_ACCOUNTS'

+ 4 - 0
bin/v-add-mail-domain

@@ -28,6 +28,8 @@ source $HESTIA/func/main.sh
 source $HESTIA/func/domain.sh
 # shellcheck source=/usr/local/hestia/func/ip.sh
 source $HESTIA/func/ip.sh
+# shellcheck source=/usr/local/hestia/func/syshealth.sh
+source $HESTIA/func/syshealth.sh
 # load config file
 source_conf "$HESTIA/conf/hestia.conf"
 
@@ -97,6 +99,8 @@ s="$s DATE='$date'"
 echo $s >> $USER_DATA/mail.conf
 touch $USER_DATA/mail/$domain.conf
 
+syshealth_repair_mail_config
+
 # Generating DKIM keys
 if [ "$dkim" = 'yes' ]; then
     openssl genrsa -out $USER_DATA/mail/$domain.pem $dkim_size &>/dev/null

+ 16 - 14
bin/v-add-mail-domain-webmail

@@ -99,22 +99,24 @@ else
     # Ensure DNS record exists if Hestia is hosting DNS zones
     if [ -n "$DNS_SYSTEM" ]; then
         dns_domain=$($BIN/v-list-dns-domains $user | grep $domain | cut -d' ' -f1)
-        webmail_record=$($BIN/v-list-dns-records $user $domain | grep -i $WEBMAIL_ALIAS | cut -d' ' -f1)
-
+        webmail_record=$($BIN/v-list-dns-records $user $domain | grep -i " $WEBMAIL_ALIAS " | cut -d' ' -f1)
         if [ "$dns_domain" = "$domain" ]; then
-            if [ -z "$webmail_record" ]; then
-                if [ "$quiet" = "yes" ]; then
-                    $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
-                else
-                    $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
-                fi
-            else
-                if [ "$quiet" = "yes" ]; then
-                    $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
-                    $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
+            if [ "$WEBMAIL_ALIAS" != "mail" ]; then
+                #Prevent mail.domain.com to be cycled
+                if [ -z "$webmail_record" ]; then
+                    if [ "$quiet" = "yes" ]; then
+                        $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
+                    else
+                        $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
+                    fi
                 else
-                    $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
-                    $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
+                    if [ "$quiet" = "yes" ]; then
+                        $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
+                        $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
+                    else
+                        $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
+                        $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
+                    fi
                 fi
             fi
         fi

+ 91 - 0
bin/v-change-mail-account-rate-limit

@@ -0,0 +1,91 @@
+#!/bin/bash
+# info: change mail domain rate limit
+# options: USER DOMAIN ACCOUNT RATE
+#
+# example: v-change-mail-domain-quota admin mydomain.tld user01 100
+#
+# This function changes email account rate limit. Use system to use domain or "server" setting
+
+#----------------------------------------------------------#
+#                Variables & Functions                     #
+#----------------------------------------------------------#
+
+# Argument definition
+user=$1
+domain=$2
+domain_idn=$2
+account=$3
+rate=$4
+
+# Includes
+# shellcheck source=/etc/hestiacp/hestia.conf
+source /etc/hestiacp/hestia.conf
+# shellcheck source=/usr/local/hestia/func/main.sh
+source $HESTIA/func/main.sh
+# shellcheck source=/usr/local/hestia/func/domain.sh
+source $HESTIA/func/domain.sh
+# load config file
+source_conf "$HESTIA/conf/hestia.conf"
+
+# Additional argument formatting
+format_domain
+format_domain_idn
+# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '4' "$#" 'USER DOMAIN ACCOUNT RATE'
+is_format_valid 'user' 'domain' 'account'
+if [ "$rate" != 'system' ]; then
+    is_format_valid 'rate'
+fi
+is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
+is_object_valid 'user' 'USER' "$user"
+is_object_unsuspended 'user' 'USER' "$user"
+is_object_valid 'mail' 'DOMAIN' "$domain"
+is_object_unsuspended 'mail' 'DOMAIN' "$domain"
+is_object_valid "mail/$domain" 'ACCOUNT' "$account"
+is_object_unsuspended "mail/$domain" 'ACCOUNT' "$account"
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+md5=$(get_object_value "mail/$domain" 'ACCOUNT' "$account" '$MD5')
+if [[ "$MAIL_SYSTEM" =~ exim ]]; then
+    if [ ! -d "$HOMEDIR/$user/conf/mail/$domain/limits/" ]; then
+        mkdir $HOMEDIR/$user/conf/mail/$domain/limits/
+    fi
+    if [ "$rate" = "system" ]; then 
+        user_rate_limit=$(get_object_value 'mail' 'DOMAIN' "$domain" '$RATE_LIMIT');
+        if [ -n "$user_rate_limit" ]; then
+            echo "$user_rate_limit" > $HOMEDIR/$user/conf/mail/$domain/limits/$account
+        else
+            rm $HOMEDIR/$user/conf/mail/$domain/limits/$account
+        fi
+    else
+        echo "$rate" > $HOMEDIR/$user/conf/mail/$domain/limits/$account
+    fi
+fi
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+if [[ "$rate" = "system" ]]; then
+    rate=''
+fi
+
+# Update quota
+update_object_value "mail/$domain" 'ACCOUNT' "$account" '$RATE_LIMIT' "$rate"
+
+# Logging
+$BIN/v-log-action "$user" "Info" "Mail" "Mail account rate limit changed (Rate: $rate, Account: $account@$domain)."
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 73 - 0
bin/v-change-mail-domain-rate-limit

@@ -0,0 +1,73 @@
+#!/bin/bash
+# info: change mail account rate limit
+# options: USER DOMAIN ACCOUNT RATE
+#
+# example: v-change-mail-account-quota admin mydomain.tld user01 100
+#
+# This function changes email account rate limit for the domain. account specific setting will overwrite domain setting!
+
+#----------------------------------------------------------#
+#                Variables & Functions                     #
+#----------------------------------------------------------#
+
+# Argument definition
+user=$1
+domain=$2
+domain_idn=$2
+rate=$3
+
+# Includes
+# shellcheck source=/etc/hestiacp/hestia.conf
+source /etc/hestiacp/hestia.conf
+# shellcheck source=/usr/local/hestia/func/main.sh
+source $HESTIA/func/main.sh
+# shellcheck source=/usr/local/hestia/func/domain.sh
+source $HESTIA/func/domain.sh
+# load config file
+source_conf "$HESTIA/conf/hestia.conf"
+
+# Additional argument formatting
+format_domain
+format_domain_idn
+# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
+
+#----------------------------------------------------------#
+#                    Verifications                         #
+#----------------------------------------------------------#
+
+check_args '3' "$#" 'USER DOMAIN  RATE'
+is_format_valid 'user' 'domain'
+if [ "$rate" != 'system' ]; then
+    is_format_valid 'rate'
+fi
+is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
+is_object_valid 'user' 'USER' "$user"
+is_object_unsuspended 'user' 'USER' "$user"
+is_object_valid 'mail' 'DOMAIN' "$domain"
+is_object_unsuspended 'mail' 'DOMAIN' "$domain"
+
+# Perform verification if read-only mode is enabled
+check_hestia_demo_mode
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+if [[ "$rate" = "system" ]]; then
+    rate=''
+fi
+
+$HESTIA/bin/v-rebuild-mail-domain "$user" "$domain"
+# Update quota
+update_object_value "mail" 'DOMAIN' "$domain" '$RATE_LIMIT' "$rate"
+
+# Logging
+$BIN/v-log-action "$user" "Info" "Mail" "Mail domain rate limit has changed ($rate)"
+log_event "$OK" "$ARGUMENTS"
+
+exit

+ 1 - 0
bin/v-delete-mail-account

@@ -60,6 +60,7 @@ if [[ "$MAIL_SYSTEM" =~ exim ]]; then
     sed -i "/^$account:/d" $HOMEDIR/$user/conf/mail/$domain/accounts
     sed -i "/^$account$/d" $HOMEDIR/$user/conf/mail/$domain/fwd_only
     rm -rf $HOMEDIR/$user/mail/$domain/$account
+    rm -f $HOMEDIR/$user/conf/mail/$domain/limits/$account
 fi
 
 #----------------------------------------------------------#

+ 10 - 8
bin/v-delete-mail-domain-webmail

@@ -58,14 +58,16 @@ if [ -n "$WEBMAIL_ALIAS" ]; then
     # Ensure that corresponding DNS records are removed
     if [ -n "$DNS_SYSTEM" ]; then
         dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1)
-        webmail_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "$WEBMAIL_ALIAS" | cut -d' ' -f1)
-
-        if [ "$dns_domain" = "$domain" ]; then
-            if [ -n "$webmail_record" ]; then
-                if [ "$quiet" = "yes" ]; then
-                    $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
-                else
-                    $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart"
+        if [ "WEBMAIL_ALIAS" != "mail" ];then
+            #Prevent mail.domain.com being removed
+            webmail_record=$($BIN/v-list-dns-records $user $domain | grep -i " $WEBMAIL_ALIAS " | cut -d' ' -f1)
+            if [ "$dns_domain" = "$domain" ]; then
+                if [ -n "$webmail_record" ]; then
+                    if [ "$quiet" = "yes" ]; then
+                        $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
+                    else
+                        $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart"
+                    fi
                 fi
             fi
         fi

+ 1 - 0
bin/v-list-mail-account

@@ -33,6 +33,7 @@ json_list() {
         "FWD_ONLY": "'$FWD_ONLY'",
         "AUTOREPLY": "'$AUTOREPLY'",
         "QUOTA": "'$QUOTA'",
+        "RATE_LIMIT": "'$RATE_LIMIT'",
         "U_DISK": "'$U_DISK'",
         "SUSPENDED": "'$SUSPENDED'",
         "TIME": "'$TIME'",

+ 1 - 0
bin/v-list-mail-domain

@@ -33,6 +33,7 @@ json_list() {
         "DKIM": "'$DKIM'",
         "CATCHALL": "'$CATCHALL'",
         "ACCOUNTS": "'$ACCOUNTS'",
+        "RATE_LIMIT": "'$RATE_LIMIT'",
         "U_DISK": "'$U_DISK'",
         "SSL": "'$SSL'",
         "LETSENCRYPT": "'$LETSENCRYPT'",

+ 6 - 0
func/main.sh

@@ -433,6 +433,10 @@ get_object_value() {
     eval echo $4
 }
 
+get_object_values() {
+    parse_object_kv_list $(grep "$2='$3'" $USER_DATA/$1.conf)   
+}
+
 # Update object value
 update_object_value() {
     row=$(grep -nF "$2='$3'" $USER_DATA/$1.conf)
@@ -1154,6 +1158,8 @@ is_format_valid() {
                 protocol)       is_fw_protocol_format_valid "$arg" ;;
                 proxy_ext)      is_extention_format_valid "$arg" ;;
                 quota)          is_int_format_valid "$arg" 'quota' ;;
+                rate)           is_int_format_valid "$arg" 'rate' ;;
+                                
                 record)         is_common_format_valid "$arg" 'record';;
                 restart)        is_restart_format_valid "$arg" 'restart' ;;
                 role)           is_role_valid "$arg" 'role' ;;

+ 13 - 2
func/rebuild.sh

@@ -529,8 +529,9 @@ rebuild_dns_domain_conf() {
 
 # MAIL domain rebuild
 rebuild_mail_domain_conf() {
+    syshealth_repair_mail_config
+    
     get_domain_values 'mail'
-
     if [[ "$domain" = *[![:ascii:]]* ]]; then
         domain_idn=$(idn -t --quiet -a $domain)
     else
@@ -570,11 +571,13 @@ rebuild_mail_domain_conf() {
         rm -f $HOMEDIR/$user/conf/mail/$domain/passwd
         rm -f $HOMEDIR/$user/conf/mail/$domain/fwd_only
         rm -f $HOMEDIR/$user/conf/mail/$domain/ip
+        rm -fr $HOMEDIR/$user/conf/mail/$domain/limits/
         touch $HOMEDIR/$user/conf/mail/$domain/accounts
         touch $HOMEDIR/$user/conf/mail/$domain/aliases
         touch $HOMEDIR/$user/conf/mail/$domain/passwd
         touch $HOMEDIR/$user/conf/mail/$domain/fwd_only
-
+        mkdir $HOMEDIR/$user/conf/mail/$domain/limits/
+        
         # Setting outgoing ip address
         if [ -n "$local_ip" ]; then
             echo "$local_ip" > $HOMEDIR/$user/conf/mail/$domain/ip
@@ -658,6 +661,14 @@ rebuild_mail_domain_conf() {
             if [ "$FWD_ONLY" = 'yes' ]; then
                 echo "$account" >> $HOMEDIR/$user/conf/mail/$domain/fwd_only
             fi
+            user_rate_limit=$(get_object_value 'mail' 'DOMAIN' "$domain" '$RATE_LIMIT');
+            if [ -n "$RATE_LIMIT" ]; then
+                #user value
+                echo "$RATE_LIMIT" >> $HOMEDIR/$user/conf/mail/$domain/limits/$account
+            elif [ -n "$user_rate_limit" ]; then
+                #revert to user value
+                echo "$user_rate_limit" >> $HOMEDIR/$user/conf/mail/$domain/limits/$account
+            fi
         fi
     done
 

+ 29 - 3
func/syshealth.sh

@@ -32,7 +32,6 @@ function write_kv_config_file() {
     fi
 
     touch $HESTIA/conf/defaults/$system.conf
-
     for key in $known_keys; do
         echo $key >> $HESTIA/conf/defaults/$system.conf
     done
@@ -84,14 +83,16 @@ function syshealth_update_mail_config_format() {
     # MAIL DOMAINS
     # Create array of known keys in configuration file
     system="mail"
-    known_keys="DOMAIN ANTIVIRUS ANTISPAM DKIM WEBMAIL SSL LETSENCRYPT CATCHALL ACCOUNTS U_DISK SUSPENDED TIME DATE"
+    known_keys="DOMAIN ANTIVIRUS ANTISPAM DKIM WEBMAIL SSL LETSENCRYPT CATCHALL ACCOUNTS RATE_LIMIT U_DISK SUSPENDED TIME DATE"
     write_kv_config_file
     unset system
     unset known_keys
+}
 
+function syshealth_update_mail_account_config_format(){
     # MAIL ACCOUNTS
     system="mail_accounts"
-    known_keys="ACCOUNT ALIAS AUTOREPLY FWD FWD_ONLY MD5 QUOTA U_DISK SUSPENDED TIME DATE"
+    known_keys="ACCOUNT ALIAS AUTOREPLY FWD FWD_ONLY MD5 QUOTA RATE_LIMIT U_DISK SUSPENDED TIME DATE"
     write_kv_config_file
     unset system
     unset known_keys
@@ -155,6 +156,31 @@ function syshealth_repair_web_config() {
     done
 }
 
+function syshealth_repair_mail_config() {
+    system="mail"
+    sanitize_config_file "$system"
+    get_domain_values 'mail'
+    prev="DOMAIN"
+    for key in $known_keys; do
+        if [ -z "${!key}" ]; then
+            add_object_key 'mail' 'DOMAIN' "$domain" "$key" "$prev"   
+        fi
+        prev=$key
+    done
+}
+
+function syshealth_repair_mail_account_config() {
+    system="mail_accounts"
+    sanitize_config_file "$system"
+    get_object_values "mail/$domain" 'ACCOUNT' "$account"
+    for key in $known_keys; do
+        if [ -z "${!key}" ]; then
+            add_object_key "mail/$domain" 'ACCOUNT' "$account" "$key" "$prev"    
+        fi
+        prev=$key
+    done
+}
+
 function syshealth_update_system_config_format() {
     # SYSTEM CONFIGURATION
     # Create array of known keys in configuration file

+ 1 - 0
func/upgrade.sh

@@ -764,6 +764,7 @@ upgrade_rebuild_users() {
 upgrade_replace_default_config() {
     syshealth_update_web_config_format
     syshealth_update_mail_config_format
+    syshealth_update_mail_account_config_format
     syshealth_update_dns_config_format
     syshealth_update_db_config_format
     syshealth_update_user_config_format

+ 4 - 3
install/deb/exim/exim4.conf.4.94.template

@@ -133,9 +133,10 @@ acl_check_rcpt:
 
 # Limit per email account for SMTP auhenticated users
   deny    message       = Email account $authenticated_id is sending too many emails - rate overlimit = $sender_rate / $sender_rate_period
-  ratelimit             = 200 / 1h / $authenticated_id
-
-  warn    ratelimit     = 100 / 1h / strict / $authenticated_id
+          set acl_c_msg_limit = ${if exists{/etc/exim4/domains/${lookup{$sender_address_domain}dsearch{/etc/exim4/domains/}}/limits/${extract{1}{:}{${lookup{$sender_address_local_part}lsearch{/etc/exim4/domains/${lookup{$sender_address_domain}dsearch{/etc/exim4/domains/}}/accounts}}}}} {${readfile{/etc/exim4/domains/${lookup{$sender_address_domain}dsearch{/etc/exim4/domains/}}/limits/${extract{1}{:}{${lookup{$sender_address_local_part}lsearch{/etc/exim4/domains/${lookup{$sender_address_domain}dsearch{/etc/exim4/domains/}}/accounts}}}}}}} {${readfile{/etc/exim4/limit.conf}}} }
+          ratelimit     = $acl_c_msg_limit / 1h / strict/ $authenticated_id
+  
+  warn    ratelimit     = ${eval:$acl_c_msg_limit / 2} / 1h / strict / $authenticated_id
   log_message           = Sender rate [limitlog]: log / email / $authenticated_id / $sender_rate / $sender_rate_period
 
   deny    message       = Restricted characters in address

+ 4 - 3
install/deb/exim/exim4.conf.template

@@ -133,10 +133,11 @@ acl_check_rcpt:
 
 # Limit per email account for SMTP auhenticated users
   deny    message       = Email account $authenticated_id is sending too many emails - rate overlimit = $sender_rate / $sender_rate_period
-  ratelimit             = 200 / 1h / $authenticated_id
+          set acl_c_msg_limit = ${if exists{/etc/exim4/domains/$sender_address_domain/limits/$sender_address_local_part} {${readfile{/etc/exim4/domains/$sender_address_domain/limits/$sender_address_local_part}}} {${readfile{/etc/exim4/limit.conf}}} }
+          ratelimit     = $acl_c_msg_limit / 1h / strict/ $authenticated_id
 
-  warn    ratelimit     = 100 / 1h / strict / $authenticated_id
-  log_message           = Sender rate [limitlog]: log / email / $authenticated_id / $sender_rate / $sender_rate_period
+  warn    ratelimit     = ${eval:$acl_c_msg_limit / 2} / 1h / strict / $authenticated_id
+          log_message           = Sender rate [limitlog]: log / email / $authenticated_id / $sender_rate / $sender_rate_period
 
   deny    message       = Restricted characters in address
           domains       = +local_domains

+ 1 - 0
install/deb/exim/limit.conf

@@ -0,0 +1 @@
+200

+ 2 - 1
install/hst-install-debian.sh

@@ -31,7 +31,7 @@ HESTIA_INSTALL_DIR="$HESTIA/install/deb"
 VERBOSE='no'
 
 # Define software versions
-HESTIA_INSTALL_VER='1.5.12~alpha'
+HESTIA_INSTALL_VER='1.6.0~alpha'
 # Dependencies
 pma_v='5.1.3'
 rc_v="1.6.0"
@@ -1680,6 +1680,7 @@ if [ "$exim" = 'yes' ]; then
     fi
     cp -f $HESTIA_INSTALL_DIR/exim/dnsbl.conf /etc/exim4/
     cp -f $HESTIA_INSTALL_DIR/exim/spam-blocks.conf /etc/exim4/
+	cp -f $HESTIA_INSTALL_DIR/exim/limit.conf /etc/exim4/
     touch /etc/exim4/white-blocks.conf
 
     if [ "$spamd" = 'yes' ]; then

+ 1 - 0
install/hst-install-ubuntu.sh

@@ -1698,6 +1698,7 @@ if [ "$exim" = 'yes' ]; then
     cp -f $HESTIA_INSTALL_DIR/exim/exim4.conf.template /etc/exim4/
     cp -f $HESTIA_INSTALL_DIR/exim/dnsbl.conf /etc/exim4/
     cp -f $HESTIA_INSTALL_DIR/exim/spam-blocks.conf /etc/exim4/
+	cp -f $HESTIA_INSTALL_DIR/exim/limit.conf /etc/exim4/
     touch /etc/exim4/white-blocks.conf
 
     if [ "$spamd" = 'yes' ]; then

+ 19 - 1
install/upgrade/versions/1.6.0.sh

@@ -18,9 +18,27 @@
 upgrade_config_set_value 'UPGRADE_UPDATE_WEB_TEMPLATES' 'false'
 upgrade_config_set_value 'UPGRADE_UPDATE_DNS_TEMPLATES' 'false'
 upgrade_config_set_value 'UPGRADE_UPDATE_MAIL_TEMPLATES' 'false'
-upgrade_config_set_value 'UPGRADE_REBUILD_USERS' 'false'
+upgrade_config_set_value 'UPGRADE_REBUILD_USERS' 'true'
 upgrade_config_set_value 'UPGRADE_UPDATE_FILEMANAGER_CONFIG' 'false'
 
+if [ "$MAIL_SYSTEM" = "exim4" ]; then 
+    echo "[ * ] Update exim4 config to support rate limits"
+    # Upgrade config exim for custom limits
+    
+    exim_version=$(exim4 --version |  head -1 | awk  '{print $3}' | cut -f -2 -d .);
+    if [ "$exim_version" = "4.94" ] || [ "$exim_version" = "4.95" ]; then
+        #For Debian 11 and Ubuntu 22.04 
+        sed -i '115,250 s/ratelimit             = 200 \/ 1h \/ $authenticated_id/          set acl_c_msg_limit = ${if exists{\/etc\/exim4\/domains\/${lookup{$sender_address_domain}dsearch{\/etc\/exim4\/domains\/}}\/limits\/${extract{1}{:}{${lookup{$sender_address_local_part}lsearch{\/etc\/exim4\/domains\/${lookup{$sender_address_domain}dsearch{\/etc\/exim4\/domains\/}}\/accounts}}}}} {${readfile{\/etc\/exim4\/domains\/${lookup{$sender_address_domain}dsearch{\/etc\/exim4\/domains\/}}\/limits\/${extract{1}{:}{${lookup{$sender_address_local_part}lsearch{\/etc\/exim4\/domains\/${lookup{$sender_address_domain}dsearch{\/etc\/exim4\/domains\/}}\/accounts}}}}}}} {${readfile{\/etc\/exim4\/limit.conf}}} } \n  ratelimit     = $acl_c_msg_limit \/ 1h \/ strict\/ $authenticated_id/g' /etc/exim4/exim4.conf.template
+        sed -i '115,250 s/warn    ratelimit     = 100 \/ 1h \/ strict \/ $authenticated_id/warn    ratelimit     = ${eval:$acl_c_msg_limit \/ 2} \/ 1h \/ strict \/ $authenticated_id/g' /etc/exim4/exim4.conf.template
+    else
+        # And the other 
+        sed -i '115,250 s/ratelimit             = 200 \/ 1h \/ $authenticated_id/ set acl_c_msg_limit = ${if exists{\/etc\/exim4\/domains\/$sender_address_domain\/limits\/$sender_address} {${readfile{\/etc\/exim4\/domains\/$sender_address_domain\/limits\/$sender_address_local_part}}} {${readfile{\/etc\/exim4\/limit.conf}}} } \n ratelimit     = $acl_c_msg_limit \/ 1h \/ strict\/ $authenticated_id/g' /etc/exim4/exim4.conf.template
+        sed -i '115,250 s/warn    ratelimit     = 100 \/ 1h \/ strict \/ $authenticated_id/warn    ratelimit     = ${eval:$acl_c_msg_limit \/ 2} \/ 1h \/ strict \/ $authenticated_id/g' /etc/exim4/exim4.conf.template
+    fi
+    # Add missing limit.conf file
+    cp $HESTIA_INSTALL_DIR/exim/limit.conf /etc/exim4/limit.conf
+fi
+
 # Adding LE autorenew cronjob if there are none
 if [ -z "$(grep v-update-lets $HESTIA/data/users/admin/cron.conf)" ]; then
 	min=$(generate_password '012345' '2')

+ 8 - 0
web/add/mail/index.php

@@ -262,6 +262,14 @@ if (!empty($_POST['ok_acc'])) {
         check_return_code($return_var, $output);
         unset($output);
     }
+    
+    // Add fwd_only flag
+    if ((!empty($_POST['v_rate'])) && (empty($_SESSION['error_msg']))  && $_SESSION['userContext'] == 'admin') {
+        $v_rate = escapeshellarg($_POST['v_rate']);
+        exec(HESTIA_CMD."v-change-mail-account-rate-limit ".$user." ".$v_domain." ".$v_account." ".$v_rate, $output, $return_var);
+        check_return_code($return_var, $output);
+        unset($output);
+    }
 
     // Get webmail url
     if (empty($_SESSION['error_msg'])) {

+ 31 - 0
web/edit/mail/index.php

@@ -38,6 +38,7 @@ if ((!empty($_GET['domain'])) && (empty($_GET['account']))) {
     $v_antivirus = $data[$v_domain]['ANTIVIRUS'];
     $v_dkim = $data[$v_domain]['DKIM'];
     $v_catchall = $data[$v_domain]['CATCHALL'];
+    $v_rate = $data[$v_domain]['RATE_LIMIT'];
     $v_date = $data[$v_domain]['DATE'];
     $v_time = $data[$v_domain]['TIME'];
     $v_suspended = $data[$v_domain]['SUSPENDED'];
@@ -100,6 +101,7 @@ if ((!empty($_GET['domain'])) && (!empty($_GET['account']))) {
     }
     $vfwd = explode(",", $data[$v_account]['FWD']);
     $v_fwd_only = $data[$v_account]['FWD_ONLY'];
+    $v_rate = $data[$v_account]['RATE_LIMIT'];
     $v_quota = $data[$v_account]['QUOTA'];
     $v_autoreply = $data[$v_account]['AUTOREPLY'];
     $v_suspended = $data[$v_account]['SUSPENDED'];
@@ -194,6 +196,21 @@ if ((!empty($_POST['save'])) && (!empty($_GET['domain'])) && (empty($_GET['accou
         $v_catchall = '';
         unset($output);
     }
+    
+    // Change rate limit 
+    if (($v_rate != $_POST['v_rate']) && (empty($_SESSION['error_msg'])) && $_SESSION['userContext'] == 'admin') {
+        if (empty($_POST['v_rate'])) {
+            $v_rate = 'system';
+        } else {
+            $v_rate = escapeshellarg($_POST['v_rate']);
+        }
+        exec(HESTIA_CMD."v-change-mail-domain-rate-limit ".$v_username." ".escapeshellarg($v_domain)." ".$v_rate, $output, $return_var);
+        check_return_code($return_var, $output);
+        if ($v_rate == 'system') {
+            $v_rate = '';
+        }
+        unset($output);
+    }
 
     // Change catchall address
     if ((!empty($v_catchall)) && (!empty($_POST['v_catchall'])) && (empty($_SESSION['error_msg']))) {
@@ -501,6 +518,20 @@ if ((!empty($_POST['save'])) && (!empty($_GET['domain'])) && (!empty($_GET['acco
         check_return_code($return_var, $output);
         unset($output);
     }
+    // Change rate limit 
+    if (($v_rate != $_POST['v_rate']) && (empty($_SESSION['error_msg'])) && $_SESSION['userContext'] == 'admin') {
+        if (empty($_POST['v_rate'])) {
+            $v_rate = 'system';
+        } else {
+            $v_rate = escapeshellarg($_POST['v_rate']);
+        }
+        exec(HESTIA_CMD."v-change-mail-account-rate-limit ".$v_username." ".escapeshellarg($v_domain)." ".escapeshellarg($v_account)." ".$v_rate, $output, $return_var);
+        check_return_code($return_var, $output);
+        if ($v_rate == 'system') {
+            $v_rate = '';
+        }
+        unset($output);
+    }
 
     // Change account aliases
     if (empty($_SESSION['error_msg'])) {

+ 10 - 0
web/templates/pages/add_mail_acc.html

@@ -131,6 +131,16 @@
 											<label><input type="checkbox" size="20" class="vst-checkbox" id="v_fwd_for" name="v_fwd_only" <?php if ($v_fwd_only == 'yes') echo "checked=yes" ?>><?=_('Do not store forwarded mail');?></label>
 										</td>
 									</tr>
+									<tr>
+										<td class="vst-text input-label">
+											<?=_('Rate limit');?> <span class="optional">(<?=_('Email / hour');?>)</span>
+										</td>
+									</tr>
+									<tr>
+										<td>
+											<input type="text" size="20" class="vst-input" name="v_rate" value="<?=htmlentities(trim($v_rate, "'"))?>" <?php if($_SESSION['userContext'] != "admin"){ echo "disabled";}?>>
+										</td>
+									</tr>
 								</table>
 							</td>
 						</tr>

+ 11 - 1
web/templates/pages/edit_mail.html

@@ -81,7 +81,17 @@
 						</tr>
 						<tr>
 							<td>
-								<input type="text" size="20" class="vst-input" name="v_catchall" value="<?=htmlentities(trim($v_catchall, "'"))?>"> <?php /*'*/ ?>
+								<input type="text" size="20" class="vst-input" name="v_catchall" value="<?=htmlentities(trim($v_catchall, "'"))?>">
+							</td>
+						</tr>
+						<tr>
+							<td class="vst-text input-label">
+								<?=_('Rate limit');?> <span class="optional">(<?=_('Email / hour / account');?>)</span>
+							</td>
+						</tr>
+						<tr>
+							<td>
+								<input type="text" size="20" class="vst-input" name="v_rate" value="<?=htmlentities(trim($v_rate, "'"))?>">
 							</td>
 						</tr>
 						<?php if (!empty($_SESSION['ANTISPAM_SYSTEM'])) {?>

+ 10 - 0
web/templates/pages/edit_mail_acc.html

@@ -153,6 +153,16 @@
 								</table>
 							</td>
 						</tr>
+						<tr>
+							<td class="vst-text input-label">
+								<?=_('Rate limit');?> <span class="optional">(<?=_('Email / hour');?>)</span>
+							</td>
+						</tr>
+						<tr>
+							<td>
+								<input type="text" size="20" class="vst-input" name="v_rate" value="<?=htmlentities(trim($v_rate, "'"))?>" <?php if($_SESSION['userContext'] != "admin"){ echo "disabled";}?>>
+							</td>
+						</tr>
 					</table>
 					<table class="data-col2">
 					</table>