Browse Source

Merge branch 'staging' into nightly

DerLinkman 1 year ago
parent
commit
772d5c51fd

+ 1 - 1
data/Dockerfiles/acme/Dockerfile

@@ -1,6 +1,6 @@
 FROM alpine:3.20
 
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 
 RUN apk upgrade --no-cache \

+ 2 - 2
data/Dockerfiles/acme/acme.sh

@@ -123,7 +123,7 @@ done
 log_f "Database OK"
 
 log_f "Waiting for Nginx..."
-until $(curl --output /dev/null --silent --head --fail http://nginx:8081); do
+until $(curl --output /dev/null --silent --head --fail http://nginx.${COMPOSE_PROJECT_NAME}_mailcow-network:8081); do
   sleep 2
 done
 log_f "Nginx OK"
@@ -137,7 +137,7 @@ log_f "Resolver OK"
 # Waiting for domain table
 log_f "Waiting for domain table..."
 while [[ -z ${DOMAIN_TABLE} ]]; do
-  curl --silent http://nginx/ >/dev/null 2>&1
+  curl --silent http://nginx.${COMPOSE_PROJECT_NAME}_mailcow-network/ >/dev/null 2>&1
   DOMAIN_TABLE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs)
   [[ -z ${DOMAIN_TABLE} ]] && sleep 10
 done

+ 7 - 7
data/Dockerfiles/acme/reload-configurations.sh

@@ -2,32 +2,32 @@
 
 # Reading container IDs
 # Wrapping as array to ensure trimmed content when calling $NGINX etc.
-NGINX=($(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"nginx-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" | tr "\n" " "))
-DOVECOT=($(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"dovecot-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" | tr "\n" " "))
-POSTFIX=($(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"postfix-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" | tr "\n" " "))
+NGINX=($(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"nginx-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" | tr "\n" " "))
+DOVECOT=($(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"dovecot-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" | tr "\n" " "))
+POSTFIX=($(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"postfix-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" | tr "\n" " "))
 
 reload_nginx(){
   echo "Reloading Nginx..."
-  NGINX_RELOAD_RET=$(curl -X POST --insecure https://dockerapi/containers/${NGINX}/exec -d '{"cmd":"reload", "task":"nginx"}' --silent -H 'Content-type: application/json' | jq -r .type)
+  NGINX_RELOAD_RET=$(curl -X POST --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${NGINX}/exec -d '{"cmd":"reload", "task":"nginx"}' --silent -H 'Content-type: application/json' | jq -r .type)
   [[ ${NGINX_RELOAD_RET} != 'success' ]] && { echo "Could not reload Nginx, restarting container..."; restart_container ${NGINX} ; }
 }
 
 reload_dovecot(){
   echo "Reloading Dovecot..."
-  DOVECOT_RELOAD_RET=$(curl -X POST --insecure https://dockerapi/containers/${DOVECOT}/exec -d '{"cmd":"reload", "task":"dovecot"}' --silent -H 'Content-type: application/json' | jq -r .type)
+  DOVECOT_RELOAD_RET=$(curl -X POST --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${DOVECOT}/exec -d '{"cmd":"reload", "task":"dovecot"}' --silent -H 'Content-type: application/json' | jq -r .type)
   [[ ${DOVECOT_RELOAD_RET} != 'success' ]] && { echo "Could not reload Dovecot, restarting container..."; restart_container ${DOVECOT} ; }
 }
 
 reload_postfix(){
   echo "Reloading Postfix..."
-  POSTFIX_RELOAD_RET=$(curl -X POST --insecure https://dockerapi/containers/${POSTFIX}/exec -d '{"cmd":"reload", "task":"postfix"}' --silent -H 'Content-type: application/json' | jq -r .type)
+  POSTFIX_RELOAD_RET=$(curl -X POST --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${POSTFIX}/exec -d '{"cmd":"reload", "task":"postfix"}' --silent -H 'Content-type: application/json' | jq -r .type)
   [[ ${POSTFIX_RELOAD_RET} != 'success' ]] && { echo "Could not reload Postfix, restarting container..."; restart_container ${POSTFIX} ; }
 }
 
 restart_container(){
   for container in $*; do
     echo "Restarting ${container}..."
-    C_REST_OUT=$(curl -X POST --insecure https://dockerapi/containers/${container}/restart --silent | jq -r '.msg')
+    C_REST_OUT=$(curl -X POST --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${container}/restart --silent | jq -r '.msg')
     echo "${C_REST_OUT}"
   done
 }

+ 1 - 1
data/Dockerfiles/clamd/Dockerfile

@@ -1,6 +1,6 @@
 FROM alpine:3.20
 
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 RUN apk upgrade --no-cache \
   && apk add --update --no-cache \

+ 1 - 1
data/Dockerfiles/dockerapi/Dockerfile

@@ -1,6 +1,6 @@
 FROM alpine:3.20
 
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG PIP_BREAK_SYSTEM_PACKAGES=1
 WORKDIR /app

+ 2 - 1
data/Dockerfiles/dovecot/Dockerfile

@@ -1,5 +1,6 @@
 FROM alpine:3.20
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
 ARG GOSU_VERSION=1.16

+ 0 - 8
data/Dockerfiles/dovecot/docker-entrypoint.sh

@@ -283,14 +283,6 @@ sievec /var/vmail/sieve/global_sieve_after.sieve
 sievec /usr/lib/dovecot/sieve/report-spam.sieve
 sievec /usr/lib/dovecot/sieve/report-ham.sieve
 
-for file in /var/vmail/*/*/sieve/*.sieve ; do
-  if [[ "$file" == "/var/vmail/*/*/sieve/*.sieve" ]]; then
-    continue
-  fi
-  sievec "$file" "$(dirname "$file")/../.dovecot.svbin"
-  chown vmail:vmail "$(dirname "$file")/../.dovecot.svbin"
-done
-
 # Fix permissions
 chown root:root /etc/dovecot/sql/*.conf
 chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/auth/passwd-verify.lua

+ 3 - 3
data/Dockerfiles/dovecot/rspamd-pipe-ham

@@ -3,8 +3,8 @@ FILE=/tmp/mail$$
 cat > $FILE
 trap "/bin/rm -f $FILE" 0 1 2 3 13 15
 
-cat ${FILE} | /usr/bin/curl -H "Flag: 11" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/fuzzydel
-cat ${FILE} | /usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/learnham
-cat ${FILE} | /usr/bin/curl -H "Flag: 13" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/fuzzyadd
+cat ${FILE} | /usr/bin/curl -H "Flag: 11" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/fuzzydel
+cat ${FILE} | /usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/learnham
+cat ${FILE} | /usr/bin/curl -H "Flag: 13" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/fuzzyadd
 
 exit 0

+ 3 - 3
data/Dockerfiles/dovecot/rspamd-pipe-spam

@@ -3,8 +3,8 @@ FILE=/tmp/mail$$
 cat > $FILE
 trap "/bin/rm -f $FILE" 0 1 2 3 13 15
 
-cat ${FILE} | /usr/bin/curl -H "Flag: 13" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/fuzzydel
-cat ${FILE} | /usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/learnspam
-cat ${FILE} | /usr/bin/curl -H "Flag: 11" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/fuzzyadd
+cat ${FILE} | /usr/bin/curl -H "Flag: 13" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/fuzzydel
+cat ${FILE} | /usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/learnspam
+cat ${FILE} | /usr/bin/curl -H "Flag: 11" -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/fuzzyadd
 
 exit 0

+ 2 - 2
data/Dockerfiles/dovecot/sa-rules.sh

@@ -21,11 +21,11 @@ sed -i -e 's/\([^\\]\)\$\([^\/]\)/\1\\$\2/g' /etc/rspamd/custom/sa-rules
 
 if [[ "$(cat /etc/rspamd/custom/sa-rules | md5sum | cut -d' ' -f1)" != "${HASH_SA_RULES}" ]]; then
   CONTAINER_NAME=rspamd-mailcow
-  CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | \
+  CONTAINER_ID=$(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | \
     jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | \
     jq -rc "select( .name | tostring | contains(\"${CONTAINER_NAME}\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id")
   if [[ ! -z ${CONTAINER_ID} ]]; then
-    curl --silent --insecure -XPOST --connect-timeout 15 --max-time 120 https://dockerapi/containers/${CONTAINER_ID}/restart
+    curl --silent --insecure -XPOST --connect-timeout 15 --max-time 120 https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/restart
   fi
 fi
 

+ 2 - 1
data/Dockerfiles/netfilter/Dockerfile

@@ -1,5 +1,6 @@
 FROM alpine:3.20
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 WORKDIR /app
 

+ 2 - 1
data/Dockerfiles/olefy/Dockerfile

@@ -1,5 +1,6 @@
 FROM alpine:3.20
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG PIP_BREAK_SYSTEM_PACKAGES=1
 WORKDIR /app

+ 2 - 1
data/Dockerfiles/phpfpm/Dockerfile

@@ -1,5 +1,6 @@
 FROM php:8.2-fpm-alpine3.18
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 # renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
 ARG APCU_PECL_VERSION=5.1.23

+ 5 - 5
data/Dockerfiles/phpfpm/docker-entrypoint.sh

@@ -23,7 +23,7 @@ done
 # Check mysql_upgrade (master and slave)
 CONTAINER_ID=
 until [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ ^[[:alnum:]]*$ ]]; do
-  CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" 2> /dev/null)
+  CONTAINER_ID=$(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" 2> /dev/null)
   echo "Could not get mysql-mailcow container id... trying again"
   sleep 2
 done
@@ -35,7 +35,7 @@ until [[ ${SQL_UPGRADE_STATUS} == 'success' ]]; do
     echo "Tried to upgrade MySQL and failed, giving up after ${SQL_LOOP_C} retries and starting container (oops, not good)"
     break
   fi
-  SQL_FULL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json')
+  SQL_FULL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json')
   SQL_UPGRADE_STATUS=$(echo ${SQL_FULL_UPGRADE_RETURN} | jq -r .type)
   SQL_LOOP_C=$((SQL_LOOP_C+1))
   echo "SQL upgrade iteration #${SQL_LOOP_C}"
@@ -60,12 +60,12 @@ done
 
 # doing post-installation stuff, if SQL was upgraded (master and slave)
 if [ ${SQL_CHANGED} -eq 1 ]; then
-  POSTFIX=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"postfix-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" 2> /dev/null)
+  POSTFIX=$(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"postfix-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id" 2> /dev/null)
   if [[ -z "${POSTFIX}" ]] || ! [[ "${POSTFIX}" =~ ^[[:alnum:]]*$ ]]; then
     echo "Could not determine Postfix container ID, skipping Postfix restart."
   else
     echo "Restarting Postfix"
-    curl -X POST --silent --insecure https://dockerapi/containers/${POSTFIX}/restart | jq -r '.msg'
+    curl -X POST --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${POSTFIX}/restart | jq -r '.msg'
     echo "Sleeping 5 seconds..."
     sleep 5
   fi
@@ -74,7 +74,7 @@ fi
 # Check mysql tz import (master and slave)
 TZ_CHECK=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT CONVERT_TZ('2019-11-02 23:33:00','Europe/Berlin','UTC') AS time;" -BN 2> /dev/null)
 if [[ -z ${TZ_CHECK} ]] || [[ "${TZ_CHECK}" == "NULL" ]]; then
-  SQL_FULL_TZINFO_IMPORT_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_tzinfo_to_sql"}' --silent -H 'Content-type: application/json')
+  SQL_FULL_TZINFO_IMPORT_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_tzinfo_to_sql"}' --silent -H 'Content-type: application/json')
   echo "MySQL mysql_tzinfo_to_sql - debug output:"
   echo ${SQL_FULL_TZINFO_IMPORT_RETURN}
 fi

+ 2 - 1
data/Dockerfiles/postfix/Dockerfile

@@ -1,5 +1,6 @@
 FROM debian:bookworm-slim
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG DEBIAN_FRONTEND=noninteractive
 ENV LC_ALL C

+ 7 - 6
data/Dockerfiles/rspamd/Dockerfile

@@ -1,10 +1,10 @@
-FROM debian:bullseye-slim
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+FROM debian:bookworm-slim
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG DEBIAN_FRONTEND=noninteractive
-ARG RSPAMD_VER=rspamd_3.7.5-2~8c86c1676   
-ARG CODENAME=bullseye
-ENV LC_ALL C
+ARG RSPAMD_VER=rspamd_3.9.1-1~82f43560f
+ARG CODENAME=bookworm
+ENV LC_ALL=C
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
   tzdata \
@@ -12,11 +12,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
   gnupg2 \
   apt-transport-https \
   dnsutils \
-  netcat \
+  netcat-traditional \
   wget \
   redis-tools \ 
   procps \ 
   nano \
+  lua-cjson \
   && arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \
   && wget -P /tmp https://rspamd.com/apt-stable/pool/main/r/rspamd/${RSPAMD_VER}~${CODENAME}_${arch}.deb\
   && apt install -y /tmp/${RSPAMD_VER}~${CODENAME}_${arch}.deb \

+ 186 - 0
data/Dockerfiles/rspamd/docker-entrypoint.sh

@@ -124,4 +124,190 @@ for file in /hooks/*; do
   fi
 done
 
+# If DQS KEY is set in mailcow.conf add Spamhaus DQS RBLs
+if [[ ! -z ${SPAMHAUS_DQS_KEY} ]]; then
+    cat <<EOF > /etc/rspamd/custom/dqs-rbl.conf
+  # Autogenerated by mailcow. DO NOT TOUCH!
+    spamhaus {
+        rbl = "${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net";
+        from = false;
+    }
+    spamhaus_from {
+        from = true;
+        received = false;
+        rbl = "${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net";
+        returncodes {
+          SPAMHAUS_ZEN = [ "127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.0.9", "127.0.0.10", "127.0.0.11" ];
+        }
+    }
+    spamhaus_authbl_received {
+        # Check if the sender client is listed in AuthBL (AuthBL is *not* part of ZEN)
+        rbl = "${SPAMHAUS_DQS_KEY}.authbl.dq.spamhaus.net";
+        from = false;
+        received = true;
+        ipv6 = true;
+        returncodes {
+          SH_AUTHBL_RECEIVED = "127.0.0.20"
+        }
+    }
+    spamhaus_dbl {
+        # Add checks on the HELO string
+        rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
+        helo = true;
+        rdns = true;
+        dkim = true;
+        disable_monitoring = true;
+        returncodes {
+            RBL_DBL_SPAM = "127.0.1.2";
+            RBL_DBL_PHISH = "127.0.1.4";
+            RBL_DBL_MALWARE = "127.0.1.5";
+            RBL_DBL_BOTNET = "127.0.1.6";
+            RBL_DBL_ABUSED_SPAM = "127.0.1.102";
+            RBL_DBL_ABUSED_PHISH = "127.0.1.104";
+            RBL_DBL_ABUSED_MALWARE = "127.0.1.105";
+            RBL_DBL_ABUSED_BOTNET = "127.0.1.106";
+            RBL_DBL_DONT_QUERY_IPS = "127.0.1.255";
+        }
+    }
+    spamhaus_dbl_fullurls {
+        ignore_defaults = true;
+        no_ip = true;
+        rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
+        selector = 'urls:get_host'
+        disable_monitoring = true;
+        returncodes {
+            DBLABUSED_SPAM_FULLURLS = "127.0.1.102";
+            DBLABUSED_PHISH_FULLURLS = "127.0.1.104";
+            DBLABUSED_MALWARE_FULLURLS = "127.0.1.105";
+            DBLABUSED_BOTNET_FULLURLS = "127.0.1.106";
+        }
+    }
+    spamhaus_zrd {
+        # Add checks on the HELO string also for DQS
+        rbl = "${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net";
+        helo = true;
+        rdns = true;
+        dkim = true;
+        disable_monitoring = true;
+        returncodes {
+            RBL_ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
+            RBL_ZRD_FRESH_DOMAIN = [
+              "127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"
+            ];
+            RBL_ZRD_DONT_QUERY_IPS = "127.0.2.255";
+        }
+    }
+    "SPAMHAUS_ZEN_URIBL" {
+      enabled = true;
+      rbl = "${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net";
+      resolve_ip = true;
+      checks = ['urls'];
+      replyto = true;
+      emails = true;
+      ipv4 = true;
+      ipv6 = true;
+      emails_domainonly = true;
+      returncodes {
+        URIBL_SBL = "127.0.0.2";
+        URIBL_SBL_CSS = "127.0.0.3";
+        URIBL_XBL = ["127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7"];
+        URIBL_PBL = ["127.0.0.10", "127.0.0.11"];
+        URIBL_DROP = "127.0.0.9";
+      }
+    }
+    SH_EMAIL_DBL {
+      ignore_defaults = true;
+      replyto = true;
+      emails_domainonly = true;
+      disable_monitoring = true;
+      rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
+      returncodes = {
+        SH_EMAIL_DBL = [
+          "127.0.1.2",
+          "127.0.1.4",
+          "127.0.1.5",
+          "127.0.1.6"
+        ];
+        SH_EMAIL_DBL_ABUSED = [
+          "127.0.1.102",
+          "127.0.1.104",
+          "127.0.1.105",
+          "127.0.1.106"
+        ];
+        SH_EMAIL_DBL_DONT_QUERY_IPS = [ "127.0.1.255" ];
+      }
+    }
+    SH_EMAIL_ZRD {
+      ignore_defaults = true;
+      replyto = true;
+      emails_domainonly = true;
+      disable_monitoring = true;
+      rbl = "${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net";
+      returncodes = {
+        SH_EMAIL_ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
+        SH_EMAIL_ZRD_FRESH_DOMAIN = [
+          "127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"
+        ];
+        SH_EMAIL_ZRD_DONT_QUERY_IPS = [ "127.0.2.255" ];
+      }
+    }
+    "DBL" {
+        # override the defaults for DBL defined in modules.d/rbl.conf
+        rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
+        disable_monitoring = true;
+    }
+    "ZRD" {
+        ignore_defaults = true;
+        rbl = "${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net";
+        no_ip = true;
+        dkim = true;
+        emails = true;
+        emails_domainonly = true;
+        urls = true;
+        returncodes = {
+            ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
+            ZRD_FRESH_DOMAIN = ["127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"];
+        }
+    }
+    spamhaus_sbl_url {
+        ignore_defaults = true
+        rbl = "${SPAMHAUS_DQS_KEY}.sbl.dq.spamhaus.net";
+        checks = ['urls'];
+        disable_monitoring = true;
+        returncodes {
+            SPAMHAUS_SBL_URL = "127.0.0.2";
+        }
+    }
+
+    SH_HBL_EMAIL {
+      ignore_defaults = true;
+      rbl = "_email.${SPAMHAUS_DQS_KEY}.hbl.dq.spamhaus.net";
+      emails_domainonly = false;
+      selector = "from('smtp').lower;from('mime').lower";
+      ignore_whitelist = true;
+      checks = ['emails', 'replyto'];
+      hash = "sha1";
+      returncodes = {
+        SH_HBL_EMAIL = [
+          "127.0.3.2"
+        ];
+      }
+    }
+
+    spamhaus_dqs_hbl {
+      symbol = "HBL_FILE_UNKNOWN";
+      rbl = "_file.${SPAMHAUS_DQS_KEY}.hbl.dq.spamhaus.net.";
+      selector = "attachments('rbase32', 'sha256')";
+      ignore_whitelist = true;
+      ignore_defaults = true;
+      returncodes {
+        SH_HBL_FILE_MALICIOUS = "127.0.3.10";
+        SH_HBL_FILE_SUSPICIOUS = "127.0.3.15";
+      }
+    }
+EOF
+else
+  rm -rf /etc/rspamd/custom/dqs-rbl.conf
+fi
+
 exec "$@"

+ 2 - 1
data/Dockerfiles/sogo/Dockerfile

@@ -1,5 +1,6 @@
 FROM debian:bullseye-slim
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG DEBIAN_FRONTEND=noninteractive
 ARG DEBIAN_VERSION=bullseye

+ 1 - 1
data/Dockerfiles/unbound/Dockerfile

@@ -1,6 +1,6 @@
 FROM alpine:3.20
 
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 RUN apk add --update --no-cache \
 	curl \

+ 2 - 1
data/Dockerfiles/watchdog/Dockerfile

@@ -1,5 +1,6 @@
 FROM alpine:3.20
-LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
+
+LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
 
 # Installation
 RUN apk add --update \

+ 7 - 7
data/Dockerfiles/watchdog/watchdog.sh

@@ -191,12 +191,12 @@ get_container_ip() {
     else
       sleep 0.5
       # get long container id for exact match
-      CONTAINER_ID=($(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring == \"${1}\") | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id"))
+      CONTAINER_ID=($(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring == \"${1}\") | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id"))
       # returned id can have multiple elements (if scaled), shuffle for random test
       CONTAINER_ID=($(printf "%s\n" "${CONTAINER_ID[@]}" | shuf))
       if [[ ! -z ${CONTAINER_ID} ]]; then
         for matched_container in "${CONTAINER_ID[@]}"; do
-          CONTAINER_IPS=($(curl --silent --insecure https://dockerapi/containers/${matched_container}/json | jq -r '.NetworkSettings.Networks[].IPAddress'))
+          CONTAINER_IPS=($(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${matched_container}/json | jq -r '.NetworkSettings.Networks[].IPAddress'))
           for ip_match in "${CONTAINER_IPS[@]}"; do
             # grep will do nothing if one of these vars is empty
             [[ -z ${ip_match} ]] && continue
@@ -716,7 +716,7 @@ rspamd_checks() {
 From: watchdog@localhost
 
 Empty
-' | usr/bin/curl --max-time 10 -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/scan | jq -rc .default.required_score | sed 's/\..*//' )
+' | usr/bin/curl --max-time 10 -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd.${COMPOSE_PROJECT_NAME}_mailcow-network/scan | jq -rc .default.required_score | sed 's/\..*//' )
     if [[ ${SCORE} -ne 9999 ]]; then
       echo "Rspamd settings check failed, score returned: ${SCORE}" 2>> /tmp/rspamd-mailcow 1>&2
       err_count=$(( ${err_count} + 1))
@@ -1095,12 +1095,12 @@ while true; do
   elif [[ ${com_pipe_answer} =~ .+-mailcow ]]; then
     kill -STOP ${BACKGROUND_TASKS[*]}
     sleep 10
-    CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${com_pipe_answer}\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id")
+    CONTAINER_ID=$(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${com_pipe_answer}\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id")
     if [[ ! -z ${CONTAINER_ID} ]]; then
       if [[ "${com_pipe_answer}" == "php-fpm-mailcow" ]]; then
-        HAS_INITDB=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/top | jq '.msg.Processes[] | contains(["php -c /usr/local/etc/php -f /web/inc/init_db.inc.php"])' | grep true)
+        HAS_INITDB=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/top | jq '.msg.Processes[] | contains(["php -c /usr/local/etc/php -f /web/inc/init_db.inc.php"])' | grep true)
       fi
-      S_RUNNING=$(($(date +%s) - $(curl --silent --insecure https://dockerapi/containers/${CONTAINER_ID}/json | jq .State.StartedAt | xargs -n1 date +%s -d)))
+      S_RUNNING=$(($(date +%s) - $(curl --silent --insecure https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/json | jq .State.StartedAt | xargs -n1 date +%s -d)))
       if [ ${S_RUNNING} -lt 360 ]; then
         log_msg "Container is running for less than 360 seconds, skipping action..."
       elif [[ ! -z ${HAS_INITDB} ]]; then
@@ -1108,7 +1108,7 @@ while true; do
         sleep 60
       else
         log_msg "Sending restart command to ${CONTAINER_ID}..."
-        curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/restart
+        curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/restart
         notify_error "${com_pipe_answer}"
         log_msg "Wait for restarted container to settle and continue watching..."
         sleep 35

+ 16 - 1
data/conf/dovecot/dovecot.folders.conf

@@ -289,5 +289,20 @@ namespace inbox {
   mailbox "Kladde" {
     special_use = \Drafts
   }
+  mailbox "Πρόχειρα" {
+    special_use = \Drafts
+  }
+  mailbox "Απεσταλμένα" {
+    special_use = \Sent
+  }
+  mailbox "Κάδος απορριμάτων" {
+    special_use = \Trash
+  }
+  mailbox "Ανεπιθύμητα" {
+    special_use = \Junk
+  }
+  mailbox "Αρχειοθετημένα" {
+    special_use = \Archive
+  }
   prefix =
-}
+}

+ 5 - 1
data/conf/rspamd/local.d/rbl.conf

@@ -2,6 +2,7 @@ rbls {
   interserver_ip {
     symbol = "RBL_INTERSERVER_IP";
     rbl = "rbl.interserver.net";
+    from = true;
     ipv6 = false;
     returncodes {
       RBL_INTERSERVER_BAD_IP = "127.0.0.2";
@@ -19,4 +20,7 @@ rbls {
       RBL_INTERSERVER_BAD_URI = "127.0.0.2";
     }
   }
-}
+
+.include(try=true,override=true,priority=5) "$LOCAL_CONFDIR/custom/dqs-rbl.conf"  
+
+}

+ 257 - 0
data/conf/rspamd/local.d/rbl_group.conf

@@ -17,4 +17,261 @@ symbols = {
     score = 4.0;
     description = "Listed on Interserver RBL";
   }
+
+  "SPAMHAUS_ZEN" {
+      weight = 7.0;
+      }
+  "SH_AUTHBL_RECEIVED" {
+      weight = 4.0;
+      }
+  "RBL_DBL_SPAM" {
+      weight = 7.0;
+      }
+  "RBL_DBL_PHISH" {
+      weight = 7.0;
+      }
+  "RBL_DBL_MALWARE" {
+      weight = 7.0;
+      }
+  "RBL_DBL_BOTNET" {
+      weight = 7.0;
+      }
+  "RBL_DBL_ABUSED_SPAM" {
+      weight = 3.0;
+      }
+  "RBL_DBL_ABUSED_PHISH" {
+      weight = 3.0;
+      }
+  "RBL_DBL_ABUSED_MALWARE" {
+      weight = 3.0;
+      }
+  "RBL_DBL_ABUSED_BOTNET" {
+      weight = 3.0;
+      }
+  "RBL_ZRD_VERY_FRESH_DOMAIN" {
+      weight = 7.0;
+      }
+  "RBL_ZRD_FRESH_DOMAIN" {
+      weight = 4.0;
+      }
+  "ZRD_VERY_FRESH_DOMAIN" {
+      weight = 7.0;
+      }
+  "ZRD_FRESH_DOMAIN" {
+      weight = 4.0;
+      }
+  "SH_EMAIL_DBL" {
+      weight = 7.0;
+      }
+  "SH_EMAIL_DBL_ABUSED" {
+      weight = 7.0;
+      }
+  "SH_EMAIL_ZRD_VERY_FRESH_DOMAIN" {
+      weight = 7.0;
+      }
+  "SH_EMAIL_ZRD_FRESH_DOMAIN" {
+      weight = 4.0;
+      }
+  "RBL_DBL_DONT_QUERY_IPS" {
+      weight = 0.0;
+      }
+  "RBL_ZRD_DONT_QUERY_IPS" {
+      weight = 0.0;
+      }
+  "SH_EMAIL_ZRD_DONT_QUERY_IPS" {
+      weight = 0.0;
+      }
+  "SH_EMAIL_DBL_DONT_QUERY_IPS" {
+      weight = 0.0;
+      }
+  "DBL" {
+      weight = 0.0;
+      description = "DBL unknown result";
+      groups = ["spamhaus"];
+  }
+  "DBL_SPAM" {
+      weight = 7;
+      description = "DBL uribl spam";
+      groups = ["spamhaus"];
+  }
+  "DBL_PHISH" {
+      weight = 7;
+      description = "DBL uribl phishing";
+      groups = ["spamhaus"];
+  }
+  "DBL_MALWARE" {
+      weight = 7;
+      description = "DBL uribl malware";
+      groups = ["spamhaus"];
+  }
+  "DBL_BOTNET" {
+      weight = 7;
+      description = "DBL uribl botnet C&C domain";
+      groups = ["spamhaus"];
+  }
+
+
+  "DBLABUSED_SPAM_FULLURLS" {
+      weight = 5.5;
+      description = "DBL uribl abused legit spam";
+      groups = ["spamhaus"];
+  }
+  "DBLABUSED_PHISH_FULLURLS" {
+      weight = 5.5;
+      description = "DBL uribl abused legit phish";
+      groups = ["spamhaus"];
+  }
+  "DBLABUSED_MALWARE_FULLURLS" {
+      weight = 5.5;
+      description = "DBL uribl abused legit malware";
+      groups = ["spamhaus"];
+  }
+  "DBLABUSED_BOTNET_FULLURLS" {
+      weight = 5.5;
+      description = "DBL uribl abused legit botnet";
+      groups = ["spamhaus"];
+  }
+  
+  "DBL_ABUSE" {
+      weight = 5.5;
+      description = "DBL uribl abused legit spam";
+      groups = ["spamhaus"];
+  }
+  "DBL_ABUSE_REDIR" {
+      weight = 1.5;
+      description = "DBL uribl abused spammed redirector domain";
+      groups = ["spamhaus"];
+  }
+  "DBL_ABUSE_PHISH" {
+      weight = 5.5;
+      description = "DBL uribl abused legit phish";
+      groups = ["spamhaus"];
+  }
+  "DBL_ABUSE_MALWARE" {
+      weight = 5.5;
+      description = "DBL uribl abused legit malware";
+      groups = ["spamhaus"];
+  }
+  "DBL_ABUSE_BOTNET" {
+      weight = 5.5;
+      description = "DBL uribl abused legit botnet C&C";
+      groups = ["spamhaus"];
+  }
+  "DBL_PROHIBIT" {
+      weight = 0.0;
+      description = "DBL uribl IP queries prohibited!";
+      groups = ["spamhaus"];
+  }
+  "DBL_BLOCKED_OPENRESOLVER" {
+    weight = 0.0;
+    description = "You are querying Spamhaus from an open resolver, please see https://www.spamhaus.org/returnc/pub/";
+    groups = ["spamhaus"];
+  }
+  "DBL_BLOCKED" {
+    weight = 0.0;
+    description = "You are exceeding the query limit, please see https://www.spamhaus.org/returnc/vol/";
+    groups = ["spamhaus"];
+  }
+  "SPAMHAUS_ZEN_URIBL" {
+      weight = 0.0;
+      description = "Spamhaus ZEN URIBL: Filtered result";
+      groups = ["spamhaus"];
+  }
+  "URIBL_SBL" {
+      weight = 6.5;
+      description = "A domain in the message body resolves to an IP listed in Spamhaus SBL";
+      one_shot = true;
+      groups = ["spamhaus"];
+  }
+  "URIBL_SBL_CSS" {
+      weight = 6.5;
+      description = "A domain in the message body resolves to an IP listed in Spamhaus SBL CSS";
+      one_shot = true;
+      groups = ["spamhaus"];
+  }
+  "URIBL_PBL" {
+      weight = 0.01;
+      description = "A domain in the message body resolves to an IP listed in Spamhaus PBL";
+      one_shot = true;
+      groups = ["spamhaus"];
+  }
+  "URIBL_DROP" {
+      weight = 6.5;
+      description = "A domain in the message body resolves to an IP listed in Spamhaus DROP";
+      one_shot = true;
+      groups = ["spamhaus"];
+  }
+  "URIBL_XBL" {
+      weight = 5.0;
+      description = "A domain in the message body resolves to an IP listed in Spamhaus XBL";
+      one_shot = true;
+      groups = ["spamhaus"];
+  }
+  "SPAMHAUS_SBL_URL" {
+      weight = 6.5;
+      description = "A numeric URL in the message body is listed in Spamhaus SBL";
+      one_shot = true;
+      groups = ["spamhaus"];
+  }
+
+  "SH_HBL_EMAIL" {
+      weight = 7;
+      description = "Email listed in HBL";
+      groups = ["spamhaus"];
+  }
+
+  "SH_HBL_FILE_MALICIOUS" {
+      weight = 7;
+      description = "An attachment hash is listed in Spamhaus HBL as malicious";
+      groups = ["spamhaus"];
+  }
+
+  "SH_HBL_FILE_SUSPICIOUS" {
+      weight = 5;
+      description = "An attachment hash is listed in Spamhaus HBL as suspicious";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_CW_BTC" {
+      score = 7;
+      description = "Bitcoin found in Spamhaus cryptowallet list";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_CW_ETH" {
+      score = 7;
+      description = "Ethereum found in Spamhaus cryptowallet list";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_CW_BCH" {
+      score = 7;
+      description = "Bitcoinhash found in Spamhaus cryptowallet list";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_CW_XMR" {
+      score = 7;
+      description = "Monero found in Spamhaus cryptowallet list";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_CW_LTC" {
+      score = 7;
+      description = "Litecoin found in Spamhaus cryptowallet list";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_CW_XRP" {
+      score = 7;
+      description = "Ripple found in Spamhaus cryptowallet list";
+      groups = ["spamhaus"];
+  }
+
+  "RBL_SPAMHAUS_HBL_URL" {
+      score = 7;
+      description = "URL found in spamhaus HBL blocklist";
+      groups = ["spamhaus"];
+  }
+
 }

+ 8 - 0
data/web/autoconfig.php

@@ -39,6 +39,9 @@ header('Content-Type: application/xml');
          <username>%EMAILADDRESS%</username>
          <authentication>password-cleartext</authentication>
       </incomingServer>
+<?php
+$records = dns_get_record('_imap._tcp.' . $domain, DNS_SRV); // check if IMAP is announced as "not provided" via SRV record
+if (count($records) == 0 || $records[0]['target'] != '') { ?>
       <incomingServer type="imap">
          <hostname><?=$autodiscover_config['imap']['server']; ?></hostname>
          <port><?=$autodiscover_config['imap']['tlsport']; ?></port>
@@ -46,6 +49,7 @@ header('Content-Type: application/xml');
          <username>%EMAILADDRESS%</username>
          <authentication>password-cleartext</authentication>
       </incomingServer>
+<?php } ?>
 
 <?php
 $records = dns_get_record('_pop3s._tcp.' . $domain, DNS_SRV); // check if POP3 is announced as "not provided" via SRV record
@@ -77,6 +81,9 @@ if (count($records) == 0 || $records[0]['target'] != '') { ?>
          <username>%EMAILADDRESS%</username>
          <authentication>password-cleartext</authentication>
       </outgoingServer>
+<?php
+$records = dns_get_record('_smtp._tcp.' . $domain, DNS_SRV); // check if SMTP is announced as "not provided" via SRV record
+if (count($records) == 0 || $records[0]['target'] != '') { ?>
       <outgoingServer type="smtp">
          <hostname><?=$autodiscover_config['smtp']['server']; ?></hostname>
          <port><?=$autodiscover_config['smtp']['tlsport']; ?></port>
@@ -84,6 +91,7 @@ if (count($records) == 0 || $records[0]['target'] != '') { ?>
          <username>%EMAILADDRESS%</username>
          <authentication>password-cleartext</authentication>
       </outgoingServer>
+<?php } ?>
 
       <enable visiturl="https://<?=$mailcow_hostname; ?><?php if ($port != 443) echo ':'.$port; ?>/admin.php">
          <instruction>If you didn't change the password given to you by the administrator or if you didn't change it in a long time, please consider doing that now.</instruction>

+ 33 - 0
data/web/inc/lib/sieve/extensions/enotify.xml

@@ -0,0 +1,33 @@
+<?xml version='1.0' standalone='yes'?>
+
+<extension name="enotify">
+    <command name="notify">
+        <parameter type="tag" name="from" occurrence="optional">
+            <parameter type="string" name="from-address" />
+        </parameter>
+
+        <parameter type="tag" name="importance" regex="(1|2|3)" occurrence="optional" />
+
+        <parameter type="tag" name="options" occurrence="optional">
+            <parameter type="stringlist" name="option-strings" />
+        </parameter>
+
+        <parameter type="tag" name="message" occurrence="optional">
+            <parameter type="string" name="message-text" />
+        </parameter>
+
+        <parameter type="string" name="method" />
+    </command>
+
+    <test name="valid_notify_method">
+        <parameter type="stringlist" name="notification-uris" />
+    </test>
+
+    <test name="notify_method_capability">
+        <parameter type="string" name="notification-uri" />
+        <parameter type="string" name="notification-capability" />
+        <parameter type="stringlist" name="key-list" />
+    </test>
+
+    <modifier name="encodeurl" />
+</extension>

+ 58 - 0
data/web/inc/lib/sieve/extensions/mime.xml

@@ -0,0 +1,58 @@
+<?xml version='1.0' standalone='yes'?>
+
+<extension name="mime">
+    <command name="foreverypart">
+        <parameter type="string" name="name" occurrence="optional" />
+        <block />
+    </command>
+
+    <command name="break">
+        <parameter type="string" name="name" occurrence="optional" />
+    </command>
+
+    <tagged-argument extends="(header|address|exists)">
+        <parameter type="tag" name="mime" regex="mime" occurrence="optional" />
+    </tagged-argument>
+    <tagged-argument extends="(header|address|exists)">
+        <parameter type="tag" name="anychild" regex="anychild" occurrence="optional" />
+    </tagged-argument>
+    <tagged-argument extends="(header)">
+        <parameter type="tag" name="type" occurrence="optional" />
+    </tagged-argument>
+    <tagged-argument extends="(header)">
+        <parameter type="tag" name="subtype" occurrence="optional" />
+    </tagged-argument>
+    <tagged-argument extends="(header)">
+        <parameter type="tag" name="contenttype" occurrence="optional" />
+    </tagged-argument>
+    <tagged-argument extends="(header)">
+        <parameter type="tag" name="param" regex="param" occurrence="optional">
+            <parameter type="stringlist" name="param-list" />
+        </parameter>
+    </tagged-argument>
+    <tagged-argument extends="(header|address|exists)">
+        <parameter type="stringlist" name="header-names" />
+    </tagged-argument>
+    <tagged-argument extends="(header)">
+        <parameter type="stringlist" name="key-list" />
+    </tagged-argument>
+
+    <action name="replace">
+        <parameter type="tag" name="mime" regex="mime" occurrence="optional" />
+        <parameter type="string" name="subject" occurrence="optional" />
+        <parameter type="string" name="from" occurrence="optional" />
+        <parameter type="string" name="replacement" />
+    </action>
+
+    <action name="enclose">
+        <parameter type="string" name="subject" occurrence="optional" />
+        <parameter type="stringlist" name="headers" occurrence="optional" />
+        <parameter type="string" name="text" />
+    </action>
+
+    <action name="extracttext">
+        <parameter type="tag" name="first" regex="first" occurrence="optional" />
+        <parameter type="number" name="number" occurrence="optional" />
+        <parameter type="string" name="varname" />
+    </action>
+</extension>

+ 143 - 0
data/web/lang/lang.nb-no.json

@@ -322,5 +322,148 @@
         "invalid_nexthop": "\"Next hop\"-format er ugyldig",
         "img_dimensions_exceeded": "Bildet overskriver maksimal bildestørrelse",
         "img_size_exceeded": "Bildet overskrider maksimal filstørrelse"
+    },
+    "debug": {
+        "logs": "Logger",
+        "update_available": "En oppdatering er tilgjengelig",
+        "service": "Tjeneste",
+        "show_ip": "Vis offentlig IP",
+        "solr_dead": "Solr starter, er deaktivert eller døde",
+        "memory": "Minne",
+        "online_users": "Tilkoblede brukere",
+        "restart_container": "Omstart",
+        "size": "Størrelse",
+        "solr_status": "Solr-status",
+        "started_at": "Startet ved",
+        "started_on": "Startet den",
+        "static_logs": "Statiske logger",
+        "success": "Suksess",
+        "system_containers": "System og kontainere",
+        "timezone": "Tidssone",
+        "uptime": "Oppetid",
+        "no_update_available": "Systemet kjører siste versjon",
+        "update_failed": "Kunne ikke se etter oppdateringer",
+        "username": "Brukernavn",
+        "wip": "Foreløpig under utvikling"
+    },
+    "diagnostics": {
+        "dns_records_24hours": "Vennligst vær oppmerksom på at endringer gjort i DNS kan ta opp til 24 timer før riktig status vises på denne siden. Den er ment som en måte for deg å enkelt se hvordan du kan sette opp DNS-oppføringene dine og se at alle oppføringer er korrekt lagret i DNS.",
+        "cname_from_a": "Verdi hentet fra A/AAAA-oppføring. Dette er støttet så lenge oppføringen peker til riktig ressurs.",
+        "dns_records_docs": "Vennligst også se <a target=\"_blank\" href=\"https://docs.mailcow.email/getstarted/prerequisite-dns\">dokumentasjonen</a>.",
+        "dns_records": "DNS-oppføringer",
+        "dns_records_data": "Korrekte data",
+        "dns_records_name": "Navn",
+        "dns_records_status": "Nåværende status",
+        "dns_records_type": "Type",
+        "optional": "Denne oppføringen er valgfri."
+    },
+    "edit": {
+        "bcc_dest_format": "BCC-destinasjon må være en enkelt, gyldig epostadresse.<br>Hvis du trenger å sende en kopi til flere adresser, opprett et alias og bruk det her.",
+        "pushover_info": "Innstillinger for pushvarslinger vil gjelde alle rene (ikke-spam) eposter levert til <b>%s</b>, inkludert aliaser (delte, ikke-delte, taggede).",
+        "relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportmappinger for et spesifikt mål for dette domenet. Hvis dette ikke er definert, blir det gjort et MX-oppslag.",
+        "delete2duplicates": "Slett duplikater på målvert",
+        "description": "Beskrivelse",
+        "disable_login": "Ikke tillat innlogging (innkommende epost blir likevel mottatt)",
+        "domain_admin": "Endre domeneadministrator",
+        "max_mailboxes": "Maks antall mailbokser",
+        "quota_warning_bcc_info": "Advarsler blir sendt som separate kopier til de følgende mottakerne. Emnet vil få lagt til det korresponderende brukernavnet i parantes, for eksempel: <code>Kvotevarsel (user@example.com)</code>.",
+        "domain_footer_skip_replies": "Ignorer bunntekst ved svar på epost",
+        "extended_sender_acl": "Eksterne avsenderadresser",
+        "extended_sender_acl_info": "En DKIM-domenenøkkel bør importeres, hvis tilgjengelig.<br>\n  Husk å legge denne serveren til den korresponderende SPF TXT-oppføringen.<br>\n  Når et domene eller aliasdomene legges til på denne serveren, og det overlapper med en ekstern adresse, blir den eksterne adressen fjernet.<br>\n  Bruk @domain.tld for å tillate sending som *@domain.tld.",
+        "password_repeat": "Bekreft passord (gjenta)",
+        "pushover_title": "Varslingstittel",
+        "pushover_vars": "Når et avsenderfilter ikke er definert, vil alle epostere bli vurdert.<br>Regex-filtre såvel så eksakte avsendersjekker kan bli individuelt definert og vil bli vurdert sekvensielt. De er ikke avhengige av hverandre.<br>Tilgjengelige variabler for tekst og tittel (vennligst observer policyer for databeskyttelse)",
+        "admin": "Endre administrator",
+        "domain_footer_info": "Bunntekst for hele domenet leggese til alle utgående eposter assosiert med en adresse innenfor dette domenet. <br> De følgende variablene kan brukes for bunnteksten:",
+        "domain_footer_info_vars": {
+            "custom": "{= foo =}         - Hvis mailboksen har en spesialattributt \"foo\" med verdi \"bar\", vil den vise \"bar\"",
+            "auth_user": "{= auth_user =}   - Autentisert brukernavn spesifisert av en MTA",
+            "from_user": "{= from_user =}   - Fra brukerdelen av konvolutten, f.eks. for \"moo@mailcow.tld\" vil den returnere \"moo\"",
+            "from_name": "{= from_name =}   - Fra-navn fra konvolutten, f.eks. for \"Mailcow &lt;moo@mailcow.tld&gt;\" vil den vise \"Mailcow\"",
+            "from_addr": "{= from_addr =}   - Fra adressedelen av konvolutten",
+            "from_domain": "{= from_domain =} - Fra domene-delen av konvolutten"
+        },
+        "mailbox_relayhost_info": "Aktiveres kun for mailboksen og direkte aliaser, overstyrer domene-videresendingsvert.",
+        "mbox_rl_info": "Denne begrensningen gjelder for SASL-innloggingsnavnet, dersom det er likt noen \"from\"-adresser benyttet av den innloggede brukeren. En mailboks-begrensning overstyrer en domene-begrensning.",
+        "allow_from_smtp_info": "La stå tom for å tillate alle avsendere.<br>IPv4/IPv6-adresser og -nettverk.",
+        "domain": "Endre domene",
+        "encryption": "Kryptering",
+        "exclude": "Ekskluder objekter (regex)",
+        "footer_exclude": "Ekskluder fra bunntekst",
+        "gal_info": "GAL inneholder alle objeker i et domene og kan ikke redigeres av noen brukere. Ledig/opptatt-informasjon i SOGo mangler dersom den deaktiveres! <b>Start SOGo på nytt for å aktivere endringene.</b>",
+        "grant_types": "Grant-typer",
+        "hostname": "Vertsnavn",
+        "inactive": "Inaktiv",
+        "kind": "Type",
+        "last_modified": "Sist endret",
+        "lookup_mx": "Målet er et regex-uttrykk for å matche mot MX_navnet (<code>*\\.google\\.com</code> for å route all epost som skal til en MX-server som slutter på google.com, via dette målet)",
+        "mailbox": "Endre mailboks",
+        "mailbox_quota_def": "Standardkvote for mailboks",
+        "max_quota": "Maks kvote pr. mailboks (MiB)",
+        "maxage": "Maksimal alder for meldinger, i dager, som vil hentes fra ekstern<br><small>(0 = ignorer alder)</small>",
+        "maxbytespersecond": "Maks bytes pr. sekund <br><small>(0 = ubegrenset)</small>",
+        "pushover_sender_array": "Bare vurder de følgende avsenderadressene <small>(komma-separert)</small>",
+        "pushover_sender_regex": "Vurder følgende avsender-regex",
+        "pushover_text": "Varslingstekst",
+        "pushover_sound": "Lyd",
+        "pushover_verify": "Bekreft identifikasjon",
+        "quota_mb": "Kvote (MiB)",
+        "quota_warning_bcc": "Kvotevarsling BCC",
+        "ratelimit": "Mengdebegrensning",
+        "domain_footer": "Bunntekst for hele domenet",
+        "private_comment": "Privat kommentar",
+        "public_comment": "Offentlig kommentar",
+        "client_id": "Klient-ID",
+        "full_name": "Fullt navn",
+        "gal": "Global adresseliste",
+        "max_aliases": "Maks. antall aliaser",
+        "mins_interval": "Intervall (min)",
+        "multiple_bookings": "Flere bookinger",
+        "none_inherit": "Ingen / arve",
+        "acl": "ACL (rettighet)",
+        "active": "Aktiv",
+        "advanced_settings": "Avanserte innstillinger",
+        "alias": "Endre alias",
+        "allow_from_smtp": "Tillat kun disse IPene å bruke <b>SMTP</b>",
+        "allowed_protocols": "Tillatte protokoller",
+        "app_name": "Appnavn",
+        "app_passwd": "App-passord",
+        "app_passwd_protocols": "Tillatte protokoller for app-passord",
+        "automap": "Prøv å automatisk mappe opp mapper (\"Sent items\", \"Sent\" => \"Sendt\" etc.)",
+        "backup_mx_options": "Videresendingsalternativer",
+        "client_secret": "Klient-hemmelighet",
+        "comment_info": "En privat kommentar er ikke synlig for brukeren, mens en offentlig kommentar vises som et tooltip når man holder muspekeren over det",
+        "created_on": "Opprettet den",
+        "custom_attributes": "Valgfrie attributter",
+        "delete1": "Slett fra kilde når fullført",
+        "delete2": "Slett meldinger på målvert som ikke finnes på kildeverten",
+        "delete_ays": "Vennligst bekreft slettingen.",
+        "domain_footer_html": "HTML-bunntekst",
+        "domain_footer_plain": "PLAIN-bunntekst",
+        "domain_quota": "Domenekvote",
+        "domains": "Domener",
+        "dont_check_sender_acl": "Deaktivere sendersjekk for domene %s (+ aliasdomener)",
+        "edit_alias_domain": "Endre aliasdomene",
+        "force_pw_update": "Tving endring av passord ved neste innlogging",
+        "force_pw_update_info": "Denne brukeren vil bare kunne logge inn på %s. App-passord kan fremdeles brukes.",
+        "generate": "generer",
+        "nexthop": "Neste hopp",
+        "password": "Passord",
+        "previous": "Forrige side",
+        "pushover": "Pushover",
+        "pushover_evaluate_x_prio": "Eskaler mail med høy prioritet [<code>X-Priority: 1</code>]",
+        "pushover_only_x_prio": "Bare vurder epost med høy prioritet [<code>X-Priority: 1</code>]",
+        "redirect_uri": "Omdirigerings-/tilbakekallings-URL",
+        "relay_all": "Videresend alle mottakere",
+        "relay_all_info": "↪ Hvis du velger å <b>ikke</b> videresende alle mottakkere, så må du legge til en (\"blind\") mailboks for hver eneste mottaker det skal videresendes for.",
+        "relay_domain": "Videresend dette domenet",
+        "relay_unknown_only": "Videresend kun ikke-eksisterende mailbokser. Eksisterende mailbokser vil bli levert lokalt.",
+        "relayhost": "Avsender-avhengige transportmetoder",
+        "remove": "Fjern",
+        "resource": "Ressurs",
+        "save": "Lagre endringer",
+        "scope": "Omfang",
+        "sender_acl": "Tillat å sende som",
+        "sender_acl_disabled": "<span class=\"badge fs-6 bg-danger\">Avsender-sjekk er deaktivert</span>"
     }
 }

+ 5 - 4
docker-compose.yml

@@ -80,7 +80,7 @@ services:
             - clamd
 
     rspamd-mailcow:
-      image: mailcow/rspamd:1.96
+      image: mailcow/rspamd:1.97
       stop_grace_period: 30s
       depends_on:
         - dovecot-mailcow
@@ -90,6 +90,7 @@ services:
         - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
         - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
         - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
+        - SPAMHAUS_DQS_KEY=${SPAMHAUS_DQS_KEY:-}
       volumes:
         - ./data/hooks/rspamd:/hooks:Z
         - ./data/conf/rspamd/custom/:/etc/rspamd/custom:z
@@ -110,7 +111,7 @@ services:
             - rspamd
 
     php-fpm-mailcow:
-      image: mailcow/phpfpm:1.87
+      image: mailcow/phpfpm:1.88
       command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
       depends_on:
         - redis-mailcow
@@ -236,7 +237,7 @@ services:
             - sogo
 
     dovecot-mailcow:
-      image: mailcow/dovecot:nightly-20240731
+      image: mailcow/dovecot:nightly-20240807
       depends_on:
         - mysql-mailcow
         - netfilter-mailcow
@@ -424,7 +425,7 @@ services:
           condition: service_started
         unbound-mailcow:
           condition: service_healthy
-      image: mailcow/acme:1.88
+      image: mailcow/acme:1.89
       dns:
         - ${IPV4_NETWORK:-172.22.1}.254
       environment:

+ 44 - 30
generate_config.sh

@@ -25,6 +25,16 @@ for bin in openssl curl docker git awk sha1sum grep cut; do
   if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
 done
 
+# Check Docker Version (need at least 24.X)
+docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | cut -d '.' -f 1)
+
+if [[ $docker_version -lt 24 ]]; then
+  echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m"
+  echo -e "\e[33mmailcow needs a newer Docker version to work properly...\e[0m"
+  echo -e "\e[31mPlease update your Docker installation... exiting\e[0m"
+  exit 1
+fi
+
 if docker compose > /dev/null 2>&1; then
     if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then
       COMPOSE_VERSION=native
@@ -147,40 +157,44 @@ done
 
 MEM_TOTAL=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
 
-if [ ${MEM_TOTAL} -le "2621440" ]; then
-  echo "Installed memory is <= 2.5 GiB. It is recommended to disable ClamAV to prevent out-of-memory situations."
-  echo "ClamAV can be re-enabled by setting SKIP_CLAMD=n in mailcow.conf."
-  read -r -p  "Do you want to disable ClamAV now? [Y/n] " response
-  case $response in
-    [nN][oO]|[nN])
-      SKIP_CLAMD=n
+if [ -z "${SKIP_CLAMD}" ]; then
+  if [ ${MEM_TOTAL} -le "2621440" ]; then
+    echo "Installed memory is <= 2.5 GiB. It is recommended to disable ClamAV to prevent out-of-memory situations."
+    echo "ClamAV can be re-enabled by setting SKIP_CLAMD=n in mailcow.conf."
+    read -r -p  "Do you want to disable ClamAV now? [Y/n] " response
+    case $response in
+      [nN][oO]|[nN])
+        SKIP_CLAMD=n
+        ;;
+      *)
+        SKIP_CLAMD=y
       ;;
-    *)
-      SKIP_CLAMD=y
-    ;;
-  esac
-else
-  SKIP_CLAMD=n
+    esac
+  else
+    SKIP_CLAMD=n
+  fi
 fi
 
-if [ ${MEM_TOTAL} -le "2097152" ]; then
-  echo "Disabling Solr on low-memory system."
-  SKIP_SOLR=y
-elif [ ${MEM_TOTAL} -le "3670016" ]; then
-  echo "Installed memory is <= 3.5 GiB. It is recommended to disable Solr to prevent out-of-memory situations."
-  echo "Solr is a prone to run OOM and should be monitored. The default Solr heap size is 1024 MiB and should be set in mailcow.conf according to your expected load."
-  echo "Solr can be re-enabled by setting SKIP_SOLR=n in mailcow.conf but will refuse to start with less than 2 GB total memory."
-  read -r -p  "Do you want to disable Solr now? [Y/n] " response
-  case $response in
-    [nN][oO]|[nN])
-      SKIP_SOLR=n
+if [ -z "${SKIP_SOLR}" ]; then
+  if [ ${MEM_TOTAL} -le "2097152" ]; then
+    echo "Disabling Solr on low-memory system."
+    SKIP_SOLR=y
+  elif [ ${MEM_TOTAL} -le "3670016" ]; then
+    echo "Installed memory is <= 3.5 GiB. It is recommended to disable Solr to prevent out-of-memory situations."
+    echo "Solr is a prone to run OOM and should be monitored. The default Solr heap size is 1024 MiB and should be set in mailcow.conf according to your expected load."
+    echo "Solr can be re-enabled by setting SKIP_SOLR=n in mailcow.conf but will refuse to start with less than 2 GB total memory."
+    read -r -p  "Do you want to disable Solr now? [Y/n] " response
+    case $response in
+      [nN][oO]|[nN])
+        SKIP_SOLR=n
+        ;;
+      *)
+        SKIP_SOLR=y
       ;;
