Open CouponCodeSwap opened 3 days ago
Any custom changes? Can not reproduce...
Do a git diff origin/master here pls
There is no issue on our side with our testing systems. There is something broken on your machine or due to some custom changes you made i guess
Hmm yes seems to be a issue with Xen Virtualized VMs, kvm VMs work fine.
Have to debug this but we might cut XEN Support if it is layered deeper down...
part 1: git diff origin/master `diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 71cd7eda..11402129 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1 @@ -github: mailcow custom: ["https://www.servercow.de/mailcow?lang=en#sal"] diff --git a/.github/ISSUE_TEMPLATE/pr_to_nighty_template.yml b/.github/ISSUE_TEMPLATE/pr_to_nighty_template.yml index d9f87858..8854ac9d 100644 --- a/.github/ISSUE_TEMPLATE/pr_to_nighty_template.yml +++ b/.github/ISSUE_TEMPLATE/pr_to_nighty_template.yml @@ -1,3 +1,13 @@ +## :memo: Brief description + + + + +## :computer: Commits + + + +
- \ No newline at end of file diff --git a/.github/workflows/rebuild_backup_image.yml b/.github/workflows/rebuild_backup_image.yml index bf5caddf..649d76a1 100644 --- a/.github/workflows/rebuild_backup_image.yml +++ b/.github/workflows/rebuild_backup_image.yml @@ -26,7 +26,7 @@ jobs: password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fae8f1d5..d7a3d86d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,52 +1,33 @@ -# Contribution Guidelines -Last modified on 15th August 2024 +# Contribution Guidelines (Last modified on 18th December 2023)
First of all, thank you for wanting to provide a bugfix or a new feature for the mailcow community, it's because of your help that the project can continue to grow!
-- Pull Requests -- Issue Reporting
-## Pull Requests -Last modified on 15th August 2024 +## Pull Requests (Last modified on 18th December 2023)
However, please note the following regarding pull requests:
feat/
for function updates or fix/
for bug fixes) and the actual content (e.g. sogo-6.0.0
for an update from SOGo to version 6 or html-escape
for a fix that includes escaping HTML in mailcow).
-2. ALWAYS report/request issues/features in the english language, even though mailcow is a german based company. This is done to allow other GitHub users to reply to your issues/requests too which did not speak german or other languages besides english.
-3. Please keep this pull request branch clean and free of commits that have nothing to do with the changes you have made (e.g. commits from other users from other branches). If you make changes to the update.sh
script or other scripts that trigger a commit, there is usually a developer mode for clean working in this case.
-4. Test your changes before you commit them as a pull request. If possible, write a small test log or demonstrate the functionality with a screenshot or GIF. We will of course also test your pull request ourselves, but proof from you will save us the question of whether you have tested your own changes yourself.
-5. Please use the pull request template we provide once creating a pull request. HINT: During editing you encounter comments which looks like: <!-- CONTENT -->
. These can be removed or kept, as they will not rendered later on GitHub! Please only create actual content without the said comments.
-6. Please ALWAYS create the actual pull request against the staging branch and NEVER directly against the master branch. If you forget to do this, our moobot will remind you to switch the branch to staging.
-7. Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
-8. If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!
+2. Please keep this pull request branch clean and free of commits that have nothing to do with the changes you have made (e.g. commits from other users from other branches). If you make changes to the update.sh
script or other scripts that trigger a commit, there is usually a developer mode for clean working in this case.
+3. Test your changes before you commit them as a pull request. If possible, write a small test log or demonstrate the functionality with a screenshot or GIF. We will of course also test your pull request ourselves, but proof from you will save us the question of whether you have tested your own changes yourself.
+4. Please ALWAYS create the actual pull request against the staging branch and NEVER directly against the master branch. If you forget to do this, our moobot will remind you to switch the branch to staging.*
+5. Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
+6. If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!-## Issue Reporting -Last modified on 15th August 2024 +## Issue Reporting (Last modified on 18th December 2023)
If you plan to report a issue within mailcow please read and understand the following rules:
-### Issue Report Guide +3. ONLY report bugs that are contained in the latest mailcow release series. The definition of the latest release series includes the last major patch (e.g. 2023-12) and all minor patches (revisions) below it (e.g. 2023-12a, b, c etc.). New issue reports published starting from January 1, 2024 must meet this criterion, as versions below the latest releases are no longer supported by us. +4. When reporting a problem, please be as detailed as possible and include even the smallest changes to your mailcow installation. Simply fill out the corresponding bug report form in detail and accurately to minimize possible questions. +5. Before you open an issue/feature request, please first check whether a similar request already exists in the mailcow tracker on GitHub. If so, please include yourself in this request. +6. When you create a issue/feature request: Please note that the creation does not guarantee an instant implementation or fix by the mailcow team or the community. +7. Please ALWAYS anonymize any sensitive information in your bug report or feature request before submitting it. + +### Quick guide to reporting problems:
-LABEL maintainer = "The Infrastructure Company GmbH info@servercow.de" +FROM alpine:3.18
+LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
+ARG PIP_BREAK_SYSTEM_PACKAGES=1 RUN apk upgrade --no-cache \ && apk add --update --no-cache \ bash \ @@ -15,7 +15,9 @@ RUN apk upgrade --no-cache \ tini \ tzdata \ python3 \
&& pip3 install acme-tiny
COPY acme.sh /srv/acme.sh COPY functions.sh /srv/functions.sh diff --git a/data/Dockerfiles/acme/acme.sh b/data/Dockerfiles/acme/acme.sh index 3c7658d8..1cd456a4 100755 --- a/data/Dockerfiles/acme/acme.sh +++ b/data/Dockerfiles/acme/acme.sh @@ -33,10 +33,6 @@ if [[ "${ONLY_MAILCOW_HOSTNAME}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then ONLY_MAILCOW_HOSTNAME=y fi
-if [[ "${AUTODISCOVER_SAN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
if [[ "${ENABLE_SSL_SNI}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then ENABLE_SSL_SNI=y @@ -117,13 +113,13 @@ fi chmod 600 ${ACME_BASE}/key.pem
log_f "Waiting for database..." -while ! /usr/bin/mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent > /dev/null; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent > /dev/null; do sleep 2 done log_f "Database OK"
log_f "Waiting for Nginx..." -until $(curl --output /dev/null --silent --head --fail http://nginx.${COMPOSE_PROJECT_NAME}_mailcow-network:8081); do +until $(curl --output /dev/null --silent --head --fail http://nginx:8081); do sleep 2 done log_f "Nginx OK" @@ -137,7 +133,7 @@ log_f "Resolver OK"
log_f "Waiting for domain table..." while [[ -z ${DOMAIN_TABLE} ]]; do
ADDITIONAL_WC_ARR+=('autodiscover' 'autoconfig')
fi
if [[ ${SKIP_IP_CHECK} != "y" ]]; then
diff --git a/data/Dockerfiles/acme/reload-configurations.sh b/data/Dockerfiles/acme/reload-configurations.sh index 8d194b68..d5461a4d 100644 --- a/data/Dockerfiles/acme/reload-configurations.sh +++ b/data/Dockerfiles/acme/reload-configurations.sh @@ -2,32 +2,32 @@
-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" " ")) +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" " "))
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} != '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} != '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} != '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') echo "${C_REST_OUT}" done } diff --git a/data/Dockerfiles/backup/Dockerfile b/data/Dockerfiles/backup/Dockerfile index 61c8bbe5..f9d849b1 100644 --- a/data/Dockerfiles/backup/Dockerfile +++ b/data/Dockerfiles/backup/Dockerfile @@ -1,3 +1,3 @@ -FROM debian:bookworm-slim +FROM debian:bullseye-slim
RUN apt update && apt install pigz \ No newline at end of file diff --git a/data/Dockerfiles/clamd/Dockerfile b/data/Dockerfiles/clamd/Dockerfile index 1850d4be..cdeedfdd 100644 --- a/data/Dockerfiles/clamd/Dockerfile +++ b/data/Dockerfiles/clamd/Dockerfile @@ -1,6 +1,6 @@ -FROM alpine:3.20 +FROM alpine:3.19
-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 \ diff --git a/data/Dockerfiles/dockerapi/Dockerfile b/data/Dockerfiles/dockerapi/Dockerfile index bbd4542e..d11f5dda 100644 --- a/data/Dockerfiles/dockerapi/Dockerfile +++ b/data/Dockerfiles/dockerapi/Dockerfile @@ -1,6 +1,6 @@ -FROM alpine:3.20 +FROM alpine:3.19
-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 @@ -24,4 +24,4 @@ COPY main.py /app/main.py COPY modules/ /app/modules/
ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"] -CMD ["python", "main.py"] \ No newline at end of file +CMD exec python main.py \ No newline at end of file diff --git a/data/Dockerfiles/dockerapi/modules/DockerApi.py b/data/Dockerfiles/dockerapi/modules/DockerApi.py index 56019909..ea1c104e 100644 --- a/data/Dockerfiles/dockerapi/modules/DockerApi.py +++ b/data/Dockerfiles/dockerapi/modules/DockerApi.py @@ -358,8 +358,8 @@ class DockerApi: for line in cmd_response.split("\n"): if '$2$' in line: hash = line.strip()
-LABEL maintainer="The Infrastructure Company GmbH info@servercow.de" +FROM alpine:3.19 +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
ARG GOSU_VERSION=1.16
-ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 +ENV LANG C.UTF-8 +ENV LC_ALL C.UTF-8
RUN addgroup -g 5000 vmail \ @@ -25,7 +24,6 @@ RUN addgroup -g 5000 vmail \ envsubst \ ca-certificates \ curl \
+# RUN cpan LockFile::Simple + COPY trim_logs.sh /usr/local/bin/trim_logs.sh COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf @@ -130,7 +129,6 @@ COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh COPY quarantine_notify.py /usr/local/bin/quarantine_notify.py COPY quota_notify.py /usr/local/bin/quota_notify.py COPY repl_health.sh /usr/local/bin/repl_health.sh -COPY optimize-fts.sh /usr/local/bin/optimize-fts.sh
ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] +CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 2f0bfadf..a9545f33 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -2,7 +2,7 @@ set -e
-while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for database to come up..." sleep 2 done @@ -29,7 +29,6 @@ ${REDIS_CMDLINE} SET DOVECOT_REPL_HEALTH 1 > /dev/null
[[ ! -d /etc/dovecot/sql/ ]] && mkdir -p /etc/dovecot/sql/ [[ ! -d /etc/dovecot/lua/ ]] && mkdir -p /etc/dovecot/lua/ -[[ ! -d /etc/dovecot/conf.d/ ]] && mkdir -p /etc/dovecot/conf.d/ [[ ! -d /var/vmail/_garbage ]] && mkdir -p /var/vmail/_garbage [[ ! -d /var/vmail/sieve ]] && mkdir -p /var/vmail/sieve [[ ! -d /etc/sogo ]] && mkdir -p /etc/sogo @@ -110,14 +109,7 @@ EOF
echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone
-if [[ "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY]) ]]; then -echo -e "\e[33mActivating Flatcurve as FTS Backend...\e[0m" -echo -e "\e[33mDepending on your previous setup a full reindex might be needed... \e[0m" -echo -e "\e[34mVisit https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-fts/#fts-related-dovecot-commands to learn how to reindex\e[0m" -echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins -echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap -echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp -elif [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then +if [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication' > /etc/dovecot/mail_plugins echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp @@ -247,51 +239,6 @@ function script_deinit() end EOF
-# Temporarily set FTS depending on user choice inside mailcow.conf. Will be removed as soon as Solr is dropped
-if [[ "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
-cat <
fts_autoindex = yes
fts_autoindex_exclude = \Junk
fts_autoindex_exclude2 = \Trash
fts = flatcurve
fts_tokenizer_email_address = maxlen=100
fts_tokenizer_generic = algorithm=simple maxlen=30
fts_languages = en es de
fts_tokenizers = generic email-address
fts_filters = normalizer-icu snowball stopwords
fts_filters_en = lowercase snowball english-possessive stopwords
-}
-EOF
-elif [[ ! "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
-cat <
fts = solr
fts_autoindex = yes
fts_autoindex_exclude = \Junk
fts_autoindex_exclude2 = \Trash
fts_solr = url=http://solr:8983/solr/dovecot-fts/
fts_tokenizers = generic email-address
fts_tokenizer_generic = algorithm=simple
fts_filters = normalizer-icu snowball stopwords
fts_filters_en = lowercase snowball english-possessive stopwords -} -EOF -fi
sed -i "s/DBUSER/${DBUSER}/g" /etc/dovecot/lua/passwd-verify.lua sed -i "s/DBPASS/${DBPASS}/g" /etc/dovecot/lua/passwd-verify.lua @@ -396,6 +343,7 @@ mail_replica = tcp:${MAILCOW_REPLICA_IP}:${DOVEADM_REPLICA_PORT} EOF fi
if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem @@ -411,6 +359,14 @@ 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
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/lua/passwd-verify.lua @@ -431,8 +387,7 @@ chmod +x /usr/lib/dovecot/sieve/rspamd-pipe-ham \ /usr/local/bin/maildir_gc.sh \ /usr/local/sbin/stop-supervisor.sh \ /usr/local/bin/quota_notify.py \
/usr/local/bin/repl_health.sh
printenv | sed 's/^(.*)$/export \1/g' > /source_env.sh diff --git a/data/Dockerfiles/dovecot/optimize-fts.sh b/data/Dockerfiles/dovecot/optimize-fts.sh deleted file mode 100644 index a6e8f91d..00000000 --- a/data/Dockerfiles/dovecot/optimize-fts.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash
-if [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ && ! "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
-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 +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
exit 0 diff --git a/data/Dockerfiles/dovecot/rspamd-pipe-spam b/data/Dockerfiles/dovecot/rspamd-pipe-spam index 3f02c487..a4b91a01 100755 --- a/data/Dockerfiles/dovecot/rspamd-pipe-spam +++ b/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.${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 +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
exit 0 diff --git a/data/Dockerfiles/dovecot/sa-rules.sh b/data/Dockerfiles/dovecot/sa-rules.sh index 2a513805..89911c19 100755 --- a/data/Dockerfiles/dovecot/sa-rules.sh +++ b/data/Dockerfiles/dovecot/sa-rules.sh @@ -11,25 +11,21 @@ else fi
-if curl --connect-timeout 15 --retry 10 --max-time 30 https://www.spamassassin.heinlein-support.de/$(dig txt 1.4.3.spamassassin.heinlein-support.de +short | tr -d '"' | tr -dc '0-9').tar.gz --output /tmp/sa-rules-heinlein.tar.gz; then
cat /tmp/sa-rules-heinlein/*cf > /etc/rspamd/custom/sa-rules fi
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
-LABEL maintainer = "The Infrastructure Company GmbH info@servercow.de" +FROM alpine:3.19 +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
WORKDIR /app
diff --git a/data/Dockerfiles/netfilter/main.py b/data/Dockerfiles/netfilter/main.py index c5667dc5..c3ca379c 100644 --- a/data/Dockerfiles/netfilter/main.py +++ b/data/Dockerfiles/netfilter/main.py @@ -80,16 +80,16 @@ def refreshF2bregex(): global exit_code if not r.get('F2B_REGEX'): f2bregex = {}
-LABEL maintainer = "The Infrastructure Company GmbH info@servercow.de" +FROM alpine:3.19 +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
ARG PIP_BREAK_SYSTEM_PACKAGES=1 WORKDIR /app diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile index 0ac722b2..22036b9b 100644 --- a/data/Dockerfiles/phpfpm/Dockerfile +++ b/data/Dockerfiles/phpfpm/Dockerfile @@ -1,6 +1,5 @@ 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"
ARG APCU_PECL_VERSION=5.1.23 diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh index 798a2585..37370113 100755 --- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh +++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh @@ -3,7 +3,7 @@ function array_by_comma { local IFS=","; echo "$*"; }
-while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for SQL..." sleep 2 done @@ -23,8 +23,7 @@ done
CONTAINER_ID= until [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ ^[[:alnum:]]*$ ]]; do
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for SQL to return, please wait" sleep 2 done @@ -60,12 +59,12 @@ done
if [ ${SQL_CHANGED} -eq 1 ]; then
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
-LABEL maintainer = "The Infrastructure Company GmbH info@servercow.de" +FROM debian:bullseye-slim +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
ARG DEBIAN_FRONTEND=noninteractive ENV LC_ALL C @@ -60,4 +59,4 @@ EXPOSE 588
ENTRYPOINT ["/docker-entrypoint.sh"]
-CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] +CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh index 8ffb76f6..b3098d3a 100755 --- a/data/Dockerfiles/postfix/postfix.sh +++ b/data/Dockerfiles/postfix/postfix.sh @@ -5,7 +5,7 @@ trap "postfix stop" EXIT [[ ! -d /opt/postfix/conf/sql/ ]] && mkdir -p /opt/postfix/conf/sql/
-while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for database to come up..." sleep 2 done @@ -415,6 +415,12 @@ postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]-2 b.barracudacentral.org=127.0.0.27 bl.mailspike.net=127.0.0.25 bl.mailspike.net=127.0.0.[10;11;12]4
dnsbl.sorbs.net=127.0.0.9*2 EOF fi DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S') diff --git a/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf b/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf index cb1d1aa0..558305ec 100644 --- a/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf +++ b/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf @@ -1,4 +1,4 @@ -@version: 3.38 +@version: 3.28 @include "scl.conf" options { chain_hostnames(off); diff --git a/data/Dockerfiles/postfix/syslog-ng.conf b/data/Dockerfiles/postfix/syslog-ng.conf index 0990f1c0..a1ccd07e 100644 --- a/data/Dockerfiles/postfix/syslog-ng.conf +++ b/data/Dockerfiles/postfix/syslog-ng.conf @@ -1,4 +1,4 @@ -@version: 3.38 +@version: 3.28 @include "scl.conf" options { chain_hostnames(off); diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile index df15a0be..b664691b 100644 --- a/data/Dockerfiles/rspamd/Dockerfile +++ b/data/Dockerfiles/rspamd/Dockerfile @@ -1,10 +1,9 @@ -FROM debian:bookworm-slim -LABEL maintainer = "The Infrastructure Company GmbH info@servercow.de" +FROM debian:bullseye-slim +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
ARG DEBIAN_FRONTEND=noninteractive -ARG RSPAMD_VER=rspamd_3.9.1-1~82f43560f -ARG CODENAME=bookworm -ENV LC_ALL=C +ARG CODENAME=bullseye +ENV LC_ALL C
RUN apt-get update && apt-get install -y \ tzdata \ @@ -12,16 +11,12 @@ RUN apt-get update && apt-get install -y \ gnupg2 \ apt-transport-https \ dnsutils \
&& rm -rf /var/lib/apt/lists/* \ && apt-get autoremove --purge \ && apt-get clean \ && mkdir -p /run/rspamd \ @@ -30,6 +25,7 @@ RUN apt-get update && apt-get install -y \ && sed -i 's/#analysis_keyword_table > 0/analysis_cat_table.macro_exist == "M"/g' /usr/share/rspamd/lualib/lua_scanners/oletools.lua
COPY settings.conf /etc/rspamd/settings.conf +COPY metadata_exporter.lua /usr/share/rspamd/plugins/metadata_exporter.lua COPY set_worker_password.sh /set_worker_password.sh COPY docker-entrypoint.sh /docker-entrypoint.sh
diff --git a/data/Dockerfiles/rspamd/docker-entrypoint.sh b/data/Dockerfiles/rspamd/docker-entrypoint.sh index cf09ee48..8af7619c 100755 --- a/data/Dockerfiles/rspamd/docker-entrypoint.sh +++ b/data/Dockerfiles/rspamd/docker-entrypoint.sh @@ -124,190 +124,4 @@ 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
exec "$@" diff --git a/data/Dockerfiles/rspamd/metadata_exporter.lua b/data/Dockerfiles/rspamd/metadata_exporter.lua new file mode 100644 index 00000000..48a5ffce --- /dev/null +++ b/data/Dockerfiles/rspamd/metadata_exporter.lua @@ -0,0 +1,632 @@ +--[[ +Copyright (c) 2016, Andrew Lewis nerf@judo.za.org +Copyright (c) 2016, Vsevolod Stakhov vsevolod@highsecure.ru
+Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at
+Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +]]--
+if confighelp then
+-- A plugin that pushes metadata (or whole messages) to external services
+local redis_params +local lua_util = require "lua_util" +local rspamd_http = require "rspamd_http" +local rspamd_util = require "rspamd_util" +local rspamd_logger = require "rspamd_logger" +local ucl = require "ucl" +local E = {} +local N = 'metadata_exporter'
+local settings = {
+Authenticated username: $user +IP: $ip +Queue ID: $qid +SMTP FROM: $from +SMTP RCPT: $rcpt +MIME From: $header_from +MIME To: $header_to +MIME Date: $header_date +Subject: $header_subject +Message-ID: $message_id +Action: $action +Score: $score +Symbols: $symbols]], +}
+local function get_general_metadata(task, flatten, no_content)
+local formatters = {
+local function is_spam(action)
+local selectors = {
+local function maybe_defer(task, rule)
+local pushers = {
+local opts = rspamd_config:get_all_opt(N) +if not opts then return end +local process_settings = {
+local function gen_exporter(rule)
+if not next(settings.rules) then
-LABEL maintainer="The Infrastructure Company GmbH info@servercow.de" +FROM debian:bullseye-slim +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
ARG DEBIAN_FRONTEND=noninteractive -ARG DEBIAN_VERSION=bookworm +ARG DEBIAN_VERSION=bullseye ARG SOGO_DEBIAN_REPOSITORY=http://www.axis.cz/linux/debian
ARG GOSU_VERSION=1.17 -ENV LC_ALL=C +ENV LC_ALL C
RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \ @@ -55,4 +54,4 @@ RUN chmod +x /bootstrap-sogo.sh \
ENTRYPOINT ["/docker-entrypoint.sh"]
-CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] \ No newline at end of file +CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf \ No newline at end of file diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 51880ea6..bae06054 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -1,7 +1,7 @@
-while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for database to come up..." sleep 2 done diff --git a/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf b/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf index 7abfc4b5..9b460bd3 100644 --- a/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf +++ b/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf @@ -1,4 +1,4 @@ -@version: 3.38 +@version: 3.28 @include "scl.conf" options { chain_hostnames(off); diff --git a/data/Dockerfiles/sogo/syslog-ng.conf b/data/Dockerfiles/sogo/syslog-ng.conf index f16a2920..889a3f32 100644 --- a/data/Dockerfiles/sogo/syslog-ng.conf +++ b/data/Dockerfiles/sogo/syslog-ng.conf @@ -1,4 +1,4 @@ -@version: 3.38 +@version: 3.28 @include "scl.conf" options { chain_hostnames(off); diff --git a/data/Dockerfiles/solr/solr.sh b/data/Dockerfiles/solr/solr.sh index 03ab7912..1c5c6f51 100755 --- a/data/Dockerfiles/solr/solr.sh +++ b/data/Dockerfiles/solr/solr.sh @@ -1,15 +1,7 @@
-if [[ "${FLATCURVE_EXPERIMENTAL}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
exec gosu solr solr-foreground
diff --git a/data/Dockerfiles/unbound/Dockerfile b/data/Dockerfiles/unbound/Dockerfile index 7e4f18de..e7204481 100644 --- a/data/Dockerfiles/unbound/Dockerfile +++ b/data/Dockerfiles/unbound/Dockerfile @@ -1,21 +1,18 @@ -FROM alpine:3.20 +FROM alpine:3.18
-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 \ bind-tools \
&& adduser unbound tty \ && chmod 775 /etc/unbound
EXPOSE 53/udp 53/tcp @@ -24,13 +21,9 @@ COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY healthcheck.sh /healthcheck.sh -COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf -COPY supervisord.conf /etc/supervisor/supervisord.conf -COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
RUN chmod +x /healthcheck.sh -HEALTHCHECK --interval=30s --timeout=10s \
CMD sh -c '[ -f /tmp/healthcheck_status ] && [ "$(cat /tmp/healthcheck_status)" -eq 0 ] || exit 1' +HEALTHCHECK --interval=30s --timeout=30s CMD [ "/healthcheck.sh" ]
ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
+CMD ["/usr/sbin/unbound"] diff --git a/data/Dockerfiles/unbound/healthcheck.sh b/data/Dockerfiles/unbound/healthcheck.sh index 7d918112..8da79bd7 100644 --- a/data/Dockerfiles/unbound/healthcheck.sh +++ b/data/Dockerfiles/unbound/healthcheck.sh @@ -1,102 +1,72 @@
-STATUS_FILE="/tmp/healthcheck_status" -RUNS=0 +# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) +if [[ "${SKIP_UNBOUND_HEALTHCHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
-# Declare log function for logfile to stdout -function log_to_stdout() { -echo "$(date +"%Y-%m-%d %H:%M:%S"): $1" +# Declare log function for logfile inside container +function log_to_file() {
echo "$(date +"%Y-%m-%d %H:%M:%S"): $1" > /var/log/healthcheck.log }
function check_ping() { -declare -a ipstoping=("1.1.1.1" "8.8.8.8" "9.9.9.9") -local fail_tolerance=1 -local failures=0
-for ip in "${ipstoping[@]}" ; do
success=false
for ((i=1; i<=3; i++)); do
ping -q -c 3 -w 5 "$ip" > /dev/null
if [ $? -eq 0 ]; then
success=true
break
else
log_to_stdout "Healthcheck: Failed to ping $ip on attempt $i. Trying again..."
fi
declare -a ipstoping=("1.1.1.1" "8.8.8.8" "9.9.9.9")
for ip in "${ipstoping[@]}" ; do
ping -q -c 3 -w 5 "$ip"
if [ $? -ne 0 ]; then
log_to_file "Healthcheck: Couldn't ping $ip for 5 seconds... Gave up!"
log_to_file "Please check your internet connection or firewall rules to fix this error, because a simple ping test should always go through from the unbound container!"
return 1
fi done
if [ "$success" = false ]; then
log_to_stdout "Healthcheck: Couldn't ping $ip after 3 attempts. Marking this IP as failed."
((failures++))
fi -done
-if [ $failures -gt $fail_tolerance ]; then
log_to_stdout "Healthcheck: Too many ping failures ($fail_tolerance failures allowed, you got $failures failures), marking Healthcheck as unhealthy..."
return 1 -fi
-return 0
log_to_file "Healthcheck: Ping Checks WORKING properly!"
return 0 }
function check_dns() { -declare -a domains=("fuzzy.mailcow.email" "github.com" "hub.docker.com") -local fail_tolerance=1 -local failures=0
-for domain in "${domains[@]}" ; do
success=false
for ((i=1; i<=3; i++)); do
dig_output=$(dig +short +timeout=2 +tries=1 "$domain" @127.0.0.1 2>/dev/null)
dig_rc=$?
if [ $dig_rc -ne 0 ] || [ -z "$dig_output" ]; then
log_to_stdout "Healthcheck: DNS Resolution Failed on attempt $i for $domain! Trying again..."
else
success=true
break
declare -a domains=("mailcow.email" "github.com" "hub.docker.com")
for domain in "${domains[@]}" ; do
for ((i=1; i<=3; i++)); do
dig +short +timeout=2 +tries=1 "$domain" @127.0.0.1 > /dev/null
if [ $? -ne 0 ]; then
log_to_file "Healthcheck: DNS Resolution Failed on $i attempt! Trying again..."
if [ $i -eq 3 ]; then
log_to_file "Healthcheck: DNS Resolution not possible after $i attempts... Gave up!"
log_to_file "Maybe check your outbound firewall, as it needs to resolve DNS over TCP AND UDP!"
return 1
fi fi
done done
if [ "$success" = false ]; then
log_to_stdout "Healthcheck: DNS Resolution not possible after 3 attempts for $domain... Gave up!"
((failures++))
fi -done
-if [ $failures -gt $fail_tolerance ]; then
log_to_stdout "Healthcheck: Too many DNS failures ($fail_tolerance failures allowed, you got $failures failures), marking Healthcheck as unhealthy..."
return 1 -fi
-return 0
}
if [[ ${SKIP_UNBOUND_HEALTHCHECK} == "y" ]]; then
log_to_stdout "Healthcheck: ALL CHECKS WERE SKIPPED! Unbound is healthy!"
echo "0" > $STATUS_FILE
sleep 365d
fi
check_ping
PING_STATUS=$? +if [[ ${SKIP_UNBOUND_HEALTHCHECK} == "y" ]]; then
log_to_file "Healthcheck: ALL CHECKS WERE SKIPPED! Unbound is healthy!"
exit 0 +fi
check_dns
DNS_STATUS=$? +# run checks, if check is not returning 0 (return value if check is ok), healthcheck will exit with 1 (marked in docker as unhealthy) +check_ping
if [ $PING_STATUS -ne 0 ] || [ $DNS_STATUS -ne 0 ]; then
echo "1" > $STATUS_FILE +if [ $? -ne 0 ]; then
exit 1 +fi
else
echo "0" > $STATUS_FILE
fi +check_dns
sleep 30 +if [ $? -ne 0 ]; then
exit 1 +fi
-while read line; do
-rm -rf /tmp/healthcheck_status \ No newline at end of file diff --git a/data/Dockerfiles/unbound/supervisord.conf b/data/Dockerfiles/unbound/supervisord.conf deleted file mode 100644 index b47c8b11..00000000 --- a/data/Dockerfiles/unbound/supervisord.conf +++ /dev/null @@ -1,32 +0,0 @@ -[supervisord] -nodaemon=true -user=root -pidfile=/var/run/supervisord.pid
-[program:syslog-ng] -command=/usr/sbin/syslog-ng --foreground --no-caps -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -autostart=true
-[program:unbound] -command=/usr/sbin/unbound -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -autorestart=true
-[program:unbound-healthcheck] -command=/bin/bash /healthcheck.sh -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -autorestart=true
-[eventlistener:processes] -command=/usr/local/sbin/stop-supervisor.sh -events=PROCESS_STATE_STOPPED, PROCESS_STATE_EXITED, PROCESS_STATE_FATAL diff --git a/data/Dockerfiles/unbound/syslog-ng.conf b/data/Dockerfiles/unbound/syslog-ng.conf deleted file mode 100644 index de858f9e..00000000 --- a/data/Dockerfiles/unbound/syslog-ng.conf +++ /dev/null @@ -1,21 +0,0 @@ -@version: 4.5 -@include "scl.conf" -options {
-LABEL maintainer = "The Infrastructure Company GmbH info@servercow.de" +FROM alpine:3.18 +LABEL maintainer "The Infrastructure Company GmbH info@servercow.de"
RUN apk add --update \ @@ -37,4 +36,4 @@ RUN apk add --update \ COPY watchdog.sh /watchdog.sh COPY check_mysql_slavestatus.sh /usr/lib/nagios/plugins/check_mysql_slavestatus.sh
-CMD ["/watchdog.sh"] +CMD /watchdog.sh diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh index 81d65d90..cb342c13 100755 --- a/data/Dockerfiles/watchdog/watchdog.sh +++ b/data/Dockerfiles/watchdog/watchdog.sh @@ -33,7 +33,7 @@ if [[ ! -p /tmp/com_pipe ]]; then fi
-while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do +while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do echo "Waiting for SQL..." sleep 2 done @@ -169,13 +169,9 @@ function notify_error() { return 1 fi
curl -X POST -H "Content-Type: application/json" ${CURL_VERBOSE} -d "${WEBHOOK_BODY}" ${WATCHDOG_NOTIFY_WEBHOOK}
@@ -195,12 +191,12 @@ get_container_ip() { else sleep 0.5
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')) for ip_match in "${CONTAINER_IPS[@]}"; do
[[ -z ${ip_match} ]] && continue
@@ -720,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.${COMPOSE_PROJECT_NAME}_mailcow-network/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/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)) @@ -1099,12 +1095,12 @@ while true; do elif [[ ${com_pipe_answer} =~ .+-mailcow ]]; then kill -STOP ${BACKGROUND_TASKS[*]} sleep 10
-Somebody requested a new password for the {{hostname}} account associated with {{username}}.
-Date of the password reset request: {{date}}
-You can reset your password by clicking the link below:
-{{link}}
-The link will be valid for the next {{token_lifetime}} minutes.
-If you did not request a new password, please ignore this email.
-Somebody requested a new password for the {{hostname}} account associated with {{username}}. -Date of the password reset request: {{date}}
-You can reset your password by clicking the link below: -{{link}}
-The link will be valid for the next {{token_lifetime}} minutes.
-If you did not request a new password, please ignore this email. diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf index c230c349..729686fb 100644 --- a/data/conf/dovecot/dovecot.conf +++ b/data/conf/dovecot/dovecot.conf @@ -10,7 +10,6 @@ auth_mechanisms = plain login
-#log_debug = category=fts-flatcurve # Activate Logging for Flatcurve FTS Searchings log_path = syslog disable_plaintext_auth = yes
@@ -195,6 +194,9 @@ plugin { acl_shared_dict = file:/var/vmail/shared-mailboxes.db acl = vfile acl_user = %u
default_client_limit = 10400 default_vsz_limit = 1024 M diff --git a/data/conf/dovecot/dovecot.folders.conf b/data/conf/dovecot/dovecot.folders.conf index fa687267..99c9670f 100644 --- a/data/conf/dovecot/dovecot.folders.conf +++ b/data/conf/dovecot/dovecot.folders.conf @@ -289,20 +289,5 @@ namespace inbox { mailbox "Kladde" { special_use = \Drafts }
} prefix = -} +} \ No newline at end of file diff --git a/data/conf/postfix/anonymize_headers.pcre b/data/conf/postfix/anonymize_headers.pcre index 1a59d53a..061a4bc0 100644 --- a/data/conf/postfix/anonymize_headers.pcre +++ b/data/conf/postfix/anonymize_headers.pcre @@ -1,6 +1,6 @@ if /^\sReceived:.Authenticated sender.*(Postcow)/
-/^Received: from .? ([\w-.] [.?])(.|\n.)(Authenticated sender: (.+))\s+by.+(Postcow) with (.)/ +/^Received: from .? ([\w-.] [.?])(.|\n.)(Authenticated sender: (.+))\s+by.+(Postcow) with (.)/ REPLACE Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with $3 endif if /^\sReceived: from. (.dovecot-mailcow.mailcow-network.).(Postcow)/ diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf index 6a87f2ec..e111b2a2 100644 --- a/data/conf/postfix/main.cf +++ b/data/conf/postfix/main.cf @@ -114,14 +114,14 @@ smtpd_tls_loglevel = 1
-smtp_tls_mandatory_protocols = >=TLSv1.2 -lmtp_tls_mandatory_protocols = >=TLSv1.2 -smtpd_tls_mandatory_protocols = >=TLSv1.2 +smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 +lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 +smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_mandatory_ciphers = high
-smtp_tls_protocols = >=TLSv1.2 -lmtp_tls_protocols = >=TLSv1.2 -smtpd_tls_protocols = >=TLSv1.2 +smtp_tls_protocols = !SSLv2, !SSLv3 +lmtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 +smtpd_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_security_level = may tls_preempt_cipherlist = yes @@ -163,13 +163,46 @@ transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf smtp_sasl_auth_soft_bounce = no postscreen_discard_ehlo_keywords = silent-discard, dsn, chunking -smtpd_discard_ehlo_keywords = chunking, silent-discard -compatibility_level = 3.7 +smtpd_discard_ehlo_keywords = chunking +compatibility_level = 2 smtputf8_enable = no
-submission_smtpd_tls_mandatory_protocols = >=TLSv1.2 -smtps_smtpd_tls_mandatory_protocols = >=TLSv1.2 +submission_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 +smtps_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 parent_domain_matches_subdomains = debug_peer_list,fast_flush_domains,mynetworks,qmqpd_authorized_clients
+ +postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
+# User Overrides +myhostname = mail.myserver.com
diff --git a/data/conf/postfix/master.cf b/data/conf/postfix/master.cf index df91a390..63ce875d 100644 --- a/data/conf/postfix/master.cf +++ b/data/conf/postfix/master.cf @@ -4,6 +4,7 @@ smtp inet n - n - 1 postscreen -o postscreen_upstream_proxy_protocol=haproxy -o syslog_name=haproxy smtpd pass - - n - - smtpd
diff --git a/data/conf/postfix/postscreen_access.cidr b/data/conf/postfix/postscreen_access.cidr index 78ffc3a8..34ff04ea 100644 --- a/data/conf/postfix/postscreen_access.cidr +++ b/data/conf/postfix/postscreen_access.cidr @@ -1,6 +1,6 @@ -# Whitelist generated by Postwhite v3.4 on Thu Aug 1 00:16:45 UTC 2024 +# Whitelist generated by Postwhite v3.4 on Mon Apr 1 00:15:02 UTC 2024
-# 1954 total rules +# 2009 total rules 2a00:1450:4000::/36 permit 2a01:111:f400::/48 permit 2a01:111:f403:8000::/50 permit @@ -13,35 +13,37 @@ 2.207.151.53 permit 3.70.123.177 permit 3.93.157.0/24 permit -3.94.40.108 permit 3.129.120.190 permit +3.137.16.58 permit 3.210.190.0/24 permit 8.20.114.31 permit 8.25.194.0/23 permit 8.25.196.0/23 permit -10.162.0.0/16 permit +8.39.54.0/23 permit +8.40.222.0/23 permit 12.130.86.238 permit +13.70.32.43 permit +13.72.50.45 permit +13.74.143.28 permit +13.78.233.182 permit +13.92.31.129 permit 13.110.208.0/21 permit 13.110.209.0/24 permit 13.110.216.0/22 permit 13.110.224.0/20 permit 13.111.0.0/16 permit -13.111.191.0/24 permit 15.200.21.50 permit 15.200.44.248 permit 15.200.201.185 permit -17.41.0.0/16 permit 17.57.155.0/24 permit 17.57.156.0/24 permit 17.58.0.0/16 permit -17.142.0.0/15 permit 18.156.89.250 permit 18.157.243.190 permit 18.194.95.56 permit 18.198.96.88 permit 18.208.124.128/25 permit 18.216.232.154 permit -18.235.27.253 permit 18.236.40.242 permit 18.236.56.161 permit 20.51.6.32/30 permit @@ -63,8 +65,10 @@ 20.107.239.64/30 permit 20.112.250.133 permit 20.118.139.208/30 permit -20.141.10.196 permit +20.185.213.160/27 permit +20.185.213.224/27 permit 20.185.214.0/27 permit +20.185.214.2 permit 20.185.214.32/27 permit 20.185.214.64/27 permit 20.231.239.246 permit @@ -103,21 +107,21 @@ 35.176.132.251 permit 35.190.247.0/24 permit 35.191.0.0/16 permit -35.205.92.9 permit 35.242.169.159 permit 37.218.248.47 permit 37.218.249.47 permit 37.218.251.62 permit 39.156.163.64/29 permit +40.71.187.0/24 permit 40.92.0.0/15 permit 40.92.0.0/16 permit 40.107.0.0/16 permit 40.112.65.63 permit 43.228.184.0/22 permit 44.206.138.57 permit -44.217.45.156 permit 44.236.56.93 permit 44.238.220.251 permit +46.19.168.0/23 permit 46.19.170.16 permit 46.226.48.0/21 permit 46.228.36.37 permit @@ -178,8 +182,11 @@ 50.18.125.237 permit 50.18.126.162 permit 50.31.32.0/19 permit -50.31.36.205 permit -50.56.130.220/30 permit +50.56.130.220 permit +50.56.130.221 permit +51.137.58.21 permit +51.140.75.55 permit +51.144.100.179 permit 52.1.14.157 permit 52.5.230.59 permit 52.27.5.72 permit @@ -187,19 +194,19 @@ 52.28.63.81 permit 52.36.138.31 permit 52.37.142.146 permit -52.50.24.208 permit 52.58.216.183 permit 52.59.143.3 permit 52.60.41.5 permit 52.60.115.116 permit 52.61.91.9 permit 52.71.0.205 permit +52.82.172.0/22 permit 52.94.124.0/28 permit 52.95.48.152/29 permit 52.95.49.88/29 permit 52.96.91.34 permit 52.96.111.82 permit -52.96.172.98 permit +52.96.214.50 permit 52.96.222.194 permit 52.96.222.226 permit 52.96.223.2 permit @@ -208,6 +215,8 @@ 52.100.0.0/14 permit 52.103.0.0/17 permit 52.119.213.144/28 permit +52.160.39.140 permit +52.165.175.144 permit 52.185.106.240/28 permit 52.200.59.0/24 permit 52.205.61.79 permit @@ -218,12 +227,15 @@ 52.222.75.85 permit 52.222.89.228 permit 52.234.172.96/28 permit -52.235.253.128 permit 52.236.28.240/28 permit +52.244.206.214 permit +52.247.53.144 permit +52.250.107.196 permit +52.250.126.174 permit 54.90.148.255 permit -54.165.19.38 permit 54.172.97.247 permit 54.174.52.0/24 permit +54.174.53.128/30 permit 54.174.57.0/24 permit 54.174.59.0/24 permit 54.174.60.0/23 permit @@ -255,6 +267,7 @@ 62.201.172.32/27 permit 62.253.227.114 permit 63.80.14.0/23 permit +63.111.28.137 permit 63.128.21.0/24 permit 63.143.57.128/25 permit 63.143.59.128/25 permit @@ -270,6 +283,17 @@ 64.127.115.252 permit 64.132.88.0/23 permit 64.132.92.0/24 permit +64.147.123.17 permit +64.147.123.18 permit +64.147.123.19 permit +64.147.123.20 permit +64.147.123.21 permit +64.147.123.24 permit +64.147.123.25 permit +64.147.123.26 permit +64.147.123.27 permit +64.147.123.28 permit +64.147.123.29 permit 64.147.123.128/27 permit 64.207.219.7 permit 64.207.219.8 permit @@ -324,9 +348,24 @@ 65.110.161.77 permit 65.123.29.213 permit 65.123.29.220 permit +65.154.166.0/24 permit 65.212.180.36 permit 66.102.0.0/20 permit +66.111.4.25 permit +66.111.4.26 permit +66.111.4.27 permit +66.111.4.28 permit +66.111.4.29 permit +66.111.4.221 permit +66.111.4.222 permit +66.111.4.224 permit +66.111.4.225 permit +66.111.4.229 permit +66.111.4.230 permit 66.119.150.192/26 permit +66.135.202.0/27 permit +66.135.215.0/24 permit +66.135.222.1 permit 66.162.193.226/31 permit 66.163.184.0/24 permit 66.163.185.0/24 permit @@ -432,17 +471,15 @@ 69.65.42.195 permit 69.65.49.192/29 permit 69.72.32.0/20 permit -69.72.40.93 permit -69.72.40.94/31 permit -69.72.40.96/30 permit -69.72.47.205 permit 69.147.84.227 permit 69.162.98.0/24 permit 69.169.224.0/20 permit 69.171.232.0/24 permit 69.171.244.0/23 permit 70.37.151.128/25 permit +70.42.149.0/24 permit 70.42.149.35 permit +72.3.237.64/28 permit 72.14.192.0/18 permit 72.21.192.0/19 permit 72.21.217.142 permit @@ -558,6 +595,7 @@ 77.238.189.142 permit 77.238.189.146/31 permit 77.238.189.148/30 permit +81.7.169.128/25 permit 81.223.46.0/27 permit 82.165.159.2 permit 82.165.159.3 permit @@ -1126,6 +1164,7 @@ 104.47.108.0/23 permit 104.130.96.0/28 permit 104.130.122.0/23 permit +104.214.25.77 permit 106.10.144.64/27 permit 106.10.144.100/31 permit 106.10.144.103 permit @@ -1247,7 +1286,6 @@ 106.10.244.0/24 permit 106.39.212.64/29 permit 106.50.16.0/28 permit -107.20.18.111 permit 107.20.210.250 permit 108.174.0.0/24 permit 108.174.0.215 permit @@ -1259,7 +1297,6 @@ 108.175.30.45 permit 108.177.8.0/21 permit 108.177.96.0/19 permit -108.179.144.0/20 permit 109.237.142.0/24 permit 111.221.23.128/25 permit 111.221.26.0/27 permit @@ -1283,6 +1320,8 @@ 117.120.16.0/21 permit 119.42.242.52/31 permit 119.42.242.156 permit +121.244.91.48 permit +122.15.156.182 permit 123.126.78.64/29 permit 124.108.96.24/31 permit 124.108.96.28/31 permit @@ -1338,14 +1377,25 @@ 134.170.141.64/26 permit 134.170.143.0/24 permit 134.170.174.0/24 permit +135.84.80.0/24 permit +135.84.81.0/24 permit +135.84.82.0/24 permit +135.84.83.0/24 permit 135.84.216.0/22 permit +136.143.160.0/24 permit +136.143.161.0/24 permit +136.143.178.49 permit +136.143.182.0/23 permit +136.143.184.0/24 permit +136.143.188.0/24 permit +136.143.190.0/23 permit 136.147.128.0/20 permit 136.147.135.0/24 permit 136.147.176.0/20 permit 136.147.176.0/24 permit 136.147.182.0/24 permit -136.147.224.0/20 permit 136.179.50.206 permit +138.91.172.26 permit 139.60.152.0/22 permit 139.138.35.44 permit 139.138.46.121 permit @@ -1356,12 +1406,6 @@ 139.180.17.0/24 permit 141.148.159.229 permit 141.193.32.0/23 permit -141.193.184.32/27 permit -141.193.184.64/26 permit -141.193.184.128/25 permit -141.193.185.32/27 permit -141.193.185.64/26 permit -141.193.185.128/25 permit 143.55.224.0/21 permit 143.55.232.0/22 permit 143.55.236.0/22 permit @@ -1375,7 +1419,8 @@ 144.178.38.0/24 permit 145.253.228.160/29 permit 145.253.239.128/29 permit -146.20.14.104/30 permit +146.20.14.105 permit +146.20.14.107 permit 146.20.112.0/26 permit 146.20.113.0/24 permit 146.20.191.0/24 permit @@ -1396,7 +1441,6 @@ 150.230.98.160 permit 152.67.105.195 permit 152.69.200.236 permit -152.70.155.126 permit 155.248.208.51 permit 157.55.0.192/26 permit 157.55.1.128/26 permit @@ -1407,6 +1451,7 @@ 157.55.61.0/24 permit 157.55.157.128/25 permit 157.55.225.0/25 permit +157.55.254.216 permit 157.56.24.0/25 permit 157.56.120.128/26 permit 157.56.232.0/21 permit @@ -1450,9 +1495,11 @@ 163.47.180.0/22 permit 163.114.130.16 permit 163.114.132.120 permit -163.114.134.16 permit -163.114.135.16 permit -164.177.132.168/30 permit +164.177.132.168 permit +164.177.132.169 permit +164.177.132.170 permit +164.177.132.171 permit +165.173.128.0/24 permit 166.78.68.0/22 permit 166.78.68.221 permit 166.78.69.169 permit @@ -1461,7 +1508,6 @@ 167.89.0.0/17 permit 167.89.46.159 permit 167.89.54.103 permit -167.89.60.95 permit 167.89.64.9 permit 167.89.65.0 permit 167.89.65.53 permit @@ -1480,6 +1526,11 @@ 168.245.12.252 permit 168.245.46.9 permit 168.245.127.231 permit +169.148.129.0/24 permit +169.148.131.0/24 permit +169.148.142.10 permit +169.148.144.0/25 permit +169.148.144.10 permit 170.10.68.0/22 permit 170.10.128.0/24 permit 170.10.129.0/24 permit @@ -1524,13 +1575,9 @@ 185.80.93.227 permit 185.80.95.31 permit 185.90.20.0/22 permit -185.138.56.128/25 permit 185.189.236.0/22 permit 185.211.120.0/22 permit 185.250.236.0/22 permit -185.250.239.148 permit -185.250.239.168 permit -185.250.239.190 permit 188.125.68.132 permit 188.125.68.152/31 permit 188.125.68.156 permit @@ -1598,6 +1645,7 @@ 193.122.128.100 permit 193.123.56.63 permit 194.19.134.0/25 permit +194.64.234.128/27 permit 194.64.234.129 permit 194.106.220.0/23 permit 194.113.24.0/22 permit @@ -1619,13 +1667,9 @@ 198.37.144.0/20 permit 198.37.152.186 permit 198.61.254.0/23 permit -198.61.254.21 permit 198.61.254.231 permit 198.178.234.57 permit 198.244.48.0/20 permit -198.244.59.30 permit -198.244.59.33 permit -198.244.59.35 permit 198.244.60.0/22 permit 198.245.80.0/20 permit 198.245.81.0/24 permit @@ -1634,7 +1678,13 @@ 199.16.156.0/22 permit 199.33.145.1 permit 199.33.145.32 permit +199.34.22.36 permit 199.59.148.0/22 permit +199.67.80.2 permit +199.67.82.2 permit +199.67.84.0/24 permit +199.67.86.0/24 permit +199.67.88.0/24 permit 199.101.161.130 permit 199.101.162.0/25 permit 199.122.120.0/21 permit @@ -1683,6 +1733,7 @@ 203.209.230.76/31 permit 204.11.168.0/21 permit 204.13.11.48/29 permit +204.13.11.48/30 permit 204.14.232.0/21 permit 204.14.232.64/28 permit 204.14.234.64/28 permit @@ -1691,6 +1742,9 @@ 204.92.114.187 permit 204.92.114.203 permit 204.92.114.204/31 permit +204.132.224.66 permit +204.141.32.0/23 permit +204.141.42.0/23 permit 204.220.160.0/20 permit 204.232.168.0/24 permit 205.139.110.0/24 permit @@ -1709,7 +1763,6 @@ 205.251.233.36 permit 206.25.247.143 permit 206.25.247.155 permit -206.55.144.0/20 permit 206.165.246.80/29 permit 206.191.224.0/19 permit 206.246.157.1 permit @@ -1727,12 +1780,14 @@ 207.46.132.128/27 permit 207.46.198.0/25 permit 207.46.200.0/27 permit +207.46.225.107 permit 207.58.147.64/28 permit 207.67.38.0/24 permit 207.67.98.192/27 permit 207.68.176.0/26 permit 207.68.176.96/27 permit -207.97.204.96/29 permit +207.97.204.96 permit +207.97.204.97 permit 207.126.144.0/20 permit 207.171.160.0/19 permit 207.211.30.64/26 permit @@ -1771,6 +1826,7 @@ 208.71.42.212/31 permit 208.71.42.214 permit 208.72.249.240/29 permit +208.74.204.0/22 permit 208.74.204.5 permit 208.74.204.9 permit 208.75.120.0/22 permit @@ -1786,9 +1842,6 @@ 209.46.117.168 permit 209.46.117.179 permit 209.61.151.0/24 permit -209.61.151.236 permit -209.61.151.249 permit -209.61.151.251 permit 209.67.98.46 permit 209.67.98.59 permit 209.85.128.0/17 permit @@ -1898,6 +1951,7 @@ 216.39.62.60/31 permit 216.39.62.136/29 permit 216.39.62.144/31 permit +216.46.168.0/24 permit 216.58.192.0/19 permit 216.66.217.240/29 permit 216.71.138.33 permit @@ -1912,6 +1966,9 @@ 216.99.5.68 permit 216.109.114.32/27 permit 216.109.114.64/29 permit +216.113.160.0/24 permit +216.113.172.0/25 permit +216.113.175.0/24 permit 216.128.126.97 permit 216.136.162.65 permit 216.136.162.120/29 permit @@ -1950,8 +2007,6 @@ 2620:109:c00d:104::/64 permit 2620:10d:c090:400::8:1 permit 2620:10d:c091:400::8:1 permit -2620:10d:c09b:400::8:1 permit -2620:10d:c09c:400::8:1 permit 2620:119:50c0:207::/64 permit 2620:119:50c0:207::215 permit 2800:3f0:4000::/36 permit diff --git a/data/conf/rspamd/local.d/composites.conf b/data/conf/rspamd/local.d/composites.conf index 9bb84424..cde34b57 100644 --- a/data/conf/rspamd/local.d/composites.conf +++ b/data/conf/rspamd/local.d/composites.conf @@ -21,10 +21,6 @@ FREEMAIL_TO_UNDISC_RCPT { SOGO_CONTACT_EXCLUDE { expression = "(-WHITELISTED_FWD_HOST | -g+:policies) & ^SOGO_CONTACT & !DMARC_POLICY_ALLOW"; } -# Remove MAILCOW_WHITE symbol for senders with broken policy recieved not from fwd hosts -MAILCOW_WHITE_EXCLUDE {
SPOOFED_UNAUTH { expression = "!MAILCOW_AUTH & !MAILCOW_WHITE & !RSPAMD_HOST & !SIEVE_HOST & MAILCOW_DOMAIN_HEADER_FROM & !WHITELISTED_FWD_HOST & -g+:policies"; @@ -107,4 +103,4 @@ CLAMD_JS_MALWARE { expression = "CLAM_SECI_JS & !MAILCOW_WHITE"; description = "JS malware found, Securite JS malware Flag set through ClamAV"; score = 8; -} +} \ No newline at end of file diff --git a/data/conf/rspamd/local.d/rbl.conf b/data/conf/rspamd/local.d/rbl.conf index 7f2976a0..f132b4d6 100644 --- a/data/conf/rspamd/local.d/rbl.conf +++ b/data/conf/rspamd/local.d/rbl.conf @@ -1,8 +1,23 @@ rbls {
-.include(try=true,override=true,priority=5) "$LOCAL_CONFDIR/custom/dqs-rbl.conf"
-} \ No newline at end of file +} diff --git a/data/conf/rspamd/local.d/rbl_group.conf b/data/conf/rspamd/local.d/rbl_group.conf index 916de4ef..4e3dce71 100644 --- a/data/conf/rspamd/local.d/rbl_group.conf +++ b/data/conf/rspamd/local.d/rbl_group.conf @@ -5,6 +5,46 @@ symbols = { "RBL_UCEPROTECT_LEVEL2" { score = 1.5; }
} diff --git a/data/conf/rspamd/local.d/statistic.conf b/data/conf/rspamd/local.d/statistic.conf index baa3f1c2..1ca3e082 100644 --- a/data/conf/rspamd/local.d/statistic.conf +++ b/data/conf/rspamd/local.d/statistic.conf @@ -1,14 +1,12 @@ classifier "bayes" {
expire = 2592000; statfile { symbol = "BAYES_HAM"; spam = false; diff --git a/data/conf/rspamd/meta_exporter/pipe.php b/data/conf/rspamd/meta_exporter/pipe.php index 1858ee66..88e66e8e 100644 --- a/data/conf/rspamd/meta_exporter/pipe.php +++ b/data/conf/rspamd/meta_exporter/pipe.php @@ -52,7 +52,7 @@ $headers = getallheaders();
$qid = $headers['X-Rspamd-Qid']; $fuzzy = $headers['X-Rspamd-Fuzzy']; -$subject = iconv_mime_decode($headers['X-Rspamd-Subject']); +$subject = $headers['X-Rspamd-Subject']; $score = $headers['X-Rspamd-Score']; $rcpts = $headers['X-Rspamd-Rcpt']; $user = $headers['X-Rspamd-User']; diff --git a/data/conf/rspamd/meta_exporter/pushover.php b/data/conf/rspamd/meta_exporter/pushover.php index f122b281..10265d15 100644 --- a/data/conf/rspamd/meta_exporter/pushover.php +++ b/data/conf/rspamd/meta_exporter/pushover.php @@ -53,7 +53,7 @@ $qid = $headers['X-Rspamd-Qid']; $rcpts = $headers['X-Rspamd-Rcpt']; $sender = $headers['X-Rspamd-From']; $ip = $headers['X-Rspamd-Ip']; -$subject = iconv_mime_decode($headers['X-Rspamd-Subject']); +$subject = $headers['X-Rspamd-Subject']; $messageid= $json_body->message_id; $priority = 0;
diff --git a/data/web/admin.php b/data/web/admin.php index 5dd7b3c6..d0fcbc99 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -107,7 +107,6 @@ $template_data = [ 'f2b_banlist_url' => getBaseUrl() . "/api/v1/get/fail2ban/banlist/" . $f2b_data['banlist_id'], 'q_data' => quarantine('settings'), 'qn_data' => quota_notification('get'),
'pw_reset_data' => reset_password('get_notification'), 'rsettings_map' => file_get_contents('http://nginx:8081/settings.php'), 'rsettings' => $rsettings, 'rspamd_regex_maps' => $rspamd_regex_maps, diff --git a/data/web/inc/ajax/container_ctrl.php b/data/web/inc/ajax/container_ctrl.php index 48c21cb1..f0e220f9 100644 --- a/data/web/inc/ajax/container_ctrl.php +++ b/data/web/inc/ajax/container_ctrl.php @@ -1,11 +1,4 @@ <?php
-// Block requests by checking the 'Sec-Fetch-Dest' header. -if (isset($_SERVER['HTTP_SEC_FETCH_DEST']) && $_SERVER['HTTP_SEC_FETCH_DEST'] !== 'empty') {
header('HTTP/1.1 403 Forbidden');
exit; -}
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'admin') { exit(); diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 25d08b9f..3cff09b9 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -284,17 +284,17 @@ function last_login($action, $username, $sasl_limit_days = 7, $ui_offset = 1) { } if (!$sasl[$k]['location']) { $curl = curl_init();
curl_setopt($curl, CURLOPT_URL,"https://dfdata.bella.network/country/" . $sasl[$k]['real_rip']);
curl_setopt($curl, CURLOPT_URL,"https://dfdata.bella.network/lookup/" . $sasl[$k]['real_rip']); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_USERAGENT, 'Moocow'); curl_setopt($curl, CURLOPT_TIMEOUT, 5); $ip_data = curl_exec($curl); if (!curl_errno($curl)) { $ip_data_array = json_decode($ip_data, true);
if ($ip_data_array !== false and !empty($ip_data_array['shortcountry'])) {
$sasl[$k]['location'] = $ip_data_array['shortcountry'];
if ($ip_data_array !== false and !empty($ip_data_array['location']['shortcountry'])) {
$sasl[$k]['location'] = $ip_data_array['location']['shortcountry']; try {
$redis->hSet('IP_SHORTCOUNTRY', $sasl[$k]['real_rip'], $ip_data_array['shortcountry']);
$redis->hSet('IP_SHORTCOUNTRY', $sasl[$k]['real_rip'], $ip_data_array['location']['shortcountry']); } catch (RedisException $e) { $_SESSION['return'][] = array( @@ -1073,17 +1073,13 @@ function update_sogo_static_view($mailbox = null) { function edit_user_account($_data) { global $lang; global $pdo;
$_data_log = $_data; !isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = ''; !isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = ''; !isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
$username = $_SESSION['mailcow_cc_username']; $role = $_SESSION['mailcow_cc_role']; $password_old = $_data['user_old_pass'];
$pw_recovery_email = $_data['pw_recovery_email'];
if (filter_var($username, FILTER_VALIDATE_EMAIL === false) || $role != 'user') { $_SESSION['return'][] = array( 'type' => 'danger', @@ -1092,24 +1088,20 @@ function edit_user_account($_data) { ); return false; }
// edit password
if (!empty($password_old) && !empty($_data['user_new_pass']) && !empty($_data['user_new_pass2'])) {
$stmt = $pdo->prepare("SELECT password
FROM mailbox
WHERE kind
NOT REGEXP 'location|thing|group'
AND username
= :user");
$stmt->execute(array(':user' => $username));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!verify_hash($row['password'], $password_old)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT password
FROM mailbox
WHERE kind
NOT REGEXP 'location|thing|group'
AND username
= :user");
$stmt->execute(array(':user' => $username));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!verify_hash($row['password'], $password_old)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (!empty($_data['user_new_pass']) && !empty($_data['user_new_pass2'])) { $password_new = $_data['user_new_pass']; $password_new2 = $_data['user_new_pass2']; if (password_check($password_new, $password_new2) !== true) { @@ -1124,29 +1116,8 @@ function edit_user_account($_data) { ':password_hashed' => $password_hashed, ':username' => $username ));
update_sogo_static_view();
}
// edit password recovery email
elseif (isset($pw_recovery_email)) {
if (!isset($_SESSION['acl']['pw_reset']) || $_SESSION['acl']['pw_reset'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
$pw_recovery_email = (!filter_var($pw_recovery_email, FILTER_VALIDATE_EMAIL)) ? '' : $pw_recovery_email;
$stmt = $pdo->prepare("UPDATE mailbox
SET attributes
= JSON_SET(attributes
, '$.recovery_email', :recovery_email)
WHERE username
= :username");
$stmt->execute(array(
':recovery_email' => $pw_recovery_email,
':username' => $username
)); }
update_sogo_static_view(); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(FUNCTION, $_data_log), @@ -1589,7 +1560,7 @@ function unset_tfa_key($_data) { } function get_tfa($username = null, $id = null) { global $pdo;
if (empty($username) && isset($_SESSION['mailcow_cc_username'])) {
if (isset($_SESSION['mailcow_cc_username'])) { $username = $_SESSION['mailcow_cc_username']; } elseif (empty($username)) { @@ -2290,386 +2261,6 @@ function uuid4() {
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); } -function reset_password($action, $data = null) {
global $pdo;
global $redis;
global $mailcow_hostname;
global $PW_RESET_TOKEN_LIMIT;
global $PW_RESET_TOKEN_LIFETIME;
$_data_log = $data;
if (isset($_data_log['new_password'])) $_data_log['new_password'] = '*';
if (isset($_data_log['new_password2'])) $_data_log['new_password2'] = '*';
switch ($action) {
case 'check':
$token = $data;
$stmt = $pdo->prepare("SELECT t1
.username
FROM reset_password
AS t1
JOIN mailbox
AS t2
ON t1
.username
= t2
.username
WHERE t1
.token
= :token AND t1
.created
> DATE_SUB(NOW(), INTERVAL :lifetime MINUTE) AND t2
.active
= 1;");
$stmt->execute(array(
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token),
':lifetime' => $PW_RESET_TOKEN_LIFETIME
));
$return = $stmt->fetch(PDO::FETCH_ASSOC);
return empty($return['username']) ? false : $return['username'];
break;
case 'issue':
$username = $data;
// perform cleanup
$stmt = $pdo->prepare("DELETE FROM reset_password
WHERE created < DATE_SUB(NOW(), INTERVAL :lifetime MINUTE);");
$stmt->execute(array(':lifetime' => $PW_RESET_TOKEN_LIFETIME));
if (filter_var($username, FILTER_VALIDATE_EMAIL) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$pw_reset_notification = reset_password('get_notification', 'raw');
if (!$pw_reset_notification) return false;
if (empty($pw_reset_notification['from']) || empty($pw_reset_notification['subject'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'password_reset_na'
);
return false;
}
$stmt = $pdo->prepare("SELECT * FROM mailbox
WHERE username
= :username");
$stmt->execute(array(':username' => $username));
$mailbox_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($mailbox_data)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'password_reset_invalid_user'
);
return false;
}
$mailbox_attr = json_decode($mailbox_data['attributes'], true);
if (empty($mailbox_attr['recovery_email']) || filter_var($mailbox_attr['recovery_email'], FILTER_VALIDATE_EMAIL) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => "password_reset_invalid_user"
);
return false;
}
$stmt = $pdo->prepare("SELECT * FROM reset_password
WHERE username
= :username");
$stmt->execute(array(':username' => $username));
$generated_token_count = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($generated_token_count >= $PW_RESET_TOKEN_LIMIT) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => "reset_token_limit_exceeded"
);
return false;
}
$token = implode('-', array(
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3)))
));
$stmt = $pdo->prepare("INSERT INTO reset_password
(username
, token
)
VALUES (:username, :token)");
$stmt->execute(array(
':username' => $username,
':token' => $token
));
$reset_link = getBaseURL() . "/reset-password?token=" . $token;
$request_date = new DateTime();
$locale_date = locale_get_default();
$date_formatter = new IntlDateFormatter(
$locale_date,
IntlDateFormatter::FULL,
IntlDateFormatter::FULL
);
$formatted_request_date = $date_formatter->format($request_date);
// set template vars
// subject
$pw_reset_notification['subject'] = str_replace('{{hostname}}', $mailcow_hostname, $pw_reset_notification['subject']);
$pw_reset_notification['subject'] = str_replace('{{link}}', $reset_link, $pw_reset_notification['subject']);
$pw_reset_notification['subject'] = str_replace('{{username}}', $username, $pw_reset_notification['subject']);
$pw_reset_notification['subject'] = str_replace('{{username2}}', $mailbox_attr['recovery_email'], $pw_reset_notification['subject']);
$pw_reset_notification['subject'] = str_replace('{{date}}', $formatted_request_date, $pw_reset_notification['subject']);
$pw_reset_notification['subject'] = str_replace('{{token_lifetime}}', $PW_RESET_TOKEN_LIFETIME, $pw_reset_notification['subject']);
// text
$pw_reset_notification['text_tmpl'] = str_replace('{{hostname}}', $mailcow_hostname, $pw_reset_notification['text_tmpl']);
$pw_reset_notification['text_tmpl'] = str_replace('{{link}}', $reset_link, $pw_reset_notification['text_tmpl']);
$pw_reset_notification['text_tmpl'] = str_replace('{{username}}', $username, $pw_reset_notification['text_tmpl']);
$pw_reset_notification['text_tmpl'] = str_replace('{{username2}}', $mailbox_attr['recovery_email'], $pw_reset_notification['text_tmpl']);
$pw_reset_notification['text_tmpl'] = str_replace('{{date}}', $formatted_request_date, $pw_reset_notification['text_tmpl']);
$pw_reset_notification['text_tmpl'] = str_replace('{{token_lifetime}}', $PW_RESET_TOKEN_LIFETIME, $pw_reset_notification['text_tmpl']);
// html
$pw_reset_notification['html_tmpl'] = str_replace('{{hostname}}', $mailcow_hostname, $pw_reset_notification['html_tmpl']);
$pw_reset_notification['html_tmpl'] = str_replace('{{link}}', $reset_link, $pw_reset_notification['html_tmpl']);
$pw_reset_notification['html_tmpl'] = str_replace('{{username}}', $username, $pw_reset_notification['html_tmpl']);
$pw_reset_notification['html_tmpl'] = str_replace('{{username2}}', $mailbox_attr['recovery_email'], $pw_reset_notification['html_tmpl']);
$pw_reset_notification['html_tmpl'] = str_replace('{{date}}', $formatted_request_date, $pw_reset_notification['html_tmpl']);
$pw_reset_notification['html_tmpl'] = str_replace('{{token_lifetime}}', $PW_RESET_TOKEN_LIFETIME, $pw_reset_notification['html_tmpl']);
$email_sent = reset_password('send_mail', array(
"from" => $pw_reset_notification['from'],
"to" => $mailbox_attr['recovery_email'],
"subject" => $pw_reset_notification['subject'],
"text" => $pw_reset_notification['text_tmpl'],
"html" => $pw_reset_notification['html_tmpl']
));
if (!$email_sent){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => "recovery_email_failed"
);
return false;
}
list($localPart, $domainPart) = explode('@', $mailbox_attr['recovery_email']);
if (strlen($localPart) > 1) {
$maskedLocalPart = $localPart[0] . str_repeat('*', strlen($localPart) - 1);
} else {
$maskedLocalPart = "*";
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => array("recovery_email_sent", $maskedLocalPart . '@' . $domainPart)
);
return array(
"username" => $username,
"issue" => "success"
);
break;
case 'reset':
$token = $data['token'];
$new_password = $data['new_password'];
$new_password2 = $data['new_password2'];
$username = $data['username'];
$check_tfa = $data['check_tfa'];
if (!$username || !$token) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'invalid_reset_token'
);
return false;
}
if (!password_check($new_password, $new_password2)) {
return false;
}
if ($check_tfa){
// check for tfa authenticators
$authenticators = get_tfa($username);
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
$_SESSION['pending_mailcow_cc_username'] = $username;
$_SESSION['pending_pw_reset_token'] = $token;
$_SESSION['pending_pw_new_password'] = $new_password;
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
$_SESSION['return'][] = array(
'type' => 'info',
'log' => array(FUNCTION, $user, '*'),
'msg' => 'awaiting_tfa_confirmation'
);
return false;
}
}
$password_hashed = hash_password($new_password);
$stmt = $pdo->prepare("UPDATE mailbox
SET
password
= :password_hashed,
attributes
= JSON_SET(attributes
, '$.passwd_update', NOW())
WHERE username
= :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username
));
// perform cleanup
$stmt = $pdo->prepare("DELETE FROM reset_password
WHERE username
= :username;");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'password_changed_success'
);
return true;
break;
case 'get_notification':
$type = $data;
try {
$settings['from'] = $redis->Get('PW_RESET_FROM');
$settings['subject'] = $redis->Get('PW_RESET_SUBJ');
$settings['html_tmpl'] = $redis->Get('PW_RESET_HTML');
$settings['text_tmpl'] = $redis->Get('PW_RESET_TEXT');
if (empty($settings['html_tmpl']) && empty($settings['text_tmpl'])) {
$settings['html_tmpl'] = file_get_contents("/tpls/pw_reset_html.tpl");
$settings['text_tmpl'] = file_get_contents("/tpls/pw_reset_text.tpl");
}
if ($type != "raw") {
$settings['html_tmpl'] = htmlspecialchars($settings['html_tmpl']);
$settings['text_tmpl'] = htmlspecialchars($settings['text_tmpl']);
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return $settings;
break;
case 'send_mail':
$from = $data['from'];
$to = $data['to'];
$text = $data['text'];
$html = $data['html'];
$subject = $data['subject'];
if (!filter_var($from, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'from_invalid'
);
return false;
}
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'to_invalid'
);
return false;
}
if (empty($subject)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'subject_empty'
);
return false;
}
if (empty($text)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'text_empty'
);
return false;
}
ini_set('max_execution_time', 0);
ini_set('max_input_time', 0);
$mail = new PHPMailer;
$mail->Timeout = 10;
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->isSMTP();
$mail->Host = 'postfix-mailcow';
$mail->SMTPAuth = false;
$mail->Port = 25;
$mail->setFrom($from);
$mail->Subject = $subject;
$mail->CharSet ="UTF-8";
if (!empty($html)) {
$mail->Body = $html;
$mail->AltBody = $text;
}
else {
$mail->Body = $text;
}
$mail->XMailer = 'MooMail';
$mail->AddAddress($to);
if (!$mail->send()) {
return false;
}
$mail->ClearAllRecipients();
return true;
break;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($action) {
case 'edit_notification':
$subject = $data['subject'];
$from = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $data['from']);
$from = (!filter_var($from, FILTER_VALIDATE_EMAIL)) ? "" : $from;
$subject = (empty($subject)) ? "" : $subject;
$text = (empty($data['text_tmpl'])) ? "" : $data['text_tmpl'];
$html = (empty($data['html_tmpl'])) ? "" : $data['html_tmpl'];
try {
$redis->Set('PW_RESET_FROM', $from);
$redis->Set('PW_RESET_SUBJ', $subject);
$redis->Set('PW_RESET_HTML', $html);
$redis->Set('PW_RESET_TEXT', $text);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(FUNCTION, $action, $_data_log),
'msg' => 'saved_settings'
);
break;
} -}
function get_logs($application, $lines = false) { if ($lines === false) { diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index c927ce49..00c11dee 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -184,7 +184,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'msg' => 'global_filter_written' ); return true;
break; case 'filter': $sieve = new Sieve\SieveParser(); if (!isset($_SESSION['acl']['filters']) || $_SESSION['acl']['filters'] != "1" ) { @@ -1250,7 +1249,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $_data['quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0; $_data['quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0; $_data['app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
$_data['pw_reset'] = (in_array('pw_reset', $_data['acl'])) ? 1 : 0; } else { $_data['spam_alias'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_spam_alias']); $_data['tls_policy'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_tls_policy']); @@ -1266,15 +1264,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $_data['quarantine_notification'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_notification']); $_data['quarantine_category'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_quarantine_category']); $_data['app_passwds'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_app_passwds']);
$_data['pw_reset'] = intval($MAILBOX_DEFAULT_ATTRIBUTES['acl_pw_reset']);
}
try {
$stmt = $pdo->prepare("INSERT INTO `user_acl`
(`username`, `spam_alias`, `tls_policy`, `spam_score`, `spam_policy`, `delimiter_action`, `syncjobs`, `eas_reset`, `sogo_profile_reset`,
pushover
, quarantine
, quarantine_attachments
, quarantine_notification
, quarantine_category
, app_passwds
, pw_reset
)
pushover
, quarantine
, quarantine_attachments
, quarantine_notification
, quarantine_category
, app_passwds
)
VALUES (:username, :spam_alias, :tls_policy, :spam_score, :spam_policy, :delimiter_action, :syncjobs, :eas_reset, :sogo_profile_reset,
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds, :pw_reset) ");
:pushover, :quarantine, :quarantine_attachments, :quarantine_notification, :quarantine_category, :app_passwds) "); $stmt->execute(array( ':username' => $username, ':spam_alias' => $_data['spam_alias'], @@ -1290,8 +1287,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ':quarantine_attachments' => $_data['quarantine_attachments'], ':quarantine_notification' => $_data['quarantine_notification'], ':quarantine_category' => $_data['quarantine_category'],
':app_passwds' => $_data['app_passwds'],
':pw_reset' => $_data['pw_reset']
':app_passwds' => $_data['app_passwds'] )); } catch (PDOException $e) { @@ -1580,7 +1576,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $attr['acl_quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0; $attr['acl_quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0; $attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
$attr['acl_pw_reset'] = (in_array('pw_reset', $_data['acl'])) ? 1 : 0; } else { $_data['acl'] = (array)$_data['acl']; $attr['acl_spam_alias'] = 0; @@ -2870,22 +2865,21 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $_data['sieve_access'] = (in_array('sieve', $_data['protocol_access'])) ? 1 : 0; } if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active']; (int)$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($is_now['attributes']['force_pw_update']);
(int)$sogo_access = (isset($_data['sogo_access']) && isset($_SESSION['acl']['sogo_access']) && $_SESSION['acl']['sogo_access'] == "1") ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']);
(int)$imap_access = (isset($_data['imap_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['imap_access']) : intval($is_now['attributes']['imap_access']);
(int)$pop3_access = (isset($_data['pop3_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']);
(int)$smtp_access = (isset($_data['smtp_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['smtp_access']) : intval($is_now['attributes']['smtp_access']);
(int)$sieve_access = (isset($_data['sieve_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['sieve_access']) : intval($is_now['attributes']['sieve_access']);
(int)$relayhost = (isset($_data['relayhost']) && isset($_SESSION['acl']['mailbox_relayhost']) && $_SESSION['acl']['mailbox_relayhost'] == "1") ? intval($_data['relayhost']) : intval($is_now['attributes']['relayhost']);
(int)$quota_m = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576);
$name = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name'];
$domain = $is_now['domain'];
$quota_b = $quota_m * 1048576;
$password = (!empty($_data['password'])) ? $_data['password'] : null;
$password2 = (!empty($_data['password2'])) ? $_data['password2'] : null;
$pw_recovery_email = (isset($_data['pw_recovery_email'])) ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email'];
$tags = (is_array($_data['tags']) ? $_data['tags'] : array());
(int)$sogo_access = (isset($_data['sogo_access']) && isset($_SESSION['acl']['sogo_access']) && $_SESSION['acl']['sogo_access'] == "1") ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']);
(int)$imap_access = (isset($_data['imap_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['imap_access']) : intval($is_now['attributes']['imap_access']);
(int)$pop3_access = (isset($_data['pop3_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']);
(int)$smtp_access = (isset($_data['smtp_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['smtp_access']) : intval($is_now['attributes']['smtp_access']);
(int)$sieve_access = (isset($_data['sieve_access']) && isset($_SESSION['acl']['protocol_access']) && $_SESSION['acl']['protocol_access'] == "1") ? intval($_data['sieve_access']) : intval($is_now['attributes']['sieve_access']);
(int)$relayhost = (isset($_data['relayhost']) && isset($_SESSION['acl']['mailbox_relayhost']) && $_SESSION['acl']['mailbox_relayhost'] == "1") ? intval($_data['relayhost']) : intval($is_now['attributes']['relayhost']);
(int)$quota_m = (isset_has_content($_data['quota'])) ? intval($_data['quota']) : ($is_now['quota'] / 1048576);
$name = (!empty($_data['name'])) ? ltrim(rtrim($_data['name'], '>'), '<') : $is_now['name'];
$domain = $is_now['domain'];
$quota_b = $quota_m * 1048576;
$password = (!empty($_data['password'])) ? $_data['password'] : null;
$password2 = (!empty($_data['password2'])) ? $_data['password2'] : null;
$tags = (is_array($_data['tags']) ? $_data['tags'] : array()); } else { $_SESSION['return'][] = array( @@ -3138,43 +3132,31 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ':address' => $username, ':active' => $active ));
try {
$stmt = $pdo->prepare("UPDATE mailbox
SET
active
= :active,
name
= :name,
quota
= :quota_b,
attributes
= JSON_SET(attributes
, '$.force_pw_update', :force_pw_update),
attributes
= JSON_SET(attributes
, '$.sogo_access', :sogo_access),
attributes
= JSON_SET(attributes
, '$.imap_access', :imap_access),
attributes
= JSON_SET(attributes
, '$.sieve_access', :sieve_access),
attributes
= JSON_SET(attributes
, '$.pop3_access', :pop3_access),
attributes
= JSON_SET(attributes
, '$.relayhost', :relayhost),
attributes
= JSON_SET(attributes
, '$.smtp_access', :smtp_access),
attributes
= JSON_SET(attributes
, '$.recovery_email', :recovery_email)
WHERE username
= :username");
$stmt->execute(array(
':active' => $active,
':name' => $name,
':quota_b' => $quota_b,
':force_pw_update' => $force_pw_update,
':sogo_access' => $sogo_access,
':imap_access' => $imap_access,
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':smtp_access' => $smtp_access,
':recovery_email' => $pw_recovery_email,
':relayhost' => $relayhost,
':username' => $username
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
return false;
}
$stmt = $pdo->prepare("UPDATE mailbox
SET
active
= :active,
name
= :name,
quota
= :quota_b,
attributes
= JSON_SET(attributes
, '$.force_pw_update', :force_pw_update),
attributes
= JSON_SET(attributes
, '$.sogo_access', :sogo_access),
attributes
= JSON_SET(attributes
, '$.imap_access', :imap_access),
attributes
= JSON_SET(attributes
, '$.sieve_access', :sieve_access),
attributes
= JSON_SET(attributes
, '$.pop3_access', :pop3_access),
attributes
= JSON_SET(attributes
, '$.relayhost', :relayhost),
attributes
= JSON_SET(attributes
, '$.smtp_access', :smtp_access)
WHERE username
= :username");
$stmt->execute(array(
':active' => $active,
':name' => $name,
':quota_b' => $quota_b,
':force_pw_update' => $force_pw_update,
':sogo_access' => $sogo_access,
':imap_access' => $imap_access,
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':smtp_access' => $smtp_access,
':relayhost' => $relayhost,
':username' => $username
)); // save tags foreach($tags as $index => $tag){ if (empty($tag)) continue; @@ -3281,7 +3263,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $attr['acl_quarantine_notification'] = (in_array('quarantine_notification', $_data['acl'])) ? 1 : 0; $attr['acl_quarantine_category'] = (in_array('quarantine_category', $_data['acl'])) ? 1 : 0; $attr['acl_app_passwds'] = (in_array('app_passwds', $_data['acl'])) ? 1 : 0;
$attr['acl_pw_reset'] = (in_array('pw_reset', $_data['acl'])) ? 1 : 0;
} else {
foreach ($is_now as $key => $value){
$attr[$key] = $is_now[$key];
@@ -5231,7 +5212,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'msg' => 'Could not move maildir to garbage collector: variables local_part and/or domain empty'
);
}
if (strtolower(getenv('SKIP_SOLR')) == 'n' && strtolower(getenv('FLATCURVE_EXPERIMENTAL')) != 'y') {
if (strtolower(getenv('SKIP_SOLR')) == 'n') { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, 'http://solr:8983/solr/dovecot-fts/update?commit=true'); curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: text/xml')); diff --git a/data/web/inc/functions.rspamd.inc.php b/data/web/inc/functions.rspamd.inc.php index c2444d99..ec86919c 100644 --- a/data/web/inc/functions.rspamd.inc.php +++ b/data/web/inc/functions.rspamd.inc.php @@ -145,22 +145,17 @@ function rspamd_maps($_action, $_data = null) { $maps = (array)$_data['map']; $valid_maps = array(); foreach ($maps as $map) {
$is_valid = false; foreach ($RSPAMD_MAPS as $rspamd_map_type) {
if (in_array($map, $rspamd_map_type)) {
$is_valid = true;
break;
if (!in_array($map, $rspamd_map_type)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $_action, '-'),
'msg' => array('global_map_invalid', $map)
);
} else {
array_push($valid_maps, $map); } }
if ($is_valid) {
array_push($valid_maps, $map);
} else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(FUNCTION, $_action, '-'),
'msg' => array('global_map_invalid', $map)
);
} } foreach ($valid_maps as $map) { try { diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index 5fe25bef..9afc288d 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -49,6 +49,7 @@ $globalVariables = [ 'app_links' => customize('get', 'app_links'), 'is_root_uri' => (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) == '/'), 'uri' => $_SERVER['REQUEST_URI'],
'last_login' => last_login('get', $_SESSION['mailcow_cc_username'], 7, 0)['ui']['time'] ];
foreach ($globalVariables as $globalVariableName => $globalVariableValue) { diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 8c4951d5..f62858b3 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -3,7 +3,7 @@ function init_db_schema() { try { global $pdo;
$db_version = "29072024_1000";
$db_version = "26022024_1433";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -483,7 +483,6 @@ function init_db_schema() { "quarantine_notification" => "TINYINT(1) NOT NULL DEFAULT '1'", "quarantine_category" => "TINYINT(1) NOT NULL DEFAULT '1'", "app_passwds" => "TINYINT(1) NOT NULL DEFAULT '1'",
"pw_reset" => "TINYINT(1) NOT NULL DEFAULT '1'", ), "keys" => array( "primary" => array( @@ -695,19 +694,6 @@ function init_db_schema() { ), "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" ),
"reset_password" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"token" => "VARCHAR(255) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
),
"keys" => array(
"primary" => array(
"" => array("token", "created")
),
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
), "imapsync" => array( "cols" => array( "id" => "INT NOT NULL AUTO_INCREMENT",`
Dude please be a bit more structured.... don't paste any github prs here or what so ever.
Keep it clean and simple. No one can read that all
Apologies. Hitting the Markdown <> button causes the browser to hang bad but the Comment button is still functional. Will try attaching git-diff as txt file. git-diff.txt
Another note that may be helpful pinpointing the problem: this server was incrementally upgraded from Debian 10 to 11 to 12 after Debian 12 was released. I wouldn't think that would be the cause of the issue since rolling back to 2024-06a works fine with the upgraded Debian but perhaps it is. @chriscroome
Apologies. Hitting the Markdown <> button causes the browser to hang bad but the Comment button is still functional. Will try attaching git-diff as txt file. git-diff.txt
Thanks,
could not see any difference except version differences as you rollback to 2024-06a.
Contribution guidelines
I've found a bug and checked that ...
Description
Logs:
Steps to reproduce:
Which branch are you using?
master
Which architecture are you using?
x86
Operating System:
Debian GNU/Linux 12 (bookworm)
Server/VM specifications:
6GB RAM, 4 CPU cores
Is Apparmor, SELinux or similar active?
yes
Virtualization technology:
Xen
Docker version:
27.3.1, build ce12230
docker-compose version or docker compose version:
v2.29.0
mailcow version:
2024-08a
Reverse proxy:
none
Logs of git diff:
Logs of iptables -L -vn:
Logs of ip6tables -L -vn:
Logs of iptables -L -vn -t nat:
Logs of ip6tables -L -vn -t nat:
DNS check: