1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2026-06-14 02:20:28 +00:00

Compare commits

...

71 Commits

Author SHA1 Message Date
DerLinkman d8a856d1cb update: backport compose v5 fix for legacy branch 2025-12-08 14:48:55 +01:00
FreddleSpl0it 11356674ba [Redis] Update to 7.4.6 2025-10-15 09:25:01 +02:00
FreddleSpl0it 8c5f6c0321 [Dovecot] Use Jinja2 sandbox for rendering quota and quarantine notifications 2025-07-15 10:46:50 +02:00
FreddleSpl0it 6a16a4886c Merge branch 'staging' into legacy 2025-03-24 11:48:11 +01:00
FreddleSpl0it ad5f07f077 update.sh: add 2025-03 as major version 2025-03-24 11:47:27 +01:00
FreddleSpl0it 91c82e8a67 Merge pull request #6384 from mailcow/feat/update-components-alp-3.21
os: updated alpine containers to 3.21
2025-03-24 11:30:58 +01:00
FreddleSpl0it 4222f73ea0 Add switch to legacy version 2025-03-20 14:40:12 +01:00
DerLinkman 463e3ab78c rspamd: update rspamd to 3.11.1 (#6374) 2025-03-14 12:18:59 +01:00
FreddleSpl0it 2a15914324 Fix major update prompt 2025-03-14 11:22:57 +01:00
Marvin A. Ruder 062539b7d7 dkim: Add support for 3072 and 4096 bit RSA keys (#6365)
* dkim: Add support for 3072 and 4096 bit RSA keys

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

* php: added missing ; in dkim function

* php: make 4096 DKIM default

* db: update schema to set dkim 4096 as default

* Revert "db: update schema to set dkim 4096 as default"

This reverts commit 790b40a695.

* Revert "php: make 4096 DKIM default"

This reverts commit 7e643376c7.

---------

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
2025-03-11 15:30:46 +01:00
DerLinkman 18acbc7a4c cold-standby: changed texts + removed --no-parallel for pull 2025-03-11 12:35:13 +01:00
DerLinkman 2f93f1d0c5 os: fixes for newer mariadb-client versions (especially on alpine 3.21) 2025-03-10 16:45:57 +01:00
DerLinkman 0860a7503e os: updated alpine containers to 3.21 2025-03-10 11:56:12 +01:00
renovate[bot] 86df78255d chore(deps): update dependency composer/composer to v2.8.6 (#5719)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 11:39:19 +01:00
milkmaker 03565df48d [Web] Updated lang.ko-kr.json (#6356)
Co-authored-by: dongsu8142 <dongsu8142@naver.com>
2025-03-07 21:37:31 +01:00
milkmaker 0435766c17 [Web] Updated lang.ko-kr.json (#6353)
Co-authored-by: dongsu8142 <dongsu8142@naver.com>
2025-03-05 17:43:37 +01:00
renovate[bot] 79f4cf4021 chore(deps): update docker/build-push-action action to v6 (#6334) 2025-03-05 16:35:46 +01:00
milkmaker 81803836f0 [Web] Updated lang.ko-kr.json (#6350)
Co-authored-by: dongsu8142 <dongsu8142@naver.com>
2025-03-03 22:49:23 +01:00
milkmaker 4bd267515a update postscreen_access.cidr (#6345) 2025-03-01 13:32:21 +01:00
DerLinkman 5296085189 update.sh: corrected typos inside update.sh 2025-02-27 11:47:08 +01:00
DerLinkman a4c2cf4c67 scripts: adapted new docker image names to docker_garbage function + removed dup 2025-02-27 11:44:52 +01:00
Peter 3c9d0c9d57 use ghcr.io for backupimage (#6333)
* use ghcr.io for backup image

* backup script: use renamed script + improved build of image

---------

Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
2025-02-27 10:58:23 +01:00
FreddleSpl0it 35a6f81d0d [Redis] use 7.4.2-alpine image 2025-02-27 09:28:52 +01:00
FreddleSpl0it 4b31c04e3e Merge pull request #6330 from mailcow/feat/major-update-prompt
Prompt user before applying major updates
2025-02-27 08:15:21 +01:00
FreddleSpl0it 3d9cc2f6dd add 2025-02 to major versions 2025-02-27 08:14:34 +01:00
DerLinkman 704dd50262 compose: use ghcr.io for new/current mailcow docker images instead of docker hub (#6332) 2025-02-26 15:20:57 +01:00
FreddleSpl0it c4a0e370b7 Merge pull request #6155 from PseudoResonance/fix2752
Fix #2752 - Allow domain recipients for address rewrite
2025-02-26 10:01:03 +01:00
FreddleSpl0it 787fa49d0c prompt user before applying major updates 2025-02-25 12:08:21 +01:00
DerLinkman a6c38590ca rspamd: upgraded rspamd to 3.11.0-2 (incl. NIXSPAM Removal) (#6328) 2025-02-25 09:23:10 +01:00
PseudoResonance e52323bf1d Fix @ prefixing domain rewrite and update localization 2025-02-24 22:36:17 -08:00
PseudoResonance f15ee39b63 Fix #2752: Domain recipient for address rewrite
(cherry picked from commit 40f6d691d8774d6f813153974f8fe462a8db9ab3)
2025-02-24 22:07:23 -08:00
FreddleSpl0it 6ec5e88793 Merge pull request #6309 from mailcow/fix/6308
[Dovecot][Netfilter] Fix dovecot failed login regex
2025-02-24 11:26:06 +01:00
FreddleSpl0it 7d35646342 [Netfilter] adjust dovecot failed login regex 2025-02-24 09:20:41 +01:00
FreddleSpl0it 321965adee [Netfilter] Fix dovecot password mismatch regex 2025-02-18 15:05:59 +01:00
Peter 7bce5d836b Move sed cmd to remove discontinued DNSBLs (#6315)
* Move sed cmd to remove discontinued DNSBLs

* compose: bump postfix version

---------

Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
2025-02-18 11:20:03 +01:00
FreddleSpl0it 351f4ce787 [Redis] Add support for masterauth via env var 2025-02-18 11:16:06 +01:00
FreddleSpl0it a567d5dc31 [Nginx] Add support for trusted proxies via env var 2025-02-18 11:03:34 +01:00
DerLinkman 4ac541f671 [Mariadb] Update to 10.11 (LTS) (#5152)
* [Mariadb] Update to 10.11 (LTS)

* mysql: set default collation to general_ci
2025-02-17 15:48:25 +01:00
Dmitriy Alekseev f6dc0b463f Update Rspamd to 3.11.0 and enable SMTPUTF8 for outgoing mail (#6216)
* Update Rspamd to 3.11

* Enable SMTPUTF8 and hide it from SMTPD greeting

* Update options.inc

* compose: increased rspamd tag
2025-02-17 14:41:39 +01:00
DerLinkman 16e22e23dc sogo: switched apt source to sogo again (supports aarch64 now) 2025-02-17 14:31:50 +01:00
FreddleSpl0it d8afa6f393 [Dovecot][Netfilter] Fix dovecot failed login regex 2025-02-14 13:12:12 +01:00
milkmaker 836e3f15b7 [Web] Updated lang.es-es.json (#6307)
Co-authored-by: Julie GINESTIERE <julien.ginestiere+git@gmail.com>
2025-02-13 19:32:39 +01:00
FreddleSpl0it aaa7e4a184 [Web] Fix incorrect session lifetime in sogo-auth.php 2025-02-13 11:54:55 +01:00
FreddleSpl0it 3912341b32 [SOGo] rename custom logo 2025-02-12 11:31:14 +01:00
FreddleSpl0it 735d5f0e56 Merge pull request #6220 from Babybatrick/staging
Adding lines to docker-compose.yml to allow for simpler SOGo web client UI customisation
2025-02-12 10:54:16 +01:00
FreddleSpl0it f375794fb7 Merge pull request #6223 from mailcow/ffdhe2048
Ffdhe2048
2025-02-12 10:48:22 +01:00
renovate[bot] 4ed3017a02 chore(deps): update devops-infra/action-pull-request action to v0.6.0 (#6302) 2025-02-12 06:56:10 +01:00
FreddleSpl0it ef2f5f7be0 [Dovecot] Use Redis ACL user quota_notify with restricted access 2025-02-11 16:59:18 +01:00
Henry Williams 743e88fd67 Update generate_config.sh version checking for wider compatibility (#6270)
* Update generate_config.sh version checking for wider compatibility 

fix: replace `grep -oP` with `grep -oE` for broader compatibility

The `-P` option (Perl-compatible regex) is not supported in all versions of `grep`, particularly the default BSD `grep` on macOS. This change replaces `-P` with `-E` (extended regex), which is more widely available and ensures compatibility across different environments.

Tested on macOS and Linux.

* Update generate_config.sh to remove use of platform dependent grep

Replaced version checking using free-form text. Instead, uses Docker’s built-in templating instead of parsing free-form text. This gives cross-platform consistency without dependency on particular versions of grep.
2025-02-11 13:55:03 +01:00
DerLinkman ac2f0c7db1 Merge pull request #6286 from mailcow/fix-workflow-staging
Fix check_prs_if_on_staging workflow
2025-02-11 13:52:44 +01:00
FreddleSpl0it 3544a2246e [Nginx] fix ADDITIONAL_SERVER_NAMES array 2025-02-04 13:30:00 +01:00
FreddleSpl0it 97890b71f1 [Nginx] Invert SKIP container condition 2025-02-03 12:22:13 +01:00
FreddleSpl0it e645f931dc [Nginx] Add env var for HTTP to HTTPS redirection 2025-02-03 12:05:08 +01:00
FreddleSpl0it bbdec0960a Merge pull request #6290 from mailcow/fix/nginx-vhosts
[Nginx] Use vhosts for additional server names
2025-02-03 11:35:09 +01:00
milkmaker 41ba7d97fa update postscreen_access.cidr (#6287) 2025-02-01 17:06:07 +01:00
Peter 83fc2c6387 It's github-token now 2025-01-31 17:20:28 +01:00
DerLinkman aac4c6b5f4 postfix: added master.pid removal and startsecs to supervisord (#6284) 2025-01-31 12:49:39 +01:00
FreddleSpl0it 3c0f775e2f Merge pull request #6281 from mailcow/fix/6275
[Nginx] Fix
2025-01-31 10:49:21 +01:00
FreddleSpl0it 3a81b84cf7 [Nginx] Fix #6275 2025-01-30 14:49:18 +01:00
FreddleSpl0it a2e87e0880 [Web] Add validation for server_name against allow list 2025-01-30 11:47:55 +01:00
DerLinkman 2407aa7895 Merge branch 'feat/clamd-rebuild' into staging 2025-01-29 14:01:39 +01:00
FreddleSpl0it 0ad327bbe5 [Nginx] Use separate vhosts for additional server names 2025-01-29 09:51:45 +01:00
DerLinkman 1a087bb2c8 clamd: cleanup dockerfile 2025-01-28 14:49:11 +01:00
DerLinkman 65bc581fab clamd: remove exposed ports from buildfile 2025-01-28 14:36:43 +01:00
DerLinkman 60a2270d1e clamd: update to 1.4.2 + build from source instead using alpine packages 2025-01-28 14:25:56 +01:00
FreddleSpl0it 8ed51e500f Merge pull request #6260 from mailcow/manitu
Remove discontinued Nixspam DNSBL
2025-01-27 16:21:29 +01:00
FreddleSpl0it 45d14254f2 [Postfix] Remove discontinued Nixspam DNSBL from existing dns_blocklists.cf 2025-01-24 10:06:50 +01:00
Michael Kuron 04116982a5 Remove discontinued Nixspam DNSBL 2025-01-23 22:16:54 +01:00
Dmitriy Alekseev d92aa4b15d Update dhparams.pem
Use https://ssl-config.mozilla.org/ffdhe2048.txt due to better security of the key
2024-12-20 15:39:41 +01:00
Amin ade20d79d4 Uploading of the necessary files, after new volumes were added to docker-compose.yml (sogo-mailcow container)
After new volumes were added to docker-compose.yml in the sogo-mailcow container, it is necessary to include the specified files in the path, in order for docker to correctly start after running `docker compose up` command, otherwise error will appear, as necessary files would be missing.
The files uploaded are original SOGo UI elements, obtained from the sogo-mailcow container. Whenever users will need to change the UI elements, they would just need to change these files. Hence simplifying the process.
2024-12-19 22:13:27 +08:00
Amin 65bc8f0972 Update docker-compose.yml (sogo-mailcow)
This commit includes the addition of 3 lines, in the volumes part of the sogo-mailcow container, to allow for better customisation of the user interface on the web client page.
2024-12-19 21:59:05 +08:00
76 changed files with 804 additions and 270 deletions
@@ -12,7 +12,7 @@ jobs:
- name: Send message - name: Send message
uses: thollander/actions-comment-pull-request@v3.0.1 uses: thollander/actions-comment-pull-request@v3.0.1
with: with:
GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }} github-token: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
message: | message: |
Thanks for contributing! Thanks for contributing!
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Run the Action - name: Run the Action
uses: devops-infra/action-pull-request@v0.5.5 uses: devops-infra/action-pull-request@v0.6.0
with: with:
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }} github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}} title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
+8 -4
View File
@@ -9,6 +9,8 @@ on:
jobs: jobs:
docker_image_build: docker_image_build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
packages: write
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -19,11 +21,13 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub - name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_USERNAME }} registry: ghcr.io
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
@@ -32,4 +36,4 @@ jobs:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
file: data/Dockerfiles/backup/Dockerfile file: data/Dockerfiles/backup/Dockerfile
push: true push: true
tags: mailcow/backup:latest tags: ghcr.io/mailcow/backup:latest
+3 -1
View File
@@ -47,7 +47,9 @@ data/conf/sogo/custom-theme.js
data/conf/sogo/plist_ldap data/conf/sogo/plist_ldap
data/conf/sogo/sieve.creds data/conf/sogo/sieve.creds
data/conf/sogo/cron.creds data/conf/sogo/cron.creds
data/conf/sogo/sogo-full.svg data/conf/sogo/custom-fulllogo.svg
data/conf/sogo/custom-shortlogo.svg
data/conf/sogo/custom-fulllogo.png
data/gitea/ data/gitea/
data/gogs/ data/gogs/
data/hooks/dovecot/* data/hooks/dovecot/*
+2 -3
View File
@@ -1,8 +1,7 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
RUN apk upgrade --no-cache \ RUN apk upgrade --no-cache \
&& apk add --update --no-cache \ && apk add --update --no-cache \
bash \ bash \
@@ -15,7 +14,7 @@ RUN apk upgrade --no-cache \
tini \ tini \
tzdata \ tzdata \
python3 \ python3 \
acme-tiny --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community/ acme-tiny
COPY acme.sh /srv/acme.sh COPY acme.sh /srv/acme.sh
COPY functions.sh /srv/functions.sh COPY functions.sh /srv/functions.sh
+2 -2
View File
@@ -138,7 +138,7 @@ log_f "Resolver OK"
log_f "Waiting for domain table..." log_f "Waiting for domain table..."
while [[ -z ${DOMAIN_TABLE} ]]; do while [[ -z ${DOMAIN_TABLE} ]]; do
curl --silent http://nginx.${COMPOSE_PROJECT_NAME}_mailcow-network/ >/dev/null 2>&1 curl --silent http://nginx.${COMPOSE_PROJECT_NAME}_mailcow-network/ >/dev/null 2>&1
DOMAIN_TABLE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs) DOMAIN_TABLE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs)
[[ -z ${DOMAIN_TABLE} ]] && sleep 10 [[ -z ${DOMAIN_TABLE} ]] && sleep 10
done done
log_f "OK" no_date log_f "OK" no_date
@@ -231,7 +231,7 @@ while true; do
######################################### #########################################
# IP and webroot challenge verification # # IP and webroot challenge verification #
SQL_DOMAINS=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain WHERE backupmx=0 and active=1" -Bs) SQL_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain WHERE backupmx=0 and active=1" -Bs)
if [[ ! $? -eq 0 ]]; then if [[ ! $? -eq 0 ]]; then
log_f "Failed to read SQL domains, retrying in 1 minute..." log_f "Failed to read SQL domains, retrying in 1 minute..."
sleep 1m sleep 1m
+1 -1
View File
@@ -1,3 +1,3 @@
FROM debian:bookworm-slim FROM debian:bookworm-slim
RUN apt update && apt install pigz RUN apt update && apt install pigz -y --no-install-recommends
+91 -6
View File
@@ -1,14 +1,99 @@
FROM alpine:3.20 FROM alpine:3.21 AS builder
WORKDIR /src
ENV CLAMD_VERSION=1.4.2
RUN apk upgrade --no-cache \
&& apk add --update --no-cache \
g++ \
gcc \
gdb \
make \
cmake \
py3-pytest \
python3 \
valgrind \
bzip2-dev \
check-dev \
curl-dev \
json-c-dev \
libmilter-dev \
libxml2-dev \
linux-headers \
ncurses-dev \
openssl-dev \
pcre2-dev \
zlib-dev \
cargo \
rust
RUN wget -P /src https://www.clamav.net/downloads/production/clamav-${CLAMD_VERSION}.tar.gz \
&& tar xzfv /src/clamav-${CLAMD_VERSION}.tar.gz \
&& cd /src/clamav-${CLAMD_VERSION} \
&& cmake . \
-D CMAKE_BUILD_TYPE="Release" \
-D CMAKE_INSTALL_PREFIX="/usr" \
-D CMAKE_INSTALL_LIBDIR="/usr/lib" \
-D APP_CONFIG_DIRECTORY="/etc/clamav" \
-D DATABASE_DIRECTORY="/var/lib/clamav" \
-D ENABLE_CLAMONACC=OFF \
-D ENABLE_EXAMPLES=OFF \
-D ENABLE_MILTER=ON \
-D ENABLE_MAN_PAGES=OFF \
-D ENABLE_STATIC_LIB=OFF \
-D ENABLE_JSON_SHARED=ON \
&& cmake --build . \
&& make DESTDIR="/clamav" -j$(($(nproc) - 1)) install \
&& rm -r "/clamav/usr/lib/pkgconfig/" \
&& sed -e "s|^\(Example\)|\# \1|" \
-e "s|.*\(LocalSocket\) .*|\1 /tmp/clamd.sock|" \
-e "s|.*\(TCPSocket\) .*|\1 3310|" \
-e "s|.*\(TCPAddr\) .*|#\1 0.0.0.0|" \
-e "s|.*\(User\) .*|\1 clamav|" \
-e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/clamd.log|" \
-e "s|^\#\(LogTime\).*|\1 yes|" \
"/clamav/etc/clamav/clamd.conf.sample" > "/clamav/etc/clamav/clamd.conf" \
&& sed -e "s|^\(Example\)|\# \1|" \
-e "s|.*\(DatabaseOwner\) .*|\1 clamav|" \
-e "s|^\#\(UpdateLogFile\) .*|\1 /var/log/clamav/freshclam.log|" \
-e "s|^\#\(NotifyClamd\).*|\1 /etc/clamav/clamd.conf|" \
-e "s|^\#\(ScriptedUpdates\).*|\1 yes|" \
"/clamav/etc/clamav/freshclam.conf.sample" > "/clamav/etc/clamav/freshclam.conf" \
&& sed -e "s|^\(Example\)|\# \1|" \
-e "s|.*\(MilterSocket\) .*|\1 inet:7357|" \
-e "s|.*\(User\) .*|\1 clamav|" \
-e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/milter.log|" \
-e "s|^\#\(LogTime\).*|\1 yes|" \
-e "s|.*\(\ClamdSocket\) .*|\1 unix:/tmp/clamd.sock|" \
"/clamav/etc/clamav/clamav-milter.conf.sample" > "/clamav/etc/clamav/clamav-milter.conf" || exit 1
FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
RUN apk upgrade --no-cache \ RUN apk upgrade --no-cache \
&& apk add --update --no-cache \ && apk add --update --no-cache \
rsync \ tzdata \
clamav \ rsync \
bind-tools \ bind-tools \
bash \ bash \
tini tini \
json-c \
libbz2 \
libcurl \
libmilter \
libxml2 \
ncurses-libs \
pcre2 \
zlib \
libgcc \
&& addgroup -S "clamav" && \
adduser -D -G "clamav" -h "/var/lib/clamav" -s "/bin/false" -S "clamav" && \
install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \
chown -R clamav:clamav /var/lib/clamav
COPY --from=builder "/clamav" "/"
# init # init
COPY clamd.sh /clamd.sh COPY clamd.sh /clamd.sh
+1
View File
@@ -91,6 +91,7 @@ done
) & ) &
BACKGROUND_TASKS+=($!) BACKGROUND_TASKS+=($!)
echo "$(clamd -V) is starting... please wait a moment."
nice -n10 clamd & nice -n10 clamd &
BACKGROUND_TASKS+=($!) BACKGROUND_TASKS+=($!)
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
+2 -2
View File
@@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
@@ -65,7 +65,7 @@ RUN addgroup -g 5000 vmail \
perl-par-packer \ perl-par-packer \
perl-parse-recdescent \ perl-parse-recdescent \
perl-lockfile-simple \ perl-lockfile-simple \
libproc \ libproc2 \
perl-readonly \ perl-readonly \
perl-regexp-common \ perl-regexp-common \
perl-sys-meminfo \ perl-sys-meminfo \
+2 -2
View File
@@ -15,6 +15,6 @@ if ! [[ ${MAX_AGE} =~ ${NUM_REGEXP} ]] ; then
exit 1 exit 1
fi fi
TO_DELETE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT COUNT(id) FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" -BN) TO_DELETE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT COUNT(id) FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" -BN)
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY"
echo "Deleted ${TO_DELETE} items from quarantine table (max age is ${MAX_AGE//[!0-9]/} days)" echo "Deleted ${TO_DELETE} items from quarantine table (max age is ${MAX_AGE//[!0-9]/} days)"
@@ -414,15 +414,15 @@ printenv | sed 's/^\(.*\)$/export \1/g' > /source_env.sh
# Clean stopped imapsync jobs # Clean stopped imapsync jobs
rm -f /tmp/imapsync_busy.lock rm -f /tmp/imapsync_busy.lock
IMAPSYNC_TABLE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'imapsync'" -Bs) IMAPSYNC_TABLE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'imapsync'" -Bs)
[[ ! -z ${IMAPSYNC_TABLE} ]] && mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "UPDATE imapsync SET is_running='0'" [[ ! -z ${IMAPSYNC_TABLE} ]] && mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "UPDATE imapsync SET is_running='0'"
# Envsubst maildir_gc # Envsubst maildir_gc
echo "$(envsubst < /usr/local/bin/maildir_gc.sh)" > /usr/local/bin/maildir_gc.sh echo "$(envsubst < /usr/local/bin/maildir_gc.sh)" > /usr/local/bin/maildir_gc.sh
# GUID generation # GUID generation
while [[ ${VERSIONS_OK} != 'OK' ]]; do while [[ ${VERSIONS_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = \"${DBNAME}\" AND TABLE_NAME = 'versions'") ]]; then if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = \"${DBNAME}\" AND TABLE_NAME = 'versions'") ]]; then
VERSIONS_OK=OK VERSIONS_OK=OK
else else
echo "Waiting for versions table to be created..." echo "Waiting for versions table to be created..."
@@ -433,11 +433,11 @@ PUBKEY_MCRYPT=$(doveconf -P 2> /dev/null | grep -i mail_crypt_global_public_key
if [ -f ${PUBKEY_MCRYPT} ]; then if [ -f ${PUBKEY_MCRYPT} ]; then
GUID=$(cat <(echo ${MAILCOW_HOSTNAME}) /mail_crypt/ecpubkey.pem | sha256sum | cut -d ' ' -f1 | tr -cd "[a-fA-F0-9.:/] ") GUID=$(cat <(echo ${MAILCOW_HOSTNAME}) /mail_crypt/ecpubkey.pem | sha256sum | cut -d ' ' -f1 | tr -cd "[a-fA-F0-9.:/] ")
if [ ${#GUID} -eq 64 ]; then if [ ${#GUID} -eq 64 ]; then
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
REPLACE INTO versions (application, version) VALUES ("GUID", "${GUID}"); REPLACE INTO versions (application, version) VALUES ("GUID", "${GUID}");
EOF EOF
else else
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
REPLACE INTO versions (application, version) VALUES ("GUID", "INVALID"); REPLACE INTO versions (application, version) VALUES ("GUID", "INVALID");
EOF EOF
fi fi
+17 -11
View File
@@ -8,7 +8,8 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate from email.utils import COMMASPACE, formatdate
import jinja2 import jinja2
from jinja2 import Template from jinja2 import TemplateError
from jinja2.sandbox import SandboxedEnvironment
import json import json
import redis import redis
import time import time
@@ -80,17 +81,22 @@ try:
if len(meta_query) == 0: if len(meta_query) == 0:
return return
msg_count = len(meta_query) msg_count = len(meta_query)
env = SandboxedEnvironment()
if r.get('Q_HTML'): if r.get('Q_HTML'):
try: try:
template = Template(r.get('Q_HTML')) template = env.from_string(r.get('Q_HTML'))
except: except Exception:
print("Error: Cannot parse quarantine template, falling back to default template.") print("Error: Cannot parse quarantine template, falling back to default template.")
with open('/templates/quarantine.tpl') as file_: with open('/templates/quarantine.tpl') as file_:
template = Template(file_.read()) template = env.from_string(file_.read())
else: else:
with open('/templates/quarantine.tpl') as file_: with open('/templates/quarantine.tpl') as file_:
template = Template(file_.read()) template = env.from_string(file_.read())
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl) try:
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
except (jinja2.exceptions.SecurityError, TemplateError) as ex:
print(f"SecurityError or TemplateError in template rendering: {ex}")
return
text = html2text.html2text(html) text = html2text.html2text(html)
count = 0 count = 0
while count < 15: while count < 15:
@@ -165,4 +171,4 @@ try:
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category']) notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
finally: finally:
os.unlink(pidfile) os.unlink(pidfile)
+17 -9
View File
@@ -6,7 +6,7 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate from email.utils import COMMASPACE, formatdate
import jinja2 import jinja2
from jinja2 import Template from jinja2.sandbox import SandboxedEnvironment
import redis import redis
import time import time
import json import json
@@ -23,7 +23,7 @@ else:
while True: while True:
try: try:
r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS']) r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, username='quota_notify', password='')
r.ping() r.ping()
except Exception as ex: except Exception as ex:
print('%s - trying again...' % (ex)) print('%s - trying again...' % (ex))
@@ -33,16 +33,24 @@ while True:
if r.get('QW_HTML'): if r.get('QW_HTML'):
try: try:
template = Template(r.get('QW_HTML')) env = SandboxedEnvironment()
except: template = env.from_string(r.get('QW_HTML'))
print("Error: Cannot parse quarantine template, falling back to default template.") except Exception:
print("Error: Cannot parse quota template, falling back to default template.")
with open('/templates/quota.tpl') as file_: with open('/templates/quota.tpl') as file_:
template = Template(file_.read()) env = SandboxedEnvironment()
template = env.from_string(file_.read())
else: else:
with open('/templates/quota.tpl') as file_: with open('/templates/quota.tpl') as file_:
template = Template(file_.read()) env = SandboxedEnvironment()
template = env.from_string(file_.read())
try:
html = template.render(username=username, percent=percent)
except (jinja2.exceptions.SecurityError, jinja2.TemplateError) as ex:
print(f"SecurityError or TemplateError in template rendering: {ex}")
sys.exit(1)
html = template.render(username=username, percent=percent)
text = html2text.html2text(html) text = html2text.html2text(html)
try: try:
@@ -91,4 +99,4 @@ except:
try: try:
sys.stderr.close() sys.stderr.close()
except: except:
pass pass
@@ -38,8 +38,13 @@ filter f_replica {
not match("User has no mail_replica in userdb" value("MESSAGE")); not match("User has no mail_replica in userdb" value("MESSAGE"));
not match("Error: sync: Unknown user in remote" value("MESSAGE")); not match("Error: sync: Unknown user in remote" value("MESSAGE"));
}; };
filter f_dovecot_auth_try {
not match("- trying the next passdb" value("MESSAGE")) and
not match("- trying the next userdb" value("MESSAGE"));
};
log { log {
source(s_dgram); source(s_dgram);
filter(f_dovecot_auth_try);
filter(f_replica); filter(f_replica);
destination(d_stdout); destination(d_stdout);
filter(f_mail); filter(f_mail);
+5
View File
@@ -38,8 +38,13 @@ filter f_replica {
not match("User has no mail_replica in userdb" value("MESSAGE")); not match("User has no mail_replica in userdb" value("MESSAGE"));
not match("Error: sync: Unknown user in remote" value("MESSAGE")); not match("Error: sync: Unknown user in remote" value("MESSAGE"));
}; };
filter f_dovecot_auth_try {
not match("- trying the next passdb" value("MESSAGE")) and
not match("- trying the next userdb" value("MESSAGE"));
};
log { log {
source(s_dgram); source(s_dgram);
filter(f_dovecot_auth_try);
filter(f_replica); filter(f_replica);
destination(d_stdout); destination(d_stdout);
filter(f_mail); filter(f_mail);
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
+4 -5
View File
@@ -85,11 +85,10 @@ def refreshF2bregex():
f2bregex[3] = r'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed: (?!.*Connection lost to authentication server).+' f2bregex[3] = r'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed: (?!.*Connection lost to authentication server).+'
f2bregex[4] = r'warning: non-SMTP command from .*\[([0-9a-f\.:]+)]:.+' f2bregex[4] = r'warning: non-SMTP command from .*\[([0-9a-f\.:]+)]:.+'
f2bregex[5] = r'NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+' f2bregex[5] = r'NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+'
f2bregex[6] = r'-login: Disconnected.+ \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),' f2bregex[6] = r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)'
f2bregex[7] = r'-login: Aborted login.+ \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+' f2bregex[7] = r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): unknown user \(SHA1 of given password: [a-f0-9]+\)'
f2bregex[8] = r'-login: Aborted login.+ \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+' f2bregex[8] = r'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
f2bregex[9] = r'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked' f2bregex[9] = r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
f2bregex[10] = r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
r.set('F2B_REGEX', json.dumps(f2bregex, ensure_ascii=False)) r.set('F2B_REGEX', json.dumps(f2bregex, ensure_ascii=False))
else: else:
try: try:
+7 -3
View File
@@ -7,7 +7,7 @@ def includes_conf(env, template_vars):
listen_plain = "listen_plain.active" listen_plain = "listen_plain.active"
listen_ssl = "listen_ssl.active" listen_ssl = "listen_ssl.active"
server_name_config = f"server_name {template_vars['MAILCOW_HOSTNAME']} autodiscover.* autoconfig.* {template_vars['ADDITIONAL_SERVER_NAMES']};" server_name_config = f"server_name {template_vars['MAILCOW_HOSTNAME']} autodiscover.* autoconfig.* {' '.join(template_vars['ADDITIONAL_SERVER_NAMES'])};"
listen_plain_config = f"listen {template_vars['HTTP_PORT']};" listen_plain_config = f"listen {template_vars['HTTP_PORT']};"
listen_ssl_config = f"listen {template_vars['HTTPS_PORT']};" listen_ssl_config = f"listen {template_vars['HTTPS_PORT']};"
if not template_vars['DISABLE_IPv6']: if not template_vars['DISABLE_IPv6']:
@@ -42,20 +42,24 @@ def nginx_conf(env, template_vars):
def prepare_template_vars(): def prepare_template_vars():
ipv4_network = os.getenv("IPV4_NETWORK", "172.22.1") ipv4_network = os.getenv("IPV4_NETWORK", "172.22.1")
additional_server_names = os.getenv("ADDITIONAL_SERVER_NAMES", "")
trusted_proxies = os.getenv("TRUSTED_PROXIES", "")
template_vars = { template_vars = {
'IPV4_NETWORK': ipv4_network, 'IPV4_NETWORK': ipv4_network,
'TRUSTED_NETWORK': os.getenv("TRUSTED_NETWORK", False), 'TRUSTED_PROXIES': [item.strip() for item in trusted_proxies.split(",") if item.strip()],
'SKIP_RSPAMD': os.getenv("SKIP_RSPAMD", "n").lower() in ("y", "yes"), 'SKIP_RSPAMD': os.getenv("SKIP_RSPAMD", "n").lower() in ("y", "yes"),
'SKIP_SOGO': os.getenv("SKIP_SOGO", "n").lower() in ("y", "yes"), 'SKIP_SOGO': os.getenv("SKIP_SOGO", "n").lower() in ("y", "yes"),
'NGINX_USE_PROXY_PROTOCOL': os.getenv("NGINX_USE_PROXY_PROTOCOL", "n").lower() in ("y", "yes"), 'NGINX_USE_PROXY_PROTOCOL': os.getenv("NGINX_USE_PROXY_PROTOCOL", "n").lower() in ("y", "yes"),
'MAILCOW_HOSTNAME': os.getenv("MAILCOW_HOSTNAME", ""), 'MAILCOW_HOSTNAME': os.getenv("MAILCOW_HOSTNAME", ""),
'ADDITIONAL_SERVER_NAMES': os.getenv("ADDITIONAL_SERVER_NAMES", "").replace(',', ' '), 'ADDITIONAL_SERVER_NAMES': [item.strip() for item in additional_server_names.split(",") if item.strip()],
'HTTP_PORT': os.getenv("HTTP_PORT", "80"), 'HTTP_PORT': os.getenv("HTTP_PORT", "80"),
'HTTPS_PORT': os.getenv("HTTPS_PORT", "443"), 'HTTPS_PORT': os.getenv("HTTPS_PORT", "443"),
'SOGOHOST': os.getenv("SOGOHOST", ipv4_network + ".248"), 'SOGOHOST': os.getenv("SOGOHOST", ipv4_network + ".248"),
'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"), 'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"),
'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"), 'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"),
'DISABLE_IPv6': os.getenv("DISABLE_IPv6", "n").lower() in ("y", "yes"), 'DISABLE_IPv6': os.getenv("DISABLE_IPv6", "n").lower() in ("y", "yes"),
'HTTP_REDIRECT': os.getenv("HTTP_REDIRECT", "n").lower() in ("y", "yes"),
} }
ssl_dir = '/etc/ssl/mail/' ssl_dir = '/etc/ssl/mail/'
+2 -2
View File
@@ -8,13 +8,13 @@ until ping ${PHPFPMHOST} -c1 > /dev/null; do
echo "Waiting for PHP..." echo "Waiting for PHP..."
sleep 1 sleep 1
done done
if printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then if ! printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
until ping ${SOGOHOST} -c1 > /dev/null; do until ping ${SOGOHOST} -c1 > /dev/null; do
echo "Waiting for SOGo..." echo "Waiting for SOGo..."
sleep 1 sleep 1
done done
fi fi
if printf "%s\n" "${SKIP_RSPAMD}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then if ! printf "%s\n" "${SKIP_RSPAMD}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
until ping ${RSPAMDHOST} -c1 > /dev/null; do until ping ${RSPAMDHOST} -c1 > /dev/null; do
echo "Waiting for Rspamd..." echo "Waiting for Rspamd..."
sleep 1 sleep 1
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
+2 -2
View File
@@ -1,4 +1,4 @@
FROM php:8.2-fpm-alpine3.20 FROM php:8.2-fpm-alpine3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
@@ -13,7 +13,7 @@ ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$ # renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=6.1.0 ARG REDIS_PECL_VERSION=6.1.0
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$ # renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.6.6 ARG COMPOSER_VERSION=2.8.6
RUN apk add -U --no-cache autoconf \ RUN apk add -U --no-cache autoconf \
aspell-dev \ aspell-dev \
+6 -6
View File
@@ -81,7 +81,7 @@ if [ ${SQL_CHANGED} -eq 1 ]; then
fi fi
# Check mysql tz import (master and slave) # Check mysql tz import (master and slave)
TZ_CHECK=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT CONVERT_TZ('2019-11-02 23:33:00','Europe/Berlin','UTC') AS time;" -BN 2> /dev/null) TZ_CHECK=$(mariadb --skip-ssl --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 if [[ -z ${TZ_CHECK} ]] || [[ "${TZ_CHECK}" == "NULL" ]]; then
SQL_FULL_TZINFO_IMPORT_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_tzinfo_to_sql"}' --silent -H 'Content-type: application/json') SQL_FULL_TZINFO_IMPORT_RETURN=$(curl --silent --insecure -XPOST https://dockerapi.${COMPOSE_PROJECT_NAME}_mailcow-network/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_tzinfo_to_sql"}' --silent -H 'Content-type: application/json')
echo "MySQL mysql_tzinfo_to_sql - debug output:" echo "MySQL mysql_tzinfo_to_sql - debug output:"
@@ -120,11 +120,11 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
while read line while read line
do do
DOMAIN_ARR+=("$line") DOMAIN_ARR+=("$line")
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain" -Bs) done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain" -Bs)
while read line while read line
do do
DOMAIN_ARR+=("$line") DOMAIN_ARR+=("$line")
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT alias_domain FROM alias_domain" -Bs) done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT alias_domain FROM alias_domain" -Bs)
if [[ ! -z ${DOMAIN_ARR} ]]; then if [[ ! -z ${DOMAIN_ARR} ]]; then
for domain in "${DOMAIN_ARR[@]}"; do for domain in "${DOMAIN_ARR[@]}"; do
@@ -146,13 +146,13 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
VALIDATED_IPS=$(array_by_comma ${VALIDATED_API_ALLOW_FROM_ARR[*]}) VALIDATED_IPS=$(array_by_comma ${VALIDATED_API_ALLOW_FROM_ARR[*]})
if [[ ! -z ${VALIDATED_IPS} ]]; then if [[ ! -z ${VALIDATED_IPS} ]]; then
if [[ ${API_KEY} != "invalid" ]] && [[ ! -z ${API_KEY} ]]; then if [[ ${API_KEY} != "invalid" ]] && [[ ! -z ${API_KEY} ]]; then
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELETE FROM api WHERE access = 'rw'; DELETE FROM api WHERE access = 'rw';
INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY}", "1", "${VALIDATED_IPS}", "rw"); INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY}", "1", "${VALIDATED_IPS}", "rw");
EOF EOF
fi fi
if [[ ${API_KEY_READ_ONLY} != "invalid" ]] && [[ ! -z ${API_KEY_READ_ONLY} ]]; then if [[ ${API_KEY_READ_ONLY} != "invalid" ]] && [[ ! -z ${API_KEY_READ_ONLY} ]]; then
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELETE FROM api WHERE access = 'ro'; DELETE FROM api WHERE access = 'ro';
INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY_READ_ONLY}", "1", "${VALIDATED_IPS}", "ro"); INSERT INTO api (api_key, active, allow_from, access) VALUES ("${API_KEY_READ_ONLY}", "1", "${VALIDATED_IPS}", "ro");
EOF EOF
@@ -161,7 +161,7 @@ EOF
fi fi
# Create events (master only, STATUS for event on slave will be SLAVESIDE_DISABLED) # Create events (master only, STATUS for event on slave will be SLAVESIDE_DISABLED)
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DROP EVENT IF EXISTS clean_spamalias; DROP EVENT IF EXISTS clean_spamalias;
DELIMITER // DELIMITER //
CREATE EVENT clean_spamalias CREATE EVENT clean_spamalias
+10 -2
View File
@@ -395,7 +395,7 @@ EOF
if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
cat <<EOF > /opt/postfix/conf/dns_blocklists.cf cat <<EOF > /opt/postfix/conf/dns_blocklists.cf
# This file can be edited. # This file can be edited.
# Delete this file and restart postfix container to revert any changes. # Delete this file and restart postfix container to revert any changes.
postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2 postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
hostkarma.junkemailfilter.com=127.0.0.1*-2 hostkarma.junkemailfilter.com=127.0.0.1*-2
@@ -403,7 +403,6 @@ postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
list.dnswl.org=127.0.[0..255].1*-4 list.dnswl.org=127.0.[0..255].1*-4
list.dnswl.org=127.0.[0..255].2*-6 list.dnswl.org=127.0.[0..255].2*-6
list.dnswl.org=127.0.[0..255].3*-8 list.dnswl.org=127.0.[0..255].3*-8
ix.dnsbl.manitu.net*2
bl.spamcop.net*2 bl.spamcop.net*2
bl.suomispam.net*2 bl.suomispam.net*2
hostkarma.junkemailfilter.com=127.0.0.2*3 hostkarma.junkemailfilter.com=127.0.0.2*3
@@ -417,6 +416,10 @@ postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
bl.mailspike.net=127.0.0.[10;11;12]*4 bl.mailspike.net=127.0.0.[10;11;12]*4
EOF EOF
fi fi
# Remove discontinued DNSBLs from existing dns_blocklists.cf
sed -i '/ix\.dnsbl\.manitu\.net\*2/d' /opt/postfix/conf/dns_blocklists.cf # Nixspam
DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S') DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S')
if [ ! -z "$DNSBL_CONFIG" ]; then if [ ! -z "$DNSBL_CONFIG" ]; then
@@ -507,6 +510,11 @@ chgrp -R postdrop /var/spool/postfix/public
chgrp -R postdrop /var/spool/postfix/maildrop chgrp -R postdrop /var/spool/postfix/maildrop
postfix set-permissions postfix set-permissions
# Checking if there is a leftover of a crashed postfix container before starting a new one
if [ -e /var/spool/postfix/pid/master.pid ]; then
rm -rf /var/spool/postfix/pid/master.pid
fi
# Check Postfix configuration # Check Postfix configuration
postconf -c /opt/postfix/conf > /dev/null postconf -c /opt/postfix/conf > /dev/null
@@ -18,6 +18,7 @@ stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0 stderr_logfile_maxbytes=0
autorestart=true autorestart=true
startsecs=10
[eventlistener:processes] [eventlistener:processes]
command=/usr/local/sbin/stop-supervisor.sh command=/usr/local/sbin/stop-supervisor.sh
+1 -1
View File
@@ -2,7 +2,7 @@ FROM debian:bookworm-slim
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG RSPAMD_VER=rspamd_3.10.2-1~b8a232043 ARG RSPAMD_VER=rspamd_3.11.1-1~ab0b44951
ARG CODENAME=bookworm ARG CODENAME=bookworm
ENV LC_ALL=C ENV LC_ALL=C
+3 -4
View File
@@ -4,7 +4,7 @@ LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG DEBIAN_VERSION=bookworm ARG DEBIAN_VERSION=bookworm
ARG SOGO_DEBIAN_REPOSITORY=http://www.axis.cz/linux/debian ARG SOGO_DEBIAN_REPOSITORY=https://packagingv2.sogo.nu/sogo-nightly-debian/
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$ # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.17 ARG GOSU_VERSION=1.17
ENV LC_ALL=C ENV LC_ALL=C
@@ -33,9 +33,8 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
&& gosu nobody true \ && gosu nobody true \
&& mkdir /usr/share/doc/sogo \ && mkdir /usr/share/doc/sogo \
&& touch /usr/share/doc/sogo/empty.sh \ && touch /usr/share/doc/sogo/empty.sh \
&& wget http://www.axis.cz/linux/debian/axis-archive-keyring.deb -O /tmp/axis-archive-keyring.deb \ && wget -O- https://keys.openpgp.org/vks/v1/by-fingerprint/74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 | gpg --dearmor | apt-key add - \
&& apt install -y /tmp/axis-archive-keyring.deb \ && echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} main" > /etc/apt/sources.list.d/sogo.list \
&& echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} sogo-v5" > /etc/apt/sources.list.d/sogo.list \
&& apt-get update && apt-get install -y --no-install-recommends \ && apt-get update && apt-get install -y --no-install-recommends \
sogo \ sogo \
sogo-activesync \ sogo-activesync \
+17 -17
View File
@@ -14,11 +14,11 @@ do
done done
# Wait for updated schema # Wait for updated schema
DBV_NOW=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN) DBV_NOW=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NEW=$(grep -oE '\$db_version = .*;' init_db.inc.php | sed 's/$db_version = //g;s/;//g' | cut -d \" -f2) DBV_NEW=$(grep -oE '\$db_version = .*;' init_db.inc.php | sed 's/$db_version = //g;s/;//g' | cut -d \" -f2)
while [[ "${DBV_NOW}" != "${DBV_NEW}" ]]; do while [[ "${DBV_NOW}" != "${DBV_NEW}" ]]; do
echo "Waiting for schema update..." echo "Waiting for schema update..."
DBV_NOW=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN) DBV_NOW=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NEW=$(grep -oE '\$db_version = .*;' init_db.inc.php | sed 's/$db_version = //g;s/;//g' | cut -d \" -f2) DBV_NEW=$(grep -oE '\$db_version = .*;' init_db.inc.php | sed 's/$db_version = //g;s/;//g' | cut -d \" -f2)
sleep 5 sleep 5
done done
@@ -27,10 +27,10 @@ echo "DB schema is ${DBV_NOW}"
# Recreate view # Recreate view
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing sogo_view..." echo "We are master, preparing sogo_view..."
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view" mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view"
while [[ ${VIEW_OK} != 'OK' ]]; do while [[ ${VIEW_OK} != 'OK' ]]; do
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) AS CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) AS
SELECT SELECT
mailbox.username, mailbox.username,
mailbox.domain, mailbox.domain,
@@ -59,7 +59,7 @@ WHERE
GROUP BY GROUP BY
mailbox.username; mailbox.username;
EOF EOF
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sogo_view'") ]]; then if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sogo_view'") ]]; then
VIEW_OK=OK VIEW_OK=OK
else else
echo "Will retry to setup SOGo view in 3s..." echo "Will retry to setup SOGo view in 3s..."
@@ -68,7 +68,7 @@ EOF
done done
else else
while [[ ${VIEW_OK} != 'OK' ]]; do while [[ ${VIEW_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sogo_view'") ]]; then if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sogo_view'") ]]; then
VIEW_OK=OK VIEW_OK=OK
else else
echo "Waiting for SOGo view to be created by master..." echo "Waiting for SOGo view to be created by master..."
@@ -81,12 +81,12 @@ fi
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing _sogo_static_view..." echo "We are master, preparing _sogo_static_view..."
while [[ ${STATIC_VIEW_OK} != 'OK' ]]; do while [[ ${STATIC_VIEW_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'") ]]; then if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'") ]]; then
STATIC_VIEW_OK=OK STATIC_VIEW_OK=OK
echo "Updating _sogo_static_view content..." echo "Updating _sogo_static_view content..."
# If changed, also update init_db.inc.php # If changed, also update init_db.inc.php
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "REPLACE INTO _sogo_static_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) SELECT c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings from sogo_view;" mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "REPLACE INTO _sogo_static_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) SELECT c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings from sogo_view;"
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "DELETE FROM _sogo_static_view WHERE c_uid NOT IN (SELECT username FROM mailbox WHERE active = '1')" mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "DELETE FROM _sogo_static_view WHERE c_uid NOT IN (SELECT username FROM mailbox WHERE active = '1')"
else else
echo "Waiting for database initialization..." echo "Waiting for database initialization..."
sleep 3 sleep 3
@@ -94,7 +94,7 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
done done
else else
while [[ ${STATIC_VIEW_OK} != 'OK' ]]; do while [[ ${STATIC_VIEW_OK} != 'OK' ]]; do
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'") ]]; then if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'") ]]; then
STATIC_VIEW_OK=OK STATIC_VIEW_OK=OK
else else
echo "Waiting for database initialization by master..." echo "Waiting for database initialization by master..."
@@ -107,9 +107,9 @@ fi
# Recreate password update trigger # Recreate password update trigger
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "We are master, preparing update trigger..." echo "We are master, preparing update trigger..."
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TRIGGER IF EXISTS sogo_update_password" mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TRIGGER IF EXISTS sogo_update_password"
while [[ ${TRIGGER_OK} != 'OK' ]]; do while [[ ${TRIGGER_OK} != 'OK' ]]; do
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
DELIMITER - DELIMITER -
CREATE TRIGGER sogo_update_password AFTER UPDATE ON _sogo_static_view CREATE TRIGGER sogo_update_password AFTER UPDATE ON _sogo_static_view
FOR EACH ROW FOR EACH ROW
@@ -119,7 +119,7 @@ END;
- -
DELIMITER ; DELIMITER ;
EOF EOF
if [[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'sogo_update_password'") ]]; then if [[ ! -z $(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'sogo_update_password'") ]]; then
TRIGGER_OK=OK TRIGGER_OK=OK
else else
echo "Will retry to setup SOGo password update trigger in 3s" echo "Will retry to setup SOGo password update trigger in 3s"
@@ -216,7 +216,7 @@ while read -r line gal
line=${line} envsubst < /etc/sogo/plist_ldap >> /var/lib/sogo/GNUstep/Defaults/sogod.plist line=${line} envsubst < /etc/sogo/plist_ldap >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
echo " </array> echo " </array>
</dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist </dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N) done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N)
# Generate footer # Generate footer
echo ' </dict> echo ' </dict>
@@ -240,8 +240,8 @@ chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
# fi # fi
#fi #fi
# Copy logo, if any # Rename custom logo, if any
[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg [[ -f /etc/sogo/sogo-full.svg ]] && mv /etc/sogo/sogo-full.svg /etc/sogo/custom-fulllogo.svg
# Rsync web content # Rsync web content
echo "Syncing web content with named volume" echo "Syncing web content with named volume"
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.20 FROM alpine:3.21
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
@@ -132,9 +132,9 @@ fi
# Connect to the DB server and store output in vars # Connect to the DB server and store output in vars
if [[ -n $socket ]]; then if [[ -n $socket ]]; then
ConnectionResult=$(mysql ${optfile} ${socket} ${user} -e "show slave ${connection} status\G" 2>&1) ConnectionResult=$(mariadb --skip-ssl ${optfile} ${socket} ${user} -e "show slave ${connection} status\G" 2>&1)
else else
ConnectionResult=$(mysql ${optfile} ${host} ${port} ${user} -e "show slave ${connection} status\G" 2>&1) ConnectionResult=$(mariadb --skip-ssl ${optfile} ${host} ${port} ${user} -e "show slave ${connection} status\G" 2>&1)
fi fi
if [ -z "`echo "${ConnectionResult}" |grep Slave_IO_State`" ]; then if [ -z "`echo "${ConnectionResult}" |grep Slave_IO_State`" ]; then
+1 -1
View File
@@ -234,7 +234,7 @@ external_checks() {
diff_c=0 diff_c=0
THRESHOLD=${EXTERNAL_CHECKS_THRESHOLD} THRESHOLD=${EXTERNAL_CHECKS_THRESHOLD}
# Reduce error count by 2 after restarting an unhealthy container # Reduce error count by 2 after restarting an unhealthy container
GUID=$(mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'GUID'" -BN) GUID=$(mariadb --skip-ssl -u${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT version FROM versions WHERE application = 'GUID'" -BN)
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1 trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do while [ ${err_count} -lt ${THRESHOLD} ]; do
err_c_cur=${err_count} err_c_cur=${err_count}
+6 -6
View File
@@ -1,8 +1,8 @@
-----BEGIN DH PARAMETERS----- -----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA9iHB0CRDhV8wfBgqnmvuJpl0fzL3qL75R4ZvQHlfMNLrxuIz2x9D MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
9zcDhPcBTVzV5Ay0AAkke4wP6r6wDQqXqBP4Y8IOkYAyLh3jM40jfHQzQt+5JdQl +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
ond3kiscBsFOch/vMfSLMu3lAb0YhPNTvrxhMz7LcVAWYl82swASupdiKR+MgaQr 87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
XsugpmDKsHW60VmIM9B7K9Y+rNHwvMWkmISd0KxA8oOy1WJvsVEissMALZDE3c4w YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
2xHmO2lXxgEx3aez28736t4m/KW3g9Zr31a1M0KusmfY//fGkPk4NUrLBOS2xrgp 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
Y/rG1qSBdcVyerM0Ki93qCyHKYu4ene0OwIBAg== ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS----- -----END DH PARAMETERS-----
+1
View File
@@ -278,6 +278,7 @@ imap_max_line_length = 2 M
#auth_cache_negative_ttl = 0 #auth_cache_negative_ttl = 0
#auth_cache_ttl = 30 s #auth_cache_ttl = 30 s
#auth_cache_size = 2 M #auth_cache_size = 2 M
auth_verbose_passwords = sha1:6
service replicator { service replicator {
process_min_avail = 1 process_min_avail = 1
} }
+2 -2
View File
@@ -1,7 +1,7 @@
[mysqld] [mysqld]
character-set-client-handshake = FALSE character-set-client-handshake = FALSE
character-set-server = utf8mb4 character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci collation-server = utf8mb4_general_ci
#innodb_file_per_table = TRUE #innodb_file_per_table = TRUE
#innodb_file_format = barracuda #innodb_file_format = barracuda
#innodb_large_prefix = TRUE #innodb_large_prefix = TRUE
@@ -20,7 +20,7 @@ thread_cache_size = 8
query_cache_type = 0 query_cache_type = 0
query_cache_size = 0 query_cache_size = 0
max_heap_table_size = 48M max_heap_table_size = 48M
thread_stack = 192K thread_stack = 256K
skip-host-cache skip-host-cache
skip-name-resolve skip-name-resolve
log-warnings = 0 log-warnings = 0
+63 -2
View File
@@ -41,25 +41,80 @@ http {
https https; https https;
} }
# Default {% if HTTP_REDIRECT %}
# HTTP to HTTPS redirect
server {
root /web;
listen {{ HTTP_PORT }} default_server;
listen [::]:{{ HTTP_PORT }} default_server;
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* {{ ADDITIONAL_SERVER_NAMES | join(' ') }};
if ( $request_uri ~* "%0A|%0D" ) { return 403; }
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
location / {
return 301 https://$host$uri$is_args$args;
}
}
{%endif%}
# Default Server Name
server { server {
listen 127.0.0.1:65510; # sogo-auth verify internal listen 127.0.0.1:65510; # sogo-auth verify internal
{% if not HTTP_REDIRECT %}
listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %} {% if not DISABLE_IPv6 %}
{% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{%endif%} {%endif%}
http2 on; http2 on;
ssl_certificate /etc/ssl/mail/cert.pem; ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem; ssl_certificate_key /etc/ssl/mail/key.pem;
server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* {{ ADDITIONAL_SERVER_NAMES }}; server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.*;
include /etc/nginx/includes/sites-default.conf; include /etc/nginx/includes/sites-default.conf;
} }
# Additional Server Names
{% for SERVER_NAME in ADDITIONAL_SERVER_NAMES %}
server {
listen 127.0.0.1:65510; # sogo-auth verify internal
{% if not HTTP_REDIRECT %}
listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %}
{% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{%endif%}
http2 on;
ssl_certificate /etc/ssl/mail/cert.pem;
ssl_certificate_key /etc/ssl/mail/key.pem;
server_name {{ SERVER_NAME }};
include /etc/nginx/includes/sites-default.conf;
}
{% endfor %}
# rspamd dynmaps: # rspamd dynmaps:
server { server {
listen 8081; listen 8081;
@@ -106,12 +161,18 @@ http {
{% for cert in valid_cert_dirs %} {% for cert in valid_cert_dirs %}
server { server {
{% if not HTTP_REDIRECT %}
listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %} {% if not DISABLE_IPv6 %}
{% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%}
listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{%endif%} {%endif%}
http2 on; http2 on;
ssl_certificate {{ cert.cert_path }}cert.pem; ssl_certificate {{ cert.cert_path }}cert.pem;
@@ -52,10 +52,12 @@ set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12; set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16; set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7; set_real_ip_from fc00::/7;
{% if not TRUSTED_NETWORK %} {% for TRUSTED_PROXY in TRUSTED_PROXIES %}
set_real_ip_from {{ TRUSTED_PROXY }};
{% endfor %}
{% if not NGINX_USE_PROXY_PROTOCOL %}
real_ip_header X-Forwarded-For; real_ip_header X-Forwarded-For;
{% else %} {% else %}
set_real_ip_from {{ TRUSTED_NETWORK }};
real_ip_header proxy_protocol; real_ip_header proxy_protocol;
{% endif %} {% endif %}
real_ip_recursive on; real_ip_recursive on;
@@ -137,13 +139,22 @@ location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
{% if not SKIP_RSPAMD %} {% if not SKIP_RSPAMD %}
location /rspamd/ { location /rspamd/ {
location /rspamd/auth {
# proxy_pass is not inherited
proxy_pass http://{{ RSPAMDHOST }}:11334/auth;
proxy_intercept_errors on;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_redirect off;
error_page 401 /_rspamderror.php;
}
proxy_pass http://{{ RSPAMDHOST }}:11334/; proxy_pass http://{{ RSPAMDHOST }}:11334/;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%}; proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%};
proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%}; proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%};
proxy_redirect off; proxy_redirect off;
proxy_intercept_errors on;
error_page 401 /_rspamderror.php;
} }
{% endif %} {% endif %}
+2 -3
View File
@@ -162,10 +162,9 @@ transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre,
proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
smtp_sasl_auth_soft_bounce = no smtp_sasl_auth_soft_bounce = no
postscreen_discard_ehlo_keywords = silent-discard, dsn, chunking postscreen_discard_ehlo_keywords = chunking, silent-discard, smtputf8, dsn
smtpd_discard_ehlo_keywords = chunking, silent-discard smtpd_discard_ehlo_keywords = chunking, silent-discard, smtputf8
compatibility_level = 3.7 compatibility_level = 3.7
smtputf8_enable = no
# Define protocols for SMTPS and submission service # Define protocols for SMTPS and submission service
submission_smtpd_tls_mandatory_protocols = >=TLSv1.2 submission_smtpd_tls_mandatory_protocols = >=TLSv1.2
smtps_smtpd_tls_mandatory_protocols = >=TLSv1.2 smtps_smtpd_tls_mandatory_protocols = >=TLSv1.2
+33 -20
View File
@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Wed Jan 1 00:18:52 UTC 2025 # Whitelist generated by Postwhite v3.4 on Sat Mar 1 00:19:29 UTC 2025
# https://github.com/stevejenkins/postwhite/ # https://github.com/stevejenkins/postwhite/
# 2014 total rules # 2000 total rules
2a00:1450:4000::/36 permit 2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit 2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit 2a01:111:f403:8000::/50 permit
@@ -8,6 +8,13 @@
2a01:111:f403::/49 permit 2a01:111:f403::/49 permit
2a01:111:f403:c000::/51 permit 2a01:111:f403:c000::/51 permit
2a01:111:f403:f000::/52 permit 2a01:111:f403:f000::/52 permit
2a01:b747:3000:200::/56 permit
2a01:b747:3001:200::/56 permit
2a01:b747:3002:200::/56 permit
2a01:b747:3003:200::/56 permit
2a01:b747:3004:200::/56 permit
2a01:b747:3005:200::/56 permit
2a01:b747:3006:200::/56 permit
2a02:a60:0:5::/64 permit 2a02:a60:0:5::/64 permit
2c0f:fb50:4000::/36 permit 2c0f:fb50:4000::/36 permit
2.207.151.53 permit 2.207.151.53 permit
@@ -19,11 +26,6 @@
8.20.114.31 permit 8.20.114.31 permit
8.25.194.0/23 permit 8.25.194.0/23 permit
8.25.196.0/23 permit 8.25.196.0/23 permit
8.39.54.0/23 permit
8.39.54.250/31 permit
8.40.222.0/23 permit
8.40.222.250/31 permit
10.162.0.0/16 permit
12.130.86.238 permit 12.130.86.238 permit
13.110.208.0/21 permit 13.110.208.0/21 permit
13.110.209.0/24 permit 13.110.209.0/24 permit
@@ -39,7 +41,9 @@
17.57.156.0/24 permit 17.57.156.0/24 permit
17.58.0.0/16 permit 17.58.0.0/16 permit
17.142.0.0/15 permit 17.142.0.0/15 permit
17.143.234.140/30 permit 18.97.0.8/30 permit
18.97.1.184/29 permit
18.97.2.64/26 permit
18.156.89.250 permit 18.156.89.250 permit
18.157.243.190 permit 18.157.243.190 permit
18.194.95.56 permit 18.194.95.56 permit
@@ -98,6 +102,7 @@
27.123.206.76/30 permit 27.123.206.76/30 permit
27.123.206.80/28 permit 27.123.206.80/28 permit
31.25.48.222 permit 31.25.48.222 permit
31.47.251.17 permit
34.195.217.107 permit 34.195.217.107 permit
34.212.163.75 permit 34.212.163.75 permit
34.215.104.144 permit 34.215.104.144 permit
@@ -110,6 +115,7 @@
35.191.0.0/16 permit 35.191.0.0/16 permit
35.205.92.9 permit 35.205.92.9 permit
35.242.169.159 permit 35.242.169.159 permit
37.188.97.188 permit
37.218.248.47 permit 37.218.248.47 permit
37.218.249.47 permit 37.218.249.47 permit
37.218.251.62 permit 37.218.251.62 permit
@@ -285,6 +291,9 @@
64.207.219.13 permit 64.207.219.13 permit
64.207.219.14 permit 64.207.219.14 permit
64.207.219.15 permit 64.207.219.15 permit
64.207.219.24 permit
64.207.219.25 permit
64.207.219.26 permit
64.207.219.71 permit 64.207.219.71 permit
64.207.219.72 permit 64.207.219.72 permit
64.207.219.73 permit 64.207.219.73 permit
@@ -294,6 +303,9 @@
64.207.219.77 permit 64.207.219.77 permit
64.207.219.78 permit 64.207.219.78 permit
64.207.219.79 permit 64.207.219.79 permit
64.207.219.88 permit
64.207.219.89 permit
64.207.219.90 permit
64.207.219.135 permit 64.207.219.135 permit
64.207.219.136 permit 64.207.219.136 permit
64.207.219.137 permit 64.207.219.137 permit
@@ -448,6 +460,7 @@
69.171.244.0/23 permit 69.171.244.0/23 permit
70.37.151.128/25 permit 70.37.151.128/25 permit
70.42.149.35 permit 70.42.149.35 permit
72.3.185.0/24 permit
72.14.192.0/18 permit 72.14.192.0/18 permit
72.21.192.0/19 permit 72.21.192.0/19 permit
72.21.217.142 permit 72.21.217.142 permit
@@ -508,6 +521,9 @@
72.30.239.228/31 permit 72.30.239.228/31 permit
72.30.239.244/30 permit 72.30.239.244/30 permit
72.30.239.248/31 permit 72.30.239.248/31 permit
72.32.154.0/24 permit
72.32.217.0/24 permit
72.32.243.0/24 permit
72.52.72.32/28 permit 72.52.72.32/28 permit
74.6.128.0/24 permit 74.6.128.0/24 permit
74.6.129.0/24 permit 74.6.129.0/24 permit
@@ -623,6 +639,7 @@
89.22.108.0/24 permit 89.22.108.0/24 permit
91.211.240.0/22 permit 91.211.240.0/22 permit
94.169.2.0/23 permit 94.169.2.0/23 permit
94.236.119.0/26 permit
94.245.112.0/27 permit 94.245.112.0/27 permit
94.245.112.10/31 permit 94.245.112.10/31 permit
95.131.104.0/21 permit 95.131.104.0/21 permit
@@ -1121,6 +1138,7 @@
103.28.42.0/24 permit 103.28.42.0/24 permit
103.151.192.0/23 permit 103.151.192.0/23 permit
103.168.172.128/27 permit 103.168.172.128/27 permit
103.237.104.0/22 permit
104.43.243.237 permit 104.43.243.237 permit
104.44.112.128/25 permit 104.44.112.128/25 permit
104.47.0.0/17 permit 104.47.0.0/17 permit
@@ -1335,6 +1353,8 @@
130.61.9.72 permit 130.61.9.72 permit
130.162.39.83 permit 130.162.39.83 permit
130.211.0.0/22 permit 130.211.0.0/22 permit
130.248.172.0/24 permit
130.248.173.0/24 permit
131.253.30.0/24 permit 131.253.30.0/24 permit
131.253.121.0/26 permit 131.253.121.0/26 permit
132.145.13.209 permit 132.145.13.209 permit
@@ -1458,6 +1478,8 @@
159.135.224.0/20 permit 159.135.224.0/20 permit
159.135.228.10 permit 159.135.228.10 permit
159.183.0.0/16 permit 159.183.0.0/16 permit
159.183.68.71 permit
159.183.79.38 permit
160.1.62.192 permit 160.1.62.192 permit
161.38.192.0/20 permit 161.38.192.0/20 permit
161.38.204.0/22 permit 161.38.204.0/22 permit
@@ -1476,9 +1498,6 @@
163.114.135.16 permit 163.114.135.16 permit
164.152.23.32 permit 164.152.23.32 permit
164.177.132.168/30 permit 164.177.132.168/30 permit
165.173.128.0/24 permit
165.173.180.250/31 permit
165.173.182.250/31 permit
166.78.68.0/22 permit 166.78.68.0/22 permit
166.78.68.221 permit 166.78.68.221 permit
166.78.69.169 permit 166.78.69.169 permit
@@ -1507,12 +1526,6 @@
168.245.12.252 permit 168.245.12.252 permit
168.245.46.9 permit 168.245.46.9 permit
168.245.127.231 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
169.148.146.0/23 permit
170.10.128.0/24 permit 170.10.128.0/24 permit
170.10.129.0/24 permit 170.10.129.0/24 permit
170.10.132.56/29 permit 170.10.132.56/29 permit
@@ -1553,6 +1566,7 @@
183.240.219.64/29 permit 183.240.219.64/29 permit
185.4.120.0/22 permit 185.4.120.0/22 permit
185.12.80.0/22 permit 185.12.80.0/22 permit
185.28.196.0/22 permit
185.58.84.93 permit 185.58.84.93 permit
185.80.93.204 permit 185.80.93.204 permit
185.80.93.227 permit 185.80.93.227 permit
@@ -1618,6 +1632,7 @@
192.18.139.154 permit 192.18.139.154 permit
192.18.145.36 permit 192.18.145.36 permit
192.18.152.58 permit 192.18.152.58 permit
192.28.128.0/18 permit
192.29.103.128/25 permit 192.29.103.128/25 permit
192.30.252.0/22 permit 192.30.252.0/22 permit
192.161.144.0/20 permit 192.161.144.0/20 permit
@@ -1658,6 +1673,7 @@
198.244.60.0/22 permit 198.244.60.0/22 permit
198.245.80.0/20 permit 198.245.80.0/20 permit
198.245.81.0/24 permit 198.245.81.0/24 permit
199.15.212.0/22 permit
199.15.213.187 permit 199.15.213.187 permit
199.15.226.37 permit 199.15.226.37 permit
199.16.156.0/22 permit 199.16.156.0/22 permit
@@ -1972,9 +1988,6 @@
2603:1030:20e:3::23c permit 2603:1030:20e:3::23c permit
2603:1030:b:3::152 permit 2603:1030:b:3::152 permit
2603:1030:c02:8::14 permit 2603:1030:c02:8::14 permit
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
2607:f8b0:4000::/36 permit 2607:f8b0:4000::/36 permit
2620:109:c003:104::/64 permit 2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit 2620:109:c003:104::215 permit
+5
View File
@@ -2,6 +2,11 @@
cat <<EOF > /redis.conf cat <<EOF > /redis.conf
requirepass $REDISPASS requirepass $REDISPASS
user quota_notify on nopass ~QW_* -@all +get +hget +ping
EOF EOF
if [ -n "$REDISMASTERPASS" ]; then
echo "masterauth $REDISMASTERPASS" >> /redis.conf
fi
exec redis-server /redis.conf exec redis-server /redis.conf
+1
View File
@@ -3,6 +3,7 @@ dns {
} }
map_watch_interval = 30s; map_watch_interval = 30s;
task_timeout = 30s; task_timeout = 30s;
enable_mime_utf = true;
disable_monitoring = true; disable_monitoring = true;
# In case a task times out (like DNS lookup), soft reject the message # In case a task times out (like DNS lookup), soft reject the message
# instead of silently accepting the message without further processing. # instead of silently accepting the message without further processing.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY st0 "fill:#50BD37;">
]>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="640px" height="350px" viewBox="78.712 58.488 640 350" style="enable-background:new 78.712 58.488 640 350;"
xml:space="preserve">
<path style="&st0;" d="M648.541,145.679c-9.947,0-17.009-7.278-17.009-17.048c0-9.777,7.062-17.057,17.009-17.057
c10.024,0,17.086,7.279,17.086,17.057C665.627,138.401,658.565,145.679,648.541,145.679z M648.511,94.893
c-19.693,0-33.679,14.4-33.679,33.738c0,19.33,13.985,33.729,33.679,33.729c19.822,0,33.808-14.4,33.808-33.729
C682.318,109.293,668.333,94.893,648.511,94.893z M648.482,179.843c-29.889,0-51.123-21.868-51.123-51.212
c0-29.353,21.234-51.209,51.123-51.209c30.082,0,51.307,21.856,51.307,51.209C699.789,157.975,678.564,179.843,648.482,179.843z
M648.442,58.488c-40.929,0-69.995,29.946-69.995,70.143c0,40.189,29.066,70.125,69.995,70.125c41.194,0,70.27-29.937,70.27-70.125
C718.712,88.434,689.637,58.488,648.442,58.488z M158.166,183.902l-21.018-5.008c-19.131-4.396-28.849-9.413-28.849-23.21
c0-15.684,15.99-21.965,30.419-21.965c14.667,0,25.382,7.329,31.693,18.737c0.02,0.048,0.051,0.097,0.09,0.157
c0.127,0.247,0.276,0.484,0.403,0.731l0.03-0.02c1.985,3.002,5.323,5.008,8.919,5.008c6.122,0,10.558-4.425,10.558-10.547
c0-2.341-0.504-4.82-1.601-6.688c-10.764-18.302-28.513-26.192-48.838-26.192c-27.594,0-54.262,13.797-54.262,44.218
c0,27.921,27.605,36.079,37.64,38.578l20.069,4.71c15.368,3.763,27.912,8.791,27.912,23.517c0,16.938-17.561,23.943-34.499,23.943
c-17.245,0-30.015-9.37-38.814-22.37h-0.01c-1.956-3-4.988-4.328-8.702-4.328c-5.984,0-10.805,5.185-10.587,11.162
c0.098,2.438,0.909,4.637,2.153,6.405c13.787,20.633,33.728,28.41,55.96,28.41c28.543,0,57.085-13.143,57.085-45.132
C193.918,203.325,178.551,188.613,158.166,183.902z M298.479,250.312c-33.866,0-55.199-25.403-55.199-58.331
c0-32.939,21.333-58.343,55.199-58.343c34.192,0,55.516,25.403,55.516,58.343C353.996,224.91,332.672,250.312,298.479,250.312z
M298.479,114.823c-45.471,0-77.777,32.93-77.777,77.158c0,44.217,32.306,77.146,77.777,77.146
c45.786,0,78.093-32.929,78.093-77.146C376.572,147.753,344.266,114.823,298.479,114.823z M518.715,234.312
c-0.771,0.74-1.549,1.472-2.399,2.175c-1.106,1.014-2.391,2.112-3.854,3.208c-8.829,6.391-19.979,10.094-33.017,10.094
c-33.876,0-55.198-25.402-55.198-58.332c0-32.939,21.322-58.342,55.198-58.342c34.183,0,55.506,25.403,55.506,58.342
C534.951,208.653,529.135,223.774,518.715,234.312z M468.097,317.938c2.528,0,5.146-0.168,7.863-0.504
c5.018-0.631,9.588-0.909,13.729-0.909c19.24,0.109,29.036,5.7,34.943,12.158c5.895,6.499,8.168,15.311,8.158,22.796
c0.01,3.586-0.555,6.795-1.177,8.721c-2.944,8.93-8.888,15.002-17.996,19.576c-9.035,4.484-21.095,6.777-33.707,6.757
c-4.514,0-9.105-0.288-13.639-0.831c-8.573-0.987-19.911-4.671-28.13-11.093c-4.138-3.199-6.458-6.991-8.858-11.485
c-2.379-4.514-2.783-9.748-2.783-16.442v-0.742c0-12.346,4.84-20.544,11.051-26.5c3.07-2.904,5.69-5.064,7.99-6.438
c0.366-0.218,0.438-0.416,0.755-0.593C452.39,316.014,459.684,317.968,468.097,317.938z M479.445,114.301
c-45.471,0-77.786,32.929-77.786,77.157c0,29.887,14.765,54.598,38.378,67.489c-0.314,0.314-0.621,0.641-0.916,0.966
c-6.104,6.687-9.226,15.25-9.236,23.913c-0.008,3.821,0.624,7.741,1.977,11.494c-3.062,1.956-6.717,4.634-10.46,8.147
c-9.026,8.408-18.734,22.541-19.021,42.097c-0.01,0.454-0.01,0.829-0.01,1.118c-0.01,10.071,2.379,19.157,6.459,26.774
c6.133,11.466,15.683,19.445,25.539,24.77c9.917,5.334,20.257,8.166,29.273,9.274c5.373,0.643,10.826,0.988,16.268,0.988
c15.151-0.02,30.261-2.578,43.409-9.019c13.085-6.34,24.333-17.253,29.192-32.562c1.443-4.553,2.212-9.719,2.231-15.428
c-0.02-11.595-3.349-25.759-13.767-37.452c-10.421-11.734-27.654-19.566-51.288-19.459c-5.138,0-10.606,0.356-16.426,1.078
c-1.877,0.227-3.596,0.334-5.166,0.334c-7.239-0.048-10.872-2.053-13.036-4.098c-2.133-2.084-3.2-4.839-3.229-8.058
c-0.01-3.28,1.284-6.727,3.467-9.078c2.231-2.332,5.008-3.91,9.846-3.97c0.436,0,0.9,0.01,1.374,0.05
c3.101,0.216,6.112,0.325,9.037,0.325c24.188,0.047,42.38-7.448,54.756-17.759c12.415-10.312,18.971-22.854,22.071-32.76l-0.04-0.01
c3.37-8.899,5.197-18.715,5.197-29.166C557.539,147.229,525.234,114.301,479.445,114.301z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY st0 "fill:#50BD37;">
]>
<svg version="1.1" id="SOGo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="140.263px" height="140.269px" viewBox="499.737 0 140.263 140.269"
style="enable-background:new 499.737 0 140.263 140.269;" xml:space="preserve">
<path style="&st0;" d="M569.697,87.024c-9.928,0-16.975-7.264-16.975-17.017c0-9.757,7.047-17.022,16.975-17.022
c10.006,0,17.054,7.265,17.054,17.022C586.751,79.76,579.703,87.024,569.697,87.024z M569.667,36.335
c-19.657,0-33.614,14.372-33.614,33.673c0,19.294,13.955,33.667,33.614,33.667c19.787,0,33.745-14.372,33.745-33.667
C603.411,50.707,589.454,36.335,569.667,36.335z M569.639,121.123c-29.833,0-51.025-21.825-51.025-51.115
c0-29.296,21.192-51.111,51.025-51.111c30.025,0,51.213,21.815,51.213,51.111C620.852,99.298,599.664,121.123,569.639,121.123z
M569.602,0c-40.854,0-69.864,29.889-69.864,70.007c0,40.112,29.01,69.993,69.864,69.993c41.116,0,70.136-29.88,70.136-69.993
C639.737,29.889,610.719,0,569.602,0z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1 -1
View File
@@ -409,7 +409,7 @@ paths:
description: a list of domains for which a dkim key should be generated description: a list of domains for which a dkim key should be generated
type: string type: string
key_size: key_size:
description: the key size (1024 or 2048) description: the key size (1024, 2048, 3072 or 4096)
type: number type: number
type: object type: object
summary: Generate DKIM Key summary: Generate DKIM Key
+3
View File
@@ -166,6 +166,9 @@ if (isset($_SESSION['mailcow_cc_role'])) {
if (substr($result['recipient_map_old'], 0, 1) == '@') { if (substr($result['recipient_map_old'], 0, 1) == '@') {
$result['recipient_map_old'] = substr($result['recipient_map_old'], 1); $result['recipient_map_old'] = substr($result['recipient_map_old'], 1);
} }
if (substr($result['recipient_map_new'], 0, 1) == '@') {
$result['recipient_map_new'] = substr($result['recipient_map_new'], 1);
}
$template = 'edit/recipient_map.twig'; $template = 'edit/recipient_map.twig';
$template_data = ['map' => $map]; $template_data = ['map' => $map];
} }
@@ -270,6 +270,9 @@ function recipient_map($_action, $_data = null, $attr = null) {
$old_dest = substr($old_dest, 1); $old_dest = substr($old_dest, 1);
} }
$new_dest = strtolower(trim($_data['recipient_map_new'])); $new_dest = strtolower(trim($_data['recipient_map_new']));
if (substr($new_dest, 0, 1) == '@') {
$new_dest = substr($new_dest, 1);
}
$active = intval($_data['active']); $active = intval($_data['active']);
if (is_valid_domain_name($old_dest)) { if (is_valid_domain_name($old_dest)) {
$old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46); $old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46);
@@ -285,7 +288,13 @@ function recipient_map($_action, $_data = null, $attr = null) {
); );
return false; return false;
} }
if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) { if (is_valid_domain_name($new_dest)) {
$new_dest_sane = '@' . idn_to_ascii($new_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
$new_dest_sane = $new_dest;
}
else {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr), 'log' => array(__FUNCTION__, $_action, $_data, $_attr),
@@ -308,7 +317,7 @@ function recipient_map($_action, $_data = null, $attr = null) {
(:old_dest, :new_dest, :active)"); (:old_dest, :new_dest, :active)");
$stmt->execute(array( $stmt->execute(array(
':old_dest' => $old_dest_sane, ':old_dest' => $old_dest_sane,
':new_dest' => $new_dest, ':new_dest' => $new_dest_sane,
':active' => $active ':active' => $active
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
@@ -325,6 +334,9 @@ function recipient_map($_action, $_data = null, $attr = null) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active']; $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$new_dest = (!empty($_data['recipient_map_new'])) ? $_data['recipient_map_new'] : $is_now['recipient_map_new']; $new_dest = (!empty($_data['recipient_map_new'])) ? $_data['recipient_map_new'] : $is_now['recipient_map_new'];
$old_dest = (!empty($_data['recipient_map_old'])) ? $_data['recipient_map_old'] : $is_now['recipient_map_old']; $old_dest = (!empty($_data['recipient_map_old'])) ? $_data['recipient_map_old'] : $is_now['recipient_map_old'];
if (substr($new_dest, 0, 1) == '@') {
$new_dest = substr($new_dest, 1);
}
if (substr($old_dest, 0, 1) == '@') { if (substr($old_dest, 0, 1) == '@') {
$old_dest = substr($old_dest, 1); $old_dest = substr($old_dest, 1);
} }
@@ -351,7 +363,13 @@ function recipient_map($_action, $_data = null, $attr = null) {
); );
continue; continue;
} }
if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) { if (is_valid_domain_name($new_dest)) {
$new_dest_sane = '@' . idn_to_ascii($new_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
$new_dest_sane = $new_dest;
}
else {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr), 'log' => array(__FUNCTION__, $_action, $_data, $_attr),
@@ -378,7 +396,7 @@ function recipient_map($_action, $_data = null, $attr = null) {
WHERE `id`= :id"); WHERE `id`= :id");
$stmt->execute(array( $stmt->execute(array(
':old_dest' => $old_dest_sane, ':old_dest' => $old_dest_sane,
':new_dest' => $new_dest, ':new_dest' => $new_dest_sane,
':active' => $active, ':active' => $active,
':id' => $id ':id' => $id
)); ));
+4 -1
View File
@@ -240,9 +240,12 @@ function dkim($_action, $_data = null, $privkey = false) {
if (strlen($dkimdata['pubkey']) < 391) { if (strlen($dkimdata['pubkey']) < 391) {
$dkimdata['length'] = "1024"; $dkimdata['length'] = "1024";
} }
elseif (strlen($dkimdata['pubkey']) < 736) { elseif (strlen($dkimdata['pubkey']) < 564) {
$dkimdata['length'] = "2048"; $dkimdata['length'] = "2048";
} }
elseif (strlen($dkimdata['pubkey']) < 736) {
$dkimdata['length'] = "3072";
}
elseif (strlen($dkimdata['pubkey']) < 1416) { elseif (strlen($dkimdata['pubkey']) < 1416) {
$dkimdata['length'] = "4096"; $dkimdata['length'] = "4096";
} }
+19 -3
View File
@@ -2275,9 +2275,25 @@ function cors($action, $data = null) {
break; break;
} }
} }
function getBaseURL() { function getBaseURL($protocol = null) {
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http'; // Get current server name
$host = $_SERVER['HTTP_HOST']; $host = strtolower($_SERVER['SERVER_NAME']);
// craft allowed server name list
$mailcow_hostname = strtolower(getenv("MAILCOW_HOSTNAME"));
$additional_server_names = strtolower(getenv("ADDITIONAL_SERVER_NAMES")) ?: "";
$additional_server_names = preg_replace('/\s+/', '', $additional_server_names);
$allowed_server_names = $additional_server_names !== "" ? explode(',', $additional_server_names) : array();
array_push($allowed_server_names, $mailcow_hostname);
// Fallback to MAILCOW HOSTNAME if current server name is not in allowed list
if (!in_array($host, $allowed_server_names)) {
$host = $mailcow_hostname;
}
if (!isset($protocol)) {
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
}
$base_url = $protocol . '://' . $host; $base_url = $protocol . '://' . $host;
return $base_url; return $base_url;
+2 -2
View File
@@ -869,7 +869,7 @@
"recipient_map": "Mapa příjemce", "recipient_map": "Mapa příjemce",
"recipient_map_info": "Mapy příjemců slouží k nahrazení cílové adresy zprávy před doručením.", "recipient_map_info": "Mapy příjemců slouží k nahrazení cílové adresy zprávy před doručením.",
"recipient_map_new": "Nový přijemce", "recipient_map_new": "Nový přijemce",
"recipient_map_new_info": "Cílová adresa mapy příjemce musí být platná emailová adresa.", "recipient_map_new_info": "Cílová adresa mapy příjemce musí být emailová adresa nebo název domény.",
"recipient_map_old": "Původní příjemce", "recipient_map_old": "Původní příjemce",
"recipient_map_old_info": "Původní příjemce musí být platná emailová adresa nebo název domény.", "recipient_map_old_info": "Původní příjemce musí být platná emailová adresa nebo název domény.",
"recipient_maps": "Mapy příjemců", "recipient_maps": "Mapy příjemců",
@@ -1307,4 +1307,4 @@
"session_token": "Token formuláře není platný: Token mismatch", "session_token": "Token formuláře není platný: Token mismatch",
"session_ua": "Token formuláře není platný: User-Agent validation error" "session_ua": "Token formuláře není platný: User-Agent validation error"
} }
} }
+2 -2
View File
@@ -706,7 +706,7 @@
"recipient_map": "Modtagerkort", "recipient_map": "Modtagerkort",
"recipient_map_info": "Modtagerkort bruges til at erstatte destinationsadressen i en meddelelse, før den leveres.", "recipient_map_info": "Modtagerkort bruges til at erstatte destinationsadressen i en meddelelse, før den leveres.",
"recipient_map_new": "Ny modtager", "recipient_map_new": "Ny modtager",
"recipient_map_new_info": "Modtagerkortdestination skal være en gyldig e-mail-adresse.", "recipient_map_new_info": "Modtagerkortdestination skal være gyldige e-mail-adresser eller et domænenavn.",
"recipient_map_old": "Original modtager", "recipient_map_old": "Original modtager",
"recipient_map_old_info": "En modtager kortlægger den originale destination, skal være gyldige e-mail-adresser eller et domænenavn.", "recipient_map_old_info": "En modtager kortlægger den originale destination, skal være gyldige e-mail-adresser eller et domænenavn.",
"recipient_maps": "Modtagerkort", "recipient_maps": "Modtagerkort",
@@ -1089,4 +1089,4 @@
"first": "Først" "first": "Først"
} }
} }
} }
+2 -2
View File
@@ -881,7 +881,7 @@
"recipient_map": "Empfängerumschreibung", "recipient_map": "Empfängerumschreibung",
"recipient_map_info": "Empfängerumschreibung ersetzen den Empfänger einer E-Mail vor dem Versand.", "recipient_map_info": "Empfängerumschreibung ersetzen den Empfänger einer E-Mail vor dem Versand.",
"recipient_map_new": "Neuer Empfänger", "recipient_map_new": "Neuer Empfänger",
"recipient_map_new_info": "Der neue Empfänger muss eine E-Mail-Adresse sein.", "recipient_map_new_info": "Der neue Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
"recipient_map_old": "Original-Empfänger", "recipient_map_old": "Original-Empfänger",
"recipient_map_old_info": "Der originale Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.", "recipient_map_old_info": "Der originale Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
"recipient_maps": "Empfängerumschreibungen", "recipient_maps": "Empfängerumschreibungen",
@@ -1336,4 +1336,4 @@
"hour": "Nachrichten / Stunde", "hour": "Nachrichten / Stunde",
"day": "Nachrichten / Tag" "day": "Nachrichten / Tag"
} }
} }
+3 -3
View File
@@ -887,9 +887,9 @@
"recipient_map": "Recipient map", "recipient_map": "Recipient map",
"recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.", "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
"recipient_map_new": "New recipient", "recipient_map_new": "New recipient",
"recipient_map_new_info": "Recipient map destination must be a valid email address.", "recipient_map_new_info": "Recipient map destination must be a valid email addresses or a domain name.",
"recipient_map_old": "Original recipient", "recipient_map_old": "Original recipient",
"recipient_map_old_info": "A recipient maps original destination must be valid email addresses or a domain name.", "recipient_map_old_info": "A recipient maps original destination must be a valid email addresses or a domain name.",
"recipient_maps": "Recipient maps", "recipient_maps": "Recipient maps",
"relay_all": "Relay all recipients", "relay_all": "Relay all recipients",
"relay_unknown": "Relay unknown mailboxes", "relay_unknown": "Relay unknown mailboxes",
@@ -1336,4 +1336,4 @@
"session_token": "Form token invalid: Token mismatch", "session_token": "Form token invalid: Token mismatch",
"session_ua": "Form token invalid: User-Agent validation error" "session_ua": "Form token invalid: User-Agent validation error"
} }
} }
+9 -4
View File
@@ -24,7 +24,9 @@
"protocol_access": "Cambiar protocolo de acceso", "protocol_access": "Cambiar protocolo de acceso",
"quarantine_category": "Cambiar categoría de las notificaciones de cuarentena", "quarantine_category": "Cambiar categoría de las notificaciones de cuarentena",
"domain_relayhost": "Cambiar relayhost por un dominio", "domain_relayhost": "Cambiar relayhost por un dominio",
"extend_sender_acl": "Permitir extender la ACL del remitente por direcciones externas" "extend_sender_acl": "Permitir extender la ACL del remitente por direcciones externas",
"pw_reset": "Permitir el reset de la contraseña del usario mailcow",
"sogo_access": "Permitir la gestión del acceso a SOGo"
}, },
"add": { "add": {
"activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.", "activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
@@ -95,7 +97,10 @@
"app_password": "Añadir contraseña para la app", "app_password": "Añadir contraseña para la app",
"public_comment": "Comentarios públicos", "public_comment": "Comentarios públicos",
"disable_login": "Desactivar login (el correo entrante seguirá activo)", "disable_login": "Desactivar login (el correo entrante seguirá activo)",
"comment_info": "Los comentarios privados no son visibles al usuario, mientras que los comentarios públicos aparecerán sobre la información general del usuario" "comment_info": "Los comentarios privados no son visibles al usuario, mientras que los comentarios públicos aparecerán sobre la información general del usuario",
"dry": "Simular la sincronización",
"private_comment": "Comentario privado",
"app_passwd_protocols": "Protocolos autorizados para la contraseña de la aplicación"
}, },
"admin": { "admin": {
"access": "Acceso", "access": "Acceso",
@@ -539,7 +544,7 @@
"recipient_map": "Regla de destinatario", "recipient_map": "Regla de destinatario",
"recipient_map_info": "Las reglas de destinatarios se utilizan para reemplazar la dirección de destino en un mensaje antes de que se entregue.", "recipient_map_info": "Las reglas de destinatarios se utilizan para reemplazar la dirección de destino en un mensaje antes de que se entregue.",
"recipient_map_new": "Destinatario nuevo", "recipient_map_new": "Destinatario nuevo",
"recipient_map_new_info": "El destino de la regla debe ser una dirección de correo válida.", "recipient_map_new_info": "El destino de la regla debe ser una dirección de correo electrónico válida o un nombre de dominio.",
"recipient_map_old": "Destinatario original", "recipient_map_old": "Destinatario original",
"recipient_map_old_info": "El destino original de una regla de destinatario debe ser una dirección de correo electrónico válida o un nombre de dominio.", "recipient_map_old_info": "El destino original de una regla de destinatario debe ser una dirección de correo electrónico válida o un nombre de dominio.",
"recipient_maps": "Reglas de destinatario", "recipient_maps": "Reglas de destinatario",
@@ -777,4 +782,4 @@
"fuzzy_learn_error": "Error aprendiendo hash: %s", "fuzzy_learn_error": "Error aprendiendo hash: %s",
"ip_invalid": "IP inválida omitida: %s" "ip_invalid": "IP inválida omitida: %s"
} }
} }
+2 -2
View File
@@ -607,7 +607,7 @@
"recipient_map": "Vastaanottajien yhdistämis määritykset", "recipient_map": "Vastaanottajien yhdistämis määritykset",
"recipient_map_info": "Vastaanottajan karttoja käytetään korvaamaan viestin kohde osoite ennen sen toimittamista.", "recipient_map_info": "Vastaanottajan karttoja käytetään korvaamaan viestin kohde osoite ennen sen toimittamista.",
"recipient_map_new": "Uusi vastaanottaja", "recipient_map_new": "Uusi vastaanottaja",
"recipient_map_new_info": "Vastaanottajan yhdistämis kartan kohteen on oltava kelvollinen sähköposti osoite.", "recipient_map_new_info": "Vastaanottajan yhdistämis kartan kohteen on oltava kelvollinen sähköposti osoite tai verkkotunnus alueen nimi.",
"recipient_map_old": "Alkuperäinen vastaanottaja", "recipient_map_old": "Alkuperäinen vastaanottaja",
"recipient_map_old_info": "Vastaanottajan yhdistämis määritysten alkuperäisen kohteen on oltava kelvollinen sähköposti osoite tai verkkotunnus alueen nimi.", "recipient_map_old_info": "Vastaanottajan yhdistämis määritysten alkuperäisen kohteen on oltava kelvollinen sähköposti osoite tai verkkotunnus alueen nimi.",
"recipient_maps": "Vastaanottajien yhdistämis määritykset", "recipient_maps": "Vastaanottajien yhdistämis määritykset",
@@ -906,4 +906,4 @@
"last": "Edellinen" "last": "Edellinen"
} }
} }
} }
+2 -2
View File
@@ -834,7 +834,7 @@
"recipient_map": "Carte du destinataire", "recipient_map": "Carte du destinataire",
"recipient_map_info": "Les cartes des destinataires sont utilisées pour remplacer ladresse de destination dun message avant sa livraison.", "recipient_map_info": "Les cartes des destinataires sont utilisées pour remplacer ladresse de destination dun message avant sa livraison.",
"recipient_map_new": "Nouveau destinataire", "recipient_map_new": "Nouveau destinataire",
"recipient_map_new_info": "La destination de la carte du destinataire doit être une adresse de courriel valide.", "recipient_map_new_info": "La destination de la carte du destinataire doit être une adresse de courriel valide ou un nom de domaine.",
"recipient_map_old": "Destinataire original", "recipient_map_old": "Destinataire original",
"recipient_map_old_info": "Une carte de destination originale doit être une adresse de courriel valide ou un nom de domaine.", "recipient_map_old_info": "Une carte de destination originale doit être une adresse de courriel valide ou un nom de domaine.",
"recipient_maps": "Cartes des bénéficiaires", "recipient_maps": "Cartes des bénéficiaires",
@@ -1335,4 +1335,4 @@
"hour": "msgs / heure", "hour": "msgs / heure",
"day": "msgs / jour" "day": "msgs / jour"
} }
} }
+3 -3
View File
@@ -822,9 +822,9 @@
"recipient_map": "Recipient map", "recipient_map": "Recipient map",
"recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.", "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
"recipient_map_new": "New recipient", "recipient_map_new": "New recipient",
"recipient_map_new_info": "Recipient map destination must be a valid email address.", "recipient_map_new_info": "Recipient map destination must be a valid email addresses or a domain name.",
"recipient_map_old": "Original recipient", "recipient_map_old": "Original recipient",
"recipient_map_old_info": "A recipient maps original destination must be valid email addresses or a domain name.", "recipient_map_old_info": "A recipient maps original destination must be a valid email addresses or a domain name.",
"recipient_maps": "Recipient maps", "recipient_maps": "Recipient maps",
"relay_all": "Trasmettere a tutti i destinatari", "relay_all": "Trasmettere a tutti i destinatari",
"remove": "Rimuovi", "remove": "Rimuovi",
@@ -1306,4 +1306,4 @@
}, },
"decimal": "." "decimal": "."
} }
} }
+1 -1
View File
@@ -887,7 +887,7 @@
"recipient_map": "受信者マップ", "recipient_map": "受信者マップ",
"recipient_map_info": "受信者マップは、メッセージが配信される前に宛先アドレスを置き換えるために使用されます。", "recipient_map_info": "受信者マップは、メッセージが配信される前に宛先アドレスを置き換えるために使用されます。",
"recipient_map_new": "新しい受信者", "recipient_map_new": "新しい受信者",
"recipient_map_new_info": "受信者マップの宛先は有効なメールアドレスである必要があります。", "recipient_map_new_info": "受信者マップの宛先は有効なメールアドレスまたはドメイン名である必要があります。",
"recipient_map_old": "元の受信者", "recipient_map_old": "元の受信者",
"recipient_map_old_info": "受信者マップの元の宛先は有効なメールアドレスまたはドメイン名である必要があります。", "recipient_map_old_info": "受信者マップの元の宛先は有効なメールアドレスまたはドメイン名である必要があります。",
"recipient_maps": "受信者マップ", "recipient_maps": "受信者マップ",
+131 -12
View File
@@ -25,7 +25,11 @@
"syncjobs": "동기화 작업", "syncjobs": "동기화 작업",
"tls_policy": "TLS 정책", "tls_policy": "TLS 정책",
"unlimited_quota": "메일에 무제한 할당", "unlimited_quota": "메일에 무제한 할당",
"domain_desc": "도메인 설명 변경" "domain_desc": "도메인 설명 변경",
"pw_reset": "mailcow 사용자 비밀번호 재설정 허용",
"domain_relayhost": "도메인의 릴레이 호스트 변경",
"mailbox_relayhost": "메일함의 릴레이 호스트 변경",
"quarantine_category": "검역소 알림 카테고리 변경"
}, },
"add": { "add": {
"activate_filter_warn": "활성화가 체크되어 있으면 모든 다른 필터들은 비활성화됩니다.", "activate_filter_warn": "활성화가 체크되어 있으면 모든 다른 필터들은 비활성화됩니다.",
@@ -101,7 +105,9 @@
"timeout2": "로컬 호스트 연결 시간 초과", "timeout2": "로컬 호스트 연결 시간 초과",
"username": "사용자명", "username": "사용자명",
"validate": "확인하기", "validate": "확인하기",
"validation_success": "성공적으로 확인됨" "validation_success": "성공적으로 확인됨",
"tags": "태그",
"app_passwd_protocols": "앱 비밀번호에 대해 허용되는 프로토콜"
}, },
"admin": { "admin": {
"access": "접근", "access": "접근",
@@ -195,7 +201,7 @@
"link": "Link", "link": "Link",
"loading": "잠시만 기다려주세요...", "loading": "잠시만 기다려주세요...",
"logo_info": "이미지 크기는 상단 탐색 막대의 경우 40px, 시작 페이지의 경우 최대 너비 250px로 조정됩니다. 확장 가능한 그래픽을 권장합니다.", "logo_info": "이미지 크기는 상단 탐색 막대의 경우 40px, 시작 페이지의 경우 최대 너비 250px로 조정됩니다. 확장 가능한 그래픽을 권장합니다.",
"lookup_mx": "MX와 목적지 일치 (.outlook.com이 홉을 통해서 MX *.outlook.com을 대상으로 는 모든 메일을 라우트한다.)", "lookup_mx": "목적지가 MX 이름과 일치하는 정규 표현식입니다. (<code>.*\\.google\\.com</code>를 사용하여 이 홉을 통해 google.com으로 끝나는 모든 메일을 대상으로 하는 MX로 라우팅합니다.)",
"main_name": "\"mailcow UI\" 이름", "main_name": "\"mailcow UI\" 이름",
"merged_vars_hint": "회색으로 표시된 행은 <code>vars.(local.)php</code> 에서 병합되었고 이는 수정할 수 없습니다.", "merged_vars_hint": "회색으로 표시된 행은 <code>vars.(local.)php</code> 에서 병합되었고 이는 수정할 수 없습니다.",
"message": "메세지", "message": "메세지",
@@ -301,7 +307,53 @@
"username": "사용자 이름", "username": "사용자 이름",
"validate_license_now": "라이선스 서버와 GUID 확인", "validate_license_now": "라이선스 서버와 GUID 확인",
"verify": "확인", "verify": "확인",
"yes": "&#10003;" "yes": "&#10003;",
"domain_admin": "도메인 관리자",
"f2b_filter": "정규식 필터",
"f2b_manage_external": "외부에서 Fail2Ban 관리",
"f2b_max_ban_time": "최대 차단 시간(초)",
"f2b_regex_info": "고려되는 로그: SOGo, Postfix, Dovecot, PHP-FPM.",
"html": "HTML",
"oauth2_apps": "OAuth2 앱",
"oauth2_add_client": "OAuth2 클라이언트 추가",
"optional": "선택 사항",
"options": "옵션",
"password_length": "비밀번호 길이",
"password_policy_chars": "하나 이상의 알파벳 문자를 포함해야 합니다.",
"password_policy_length": "최소 암호 길이가 %d입니다.",
"password_policy_numbers": "숫자 하나 이상을 포함해야 합니다.",
"password_policy_special_chars": "특수 문자를 포함해야 합니다.",
"password_reset_info": "복구 이메일이 제공되지 않으면 이 기능을 사용할 수 없습니다.",
"password_reset_settings": "비밀번호 복구 설정",
"password_reset_tmpl_html": "HTML 템플릿",
"password_reset_tmpl_text": "Text 템플릿",
"password_settings": "비밀번호 설정",
"queue_unban": "차단 해제",
"restore_template": "기본 템플릿을 복원하려면 비워둡니다.",
"service": "서비스",
"success": "성공",
"dkim_overwrite_key": "기존 DKIM 키 덮어쓰기",
"f2b_ban_time_increment": "차단 시간은 차단될 때마다 증가합니다.",
"password_policy": "비밀번호 정책",
"quarantine_max_score": "메일의 스팸 점수가 이 값보다 높으면 알림을 삭제합니다:<br><small>기본값: 9999.0</small>",
"f2b_manage_external_info": "Fail2ban은 차단 목록을 유지하지만 트래픽을 차단하는 규칙을 능동적으로 설정하지는 않습니다. 트래픽을 외부에서 차단하려면 아래 생성된 차단 목록을 사용하세요.",
"password_policy_lowerupper": "소문자 및 대문자를 포함해야 합니다.",
"transport_test_rcpt_info": "&#8226; null@hosted.mailcow.de 을 사용하여 해외 목적지로 릴레이를 테스트하세요.",
"ip_check_disabled": "IP 확인이 비활성화됩니다. 아래에서 활성화할 수 있습니다<br> <strong>시스템 > 구성 > 옵션 > 사용자 정의</strong>",
"logo_normal_label": "일반",
"logo_dark_label": "다크 모드의 경우 반전",
"convert_html_to_text": "HTML을 일반 텍스트로 변환",
"copy_to_clipboard": "클립보드에 텍스트가 복사되었습니다!",
"cors_settings": "CORS 설정",
"rsettings_preset_4": "도메인에 대해 Rspamd 비활성화",
"ip_check": "IP 확인",
"admins": "관리자",
"admins_ldap": "LDAP 관리자",
"api_read_only": "읽기 전용 액세스",
"api_read_write": "읽기-쓰기 액세스",
"is_mx_based": "MX 기반",
"login_time": "로그인 시간",
"ip_check_opt_in": "외부 IP 주소 확인을 위해 타사 서비스 <strong>ipv4.mailcow.email</strong> 및 <strong>ipv6.mailcow.email</strong>을 사용하도록 설정합니다."
}, },
"danger": { "danger": {
"access_denied": "접근이 거부되거나 잘못된 데이터 양식", "access_denied": "접근이 거부되거나 잘못된 데이터 양식",
@@ -415,7 +467,30 @@
"username_invalid": "%s는 사용지 이름으로 사용할 수 없습니다.", "username_invalid": "%s는 사용지 이름으로 사용할 수 없습니다.",
"validity_missing": "유효 기간을 지정해주세요.", "validity_missing": "유효 기간을 지정해주세요.",
"value_missing": "모든 값을 입력해주세요.", "value_missing": "모든 값을 입력해주세요.",
"yotp_verification_failed": "Yubico OTP 검증 실패: %s" "yotp_verification_failed": "Yubico OTP 검증 실패: %s",
"dkim_domain_or_sel_exists": "“%s\"에 대한 DKIM 키가 존재하며 덮어쓰지 않습니다.",
"img_size_exceeded": "이미지가 최대 파일 크기를 초과합니다.",
"invalid_reset_token": "잘못된 리셋 토큰",
"nginx_reload_failed": "Nginx 리로드 실패: %s",
"password_reset_na": "현재 비밀번호 복구를 사용할 수 없습니다. 관리자에게 문의하세요.",
"reset_f2b_regex": "정규식 필터를 제때 재설정하지 못했습니다. 다시 시도하거나 몇 초 더 기다렸다가 웹사이트를 다시 로드하세요.",
"template_exists": "템플릿 %s이(가) 이미 존재합니다.",
"template_id_invalid": "템플릿 ID %s가 잘못되었습니다.",
"template_name_invalid": "템플릿 이름이 잘못되었습니다.",
"tfa_token_invalid": "TFA 토큰이 유효하지 않습니다.",
"to_invalid": "수신자가 비어 있지 않아야 합니다.",
"webauthn_authenticator_failed": "선택한 인증기를 찾을 수 없습니다.",
"webauthn_username_failed": "선택한 인증기가 다른 계정에 속해 있습니다.",
"demo_mode_enabled": "데모 모드가 활성화됨",
"recovery_email_failed": "복구 이메일을 보낼 수 없습니다. 관리자에게 문의하세요.",
"password_reset_invalid_user": "사서함을 찾을 수 없거나 복구 이메일이 설정되어 있지 않습니다.",
"webauthn_publickey_failed": "선택한 인증기에 대한 공개 키가 저장되지 않았습니다.",
"fido2_verification_failed": "FIDO2 인증 실패: %s",
"extended_sender_acl_denied": "외부 발신자 주소를 설정하는 ACL 누락",
"img_dimensions_exceeded": "이미지가 최대 이미지 크기를 초과합니다.",
"reset_token_limit_exceeded": "토큰 재설정 한도를 초과했습니다. 나중에 다시 시도해 주세요.",
"cors_invalid_method": "잘못된 허용 메서드를 지정했습니다.",
"cors_invalid_origin": "잘못된 허용 원본을 지정했습니다."
}, },
"debug": { "debug": {
"chart_this_server": "Chart (this server)", "chart_this_server": "Chart (this server)",
@@ -434,7 +509,24 @@
"uptime": "Uptime", "uptime": "Uptime",
"started_on": "Started on", "started_on": "Started on",
"static_logs": "Static logs", "static_logs": "Static logs",
"system_containers": "System & Containers" "system_containers": "System & Containers",
"current_time": "시스템 시간",
"no_update_available": "시스템이 최신 버전입니다.",
"architecture": "아키텍처",
"container_running": "실행 중",
"container_disabled": "컨테이너 중지 또는 비활성화",
"container_stopped": "중지됨",
"online_users": "온라인 사용자",
"service": "서비스",
"success": "성공",
"show_ip": "공인 IP 표시",
"timezone": "시간대",
"update_available": "사용 가능한 업데이트가 있습니다.",
"update_failed": "업데이트를 확인할 수 없습니다",
"username": "사용자 이름",
"memory": "메모리",
"error_show_ip": "공인 IP 주소를 확인할 수 없습니다",
"login_time": "시간"
}, },
"diagnostics": { "diagnostics": {
"cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.", "cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
@@ -542,7 +634,13 @@
"title": "Edit object", "title": "Edit object",
"unchanged_if_empty": "If unchanged leave blank", "unchanged_if_empty": "If unchanged leave blank",
"username": "Username", "username": "Username",
"validate_save": "Validate and save" "validate_save": "Validate and save",
"allow_from_smtp": "다음 IP만 <b>SMTP</b>를 사용하도록 허용합니다.",
"allow_from_smtp_info": "모든 발신자를 허용하려면 비워둡니다.<br>IPv4/IPv6 주소 및 네트워크.",
"allowed_protocols": "허용된 프로토콜",
"app_passwd_protocols": "앱 비밀번호에 대해 허용되는 프로토콜",
"acl": "ACL (권한)",
"admin": "관리자 수정"
}, },
"footer": { "footer": {
"cancel": "Cancel", "cancel": "Cancel",
@@ -560,13 +658,14 @@
"header": { "header": {
"administration": "Configuration & Details", "administration": "Configuration & Details",
"apps": "Apps", "apps": "Apps",
"debug": "System Information", "debug": "정보",
"email": "E-Mail", "email": "E-Mail",
"mailcow_config": "Configuration", "mailcow_config": "Configuration",
"quarantine": "Quarantine", "quarantine": "Quarantine",
"restart_netfilter": "Restart netfilter", "restart_netfilter": "Restart netfilter",
"restart_sogo": "Restart SOGo", "restart_sogo": "Restart SOGo",
"user_settings": "User Settings" "user_settings": "User Settings",
"mailcow_system": "시스템"
}, },
"info": { "info": {
"awaiting_tfa_confirmation": "Awaiting TFA confirmation", "awaiting_tfa_confirmation": "Awaiting TFA confirmation",
@@ -673,9 +772,9 @@
"recipient_map": "Recipient map", "recipient_map": "Recipient map",
"recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.", "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
"recipient_map_new": "New recipient", "recipient_map_new": "New recipient",
"recipient_map_new_info": "Recipient map destination must be a valid email address.", "recipient_map_new_info": "Recipient map destination must be a valid email addresses or a domain name.",
"recipient_map_old": "Original recipient", "recipient_map_old": "Original recipient",
"recipient_map_old_info": "A recipient maps original destination must be valid email addresses or a domain name.", "recipient_map_old_info": "A recipient maps original destination must be a valid email addresses or a domain name.",
"recipient_maps": "Recipient maps", "recipient_maps": "Recipient maps",
"relay_all": "모든 수신자에게 릴레이", "relay_all": "모든 수신자에게 릴레이",
"remove": "Remove", "remove": "Remove",
@@ -1017,5 +1116,25 @@
"quota_exceeded_scope": "Domain quota exceeded: Only unlimited mailboxes can be created in this domain scope.", "quota_exceeded_scope": "Domain quota exceeded: Only unlimited mailboxes can be created in this domain scope.",
"session_token": "Form token invalid: Token mismatch", "session_token": "Form token invalid: Token mismatch",
"session_ua": "Form token invalid: User-Agent validation error" "session_ua": "Form token invalid: User-Agent validation error"
},
"datatables": {
"collapse_all": "모두 접기",
"decimal": ".",
"emptyTable": "테이블에 사용 가능한 데이터가 없습니다.",
"expand_all": "모두 펼치기",
"infoEmpty": "0개 항목 중 0개부터 0개까지 표시",
"infoFiltered": "(_MAX_ 총 항목에서 필터링됨)",
"thousands": ",",
"lengthMenu": "_MENU_ 항목 표시",
"loadingRecords": "로딩 중...",
"processing": "잠시만 기다려 주세요...",
"search": "검색:",
"zeroRecords": "일치하는 레코드가 없습니다.",
"paginate": {
"first": "처음",
"last": "마지막",
"next": "다음",
"previous": "이전"
}
} }
} }
+2 -2
View File
@@ -764,7 +764,7 @@
"recipient_map": "Ontvanger-map", "recipient_map": "Ontvanger-map",
"recipient_map_info": "Ontvanger-maps worden gebruikt om het doeladres van een bericht te vervangen voordat het in een mailbox terecht komt.", "recipient_map_info": "Ontvanger-maps worden gebruikt om het doeladres van een bericht te vervangen voordat het in een mailbox terecht komt.",
"recipient_map_new": "Nieuwe ontvanger", "recipient_map_new": "Nieuwe ontvanger",
"recipient_map_new_info": "De bestemming van een ontvanger-map dient een geldig mailadres te zijn.", "recipient_map_new_info": "De bestemming van een ontvanger-map dient een geldig mailadres of domeinnaam te zijn.",
"recipient_map_old": "Oorspronkelijke ontvanger", "recipient_map_old": "Oorspronkelijke ontvanger",
"recipient_map_old_info": "De oorspronkelijke bestemming van een ontvanger-map dient een geldig mailadres of domeinnaam te zijn.", "recipient_map_old_info": "De oorspronkelijke bestemming van een ontvanger-map dient een geldig mailadres of domeinnaam te zijn.",
"recipient_maps": "Ontvanger-maps", "recipient_maps": "Ontvanger-maps",
@@ -1165,4 +1165,4 @@
"search": "Zoeken:", "search": "Zoeken:",
"zeroRecords": "Geen overeenkomsten gevonden" "zeroRecords": "Geen overeenkomsten gevonden"
} }
} }
+2 -2
View File
@@ -882,7 +882,7 @@
"recipient_map": "Mapa do destinatário", "recipient_map": "Mapa do destinatário",
"recipient_map_info": "Os mapas de destinatários são usados para substituir o endereço de destino em uma mensagem antes que ela seja entregue.", "recipient_map_info": "Os mapas de destinatários são usados para substituir o endereço de destino em uma mensagem antes que ela seja entregue.",
"recipient_map_new": "Novo destinatário", "recipient_map_new": "Novo destinatário",
"recipient_map_new_info": "O destino do mapa do destinatário deve ser um endereço de e-mail válido.", "recipient_map_new_info": "O destino do mapa do destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
"recipient_map_old": "Destinatário original", "recipient_map_old": "Destinatário original",
"recipient_map_old_info": "O destino original do mapa de um destinatário deve ser um endereço de e-mail válido ou um nome de domínio.", "recipient_map_old_info": "O destino original do mapa de um destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
"recipient_maps": "Mapas de destinatários", "recipient_maps": "Mapas de destinatários",
@@ -1330,4 +1330,4 @@
"session_token": "Token de formulário inválido: incompatibilidade de token", "session_token": "Token de formulário inválido: incompatibilidade de token",
"session_ua": "Token de formulário inválido: erro de validação do agente de usuário" "session_ua": "Token de formulário inválido: erro de validação do agente de usuário"
} }
} }
+2 -2
View File
@@ -789,7 +789,7 @@
"recipient_map": "Hartă destinatar", "recipient_map": "Hartă destinatar",
"recipient_map_info": "Hărțile destinatarilor sunt folosite pentru a înlocui adresa de destinație a unui mesaj înainte de a fi livrat.", "recipient_map_info": "Hărțile destinatarilor sunt folosite pentru a înlocui adresa de destinație a unui mesaj înainte de a fi livrat.",
"recipient_map_new": "Destinatar nou", "recipient_map_new": "Destinatar nou",
"recipient_map_new_info": "Destinația hărții destinatarului trebuie să fie o adresă de email validă.", "recipient_map_new_info": "Destinația hărții destinatarului trebuie să fie adrese de email valide sau nume de domeniu.",
"recipient_map_old": "Destinatar original", "recipient_map_old": "Destinatar original",
"recipient_map_old_info": "Destinația originală a hărților destinatarilor trebuie să fie adrese de email valide sau nume de domeniu.", "recipient_map_old_info": "Destinația originală a hărților destinatarilor trebuie să fie adrese de email valide sau nume de domeniu.",
"recipient_maps": "Hărți destinatar", "recipient_maps": "Hărți destinatar",
@@ -1208,4 +1208,4 @@
"expand_all": "Expandează tot", "expand_all": "Expandează tot",
"decimal": "," "decimal": ","
} }
} }
+2 -2
View File
@@ -887,7 +887,7 @@
"recipient_map": "Перезапись получателя", "recipient_map": "Перезапись получателя",
"recipient_map_info": "Перезапись получателя используются для замены получателя в сообщении до его доставки.", "recipient_map_info": "Перезапись получателя используются для замены получателя в сообщении до его доставки.",
"recipient_map_new": "Перезапись на", "recipient_map_new": "Перезапись на",
"recipient_map_new_info": "Должен быть действующим почтовым ящиком.", "recipient_map_new_info": "Должен быть действующим почтовым ящиком или доменом.",
"recipient_map_old": "Получатель", "recipient_map_old": "Получатель",
"recipient_map_old_info": "Должен быть действующим почтовым ящиком или доменом.", "recipient_map_old_info": "Должен быть действующим почтовым ящиком или доменом.",
"recipient_maps": "Перезапись получателя", "recipient_maps": "Перезапись получателя",
@@ -1336,4 +1336,4 @@
"session_token": "Неверный токен формы: несоответствие токена", "session_token": "Неверный токен формы: несоответствие токена",
"session_ua": "Неверный токен формы: ошибка проверки User-Agent" "session_ua": "Неверный токен формы: ошибка проверки User-Agent"
} }
} }
+2 -2
View File
@@ -822,7 +822,7 @@
"recipient_map": "Mapa príjemcu", "recipient_map": "Mapa príjemcu",
"recipient_map_info": "Mapy príjemcov sú používané ako náhrada cieľovej adresy u správy pred doručením.", "recipient_map_info": "Mapy príjemcov sú používané ako náhrada cieľovej adresy u správy pred doručením.",
"recipient_map_new": "Nový príjemca", "recipient_map_new": "Nový príjemca",
"recipient_map_new_info": "Mapový cieľ príjemcu musí byť platná emailová adresa.", "recipient_map_new_info": "Mapový cieľ príjemcu musí byť platná emailová adresa alebo meno domény.",
"recipient_map_old": "Originálny príjemca", "recipient_map_old": "Originálny príjemca",
"recipient_map_old_info": "Originálny cieľ mapy príjemcu musí byť platná emailová adresa alebo meno domény.", "recipient_map_old_info": "Originálny cieľ mapy príjemcu musí byť platná emailová adresa alebo meno domény.",
"recipient_maps": "Mapy príjemcov", "recipient_maps": "Mapy príjemcov",
@@ -1256,4 +1256,4 @@
"session_token": "Formulárový token neplatný: Tokenová nezhoda", "session_token": "Formulárový token neplatný: Tokenová nezhoda",
"session_ua": "Formulárový token neplatný: User-Agent validation error" "session_ua": "Formulárový token neplatný: User-Agent validation error"
} }
} }
+2 -2
View File
@@ -726,7 +726,7 @@
"recipient_map": "Mottagaromskrivning", "recipient_map": "Mottagaromskrivning",
"recipient_map_info": "En omskrivning av mottagaradressen används för att ersätta destinationsadressen i ett meddelande innan den levereras.", "recipient_map_info": "En omskrivning av mottagaradressen används för att ersätta destinationsadressen i ett meddelande innan den levereras.",
"recipient_map_new": "Ny mottagare", "recipient_map_new": "Ny mottagare",
"recipient_map_new_info": "Den ursprungliga mottagaren måste vara en giltig e-postadress.", "recipient_map_new_info": "Den ursprungliga mottagaren måste vara en giltiga e-postadresser eller ett domännamn.",
"recipient_map_old": "Ursprunglig mottagaren", "recipient_map_old": "Ursprunglig mottagaren",
"recipient_map_old_info": "Den ursprungliga mottagaren måste vara en giltiga e-postadresser eller ett domännamn.", "recipient_map_old_info": "Den ursprungliga mottagaren måste vara en giltiga e-postadresser eller ett domännamn.",
"recipient_maps": "Skriv om mottagaradressen", "recipient_maps": "Skriv om mottagaradressen",
@@ -1110,4 +1110,4 @@
"session_token": "Formulär-nyckeln är ogiltig: Nyckeln matchar inte", "session_token": "Formulär-nyckeln är ogiltig: Nyckeln matchar inte",
"session_ua": "Formulär-nyckeln är ogiltig: User-Agenten kunde inte valideras" "session_ua": "Formulär-nyckeln är ogiltig: User-Agenten kunde inte valideras"
} }
} }
+6 -1
View File
@@ -72,7 +72,12 @@ elseif (isset($_GET['login'])) {
// only check for admin-login on sogo GUI requests // only check for admin-login on sogo GUI requests
elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) { elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) {
// this is an nginx auth_request call, we check for existing sogo-sso session variables // this is an nginx auth_request call, we check for existing sogo-sso session variables
session_start(); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php')) {
include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php';
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// extract email address from "/SOGo/so/user@domain/xy" // extract email address from "/SOGo/so/user@domain/xy"
$url_parts = explode("/", $_SERVER['HTTP_X_ORIGINAL_URI']); $url_parts = explode("/", $_SERVER['HTTP_X_ORIGINAL_URI']);
$email_list = array( $email_list = array(
@@ -117,6 +117,8 @@
<select data-style="btn btn-light btn-sm" class="form-control" id="key_size" name="key_size" title="{{ lang.admin.dkim_key_length }}" required> <select data-style="btn btn-light btn-sm" class="form-control" id="key_size" name="key_size" title="{{ lang.admin.dkim_key_length }}" required>
<option data-subtext="bits">1024</option> <option data-subtext="bits">1024</option>
<option data-subtext="bits">2048</option> <option data-subtext="bits">2048</option>
<option data-subtext="bits">3072</option>
<option data-subtext="bits">4096</option>
</select> </select>
</div> </div>
</div> </div>
+16 -8
View File
@@ -235,7 +235,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
$(".totp-authenticator-selection").click(function(){ $(".totp-authenticator-selection").click(function(){
$(".totp-authenticator-selection").removeClass("active"); $(".totp-authenticator-selection").removeClass("active");
$(this).addClass("active"); $(this).addClass("active");
var id = $(this).children('input').first().val(); var id = $(this).children('input').first().val();
$("#totp_selected_id").val(id); $("#totp_selected_id").val(id);
@@ -244,7 +244,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
if ($('.totp-authenticator-selection').length == 1 && if ($('.totp-authenticator-selection').length == 1 &&
$('#pending_tfa_tab_yubi_otp').length == 0 && $('#pending_tfa_tab_yubi_otp').length == 0 &&
$('.webauthn-authenticator-selection').length == 0){ $('.webauthn-authenticator-selection').length == 0){
// select default if only one authenticator exists // select default if only one authenticator exists
$('.totp-authenticator-selection').addClass("active"); $('.totp-authenticator-selection').addClass("active");
@@ -257,7 +257,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
$('#pending_tfa_tab_totp').on('shown.bs.tab', function() { $('#pending_tfa_tab_totp').on('shown.bs.tab', function() {
// autofocus // autofocus
setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 200); setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 200);
}); });
// validate Yubi OTP tfa // validate Yubi OTP tfa
if ($('.webauthn-authenticator-selection').length == 0){ if ($('.webauthn-authenticator-selection').length == 0){
// autofocus // autofocus
@@ -276,10 +276,10 @@ function recursiveBase64StrToArrayBuffer(obj) {
$(".webauthn-authenticator-selection").click(function(){ $(".webauthn-authenticator-selection").click(function(){
$(".webauthn-authenticator-selection").removeClass("active"); $(".webauthn-authenticator-selection").removeClass("active");
$(this).addClass("active"); $(this).addClass("active");
var id = $(this).children('input').first().val(); var id = $(this).children('input').first().val();
$("#webauthn_selected_id").val(id); $("#webauthn_selected_id").val(id);
var webauthn_status_auth = document.getElementById('webauthn_status_auth'); var webauthn_status_auth = document.getElementById('webauthn_status_auth');
webauthn_status_auth.style.setProperty('display', 'flex', 'important'); webauthn_status_auth.style.setProperty('display', 'flex', 'important');
var webauthn_return_code = document.getElementById('webauthn_return_code'); var webauthn_return_code = document.getElementById('webauthn_return_code');
@@ -302,7 +302,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
console.log(json); console.log(json);
if (json.success === false) throw new Error(); if (json.success === false) throw new Error();
if (json.type === "error") throw new Error(json.msg); if (json.type === "error") throw new Error(json.msg);
recursiveBase64StrToArrayBuffer(json); recursiveBase64StrToArrayBuffer(json);
return json; return json;
}).then(getCredentialArgs => { }).then(getCredentialArgs => {
@@ -329,7 +329,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
webauthn_return_code.style.setProperty('display', 'block', 'important'); webauthn_return_code.style.setProperty('display', 'block', 'important');
webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry; webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry;
}); });
} }
}); });
$('#ConfirmTFAModal').on('hidden.bs.modal', function(){ $('#ConfirmTFAModal').on('hidden.bs.modal', function(){
// cancel pending login // cancel pending login
@@ -540,7 +540,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
Version: <a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">{{ mailcow_info.version_tag }} Version: <a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">{{ mailcow_info.version_tag }}
</a> </a>
</span> </span>
{% endif %} {% endif %}
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %} {% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %}
<span class="version"> <span class="version">
🛠️🐮 + 🐋 = 💕 🛠️🐮 + 🐋 = 💕
@@ -549,6 +549,14 @@ function recursiveBase64StrToArrayBuffer(obj) {
<span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span> <span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span>
</span> </span>
{% endif %} {% endif %}
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "legacy" and mailcow_info.version_tag|default %}
<span class="version">
⚰️🐮 + 🐋 = 💕
Legacy: <a href="{{ mailcow_info.git_project_url }}/commit/{{ mailcow_info.git_commit }}" target="_blank">{{ mailcow_info.version_tag }}
</a><br>
<span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span>
</span>
{% endif %}
</div> </div>
</body> </body>
</html> </html>
@@ -103,6 +103,8 @@
<select data-style="btn btn-light" class="form-control" id="key_size" name="key_size"> <select data-style="btn btn-light" class="form-control" id="key_size" name="key_size">
<option value="1024" data-subtext="bits" {% if template.attributes.key_size == 1024 %} selected{% endif %}>1024</option> <option value="1024" data-subtext="bits" {% if template.attributes.key_size == 1024 %} selected{% endif %}>1024</option>
<option value="2048" data-subtext="bits" {% if template.attributes.key_size == 2048 %} selected{% endif %}>2048</option> <option value="2048" data-subtext="bits" {% if template.attributes.key_size == 2048 %} selected{% endif %}>2048</option>
<option value="3072" data-subtext="bits" {% if template.attributes.key_size == 3072 %} selected{% endif %}>3072</option>
<option value="4096" data-subtext="bits" {% if template.attributes.key_size == 4096 %} selected{% endif %}>4096</option>
</select> </select>
</div> </div>
</div> </div>
+6
View File
@@ -490,6 +490,8 @@
<select data-style="btn btn-light" class="form-control" id="key_size" name="key_size"> <select data-style="btn btn-light" class="form-control" id="key_size" name="key_size">
<option data-subtext="bits" value="1024">1024</option> <option data-subtext="bits" value="1024">1024</option>
<option data-subtext="bits" value="2048" selected>2048</option> <option data-subtext="bits" value="2048" selected>2048</option>
<option data-subtext="bits" value="3072">3072</option>
<option data-subtext="bits" value="4096">4096</option>
</select> </select>
</div> </div>
</div> </div>
@@ -628,6 +630,8 @@
<select data-style="btn btn-light" class="form-control" id="key_size" name="key_size"> <select data-style="btn btn-light" class="form-control" id="key_size" name="key_size">
<option data-subtext="bits">1024</option> <option data-subtext="bits">1024</option>
<option data-subtext="bits" selected>2048</option> <option data-subtext="bits" selected>2048</option>
<option data-subtext="bits">3072</option>
<option data-subtext="bits">4096</option>
</select> </select>
</div> </div>
</div> </div>
@@ -843,6 +847,8 @@
<select data-style="btn btn-light" class="form-control" id="key_size2" name="key_size"> <select data-style="btn btn-light" class="form-control" id="key_size2" name="key_size">
<option data-subtext="bits">1024</option> <option data-subtext="bits">1024</option>
<option data-subtext="bits" selected>2048</option> <option data-subtext="bits" selected>2048</option>
<option data-subtext="bits">3072</option>
<option data-subtext="bits">4096</option>
</select> </select>
</div> </div>
</div> </div>
+24 -16
View File
@@ -1,7 +1,7 @@
services: services:
unbound-mailcow: unbound-mailcow:
image: mailcow/unbound:1.23 image: ghcr.io/mailcow/unbound:1.24
environment: environment:
- TZ=${TZ} - TZ=${TZ}
- SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n} - SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n}
@@ -17,7 +17,7 @@ services:
- unbound - unbound
mysql-mailcow: mysql-mailcow:
image: mariadb:10.5 image: mariadb:10.11
depends_on: depends_on:
- unbound-mailcow - unbound-mailcow
- netfilter-mailcow - netfilter-mailcow
@@ -42,8 +42,8 @@ services:
- mysql - mysql
redis-mailcow: redis-mailcow:
image: redis:7-alpine image: redis:7.4.6-alpine
entrypoint: /redis-conf.sh entrypoint: ["/bin/sh","/redis-conf.sh"]
volumes: volumes:
- redis-vol-1:/data/ - redis-vol-1:/data/
- ./data/conf/redis/redis-conf.sh:/redis-conf.sh:z - ./data/conf/redis/redis-conf.sh:/redis-conf.sh:z
@@ -55,6 +55,7 @@ services:
environment: environment:
- TZ=${TZ} - TZ=${TZ}
- REDISPASS=${REDISPASS} - REDISPASS=${REDISPASS}
- REDISMASTERPASS=${REDISMASTERPASS:-}
sysctls: sysctls:
- net.core.somaxconn=4096 - net.core.somaxconn=4096
networks: networks:
@@ -64,7 +65,7 @@ services:
- redis - redis
clamd-mailcow: clamd-mailcow:
image: mailcow/clamd:1.66 image: ghcr.io/mailcow/clamd:1.70
restart: always restart: always
depends_on: depends_on:
unbound-mailcow: unbound-mailcow:
@@ -83,7 +84,7 @@ services:
- clamd - clamd
rspamd-mailcow: rspamd-mailcow:
image: mailcow/rspamd:1.99 image: ghcr.io/mailcow/rspamd:2.1
stop_grace_period: 30s stop_grace_period: 30s
depends_on: depends_on:
- dovecot-mailcow - dovecot-mailcow
@@ -116,7 +117,7 @@ services:
- rspamd - rspamd
php-fpm-mailcow: php-fpm-mailcow:
image: mailcow/phpfpm:1.92 image: ghcr.io/mailcow/phpfpm:1.93
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on: depends_on:
- redis-mailcow - redis-mailcow
@@ -174,6 +175,7 @@ services:
- DEMO_MODE=${DEMO_MODE:-n} - DEMO_MODE=${DEMO_MODE:-n}
- WEBAUTHN_ONLY_TRUSTED_VENDORS=${WEBAUTHN_ONLY_TRUSTED_VENDORS:-n} - WEBAUTHN_ONLY_TRUSTED_VENDORS=${WEBAUTHN_ONLY_TRUSTED_VENDORS:-n}
- CLUSTERMODE=${CLUSTERMODE:-} - CLUSTERMODE=${CLUSTERMODE:-}
- ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-}
restart: always restart: always
networks: networks:
mailcow-network: mailcow-network:
@@ -181,7 +183,7 @@ services:
- phpfpm - phpfpm
sogo-mailcow: sogo-mailcow:
image: mailcow/sogo:1.128 image: ghcr.io/mailcow/sogo:1.129
environment: environment:
- DBNAME=${DBNAME} - DBNAME=${DBNAME}
- DBUSER=${DBUSER} - DBUSER=${DBUSER}
@@ -206,6 +208,9 @@ services:
- ./data/conf/sogo/:/etc/sogo/:z - ./data/conf/sogo/:/etc/sogo/:z
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:z - ./data/web/inc/init_db.inc.php:/init_db.inc.php:z
- ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
- ./data/conf/sogo/custom-shortlogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-compact.svg:z
- ./data/conf/sogo/custom-fulllogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg:z
- ./data/conf/sogo/custom-fulllogo.png:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-logo.png:z
- ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z
- ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
- mysql-socket-vol-1:/var/run/mysqld/ - mysql-socket-vol-1:/var/run/mysqld/
@@ -229,7 +234,7 @@ services:
- sogo - sogo
dovecot-mailcow: dovecot-mailcow:
image: mailcow/dovecot:2.3 image: ghcr.io/mailcow/dovecot:2.34-legacy
depends_on: depends_on:
- mysql-mailcow - mysql-mailcow
- netfilter-mailcow - netfilter-mailcow
@@ -316,7 +321,7 @@ services:
- dovecot - dovecot
postfix-mailcow: postfix-mailcow:
image: mailcow/postfix:1.78 image: ghcr.io/mailcow/postfix:1.80
depends_on: depends_on:
mysql-mailcow: mysql-mailcow:
condition: service_started condition: service_started
@@ -372,7 +377,7 @@ services:
- php-fpm-mailcow - php-fpm-mailcow
- sogo-mailcow - sogo-mailcow
- rspamd-mailcow - rspamd-mailcow
image: mailcow/nginx:1.01 image: ghcr.io/mailcow/nginx:1.03
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
@@ -384,11 +389,14 @@ services:
- SKIP_SOGO=${SKIP_SOGO:-n} - SKIP_SOGO=${SKIP_SOGO:-n}
- SKIP_RSPAMD=${SKIP_RSPAMD:-n} - SKIP_RSPAMD=${SKIP_RSPAMD:-n}
- DISABLE_IPv6=${DISABLE_IPv6:-n} - DISABLE_IPv6=${DISABLE_IPv6:-n}
- HTTP_REDIRECT=${HTTP_REDIRECT:-n}
- PHPFPMHOST=${PHPFPMHOST:-} - PHPFPMHOST=${PHPFPMHOST:-}
- SOGOHOST=${SOGOHOST:-} - SOGOHOST=${SOGOHOST:-}
- RSPAMDHOST=${RSPAMDHOST:-} - RSPAMDHOST=${RSPAMDHOST:-}
- REDISHOST=${REDISHOST:-} - REDISHOST=${REDISHOST:-}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- NGINX_USE_PROXY_PROTOCOL=${NGINX_USE_PROXY_PROTOCOL:-n}
- TRUSTED_PROXIES=${TRUSTED_PROXIES:-}
volumes: volumes:
- ./data/web:/web:ro,z - ./data/web:/web:ro,z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
@@ -411,7 +419,7 @@ services:
condition: service_started condition: service_started
unbound-mailcow: unbound-mailcow:
condition: service_healthy condition: service_healthy
image: mailcow/acme:1.91 image: ghcr.io/mailcow/acme:1.92
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
@@ -449,7 +457,7 @@ services:
- acme - acme
netfilter-mailcow: netfilter-mailcow:
image: mailcow/netfilter:1.60 image: ghcr.io/mailcow/netfilter:1.62
stop_grace_period: 30s stop_grace_period: 30s
restart: always restart: always
privileged: true privileged: true
@@ -469,7 +477,7 @@ services:
- /lib/modules:/lib/modules:ro - /lib/modules:/lib/modules:ro
watchdog-mailcow: watchdog-mailcow:
image: mailcow/watchdog:2.06 image: ghcr.io/mailcow/watchdog:2.07
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
tmpfs: tmpfs:
@@ -541,7 +549,7 @@ services:
- watchdog - watchdog
dockerapi-mailcow: dockerapi-mailcow:
image: mailcow/dockerapi:2.10 image: ghcr.io/mailcow/dockerapi:2.11
security_opt: security_opt:
- label=disable - label=disable
restart: always restart: always
@@ -561,7 +569,7 @@ services:
- dockerapi - dockerapi
olefy-mailcow: olefy-mailcow:
image: mailcow/olefy:1.13 image: ghcr.io/mailcow/olefy:1.14
restart: always restart: always
environment: environment:
- TZ=${TZ} - TZ=${TZ}
+13 -2
View File
@@ -26,7 +26,7 @@ for bin in openssl curl docker git awk sha1sum grep cut; do
done done
# Check Docker Version (need at least 24.X) # Check Docker Version (need at least 24.X)
docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | head -n 1 | cut -d '.' -f 1) docker_version=$(docker version --format '{{.Server.Version}}' | cut -d '.' -f 1)
if [[ $docker_version -lt 24 ]]; then if [[ $docker_version -lt 24 ]]; then
echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m" echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m"
@@ -181,11 +181,15 @@ if [[ ${SKIP_BRANCH} != y ]]; then
echo "Available Branches:" echo "Available Branches:"
echo "- master branch (stable updates) | default, recommended [1]" echo "- master branch (stable updates) | default, recommended [1]"
echo "- nightly branch (unstable updates, testing) | not-production ready [2]" echo "- nightly branch (unstable updates, testing) | not-production ready [2]"
echo "- legacy branch (supported until February 2026) | deprecated, security updates only [3]"
sleep 1 sleep 1
while [ -z "${MAILCOW_BRANCH}" ]; do while [ -z "${MAILCOW_BRANCH}" ]; do
read -r -p "Choose the Branch with it's number [1/2] " branch read -r -p "Choose the Branch with it's number [1/2/3] " branch
case $branch in case $branch in
[3])
MAILCOW_BRANCH="legacy"
;;
[2]) [2])
MAILCOW_BRANCH="nightly" MAILCOW_BRANCH="nightly"
;; ;;
@@ -267,6 +271,9 @@ HTTP_BIND=
HTTPS_PORT=443 HTTPS_PORT=443
HTTPS_BIND= HTTPS_BIND=
# Redirect HTTP connections to HTTPS - y/n
HTTP_REDIRECT=n
# ------------------------------ # ------------------------------
# Other bindings # Other bindings
# ------------------------------ # ------------------------------
@@ -531,6 +538,10 @@ case ${git_branch} in
mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream})) mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
mailcow_last_git_version="" mailcow_last_git_version=""
;; ;;
legacy)
mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
mailcow_last_git_version=""
;;
*) *)
mailcow_git_version=$(git rev-parse --short HEAD) mailcow_git_version=$(git rev-parse --short HEAD)
mailcow_last_git_version="" mailcow_last_git_version=""
+3 -43
View File
@@ -10,46 +10,6 @@ echo "If this script is run automatically by cron or a timer AND you are using b
echo "The snapshots of your backup destination should run AFTER the cold standby script finished to ensure consistent snapshots." echo "The snapshots of your backup destination should run AFTER the cold standby script finished to ensure consistent snapshots."
echo echo
function docker_garbage() {
IMGS_TO_DELETE=()
for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do
REPOSITORY=${container/:*}
TAG=${container/*:}
V_MAIN=${container/*.}
V_SUB=${container/*.}
EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }')
for existing_tag in ${EXISTING_TAGS[@]}; do
V_MAIN_EXISTING=${existing_tag/*.}
V_SUB_EXISTING=${existing_tag/*.}
# Not an integer
[[ ! ${V_MAIN_EXISTING} =~ ^[0-9]+$ ]] && continue
[[ ! ${V_SUB_EXISTING} =~ ^[0-9]+$ ]] && continue
if [[ ${V_MAIN_EXISTING} == "latest" ]]; then
echo "Found deprecated label \"latest\" for repository ${REPOSITORY}, it should be deleted."
IMGS_TO_DELETE+=(${REPOSITORY}:${existing_tag})
elif [[ ${V_MAIN_EXISTING} -lt ${V_MAIN} ]]; then
echo "Found tag ${existing_tag} for ${REPOSITORY}, which is older than the current tag ${TAG} and should be deleted."
IMGS_TO_DELETE+=(${REPOSITORY}:${existing_tag})
elif [[ ${V_SUB_EXISTING} -lt ${V_SUB} ]]; then
echo "Found tag ${existing_tag} for ${REPOSITORY}, which is older than the current tag ${TAG} and should be deleted."
IMGS_TO_DELETE+=(${REPOSITORY}:${existing_tag})
fi
done
done
if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
docker rmi ${IMGS_TO_DELETE[*]}
fi
}
function preflight_local_checks() { function preflight_local_checks() {
if [[ -z "${REMOTE_SSH_KEY}" ]]; then if [[ -z "${REMOTE_SSH_KEY}" ]]; then
>&2 echo -e "\e[31mREMOTE_SSH_KEY is not set\e[0m" >&2 echo -e "\e[31mREMOTE_SSH_KEY is not set\e[0m"
@@ -139,11 +99,11 @@ EOF
if [ $? = 0 ]; then if [ $? = 0 ]; then
COMPOSE_COMMAND="docker compose" COMPOSE_COMMAND="docker compose"
echo "DEBUG: Using native docker compose on remote" echo "INFO: Using native docker compose on remote"
elif [ $? = 1 ]; then elif [ $? = 1 ]; then
COMPOSE_COMMAND="docker-compose" COMPOSE_COMMAND="docker-compose"
echo "DEBUG: Using standalone docker compose on remote" echo "INFO: Using standalone docker compose on remote"
else else
echo -e "\e[31mCannot find any Docker Compose on remote, exiting...\e[0m" echo -e "\e[31mCannot find any Docker Compose on remote, exiting...\e[0m"
@@ -324,7 +284,7 @@ echo "OK"
-i "${REMOTE_SSH_KEY}" \ -i "${REMOTE_SSH_KEY}" \
${REMOTE_SSH_HOST} \ ${REMOTE_SSH_HOST} \
-p ${REMOTE_SSH_PORT} \ -p ${REMOTE_SSH_PORT} \
${COMPOSE_COMMAND} -f "${SCRIPT_DIR}/../docker-compose.yml" pull --no-parallel --quiet 2>&1 ; then ${COMPOSE_COMMAND} -f "${SCRIPT_DIR}/../docker-compose.yml" pull --quiet 2>&1 ; then
>&2 echo -e "\e[31m[ERR]\e[0m - Could not pull images on remote" >&2 echo -e "\e[31m[ERR]\e[0m - Could not pull images on remote"
fi fi
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
DEBIAN_DOCKER_IMAGE="mailcow/backup:latest" DEBIAN_DOCKER_IMAGE="ghcr.io/mailcow/backup:latest"
if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then
BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}" BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}"
+101 -10
View File
@@ -36,13 +36,19 @@ docker_garbage() {
IMGS_TO_DELETE=() IMGS_TO_DELETE=()
declare -A IMAGES_INFO declare -A IMAGES_INFO
COMPOSE_IMAGES=($(grep -oP "image: \Kmailcow.+" "${SCRIPT_DIR}/docker-compose.yml")) COMPOSE_IMAGES=($(grep -oP "image: \K(ghcr\.io/)?mailcow.+" "${SCRIPT_DIR}/docker-compose.yml"))
for existing_image in $(docker images --format "{{.ID}}:{{.Repository}}:{{.Tag}}" | grep 'mailcow/'); do for existing_image in $(docker images --format "{{.ID}}:{{.Repository}}:{{.Tag}}" | grep -E '(mailcow/|ghcr\.io/mailcow/)'); do
ID=$(echo "$existing_image" | cut -d ':' -f 1) ID=$(echo "$existing_image" | cut -d ':' -f 1)
REPOSITORY=$(echo "$existing_image" | cut -d ':' -f 2) REPOSITORY=$(echo "$existing_image" | cut -d ':' -f 2)
TAG=$(echo "$existing_image" | cut -d ':' -f 3) TAG=$(echo "$existing_image" | cut -d ':' -f 3)
if [[ "$REPOSITORY" == "mailcow/backup" || "$REPOSITORY" == "ghcr.io/mailcow/backup" ]]; then
if [[ "$TAG" != "<none>" ]]; then
continue
fi
fi
if [[ " ${COMPOSE_IMAGES[@]} " =~ " ${REPOSITORY}:${TAG} " ]]; then if [[ " ${COMPOSE_IMAGES[@]} " =~ " ${REPOSITORY}:${TAG} " ]]; then
continue continue
else else
@@ -57,7 +63,7 @@ docker_garbage() {
echo " ${IMAGES_INFO[$id]} ($id)" echo " ${IMAGES_INFO[$id]} ($id)"
done done
if [ ! $FORCE ]; then if [ -z "$FORCE" ]; then
read -r -p "Do you want to delete them to free up some space? [y/N] " response read -r -p "Do you want to delete them to free up some space? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
docker rmi ${IMGS_TO_DELETE[*]} docker rmi ${IMGS_TO_DELETE[*]}
@@ -171,7 +177,7 @@ remove_obsolete_nginx_ports() {
detect_docker_compose_command(){ detect_docker_compose_command(){
if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
if docker compose > /dev/null 2>&1; then if docker compose > /dev/null 2>&1; then
if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then if docker compose version --short | grep -e "^[2-9]\." -e "^v[2-9]\." -e "^[1-9][0-9]\." -e "^v[1-9][0-9]\." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=native DOCKER_COMPOSE_VERSION=native
COMPOSE_COMMAND="docker compose" COMPOSE_COMMAND="docker compose"
echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m" echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m"
@@ -181,12 +187,12 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m" echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
else else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1 exit 1
fi fi
elif docker-compose > /dev/null 2>&1; then elif docker-compose > /dev/null 2>&1; then
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then if docker-compose version --short | grep -e "^[2-9]\." -e "^[1-9][0-9]\." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=standalone DOCKER_COMPOSE_VERSION=standalone
COMPOSE_COMMAND="docker-compose" COMPOSE_COMMAND="docker-compose"
echo -e "\e[33mFound Docker Compose Standalone.\e[0m" echo -e "\e[33mFound Docker Compose Standalone.\e[0m"
@@ -196,7 +202,7 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
else else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/install/\e[0m" echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1 exit 1
fi fi
fi fi
@@ -352,6 +358,7 @@ adapt_new_options() {
"SPAMHAUS_DQS_KEY" "SPAMHAUS_DQS_KEY"
"SKIP_UNBOUND_HEALTHCHECK" "SKIP_UNBOUND_HEALTHCHECK"
"DISABLE_NETFILTER_ISOLATION_RULE" "DISABLE_NETFILTER_ISOLATION_RULE"
"HTTP_REDIRECT"
) )
sed -i --follow-symlinks '$a\' mailcow.conf sed -i --follow-symlinks '$a\' mailcow.conf
@@ -637,7 +644,13 @@ adapt_new_options() {
echo '# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n' >> mailcow.conf echo '# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n' >> mailcow.conf
echo '# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf echo '# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf
echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf
fi fi
elif [[ ${option} == "HTTP_REDIRECT" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Redirect HTTP connections to HTTPS - y/n' >> mailcow.conf
echo 'HTTP_REDIRECT=n' >> mailcow.conf
fi
elif ! grep -q ${option} mailcow.conf; then elif ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=n" >> mailcow.conf echo "${option}=n" >> mailcow.conf
@@ -703,6 +716,50 @@ migrate_solr_config_options() {
fi fi
} }
detect_major_update() {
if [ ${BRANCH} == "master" ]; then
# Array with major versions
# Add major versions here
MAJOR_VERSIONS=(
"2025-02"
"2025-03"
)
current_version=""
if [[ -f "${SCRIPT_DIR}/data/web/inc/app_info.inc.php" ]]; then
current_version=$(grep 'MAILCOW_GIT_VERSION' ${SCRIPT_DIR}/data/web/inc/app_info.inc.php | sed -E 's/.*MAILCOW_GIT_VERSION="([^"]+)".*/\1/')
fi
if [[ -z "$current_version" ]]; then
return 1
fi
release_url="https://github.com/mailcow/mailcow-dockerized/releases/tag"
updates_to_apply=()
for version in "${MAJOR_VERSIONS[@]}"; do
if [[ "$current_version" < "$version" ]]; then
updates_to_apply+=("$version")
fi
done
if [[ ${#updates_to_apply[@]} -gt 0 ]]; then
echo -e "\e[33m\nMAJOR UPDATES to be applied:\e[0m"
for update in "${updates_to_apply[@]}"; do
echo "$update - $release_url/$update"
done
echo -e "\nPlease read the release notes before proceeding."
read -p "Do you want to proceed with the update? [y/n] " response
if [[ "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "Proceeding with the update..."
else
echo "Update canceled. Exiting."
exit 1
fi
fi
fi
}
############## End Function Section ############## ############## End Function Section ##############
# Check permissions # Check permissions
@@ -838,8 +895,12 @@ while (($#)); do
echo -e "\e[32mRunning in Developer mode...\e[0m" echo -e "\e[32mRunning in Developer mode...\e[0m"
DEV=y DEV=y
;; ;;
--legacy)
CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"
NEW_BRANCH="legacy"
;;
--help|-h) --help|-h)
echo './update.sh [-c|--check, --check-tags, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help] echo './update.sh [-c|--check, --check-tags, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, --legacy, -f|--force, -d|--dev, -h|--help]
-c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates) -c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
--check-tags - Check for newer tags and exit (exit codes => 0: newer tag available, 3: no newer tag) --check-tags - Check for newer tags and exit (exit codes => 0: newer tag available, 3: no newer tag)
@@ -849,7 +910,8 @@ while (($#)); do
--prefetch - Only prefetch new images and exit (useful to prepare updates) --prefetch - Only prefetch new images and exit (useful to prepare updates)
--skip-start - Do not start mailcow after update --skip-start - Do not start mailcow after update
--skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you'\''ve blocked any ICMP Connections to your mailcow machine) --skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you'\''ve blocked any ICMP Connections to your mailcow machine)
--stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly. --stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly or --legacy.
--legacy - Switch your mailcow updates to the legacy branch. The legacy branch will only recieve security updates until February 2026.
-f|--force - Force update, do not ask questions -f|--force - Force update, do not ask questions
-d|--dev - Enables Developer Mode (No Checkout of update.sh for tests) -d|--dev - Enables Developer Mode (No Checkout of update.sh for tests)
' '
@@ -1255,6 +1317,11 @@ if ! [ "$NEW_BRANCH" ]; then
sleep 1 sleep 1
echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m" echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
elif [ "${BRANCH}" == "legacy" ]; then
echo -e "\e[31mYou are receiving legacy updates. The legacy branch will only recieve security updates until February 2026.\e[0m"
sleep 1
echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
else else
echo -e "\e[33mYou are receiving updates from an unsupported branch.\e[0m" echo -e "\e[33mYou are receiving updates from an unsupported branch.\e[0m"
sleep 1 sleep 1
@@ -1317,6 +1384,29 @@ elif [ "$NEW_BRANCH" == "nightly" ] && [ "$CURRENT_BRANCH" != "nightly" ]; then
fi fi
git fetch origin git fetch origin
git checkout -f "${BRANCH}" git checkout -f "${BRANCH}"
elif [ "$NEW_BRANCH" == "legacy" ] && [ "$CURRENT_BRANCH" != "legacy" ]; then
echo -e "\e[33mYou are about to switch your mailcow Updates to the legacy branch.\e[0m"
sleep 1
echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no Data is lost...\e[0m"
sleep 1
echo -e "\e[31mWARNING: A switch to stable or nightly is possible any time.\e[0m"
read -r -p "Are you sure you want to continue upgrading to the legacy branch? [y/N] " response
if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "OK. If you prepared yourself for that please run the update.sh Script with the --legacy parameter again to trigger this process here."
exit 0
fi
BRANCH=$NEW_BRANCH
DIFF_DIRECTORY=update_diffs
DIFF_FILE=${DIFF_DIRECTORY}/diff_before_upgrade_to_legacy_$(date +"%Y-%m-%d-%H-%M-%S")
mv diff_before_upgrade* ${DIFF_DIRECTORY}/ 2> /dev/null
if ! git diff-index --quiet HEAD; then
echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
mkdir -p ${DIFF_DIRECTORY}
git diff "${BRANCH}" --stat > "${DIFF_FILE}"
git diff "${BRANCH}" >> "${DIFF_FILE}"
fi
git fetch origin
git checkout -f "${BRANCH}"
fi fi
if [ ! "$DEV" ]; then if [ ! "$DEV" ]; then
@@ -1338,6 +1428,7 @@ if [ ! "$FORCE" ]; then
echo "OK, exiting." echo "OK, exiting."
exit 0 exit 0
fi fi
detect_major_update
migrate_docker_nat migrate_docker_nat
fi fi