-    *)
-      SKIP_SOLR=y
-    ;;
-  esac
-else
-  SKIP_SOLR=n
+    esac
+  else
+    SKIP_SOLR=n
+  fi
 fi
 
 if [[ ${SKIP_BRANCH} != y ]]; then

+ 22 - 11
update.sh

@@ -328,6 +328,16 @@ for bin in curl docker git awk sha1sum grep cut; do
   fi  
 done
 
+# Check Docker Version (need at least 24.X)
+docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | cut -d '.' -f 1)
+
+if [[ $docker_version -lt 24 ]]; then
+  echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m"
+  echo -e "\e[33mmailcow needs a newer Docker version to work properly... continuing on your own risk!\e[0m"
+  echo -e "\e[31mPlease update your Docker installation... sleeping 10s\e[0m"
+  sleep 10
+fi
+
 export LC_ALL=C
 DATE=$(date +%Y-%m-%d_%H_%M_%S)
 BRANCH=$(cd ${SCRIPT_DIR}; git rev-parse --abbrev-ref HEAD)
@@ -399,17 +409,18 @@ while (($#)); do
   -f|--force           -   Force update, do not ask questions
   -d|--dev             -   Enables Developer Mode (No Checkout of update.sh for tests)
 '
-    exit 1
+    exit 0
   esac
   shift
 done
 
+[[ ! -f mailcow.conf ]] && { echo -e "\e[31mmailcow.conf is missing! Is mailcow installed?\e[0m"; exit 1;}
+
 chmod 600 mailcow.conf
 source mailcow.conf
 
 detect_docker_compose_command
 
-[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing! Is mailcow installed?"; exit 1;}
 DOTS=${MAILCOW_HOSTNAME//[^.]};
 if [ ${#DOTS} -lt 1 ]; then
   echo -e "\e[31mMAILCOW_HOSTNAME (${MAILCOW_HOSTNAME}) is not a FQDN!\e[0m"
@@ -424,7 +435,7 @@ elif [ ${#DOTS} -eq 1 ]; then
   echo "Find more information about why this message exists here: https://github.com/mailcow/mailcow-dockerized/issues/1572"
   read -r -p "Do you want to proceed anyway? [y/N] " response
   if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
-    echo "OK. Procceding."
+    echo "OK. Proceeding."
   else
     echo "OK. Exiting."
     exit 1
@@ -807,7 +818,7 @@ if ! [ $NEW_BRANCH ]; then
     echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
 
   else
-    echo -e "\e[33mYou are receiving updates from a unsupported branch.\e[0m"
+    echo -e "\e[33mYou are receiving updates from an unsupported branch.\e[0m"
     sleep 1
     echo -e "\e[33mThe mailcow stack might still work but it is recommended to switch to the master branch (stable builds).\e[0m"
     echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
@@ -818,14 +829,14 @@ elif [ $FORCE ]; then
   echo -e "\e[31mPlease rerun the update.sh Script without the --force/-f parameter.\e[0m"
   sleep 1
 elif [ $NEW_BRANCH == "master" ] && [ $CURRENT_BRANCH != "master" ]; then
-  echo -e "\e[33mYou are about to switch your mailcow Updates to the stable (master) branch.\e[0m"
+  echo -e "\e[33mYou are about to switch your mailcow updates to the stable (master) branch.\e[0m"
   sleep 1
-  echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no Data is lost...\e[0m"
+  echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no data is lost...\e[0m"
   sleep 1
-  echo -e "\e[31mWARNING: Please see on GitHub or ask in the communitys if a switch to master is stable or not.
-  In some rear cases a Update back to master can destroy your mailcow configuration in case of Database Upgrades etc.
-  Normally a upgrade back to master should be safe during each full release. 
-  Check GitHub for Database Changes and Update only if there similar to the full release!\e[0m"
+  echo -e "\e[31mWARNING: Please see on GitHub or ask in the community if a switch to master is stable or not.
+  In some rear cases an update back to master can destroy your mailcow configuration such as database upgrade, etc.
+  Normally an upgrade back to master should be safe during each full release.
+  Check GitHub for Database changes and update only if there similar to the full release!\e[0m"
   read -r -p "Are you sure you that want to continue upgrading to the stable (master) branch? [y/N] " response
   if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
     echo "OK. If you prepared yourself for that please run the update.sh Script with the --stable parameter again to trigger this process here."
@@ -972,7 +983,7 @@ if [ ! $DEV ]; then
     echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
     exit 1
   elif [[ ${MERGE_RETURN} == 1 ]]; then
-    echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
+    echo -e "\e[93mPotential conflict, trying to fix...\e[0m"
     git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
     git add -A
     git commit -m "After update on ${DATE}" > /dev/null