diff --git a/.gitignore b/.gitignore
index 624e1c06..5fd3c0f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
rebuild-images.sh
data/conf/sogo/sieve.creds
+data/conf/phpfpm/sogo-sso/sogo-sso.pass
data/conf/dovecot/dovecot-master.passwd
data/conf/dovecot/dovecot-master.userdb
mailcow.conf
@@ -24,6 +25,7 @@ data/conf/nginx/*.custom
data/conf/nginx/*.bak
data/conf/dovecot/acl_anyone
data/conf/dovecot/mail_plugins*
+data/conf/dovecot/sogo-sso.conf
data/conf/dovecot/extra.conf
data/conf/rspamd/custom/*
data/conf/portainer/
diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh
index bb9a5a53..e79ef977 100755
--- a/data/Dockerfiles/acme/docker-entrypoint.sh
+++ b/data/Dockerfiles/acme/docker-entrypoint.sh
@@ -5,6 +5,16 @@ exec 5>&1
# Thanks to https://github.com/cvmiller -> https://github.com/cvmiller/expand6
source /srv/expand6.sh
+# Skipping IP check when we like to live dangerously
+if [[ "${SKIP_IP_CHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
+ SKIP_IP_CHECK=y
+fi
+
+# Skipping HTTP check when we like to live dangerously
+if [[ "${SKIP_HTTP_VERIFICATION}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
+ SKIP_HTTP_VERIFICATION=y
+fi
+
log_f() {
if [[ ${2} == "no_nl" ]]; then
echo -n "$(date) - ${1}"
@@ -42,7 +52,6 @@ mkdir -p ${ACME_BASE}/acme
[[ -f ${ACME_BASE}/acme/private/privkey.pem ]] && mv ${ACME_BASE}/acme/private/privkey.pem ${ACME_BASE}/acme/key.pem
[[ -f ${ACME_BASE}/acme/private/account.key ]] && mv ${ACME_BASE}/acme/private/account.key ${ACME_BASE}/acme/account.pem
-
reload_configurations(){
# Reading container IDs
# Wrapping as array to ensure trimmed content when calling $NGINX etc.
@@ -121,7 +130,10 @@ verify_challenge_path(){
# verify_challenge_path URL 4|6
RAND_FILE=${RANDOM}${RANDOM}${RANDOM}
touch /var/www/acme/${RAND_FILE}
- if [[ "$(curl -${2} http://${1}/.well-known/acme-challenge/${RAND_FILE} --write-out %{http_code} --silent --output /dev/null)" =~ ^(2|3) ]]; then
+ if [[ ${SKIP_HTTP_VERIFICATION} == "y" ]]; then
+ echo '(skipping check, returning 0)'
+ return 0
+ elif [[ "$(curl -${2} http://${1}/.well-known/acme-challenge/${RAND_FILE} --write-out %{http_code} --silent --output /dev/null)" =~ ^(2|3) ]]; then
rm /var/www/acme/${RAND_FILE}
return 0
else
@@ -156,6 +168,7 @@ else
exec env TRIGGER_RESTART=1 $(readlink -f "$0")
fi
fi
+chmod 600 ${ACME_BASE}/key.pem
log_f "Waiting for database... " no_nl
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
@@ -196,10 +209,8 @@ while true; do
log_f "Using existing Lets Encrypt account key ${ACME_BASE}/acme/account.pem"
fi
- # Skipping IP check when we like to live dangerously
- if [[ "${SKIP_IP_CHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
- SKIP_IP_CHECK=y
- fi
+ chmod 600 ${ACME_BASE}/acme/key.pem
+ chmod 600 ${ACME_BASE}/acme/account.pem
# Cleaning up and init validation arrays
unset SQL_DOMAIN_ARR
@@ -476,6 +487,7 @@ while true; do
ACME_RESPONSE_B64=$(echo "${ACME_RESPONSE}" | openssl enc -e -A -base64)
log_f "${ACME_RESPONSE_B64}" redis_only b64
log_f "Retrying in 30 minutes..."
+ redis-cli -h redis SET ACME_FAIL_TIME "$(date +%s)"
sleep 30m
exec $(readlink -f "$0")
;;
diff --git a/data/Dockerfiles/clamd/bootstrap.sh b/data/Dockerfiles/clamd/bootstrap.sh
index fcfe0934..1d49cd20 100755
--- a/data/Dockerfiles/clamd/bootstrap.sh
+++ b/data/Dockerfiles/clamd/bootstrap.sh
@@ -7,19 +7,29 @@ if [[ "${SKIP_CLAMD}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
fi
# Prepare whitelist
+
+mkdir -p /run/clamav /var/lib/clamav
+
if [[ -s /etc/clamav/whitelist.ign2 ]]; then
+ echo "Copying non-empty whitelist.ign2 to /var/lib/clamav/whitelist.ign2"
cp /etc/clamav/whitelist.ign2 /var/lib/clamav/whitelist.ign2
fi
if [[ ! -f /var/lib/clamav/whitelist.ign2 ]]; then
+ echo "Creating /var/lib/clamav/whitelist.ign2"
echo "Example-Signature.Ignore-1" > /var/lib/clamav/whitelist.ign2
fi
-chown clamav:clamav /var/lib/clamav/whitelist.ign2
-mkdir -p /run/clamav /var/lib/clamav
-chown clamav:clamav /run/clamav /var/lib/clamav
-chmod 750 /run/clamav
+
+chown clamav:clamav -R /var/lib/clamav /run/clamav
+
chmod 755 /var/lib/clamav
+chmod 644 -R /var/lib/clamav/*
+chmod 750 /run/clamav
+
+echo "Stating whitelist.ign2"
+stat /var/lib/clamav/whitelist.ign2
dos2unix /var/lib/clamav/whitelist.ign2
+
sed -i '/^\s*$/d' /var/lib/clamav/whitelist.ign2
BACKGROUND_TASKS=()
@@ -38,7 +48,7 @@ while true; do
sleep 2m
SANE_MIRRORS="$(dig +ignore +short rsync.sanesecurity.net)"
for sane_mirror in ${SANE_MIRRORS}; do
- rsync -avp --chown=clamav:clamav --timeout=5 rsync://${sane_mirror}/sanesecurity/ \
+ rsync -avp --chown=clamav:clamav --chmod=Du=rwx,Dgo=rx,Fu=rw,Fog=r --timeout=5 rsync://${sane_mirror}/sanesecurity/ \
--include 'blurl.ndb' \
--include 'junk.ndb' \
--include 'jurlbl.ndb' \
diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile
index 1320df1f..c5517499 100644
--- a/data/Dockerfiles/dovecot/Dockerfile
+++ b/data/Dockerfiles/dovecot/Dockerfile
@@ -3,8 +3,8 @@ LABEL maintainer "Andre Peters "
ARG DEBIAN_FRONTEND=noninteractive
ENV LC_ALL C
-ENV DOVECOT_VERSION 2.3.4
-ENV PIGEONHOLE_VERSION 0.5.4
+ENV DOVECOT_VERSION 2.3.5.1
+ENV PIGEONHOLE_VERSION 0.5.5
RUN apt-get update && apt-get -y --no-install-recommends install \
automake \
diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh
index 0589579d..7034fc08 100755
--- a/data/Dockerfiles/dovecot/docker-entrypoint.sh
+++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh
@@ -106,7 +106,7 @@ chmod 644 /usr/local/etc/dovecot/mail_plugins /usr/local/etc/dovecot/mail_plugin
cat < /usr/local/etc/dovecot/sql/dovecot-dict-sql-userdb.conf
driver = mysql
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
-user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/:VOLATILEDIR=/var/volatile/%u') AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
+user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_EXTRACT(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/${MAILDIR_SUB}:VOLATILEDIR=/var/volatile/%u') AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
iterate_query = SELECT username FROM mailbox WHERE active='1';
EOF
@@ -127,6 +127,10 @@ if [[ $(stat -c %U /var/vmail/) != "vmail" ]] ; then chown -R vmail:vmail /var/v
if [[ $(stat -c %U /var/vmail/_garbage) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail/_garbage ; fi
if [[ $(stat -c %U /var/attachments) != "vmail" ]] ; then chown -R vmail:vmail /var/attachments ; fi
+# Cleanup random user maildirs
+rm -rf /var/vmail/mailcow.local/*
+
+
# Create random master for SOGo sieve features
RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1)
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1)
@@ -135,6 +139,21 @@ echo ${RAND_USER}@mailcow.local:{SHA1}$(echo -n ${RAND_PASS} | sha1sum | awk '{p
echo ${RAND_USER}@mailcow.local::5000:5000:::: > /usr/local/etc/dovecot/dovecot-master.userdb
echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds
+if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
+ # Create random master Password for SOGo 'login as user' via proxy auth
+ RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
+ echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
+ cat < /usr/local/etc/dovecot/sogo-sso.conf
+passdb {
+ driver = static
+ args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
+}
+EOF
+else
+ rm -f /usr/local/etc/dovecot/sogo-sso.pass
+ rm -f /usr/local/etc/dovecot/sogo-sso.conf
+fi
+
# 401 is user dovecot
if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then
openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem
@@ -174,7 +193,7 @@ echo '30 3 * * * vmail /usr/local/bin/doveadm quota recalc -A' > /etc/cron.d/d
echo '* * * * * vmail /usr/local/bin/trim_logs.sh >> /dev/console 2>&1' > /etc/cron.d/trim_logs
echo '25 * * * * vmail /usr/local/bin/maildir_gc.sh >> /dev/console 2>&1' > /etc/cron.d/maildir_gc
echo '30 1 * * * root /usr/local/bin/sa-rules.sh >> /dev/console 2>&1' > /etc/cron.d/sa-rules
-echo '0 2 * * * root /usr/bin/curl http://solr:8983/solr/dovecot/update?optimize=true >> /dev/console 2>&1' > /etc/cron.d/solr-optimize
+echo '0 2 * * * root /usr/bin/curl http://solr:8983/solr/dovecot-fts/update?optimize=true >> /dev/console 2>&1' > /etc/cron.d/solr-optimize
echo '*/20 * * * * vmail /usr/local/bin/quarantine_notify.py >> /dev/console 2>&1' > /etc/cron.d/quarantine_notify
# Fix more than 1 hardlink issue
diff --git a/data/Dockerfiles/dovecot/quarantine_notify.py b/data/Dockerfiles/dovecot/quarantine_notify.py
index 0902e4aa..28a7aabe 100755
--- a/data/Dockerfiles/dovecot/quarantine_notify.py
+++ b/data/Dockerfiles/dovecot/quarantine_notify.py
@@ -123,5 +123,3 @@ for record in records:
if last_notification == 0 or (last_notification + 604800) < time_now:
print "Notifying %s about %d new items in quarantine" % (record['rcpt'], record['counter'])
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])
- else:
- break
diff --git a/data/Dockerfiles/netfilter/server.py b/data/Dockerfiles/netfilter/server.py
index f43122ea..910679c6 100644
--- a/data/Dockerfiles/netfilter/server.py
+++ b/data/Dockerfiles/netfilter/server.py
@@ -31,7 +31,8 @@ RULES[2] = '-login: Disconnected \(auth failed, .+\): user=.*, method=.+, rip=([
RULES[3] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
RULES[4] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
RULES[5] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
-#RULES[6] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
+RULES[6] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
+#RULES[7] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
bans = {}
log = {}
diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh
index 76c4035e..bf055f3a 100755
--- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh
+++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh
@@ -25,23 +25,26 @@ CONTAINER_ID=
# Todo: Better check if upgrade failed
# This can happen due to a broken sogo_view
[ -s /mysql_upgrade_loop ] && SQL_LOOP_C=$(cat /mysql_upgrade_loop)
-CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | .id")
-if [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ [^a-zA-Z0-9] ]]; then
- SQL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json' | jq -r .type)
- if [[ ${SQL_UPGRADE_RETURN} == 'warning' ]]; then
- if [ -z ${SQL_LOOP_C} ]; then
- echo 1 > /mysql_upgrade_loop
- echo "MySQL applied an upgrade, restarting PHP-FPM..."
- exit 1
- else
- rm /mysql_upgrade_loop
- echo "MySQL was not applied previously, skipping. Restart php-fpm-mailcow to retry or run mysql_upgrade manually."
- while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
- echo "Waiting for SQL to return..."
- sleep 2
- done
- fi
+until [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ ^[[:alnum:]]*$ ]]; do
+ CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" 2> /dev/null | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | .id" 2> /dev/null)
+done
+echo "MySQL @ ${CONTAINER_ID}"
+SQL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json' | jq -r .type)
+if [[ ${SQL_UPGRADE_RETURN} == 'warning' ]]; then
+ if [ -z ${SQL_LOOP_C} ]; then
+ echo 1 > /mysql_upgrade_loop
+ echo "MySQL applied an upgrade, restarting PHP-FPM..."
+ exit 1
+ else
+ rm /mysql_upgrade_loop
+ echo "MySQL was not applied previously, skipping. Restart php-fpm-mailcow to retry or run mysql_upgrade manually."
+ while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
+ echo "Waiting for SQL to return..."
+ sleep 2
+ done
fi
+else
+ echo "MySQL is up-to-date"
fi
# Trigger db init
diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh
index 6ec5cc1d..d3e9ed0c 100755
--- a/data/Dockerfiles/postfix/postfix.sh
+++ b/data/Dockerfiles/postfix/postfix.sh
@@ -104,8 +104,8 @@ query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
WHERE id IN (
SELECT relayhost FROM domain
WHERE CONCAT('@', domain) = '%s'
- OR '%s' IN (
- SELECT CONCAT('@', alias_domain) FROM alias_domain
+ OR domain IN (
+ SELECT target_domain FROM alias_domain WHERE CONCAT('@', alias_domain) = '%s'
)
)
AND active = '1'
diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile
index a2b5f7f8..87d92139 100644
--- a/data/Dockerfiles/rspamd/Dockerfile
+++ b/data/Dockerfiles/rspamd/Dockerfile
@@ -13,7 +13,6 @@ RUN apt-get update && apt-get install -y \
&& echo "deb https://rspamd.com/apt-stable/ bionic main" > /etc/apt/sources.list.d/rspamd.list \
&& apt-get update && apt-get install -y rspamd \
&& rm -rf /var/lib/apt/lists/* \
- && echo '.include $LOCAL_CONFDIR/local.d/rspamd.conf.local' > /etc/rspamd/rspamd.conf.local \
&& apt-get autoremove --purge \
&& apt-get clean \
&& mkdir -p /run/rspamd \
@@ -21,7 +20,6 @@ RUN apt-get update && apt-get install -y \
COPY settings.conf /etc/rspamd/settings.conf
COPY docker-entrypoint.sh /docker-entrypoint.sh
-COPY metadata_exporter.lua /usr/share/rspamd/lua/metadata_exporter.lua
ENTRYPOINT ["/docker-entrypoint.sh"]
diff --git a/data/Dockerfiles/sogo/Dockerfile b/data/Dockerfiles/sogo/Dockerfile
index 085f1bc2..970ec252 100644
--- a/data/Dockerfiles/sogo/Dockerfile
+++ b/data/Dockerfiles/sogo/Dockerfile
@@ -13,6 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
gettext \
gnupg \
mysql-client \
+ rsync \
supervisor \
syslog-ng \
syslog-ng-core \
@@ -52,6 +53,4 @@ RUN chmod +x /bootstrap-sogo.sh \
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
-VOLUME /usr/lib/GNUstep/SOGo/
-
RUN rm -rf /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh
index 5072a306..7f1835db 100755
--- a/data/Dockerfiles/sogo/bootstrap-sogo.sh
+++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh
@@ -83,9 +83,16 @@ EOF
done
-mkdir -p /var/lib/sogo/GNUstep/Defaults/
+if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
+ TRUST_PROXY="YES"
+else
+ TRUST_PROXY="NO"
+fi
+# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl
+RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)
# Generate plist header with timezone data
+mkdir -p /var/lib/sogo/GNUstep/Defaults/
cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist
@@ -93,6 +100,12 @@ cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist
OCSAclURL
mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl
+ SOGoIMAPServer
+ imap://${IPV4_NETWORK}.250:143/?tls=YES
+ SOGoTrustProxyAuthentication
+ ${TRUST_PROXY}
+ SOGoEncryptionKey
+ ${RAND_PASS}
OCSCacheFolderURL
mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_cache_folder
OCSEMailAlarmsFolderURL
@@ -183,4 +196,8 @@ fi
# Copy logo, if any
[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
+# Rsync web content
+echo "Syncing web content with named volume"
+rsync -a /usr/lib/GNUstep/SOGo/. /sogo_web/
+
exec gosu sogo /usr/sbin/sogod
diff --git a/data/Dockerfiles/solr/Dockerfile b/data/Dockerfiles/solr/Dockerfile
index 8f2a873e..eae8f55a 100644
--- a/data/Dockerfiles/solr/Dockerfile
+++ b/data/Dockerfiles/solr/Dockerfile
@@ -1,10 +1,13 @@
-FROM solr:7-alpine
+FROM solr:7.7-alpine
USER root
COPY docker-entrypoint.sh /
+COPY solr-config-7.7.0.xml /
+COPY solr-schema-7.7.0.xml /
+
RUN apk --no-cache add su-exec curl tzdata \
&& chmod +x /docker-entrypoint.sh \
&& sync \
- && /docker-entrypoint.sh --bootstrap
+ && bash /docker-entrypoint.sh --bootstrap
ENTRYPOINT ["/docker-entrypoint.sh"]
diff --git a/data/Dockerfiles/solr/docker-entrypoint.sh b/data/Dockerfiles/solr/docker-entrypoint.sh
old mode 100755
new mode 100644
index 430e6342..5a33620d
--- a/data/Dockerfiles/solr/docker-entrypoint.sh
+++ b/data/Dockerfiles/solr/docker-entrypoint.sh
@@ -18,192 +18,44 @@ fi
set -e
-# allow easier debugging with `docker run -e VERBOSE=yes`
-if [[ "$VERBOSE" = "yes" ]]; then
- set -x
-fi
-
# run the optional initdb
. /opt/docker-solr/scripts/run-initdb
-function solr_config() {
- curl -XPOST http://localhost:8983/solr/dovecot/schema -H 'Content-type:application/json' -d '{
- "add-field-type":{
- "name":"long",
- "class":"solr.TrieLongField"
- },
- "add-field-type":{
- "name":"text",
- "class":"solr.TextField",
- "positionIncrementGap":100,
- "indexAnalyser":{
- "tokenizer":{
- "class":"solr.StandardTokenizerFactory"
- },
- "filter":{
- "class":"solr.WordDelimiterFilterFactory",
- "generateWordParts":1,
- "generateNumberParts":1,
- "catenateWorks":1,
- "catenateNumbers":1,
- "catenateAll":0
- },
- "filter":{
- "class":"solr.LowerCaseFilterFactory"
- },
- "filter":{
- "class":"solr.KeywordMarkerFilterFactory",
- "protected":"protwords.txt"
- }
- },
- "queryAnalyzer":{
- "tokenizer":{
- "class":"solr.StandardTokenizerFactory"
- },
- "filter":{
- "synonyms":"synonyms.txt",
- "ignoreCase":true,
- "expand":true
- },
- "filter":{
- "class":"solr.LowerCaseFilterFactory"
- },
- "filter":{
- "class":"solr.WordDelimiterFilterFactory",
- "generateWordParts":1,
- "generateNumberParts":1,
- "catenateWords":0,
- "catenateNumbers":0,
- "catenateAll":0,
- "splitOnCaseChange":1
- }
- }
- },
- "add-field":{
- "name":"uid",
- "type":"long",
- "indexed":true,
- "stored":true,
- "required":true
- },
- "add-field":{
- "name":"box",
- "type":"string",
- "indexed":true,
- "stored":true,
- "required":true
- },
- "add-field":{
- "name":"user",
- "type":"string",
- "indexed":true,
- "stored":true,
- "required":true
- },
- "add-field":{
- "name":"hdr",
- "type":"text",
- "indexed":true,
- "stored":false
-
- },
- "add-field":{
- "name":"body",
- "type":"text",
- "indexed":true,
- "stored":false
- },
- "add-field":{
- "name":"from",
- "type":"text",
- "indexed":true,
- "stored":false
- },
- "add-field":{
- "name":"to",
- "type":"text",
- "indexed":true,
- "stored":false
- },
- "add-field":{
- "name":"cc",
- "type":"text",
- "indexed":true,
- "stored":false
- },
- "add-field":{
- "name":"bcc",
- "type":"text",
- "indexed":true,
- "stored":false
- },
- "add-field":{
- "name":"subject",
- "type":"text",
- "indexed":true,
- "stored":false
- }
- }'
-
- curl -XPOST http://localhost:8983/solr/dovecot/config -H 'Content-type:application/json' -d '{
- "update-requesthandler":{
- "name":"/select",
- "class":"solr.SearchHandler",
- "defaults":{
- "wt":"xml"
- }
- }
- }'
-
- curl -XPOST http://localhost:8983/solr/dovecot/config/updateHandler -d '{
- "set-property": {
- "updateHandler.autoSoftCommit.maxDocs":500,
- "updateHandler.autoSoftCommit.maxTime":120000,
- "updateHandler.autoCommit.maxDocs":200,
- "updateHandler.autoCommit.maxTime":1800000,
- "updateHandler.autoCommit.openSearcher":false
- }
- }'
-}
-
# fixing volume permission
-[[ -d /opt/solr/server/solr/dovecot/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot/data
+[[ -d /opt/solr/server/solr/dovecot-fts/data ]] && chown -R solr:solr /opt/solr/server/solr/dovecot-fts/data
if [[ "${1}" != "--bootstrap" ]]; then
sed -i '/SOLR_HEAP=/c\SOLR_HEAP="'${SOLR_HEAP:-1024}'m"' /opt/solr/bin/solr.in.sh
else
sed -i '/SOLR_HEAP=/c\SOLR_HEAP="256m"' /opt/solr/bin/solr.in.sh
fi
-# start a Solr so we can use the Schema API, but only on localhost,
-# so that clients don't see Solr until we have configured it.
-echo "Starting local Solr instance to setup configuration"
-su-exec solr start-local-solr
+if [[ "${1}" == "--bootstrap" ]]; then
+ echo "Creating initial configuration"
+ echo "Modifying default config set"
+ cp /solr-config-7.7.0.xml /opt/solr/server/solr/configsets/_default/conf/solrconfig.xml
+ cp /solr-schema-7.7.0.xml /opt/solr/server/solr/configsets/_default/conf/schema.xml
+ rm /opt/solr/server/solr/configsets/_default/conf/managed-schema
-# keep a sentinel file so we don't try to create the core a second time
-# for example when we restart a container.
-SENTINEL=/opt/docker-solr/core_created
-if [[ -f ${SENTINEL} ]]; then
- echo "skipping core creation"
-else
- echo "Creating core \"dovecot\""
- su-exec solr /opt/solr/bin/solr create -c "dovecot"
+ echo "Starting local Solr instance to setup configuration"
+ su-exec solr start-local-solr
+
+ echo "Creating core \"dovecot-fts\""
+ su-exec solr /opt/solr/bin/solr create -c "dovecot-fts"
# See https://github.com/docker-solr/docker-solr/issues/27
echo "Checking core"
while ! wget -O - 'http://localhost:8983/solr/admin/cores?action=STATUS' | grep -q instanceDir; do
echo "Could not find any cores, waiting..."
- sleep 5
+ sleep 3
done
- echo "Created core \"dovecot\""
- touch ${SENTINEL}
+
+ echo "Created core \"dovecot-fts\""
+
+ echo "Stopping local Solr"
+ su-exec solr stop-local-solr
+
+ exit 0
fi
-echo "Starting configuration"
-solr_config
-echo "Stopping local Solr"
-su-exec solr stop-local-solr
-if [[ "${1}" == "--bootstrap" ]]; then
- exit 0
-else
- exec su-exec solr solr-foreground
-fi
+exec su-exec solr solr-foreground
+
diff --git a/data/Dockerfiles/solr/solr-config-7.7.0.xml b/data/Dockerfiles/solr/solr-config-7.7.0.xml
new file mode 100644
index 00000000..3661874d
--- /dev/null
+++ b/data/Dockerfiles/solr/solr-config-7.7.0.xml
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+ 7.7.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${solr.data.dir:}
+
+
+
+
+
+
+ ${solr.ulog.dir:}
+ ${solr.ulog.numVersionBuckets:65536}
+
+
+
+
+ ${solr.autoCommit.maxTime:15000}
+ false
+
+
+
+
+ ${solr.autoSoftCommit.maxTime:-1}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ 20
+
+
+ 200
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ explicit
+ 10
+
+
+
+
+
+ _text_
+
+
+
+
+
+
diff --git a/data/Dockerfiles/solr/solr-schema-7.7.0.xml b/data/Dockerfiles/solr/solr-schema-7.7.0.xml
new file mode 100644
index 00000000..2c2e6343
--- /dev/null
+++ b/data/Dockerfiles/solr/solr-schema-7.7.0.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id
+
diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh
index 7d824236..cd9920a1 100755
--- a/data/Dockerfiles/watchdog/watchdog.sh
+++ b/data/Dockerfiles/watchdog/watchdog.sh
@@ -5,6 +5,8 @@ trap "kill 0" EXIT
# Prepare
BACKGROUND_TASKS=()
+echo "Waiting for containers to settle..."
+sleep 10
if [[ "${USE_WATCHDOG}" =~ ^([nN][oO]|[nN])+$ ]]; then
echo -e "$(date) - USE_WATCHDOG=n, skipping watchdog..."
@@ -37,7 +39,7 @@ progress() {
log_msg() {
if [[ ${2} != "no_redis" ]]; then
redis-cli -h redis LPUSH WATCHDOG_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${1}" | \
- tr '%&;$"_[]{}-\r\n' ' ')\"}" > /dev/null
+ tr '\r\n%&;$"_[]{}-' ' ')\"}" > /dev/null
fi
echo $(date) $(printf '%s\n' "${1}")
}
@@ -115,7 +117,7 @@ nginx_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/nginx-mailcow
+ touch /tmp/nginx-mailcow; echo "$(tail -50 /tmp/nginx-mailcow)" > /tmp/nginx-mailcow
host_ip=$(get_container_ip nginx-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_http -4 -H ${host_ip} -u / -p 8081 2>> /tmp/nginx-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -140,7 +142,7 @@ unbound_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/unbound-mailcow
+ touch /tmp/unbound-mailcow; echo "$(tail -50 /tmp/unbound-mailcow)" > /tmp/unbound-mailcow
host_ip=$(get_container_ip unbound-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_dns -s ${host_ip} -H stackoverflow.com 2>> /tmp/unbound-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -172,7 +174,7 @@ mysql_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/mysql-mailcow
+ touch /tmp/mysql-mailcow; echo "$(tail -50 /tmp/mysql-mailcow)" > /tmp/mysql-mailcow
host_ip=$(get_container_ip mysql-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_mysql -s /var/run/mysqld/mysqld.sock -u ${DBUSER} -p ${DBPASS} -d ${DBNAME} 2>> /tmp/mysql-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -198,7 +200,7 @@ sogo_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/sogo-mailcow
+ touch /tmp/sogo-mailcow; echo "$(tail -50 /tmp/sogo-mailcow)" > /tmp/sogo-mailcow
host_ip=$(get_container_ip sogo-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_http -4 -H ${host_ip} -u /SOGo.index/ -p 20000 -R "SOGo\.MainUI" 2>> /tmp/sogo-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -223,7 +225,7 @@ postfix_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/postfix-mailcow
+ touch /tmp/postfix-mailcow; echo "$(tail -50 /tmp/postfix-mailcow)" > /tmp/postfix-mailcow
host_ip=$(get_container_ip postfix-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 589 -f "watchdog@invalid" -C "RCPT TO:null@localhost" -C DATA -C . -R 250 2>> /tmp/postfix-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -249,7 +251,7 @@ clamd_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/clamd-mailcow
+ touch /tmp/clamd-mailcow; echo "$(tail -50 /tmp/clamd-mailcow)" > /tmp/clamd-mailcow
host_ip=$(get_container_ip clamd-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_clamd -4 -H ${host_ip} 2>> /tmp/clamd-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -274,7 +276,7 @@ dovecot_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/dovecot-mailcow
+ touch /tmp/dovecot-mailcow; echo "$(tail -50 /tmp/dovecot-mailcow)" > /tmp/dovecot-mailcow
host_ip=$(get_container_ip dovecot-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_smtp -4 -H ${host_ip} -p 24 -f "watchdog@invalid" -C "RCPT TO:" -L -R "User doesn't exist" 2>> /tmp/dovecot-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -303,7 +305,7 @@ phpfpm_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/php-fpm-mailcow
+ touch /tmp/php-fpm-mailcow; echo "$(tail -50 /tmp/php-fpm-mailcow)" > /tmp/php-fpm-mailcow
host_ip=$(get_container_ip php-fpm-mailcow)
err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_tcp -H ${host_ip} -p 9001 2>> /tmp/php-fpm-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
@@ -350,6 +352,38 @@ ratelimit_checks() {
return 1
}
+acme_checks() {
+ err_count=0
+ diff_c=0
+ THRESHOLD=1
+ ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME)
+ if [[ -z "${ACME_LOG_STATUS}" ]]; then
+ redis-cli -h redis SET ACME_FAIL_TIME 0
+ ACME_LOG_STATUS=0
+ fi
+ # Reduce error count by 2 after restarting an unhealthy container
+ trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
+ while [ ${err_count} -lt ${THRESHOLD} ]; do
+ err_c_cur=${err_count}
+ ACME_LOG_STATUS_PREV=${ACME_LOG_STATUS}
+ ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME)
+ if [[ ${ACME_LOG_STATUS_PREV} != ${ACME_LOG_STATUS} ]]; then
+ err_count=$(( ${err_count} + 1 ))
+ fi
+ [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
+ [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
+ progress "ACME" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
+ if [[ $? == 10 ]]; then
+ diff_c=0
+ sleep 1
+ else
+ diff_c=0
+ sleep $(( ( RANDOM % 30 ) + 10 ))
+ fi
+ done
+ return 1
+}
+
ipv6nat_checks() {
err_count=0
diff_c=0
@@ -358,10 +392,11 @@ ipv6nat_checks() {
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
err_c_cur=${err_count}
- IPV6NAT_CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\")) | .id")
+ CONTAINERS=$(curl --silent --insecure https://dockerapi/containers/json)
+ IPV6NAT_CONTAINER_ID=$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\")) | .id")
if [[ ! -z ${IPV6NAT_CONTAINER_ID} ]]; then
- LATEST_STARTED="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
- LATEST_IPV6NAT="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
+ LATEST_STARTED="$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
+ LATEST_IPV6NAT="$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
DIFFERENCE_START_TIME=$(expr ${LATEST_IPV6NAT} - ${LATEST_STARTED} 2>/dev/null)
if [[ "${DIFFERENCE_START_TIME}" -lt 30 ]]; then
err_count=$(( ${err_count} + 1 ))
@@ -375,12 +410,13 @@ ipv6nat_checks() {
sleep 1
else
diff_c=0
- sleep 3600
+ sleep 300
fi
done
return 1
}
+
rspamd_checks() {
err_count=0
diff_c=0
@@ -388,15 +424,14 @@ rspamd_checks() {
# Reduce error count by 2 after restarting an unhealthy container
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
while [ ${err_count} -lt ${THRESHOLD} ]; do
- cat /dev/null > /tmp/rspamd-mailcow
+ touch /tmp/rspamd-mailcow; echo "$(tail -50 /tmp/rspamd-mailcow)" > /tmp/rspamd-mailcow
host_ip=$(get_container_ip rspamd-mailcow)
err_c_cur=${err_count}
- SCORE=$(/usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/scan -d '
-To: null@localhost
+ SCORE=$(echo 'To: null@localhost
From: watchdog@localhost
Empty
-' | jq -rc .required_score)
+' | usr/bin/curl -s --data-binary @- --unix-socket /var/lib/rspamd/rspamd.sock http://rspamd/scan | jq -rc .required_score)
if [[ ${SCORE} != "9999" ]]; then
echo "Rspamd settings check failed" 2>> /tmp/rspamd-mailcow 1>&2
err_count=$(( ${err_count} + 1))
@@ -517,11 +552,21 @@ done
) &
BACKGROUND_TASKS+=($!)
+(
+while true; do
+ if ! acme_checks; then
+ log_msg "ACME client hit error limit"
+ echo acme-tiny > /tmp/com_pipe
+ fi
+done
+) &
+BACKGROUND_TASKS+=($!)
+
(
while true; do
if ! ipv6nat_checks; then
- log_msg "IPv6 NAT warning: ipv6nat container was not started at least 30s after siblings (not an error)"
- echo ipv6nat > /tmp/com_pipe
+ log_msg "IPv6 NAT warning: ipv6nat-mailcow container was not started at least 30s after siblings (not an error)"
+ echo ipv6nat-mailcow > /tmp/com_pipe
fi
done
) &
@@ -561,10 +606,16 @@ while true; do
CONTAINER_ID=
HAS_INITDB=
read com_pipe_answer /dev/null; then
+ echo 'auth_request /sogo-auth-verify;
+auth_request_set $user $upstream_http_x_user;
+auth_request_set $auth $upstream_http_x_auth;
+auth_request_set $auth_type $upstream_http_x_auth_type;
+proxy_set_header x-webobjects-remote-user "$user";
+proxy_set_header Authorization "$auth";
+proxy_set_header x-webobjects-auth-type "$auth_type";
+'
+fi
diff --git a/data/conf/phpfpm/sogo-sso/.gitkeep b/data/conf/phpfpm/sogo-sso/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/data/conf/postfix/local_transport b/data/conf/postfix/local_transport
new file mode 100644
index 00000000..5d10028c
--- /dev/null
+++ b/data/conf/postfix/local_transport
@@ -0,0 +1 @@
+/localhost$/ local:
diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf
index 83a252d8..88d905e7 100644
--- a/data/conf/postfix/main.cf
+++ b/data/conf/postfix/main.cf
@@ -94,12 +94,16 @@ smtpd_tls_dh1024_param_file = /etc/ssl/mail/dhparams.pem
smtpd_tls_eecdh_grade = auto
smtpd_tls_exclude_ciphers = ECDHE-RSA-RC4-SHA, RC4, aNULL, DES-CBC3-SHA, ECDHE-RSA-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA
smtpd_tls_loglevel = 1
-smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+
+smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3
-lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3
-lmtp_tls_protocols = !SSLv2, !SSLv2, !SSLv3
-smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+
+lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
+lmtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
+
+smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3
+
smtpd_tls_security_level = may
tls_preempt_cipherlist = yes
tls_ssl_options = NO_COMPRESSION
@@ -134,5 +138,5 @@ smtp_sasl_mechanism_filter = plain, login
smtp_tls_policy_maps=proxy:mysql:/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf
smtp_header_checks = pcre:/opt/postfix/conf/anonymize_headers.pcre
mail_name = Postcow
-transport_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
+transport_maps = pcre:/opt/postfix/conf/local_transport, proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
smtp_sasl_auth_soft_bounce = no
diff --git a/data/conf/postfix/master.cf b/data/conf/postfix/master.cf
index 79642f6d..fcc99717 100644
--- a/data/conf/postfix/master.cf
+++ b/data/conf/postfix/master.cf
@@ -2,14 +2,17 @@ smtp inet n - n - 1 postscreen
smtpd pass - - n - - smtpd
-o smtpd_helo_restrictions=permit_mynetworks,reject_non_fqdn_helo_hostname
-o smtpd_sasl_auth_enable=no
+ -o smtpd_sender_restrictions=permit_mynetworks,reject_unlisted_sender,reject_unknown_sender_domain
smtps inet n - n - - smtpd
-o smtpd_tls_wrappermode=yes
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
+ -o smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
-o tls_preempt_cipherlist=yes
submission inet n - n - - smtpd
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_enforce_tls=yes
-o smtpd_tls_security_level=encrypt
+ -o smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
-o tls_preempt_cipherlist=yes
588 inet n - n - - smtpd
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
diff --git a/data/conf/rspamd/dynmaps/settings.php b/data/conf/rspamd/dynmaps/settings.php
index 8bdfb49d..a2be8ceb 100644
--- a/data/conf/rspamd/dynmaps/settings.php
+++ b/data/conf/rspamd/dynmaps/settings.php
@@ -6,6 +6,8 @@ then any of these will trigger the rule. If a rule is triggered then no more rul
*/
header('Content-Type: text/plain');
require_once "vars.inc.php";
+// Getting headers sent by the client.
+//$headers = apache_request_headers();
ini_set('error_reporting', 0);
@@ -25,6 +27,23 @@ catch (PDOException $e) {
exit;
}
+// Check if db changed and return header
+/*$stmt = $pdo->prepare("SELECT UNIX_TIMESTAMP(UPDATE_TIME) AS `db_update_time` FROM information_schema.tables
+ WHERE `TABLE_NAME` = 'filterconf'
+ AND TABLE_SCHEMA = :dbname;");
+$stmt->execute(array(
+ ':dbname' => $database_name
+));
+$db_update_time = $stmt->fetch(PDO::FETCH_ASSOC)['db_update_time'];
+
+if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $db_update_time)) {
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 304);
+ exit;
+} else {
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 200);
+}
+*/
+
function parse_email($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
@@ -107,8 +126,8 @@ function ucl_rcpts($object, $type) {
settings {
watchdog {
priority = 10;
- rcpt = "/null@localhost/i";
- from = "/watchdog@localhost/i";
+ rcpt_mime = "/null@localhost/i";
+ from_mime = "/watchdog@localhost/i";
apply "default" {
actions {
reject = 9999.0;
@@ -179,7 +198,7 @@ foreach (wl_by_sogo() as $user => $contacts) {
}
?>
apply "default" {
- SOGO_CONTACT = -999.0;
+ SOGO_CONTACT = -99.0;
}
symbols [
"SOGO_CONTACT"
@@ -199,12 +218,13 @@ while ($row = array_shift($rows)) {
?>
whitelist_=$username_sane;?> {
prepare("SELECT `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'whitelist_from'");
$stmt->execute(array(':object' => $row['object']));
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
- while ($item = array_shift($list_items)) {
+ foreach ($list_items as $item) {
?>
from = "/='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
{
+ whitelist_mime_=$username_sane;?> {
prepare("SELECT `value` FROM `filterconf`
- WHERE `object`= :object
- AND `option` = 'whitelist_from'");
- $stmt->execute(array(':object' => $row['object']));
- $list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($list_items as $item) {
?>
- header = {
+ from_mime = "/='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
- "From" = "/(=implode('|', $header_from);?>)/i";
- }
-
priority = 5;
@@ -297,13 +306,13 @@ while ($row = array_shift($rows)) {
?>
blacklist_=$username_sane;?> {
prepare("SELECT `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'blacklist_from'");
$stmt->execute(array(':object' => $row['object']));
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
- while ($item = array_shift($list_items)) {
+ foreach ($list_items as $item) {
?>
from = "/='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
{
prepare("SELECT `value` FROM `filterconf`
- WHERE `object`= :object
- AND `option` = 'blacklist_from'");
- $stmt->execute(array(':object' => $row['object']));
- $list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($list_items as $item) {
?>
- header = {
+ from_mime = "/='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
- "From" = "/(=implode('|', $header_from);?>)/i";
- }
-
priority = 5;
@@ -425,4 +423,4 @@ while ($row = array_shift($rows)) {
-}
\ No newline at end of file
+}
diff --git a/data/conf/rspamd/local.d/rspamd.conf.local b/data/conf/rspamd/local.d/rspamd.conf.local
deleted file mode 100644
index 0662c47d..00000000
--- a/data/conf/rspamd/local.d/rspamd.conf.local
+++ /dev/null
@@ -1,16 +0,0 @@
-# rspamd.conf.local
-
-worker "fuzzy" {
- # Socket to listen on (UDP and TCP from rspamd 1.3)
- bind_socket = "*:11445";
- allow_update = ["127.0.0.1", "::1"];
- # Number of processes to serve this storage (useful for read scaling)
- count = 2;
- # Backend ("sqlite" or "redis" - default "sqlite")
- backend = "redis";
- # Hashes storage time (3 months)
- expire = 90d;
- # Synchronize updates to the storage each minute
- sync = 1min;
-}
-
diff --git a/data/conf/rspamd/meta_exporter/pipe.php b/data/conf/rspamd/meta_exporter/pipe.php
index 3e29d207..692a0c2e 100644
--- a/data/conf/rspamd/meta_exporter/pipe.php
+++ b/data/conf/rspamd/meta_exporter/pipe.php
@@ -84,6 +84,9 @@ $rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach (json_decode($rcpts, true) as $rcpt) {
+ // Remove tag
+ $rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
+
// Break rcpt into local part and domain part
$parsed_rcpt = parse_email($rcpt);
diff --git a/data/conf/rspamd/override.d/worker-fuzzy.inc b/data/conf/rspamd/override.d/worker-fuzzy.inc
new file mode 100644
index 00000000..09b39c93
--- /dev/null
+++ b/data/conf/rspamd/override.d/worker-fuzzy.inc
@@ -0,0 +1,12 @@
+# Socket to listen on (UDP and TCP from rspamd 1.3)
+bind_socket = "*:11445";
+allow_update = ["127.0.0.1", "::1"];
+# Number of processes to serve this storage (useful for read scaling)
+count = 2;
+# Backend ("sqlite" or "redis" - default "sqlite")
+backend = "redis";
+# Hashes storage time (3 months)
+expire = 90d;
+# Synchronize updates to the storage each minute
+sync = 1min;
+
diff --git a/data/conf/rspamd/override.d/worker-proxy.inc b/data/conf/rspamd/override.d/worker-proxy.inc
index 0df926a7..92527f2b 100644
--- a/data/conf/rspamd/override.d/worker-proxy.inc
+++ b/data/conf/rspamd/override.d/worker-proxy.inc
@@ -1,6 +1,6 @@
bind_socket = "rspamd:9900";
milter = true;
-upstream {
+upstream "local" {
name = "localhost";
default = true;
hosts = "rspamd:11333"
diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf
index aa1a86ec..f9e9e077 100644
--- a/data/conf/sogo/sogo.conf
+++ b/data/conf/sogo/sogo.conf
@@ -26,7 +26,6 @@
// (domain3.tld, domain2.tld)
// );
- SOGoIMAPServer = "imap://dovecot:143/?tls=YES";
SOGoSieveServer = "sieve://dovecot:4190/?tls=YES";
SOGoSMTPServer = "postfix:588";
WOPort = "0.0.0.0:20000";
diff --git a/data/web/admin.php b/data/web/admin.php
index 6ca89e97..6826fec5 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -746,6 +746,7 @@ $tfa_data = get_tfa();
+
'
- );
- });
- }
- else {
- $( "#qid_detail_atts" ).text('-');
- }
+ $('body').on('click', '.show_qid_info', function (e) {
+ e.preventDefault();
+ var qitem = $(this).data('item');
+ var qError = $("#qid_error");
+
+ $('#qidDetailModal').modal('show');
+ qError.hide();
+
+ $.ajax({
+ url: '/inc/ajax/qitem_details.php',
+ data: { id: qitem },
+ dataType: 'json',
+ success: function(data){
+ if (typeof data.error !== 'undefined') {
+ qError.text(data.error);
+ qError.show();
}
- });
- })
- }
+ $('[data-id="qitems_single"]').each(function(index) {
+ $(this).attr("data-item", qitem);
+ });
+
+ $('#qid_detail_subj').text(data.subject);
+ $('#qid_detail_text').text(data.text_plain);
+ $('#qid_detail_text_from_html').text(data.text_html);
+
+ if (typeof data.attachments !== 'undefined') {
+ qAtts = $("#qid_detail_atts");
+ qAtts.text('');
+ $.each(data.attachments, function(index, value) {
+ qAtts.append(
+ '' + value[0] + ' (' + value[1] + ')' +
+ ' - ' + lang.check_hash + '
'
+ );
+ });
+ }
+ else {
+ qAtts.text('-');
+ }
+ }
+ });
+ });
+
// Initial table drawings
draw_quarantine_table();
});
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index 81aa0b67..4d0352d0 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -19,6 +19,7 @@ $lang['footer']['cancel'] = 'Abbrechen';
$lang['footer']['hibp_nok'] = 'Übereinstimmung gefunden! Dieses Passwort ist potentiell gefährlich!';
$lang['footer']['hibp_ok'] = 'Keine Übereinstimmung gefunden.';
+$lang['danger']['unlimited_quota_acl'] = "Unendliche Quota untersagt durch ACL";
$lang['danger']['mysql_error'] = "MySQL Fehler: %s";
$lang['danger']['redis_error'] = "Redis Fehler: %s";
$lang['danger']['unknown_tfa_method'] = "Unbekannte TFA Methode";
@@ -38,7 +39,7 @@ $lang['danger']['rspamd_ui_pw_length'] = "Rspamd UI Passwort muss mindestens 6 Z
$lang['success']['rspamd_ui_pw_set'] = "Rspamd UI Passwort wurde gesetzt";
$lang['success']['queue_command_success'] = "Queue-Aufgabe erfolgreich ausgeführt";
$lang['danger']['unknown'] = "Ein unbekannter Fehler trat auf";
-$lang['danger']['malformed_username'] = "Benutzername hat falsches Format";
+$lang['danger']['malformed_username'] = "Benutzername hat ein falsches Format";
$lang['info']['awaiting_tfa_confirmation'] = "Warte auf TFA Verifizierung";
$lang['success']['logged_in_as'] = "Eingeloggt als %s";
$lang['danger']['login_failed'] = "Anmeldung fehlgeschlagen";
@@ -49,7 +50,7 @@ $lang['danger']['sieve_error'] = "Sieve Parser: %s";
$lang['danger']['value_missing'] = "Bitte alle Felder ausfüllen";
$lang['danger']['filter_type'] = "Falscher Filtertyp";
$lang['danger']['domain_cannot_match_hostname'] = "Domain darf nicht dem Hostnamen entsprechen";
-$lang['warning']['domain_added_sogo_failed'] = "Domain wurde hinzugefügt aber SOGo konnte nicht neugestartet werden";
+$lang['warning']['domain_added_sogo_failed'] = "Domain wurde hinzugefügt, aber SOGo konnte nicht neugestartet werden";
$lang['danger']['rl_timeframe'] = "Ratelimit Zeitraum ist inkorrekt";
$lang['success']['rl_saved'] = "Ratelimit für Objekt %s wurde gesetzt";
$lang['success']['acl_saved'] = "ACL für Objekt %s wurde gesetzt";
@@ -87,7 +88,7 @@ $lang['success']['item_deleted'] = "Objekt %s wurde entfernt";
$lang['danger']['alias_empty'] = 'Alias-Adresse darf nicht leer sein';
$lang['danger']['goto_empty'] = 'Ziel-Adresse darf nicht leer sein';
$lang['danger']['policy_list_from_exists'] = 'Ein Eintrag mit diesem Wert existiert bereits';
-$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ungültiges Format';
+$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ein ungültiges Format';
$lang['danger']['alias_invalid'] = 'Alias-Adresse %s ist ungültig';
$lang['danger']['goto_invalid'] = 'Ziel-Adresse %s ist ungültig';
$lang['danger']['last_key'] = 'Letzter Key kann nicht gelöscht werden';
@@ -104,7 +105,7 @@ $lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden ges
$lang['success']['mailbox_modified'] = 'Änderungen an Mailbox %s wurden gespeichert';
$lang['success']['resource_modified'] = "Änderungen an Ressource %s wurden gespeichert";
$lang['success']['object_modified'] = "Änderungen an Objekt %s wurden gespeichert";
-$lang['success']['f2b_modified'] = "Änderungen an Fail2ban Parametern wurden gespeichert";
+$lang['success']['f2b_modified'] = "Änderungen an Fail2ban-Parametern wurden gespeichert";
$lang['danger']['targetd_not_found'] = 'Ziel-Domain %s nicht gefunden';
$lang['success']['aliasd_added'] = 'Alias-Domain %s wurde angelegt';
$lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden gespeichert';
@@ -139,8 +140,8 @@ $lang['success']['alias_domain_removed'] = 'Alias-Domain %s wurde entfernt';
$lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt';
$lang['success']['admin_removed'] = 'Administrator %s wurde entfernt';
$lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt';
-$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
-$lang['success']['sogo_profile_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
+$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurde zurückgesetzt";
+$lang['success']['sogo_profile_reset'] = "ActiveSync Gerät des Benutzers %s wurde zurückgesetzt";
$lang['success']['resource_removed'] = 'Ressource %s wurde entfernt';
$lang['warning']['cannot_delete_self'] = 'Kann derzeit eingeloggten Benutzer nicht entfernen';
$lang['warning']['no_active_admin'] = 'Kann letzten aktiven Administrator nicht deaktivieren';
@@ -171,7 +172,7 @@ $lang['user']['new_password_description'] = 'Mindestanforderung: 6 Zeichen lang,
$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
$lang['user']['alias'] = 'Alias';
$lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen';
-$lang['user']['shared_aliases_desc'] = 'Geteilte Alias-Adressen werden nicht bei benutzerdefinierten Einstellungen wie die des Spam-Filters oder der Verschlüsselungsrichtlinie berücksichtigt. Entsprechende Spam-Filter können lediglich von einem Administrator vorgenommen werden.';
+$lang['user']['shared_aliases_desc'] = 'Geteilte Alias-Adressen werden nicht bei benutzerdefinierten Einstellungen, wie die des Spam-Filters oder der Verschlüsselungsrichtlinie, berücksichtigt. Entsprechende Spam-Filter können lediglich von einem Administrator vorgenommen werden.';
$lang['user']['direct_aliases'] = 'Direkte Alias-Adressen';
$lang['user']['direct_aliases_desc'] = 'Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.';
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
@@ -200,7 +201,7 @@ $lang['user']['spamfilter_bl_desc'] = 'Für E-Mail-Adressen, die vom Spamfilter
$lang['user']['spamfilter_table_rule'] = 'Regel';
$lang['user']['spamfilter_table_action'] = 'Aktion';
$lang['user']['spamfilter_table_empty'] = 'Keine Einträge vorhanden';
-$lang['user']['spamfilter_table_remove'] = 'entfernen';
+$lang['user']['spamfilter_table_remove'] = 'Entfernen';
$lang['user']['spamfilter_table_add'] = 'Eintrag hinzufügen';
$lang['user']['spamfilter_behavior'] = 'Bewertung';
$lang['user']['spamfilter_green'] = 'Grün: Die Nachricht ist kein Spam';
@@ -247,7 +248,7 @@ $lang['user']['edit'] = 'Bearbeiten';
$lang['user']['remove'] = 'Entfernen';
$lang['user']['create_syncjob'] = 'Neuen Sync-Job erstellen';
-$lang['start']['mailcow_apps_detail'] = 'Verwenden Sie mailcow Apps, um E-Mails abzurufen, Kalender- und Kontakte zu verwalten und vieles mehr.';
+$lang['start']['mailcow_apps_detail'] = 'Verwenden Sie mailcow Apps, um E-Mails abzurufen, Kalender und Kontakte zu verwalten und vieles mehr.';
$lang['start']['mailcow_panel_detail'] = 'Domain-Administratoren erstellen, verändern oder löschen Mailboxen, verwalten die Domäne und sehen sonstige Einstellungen ein.
Als Mailbox-Benutzer erstellen Sie hier zeitlich limitierte Aliasse, ändern das Verhalten des Spamfilters, setzen ein neues Passwort und vieles mehr.';
$lang['start']['imap_smtp_server_auth_info'] = 'Bitte verwenden Sie Ihre vollständige E-Mail-Adresse sowie das PLAIN-Authentifizierungsverfahren.
@@ -390,7 +391,7 @@ $lang['add']['comment_info'] = 'Ein privater Kommentar ist für den Benutzer nic
$lang['acl']['spam_alias'] = 'Temporäre E-Mail Aliasse';
$lang['acl']['tls_policy'] = 'Verschlüsselungsrichtlinie';
-$lang['acl']['spam_score'] = 'Spam Bewertung';
+$lang['acl']['spam_score'] = 'Spam-Bewertung';
$lang['acl']['spam_policy'] = 'Blacklist/Whitelist';
$lang['acl']['delimiter_action'] = 'Delimiter Aktionen (tags)';
$lang['acl']['syncjobs'] = 'Sync Jobs';
@@ -405,6 +406,7 @@ $lang['acl']['bcc_maps'] = 'BCC Maps';
$lang['acl']['filters'] = 'Filter';
$lang['acl']['ratelimit'] = 'Rate limit';
$lang['acl']['recipient_maps'] = 'Empfängerumschreibungen';
+$lang['acl']['unlimited_quota'] = 'Unendliche Quota für Mailboxen';
$lang['acl']['prohibited'] = 'Untersagt durch Richtlinie';
$lang['mailbox']['quarantine_notification'] = 'Quarantäne-Benachrichtigung';
@@ -466,7 +468,7 @@ $lang['add']['select'] = 'Bitte auswählen';
$lang['add']['target_domain'] = 'Ziel-Domain';
$lang['add']['kind'] = 'Art';
$lang['add']['mailbox_username'] = 'Benutzername (linker Teil der E-Mail-Adresse)';
-$lang['add']['full_name'] = 'Vor- und Zuname';
+$lang['add']['full_name'] = 'Vor- und Nachname';
$lang['add']['quota_mb'] = 'Speicherplatz (MiB)';
$lang['add']['select_domain'] = 'Bitte zuerst eine Domain auswählen';
$lang['add']['password'] = 'Passwort';
@@ -526,12 +528,12 @@ $lang['admin']['import'] = 'Importieren';
$lang['admin']['duplicate'] = 'Duplizieren';
$lang['admin']['import_private_key'] = 'Private Key importieren';
$lang['admin']['duplicate_dkim'] = 'DKIM duplizieren';
-$lang['admin']['f2b_parameters'] = 'Fail2ban Parameter';
-$lang['admin']['f2b_ban_time'] = 'Banzeit (s)';
+$lang['admin']['f2b_parameters'] = 'Fail2ban-Parameter';
+$lang['admin']['f2b_ban_time'] = 'Bannzeit (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. Versuche';
$lang['admin']['f2b_retry_window'] = 'Wiederholungen im Zeitraum von (s)';
-$lang['admin']['f2b_netban_ipv4'] = 'Netzbereich für IPv4 Bans (8-32)';
-$lang['admin']['f2b_netban_ipv6'] = 'Netzbereich für IPv6 Bans (8-128)';
+$lang['admin']['f2b_netban_ipv4'] = 'Netzbereich für IPv4-Bans (8-32)';
+$lang['admin']['f2b_netban_ipv6'] = 'Netzbereich für IPv6-Bans (8-128)';
$lang['admin']['f2b_whitelist'] = 'Whitelist für Netzwerke und Hosts';
$lang['admin']['r_inactive'] = 'Inaktive Restriktionen';
$lang['admin']['r_active'] = 'Aktive Restriktionen';
@@ -607,11 +609,11 @@ $lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den
$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.';
$lang['admin']['relayhosts_hint'] = 'Erstellen Sie senderabhängige Transporte, um diese im Einstellungsdialog einer Domain auszuwählen.
Der Transporttyp lautet immer "smtp:". Benutzereinstellungen bezüglich Verschlüsselungsrichtlinie werden beim Transport berücksichtigt.';
-$lang['admin']['transports_hint'] = 'Transport Maps überwiegen senderabhängige Transport Maps und ignorieren die individuellen Einstellungen eines Benutzers bezüglich Verschlüsselungsrichtlinie, da der Absender bei Ermittlung der Transportregel nicht berücksichtigt wird.
- Der Transport erfolgt immer via "smtp:".
- Ein Eintrag in der TLS Policy Map kann eine Verschlüsselung erzwingen.
- Die Authentifizierung wird anhand des Host Parameters ermittelt, hierbei würde bei einem beispielhaften Next Hop "[host]:25" immer zuerst "host" abfragt und erst im Anschluss "[host]:25".
- Dieses Verhalten schließt die gleichzeitige Verwendung von Einträgen der Art "host" sowie "[host]:25" aus.';
+$lang['admin']['transports_hint'] = '→ Transport Maps überwiegen senderabhängige Transport Maps.
+→ Transport Maps ignorieren Mailbox-Einstellungen für ausgehende Verschlüsselung. Eine serverweite TLS-Richtlinie wird jedoch angewendet.
+→ Der Transport erfolgt immer via "smtp:".
+→ Adressen, die mit "/localhost$/" übereinstimmen, werden immer via "local:" transportiert, daher sind sie von einer Zieldefinition "*" ausgeschlossen.
+→ Die Authentifizierung wird anhand des "Next hop" Parameters ermittelt. Hierbei würde bei einem beispielhaften Wert "[host]:25" immer zuerst "host" abfragt und erst im Anschluss "[host]:25". Dieses Verhalten schließt die gleichzeitige Verwendung von Einträgen der Art "host" sowie "[host]:25" aus.';
$lang['admin']['add_relayhost_hint'] = 'Bitte beachten Sie, dass Anmeldedaten unverschlüsselt gespeichert werden.
Angelegte Transporte dieser Art sind senderabhängig und müssen erst einer Domain zugewiesen werden, bevor sie als Transport verwendet werden.
Diese Einstellungen entsprechen demach nicht dem "relayhost" Parameter in Postfix.';
@@ -769,8 +771,8 @@ $lang['mailbox']['bcc_type'] = "BCC Typ";
$lang['mailbox']['bcc_sender_map'] = "Senderabhängig";
$lang['mailbox']['bcc_rcpt_map'] = "Empfängerabhängig";
$lang['mailbox']['bcc_local_dest'] = "Lokales Ziel";
-$lang['mailbox']['bcc_destinations'] = "BCC Ziel";
-$lang['mailbox']['bcc_destination'] = "BCC Ziel";
+$lang['mailbox']['bcc_destinations'] = "BCC-Ziel";
+$lang['mailbox']['bcc_destination'] = "BCC-Ziel";
$lang['edit']['bcc_dest_format'] = 'BCC-Ziel muss eine gültige E-Mail-Adresse sein.';
$lang['mailbox']['bcc'] = "BCC";
@@ -809,7 +811,7 @@ $lang['oauth2']['authorize_app'] = 'Anwendung authorisieren';
$lang['oauth2']['deny'] = 'Ablehnen';
$lang['oauth2']['access_denied'] = 'Bitte als Mailbox-Nutzer einloggen, um den Zugriff via OAuth2 zu erlauben.';
-$lang['admin']['sys_mails'] = 'System E-Mails';
+$lang['admin']['sys_mails'] = 'System-E-Mails';
$lang['admin']['subject'] = 'Betreff';
$lang['admin']['from'] = 'Absender';
$lang['admin']['include_exclude'] = 'Ein- und Ausschlüsse';
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index a8a276e0..3efe810e 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -20,6 +20,7 @@ $lang['footer']['cancel'] = 'Cancel';
$lang['footer']['hibp_nok'] = 'Matched! This is a potentially dangerous password!';
$lang['footer']['hibp_ok'] = 'No match found.';
+$lang['danger']['unlimited_quota_acl'] = "Unlimited quota prohibited by ACL";
$lang['danger']['mysql_error'] = "MySQL error: %s";
$lang['danger']['redis_error'] = "Redis error: %s";
$lang['danger']['unknown_tfa_method'] = "Unknown TFA method";
@@ -418,6 +419,7 @@ $lang['acl']['bcc_maps'] = 'BCC maps';
$lang['acl']['filters'] = 'Filters';
$lang['acl']['ratelimit'] = 'Rate limit';
$lang['acl']['recipient_maps'] = 'Recipient maps';
+$lang['acl']['unlimited_quota'] = 'Unlimited quota for mailboxes';
$lang['acl']['prohibited'] = 'Prohibited by ACL';
$lang['mailbox']['quarantine_notification'] = 'Quarantine notifications';
@@ -631,9 +633,11 @@ $lang['admin']['forwarding_hosts_hint'] = 'Incoming messages are unconditionally
$lang['admin']['forwarding_hosts_add_hint'] = 'You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).';
$lang['admin']['relayhosts_hint'] = 'Define sender-dependent transports to be able to select them in a domains configuration dialog.
The transport service is always "smtp:". A users individual outbound TLS policy setting is taken into account.';
-$lang['admin']['transports_hint'] = 'A transport map entry overrules a sender-dependent transport map.
-Outbound TLS policy settings per-user are ignored and can only be enfored by TLS policy map entries. The transport service is always "smtp:".
-To determine credentials for an exemplary next hop "[host]:25", Postfix always queries for "nexthop" before searching for "[nexthop]:25". This behavior makes it impossible to use "nexthop" and "[nexthop]:25" at the same time.';
+$lang['admin']['transports_hint'] = '→ A transport map entry overrules a sender-dependent transport map.
+→ Outbound TLS policy settings per-user are ignored and can only be enfored by TLS policy map entries.
+→ The transport service for defined transports is always "smtp:".
+→ Adresses matching "/localhost$/" will always be transported via "local:", therefore a "*" destination will not apply to those addresses.
+→ To determine credentials for an exemplary next hop "[host]:25", Postfix always queries for "host" before searching for "[host]:25". This behavior makes it impossible to use "host" and "[host]:25" at the same time.';
$lang['admin']['add_relayhost_hint'] = 'Please be aware that authentication data, if any, will be stored as plain text.';
$lang['admin']['add_transports_hint'] = 'Please be aware that authentication data, if any, will be stored as plain text.';
$lang['admin']['host'] = 'Host';
diff --git a/data/web/mailbox.php b/data/web/mailbox.php
index 96c2e16d..392e9adf 100644
--- a/data/web/mailbox.php
+++ b/data/web/mailbox.php
@@ -348,6 +348,11 @@ $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? 'true' : 'false';
echo "var role = '". $role . "';\n";
echo "var is_dual = " . $is_dual . ";\n";
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
+$ALLOW_ADMIN_EMAIL_LOGIN = (preg_match(
+ "/^([yY][eE][sS]|[yY])+$/",
+ $_ENV["ALLOW_ADMIN_EMAIL_LOGIN"]
+)) ? "true" : "false";
+echo "var ALLOW_ADMIN_EMAIL_LOGIN = " . $ALLOW_ADMIN_EMAIL_LOGIN . ";\n";
?>
=$lang['tfa']['scan_qr_code'];?>
-
+
=$lang['tfa']['enter_qr_code'];?>:
=$totp_secret;?>
diff --git a/data/web/modals/mailbox.php b/data/web/modals/mailbox.php
index c12df381..aeb88cca 100644
--- a/data/web/modals/mailbox.php
+++ b/data/web/modals/mailbox.php
@@ -43,8 +43,8 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
max. - MiB
-
- min. 1
+
+ 0 = ∞
@@ -785,24 +785,3 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
-
diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php
new file mode 100644
index 00000000..08fb1b0b
--- /dev/null
+++ b/data/web/sogo-auth.php
@@ -0,0 +1,86 @@
+
+
-
=$lang['user']['user_settings'];?>
-
-
=$lang['user']['mailbox_details'];?>
-
-
+
+
-
-
-
=$lang['user']['direct_aliases'];?>:
-
=$lang['user']['direct_aliases_desc'];?>
-
-
- $direct_alias_meta) {
- (!empty($direct_alias_meta['public_comment'])) ?
- printf('%s — %s ', $direct_alias, $direct_alias_meta['public_comment']) :
- printf('%s ', $direct_alias);
- }
- }
- ?>
-
-
-
-
=$lang['user']['shared_aliases'];?>:
-
=$lang['user']['shared_aliases_desc'];?>
-
-
- $shared_alias_meta) {
- (!empty($shared_alias_meta['public_comment'])) ?
- printf('%s — %s ', $shared_alias, $shared_alias_meta['public_comment']) :
- printf('%s ', $shared_alias);
- }
- }
- ?>
-
-
-
-
-
=$lang['user']['aliases_also_send_as'];?>:
-
-
=($user_get_alias_details['aliases_also_send_as'] == '*') ? $lang['user']['sender_acl_disabled'] : $user_get_alias_details['aliases_also_send_as'];?>
-
-
-
-
=$lang['user']['aliases_send_as_all'];?>:
-
-
=$user_get_alias_details['aliases_send_as_all'];?>
-
-
-
-
=$lang['user']['is_catch_all'];?>:
-
-
=$user_get_alias_details['is_catch_all'];?>
-
-
-
-
-
=$lang['user']['in_use'];?>:
-
-
-
- =$mailboxdata['percent_in_use'];?>%
+
+
+
+
+
=$lang['user']['mailbox_details'];?>
+
+
+
+
+
+
=$lang['user']['direct_aliases'];?>:
+
=$lang['user']['direct_aliases_desc'];?>
+
+
+ $direct_alias_meta) {
+ (!empty($direct_alias_meta['public_comment'])) ?
+ printf('%s — %s ', $direct_alias, $direct_alias_meta['public_comment']) :
+ printf('%s ', $direct_alias);
+ }
+ }
+ ?>
+
+
+
+
=$lang['user']['shared_aliases'];?>:
+
=$lang['user']['shared_aliases_desc'];?>
+
+
+ $shared_alias_meta) {
+ (!empty($shared_alias_meta['public_comment'])) ?
+ printf('%s — %s ', $shared_alias, $shared_alias_meta['public_comment']) :
+
+ printf('%s ', $shared_alias);
+ }
+ }
+ ?>
+
+
+
+
+
=$lang['user']['aliases_also_send_as'];?>:
+
+
=($user_get_alias_details['aliases_also_send_as'] == '*') ? $lang['user']['sender_acl_disabled'] : $user_get_alias_details['aliases_also_send_as'];?>
+
+
+
+
=$lang['user']['aliases_send_as_all'];?>:
+
+
=$user_get_alias_details['aliases_send_as_all'];?>
+
+
+
+
=$lang['user']['is_catch_all'];?>:
+
+
=$user_get_alias_details['is_catch_all'];?>
+
+
+
+
+
=$lang['user']['in_use'];?>:
+
+
+
+ =$mailboxdata['percent_in_use'];?>%
+
+
+
=formatBytes($mailboxdata['quota_used'], 2);?> / =($mailboxdata['quota'] == 0) ? '∞' : formatBytes($mailboxdata['quota'], 2);?> =$mailboxdata['messages'];?> =$lang['user']['messages'];?>
+
+
+
+
+
+
=$lang['user']['tag_handling'];?>:
+
+
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="delimiter_action"
+ data-api-url='edit/delimiter_action'
+ data-api-attr='{"tagged_mail_handler":"subfolder"}'>=$lang['user']['tag_in_subfolder'];?>
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="delimiter_action"
+ data-api-url='edit/delimiter_action'
+ data-api-attr='{"tagged_mail_handler":"subject"}'>=$lang['user']['tag_in_subject'];?>
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="delimiter_action"
+ data-api-url='edit/delimiter_action'
+ data-api-attr='{"tagged_mail_handler":"none"}'>=$lang['user']['tag_in_none'];?>
+
+
=$lang['user']['tag_help_explain'];?>
+
=$lang['user']['tag_help_example'];?>
+
+
+
+
+
=$lang['user']['tls_policy'];?>:
+
+
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="tls_policy"
+ data-api-url='edit/tls_policy'
+ data-api-attr='{"tls_enforce_in":=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'>=$lang['user']['tls_enforce_in'];?>
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="tls_policy"
+ data-api-url='edit/tls_policy'
+ data-api-attr='{"tls_enforce_out":=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'>=$lang['user']['tls_enforce_out'];?>
+
+
=$lang['user']['tls_policy_warning'];?>
+
+
+
+
+
=$lang['user']['quarantine_notification'];?>:
+
+
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"never"}'>=$lang['user']['never'];?>
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"hourly"}'>=$lang['user']['hourly'];?>
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"daily"}'>=$lang['user']['daily'];?>
+ "
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"weekly"}'>=$lang['user']['weekly'];?>
+
+
=$lang['user']['quarantine_notification_info'];?>
+
+
+
+
+
=$lang['user']['eas_reset'];?>:
+
+
=$lang['user']['eas_reset_now'];?>
+
=$lang['user']['eas_reset_help'];?>
+
+
+
+
=$lang['user']['sogo_profile_reset'];?>:
+
+
=$lang['user']['sogo_profile_reset_now'];?>
+
=$lang['user']['sogo_profile_reset_help'];?>
+
-
=formatBytes($mailboxdata['quota_used'], 2);?> / =formatBytes($mailboxdata['quota'], 2);?>, =$mailboxdata['messages'];?> =$lang['user']['messages'];?>
-
-
-
-
-
-
=$lang['user']['tag_handling'];?>:
-
-
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="delimiter_action"
- data-api-url='edit/delimiter_action'
- data-api-attr='{"tagged_mail_handler":"subfolder"}'>=$lang['user']['tag_in_subfolder'];?>
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="delimiter_action"
- data-api-url='edit/delimiter_action'
- data-api-attr='{"tagged_mail_handler":"subject"}'>=$lang['user']['tag_in_subject'];?>
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="delimiter_action"
- data-api-url='edit/delimiter_action'
- data-api-attr='{"tagged_mail_handler":"none"}'>=$lang['user']['tag_in_none'];?>
-
-
=$lang['user']['tag_help_explain'];?>
-
=$lang['user']['tag_help_example'];?>
-
-
-
-
-
=$lang['user']['tls_policy'];?>:
-
-
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="tls_policy"
- data-api-url='edit/tls_policy'
- data-api-attr='{"tls_enforce_in":=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'>=$lang['user']['tls_enforce_in'];?>
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="tls_policy"
- data-api-url='edit/tls_policy'
- data-api-attr='{"tls_enforce_out":=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'>=$lang['user']['tls_enforce_out'];?>
-
-
=$lang['user']['tls_policy_warning'];?>
-
-
-
-
-
=$lang['user']['quarantine_notification'];?>:
-
-
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="quarantine_notification"
- data-api-url='edit/quarantine_notification'
- data-api-attr='{"quarantine_notification":"never"}'>=$lang['user']['never'];?>
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="quarantine_notification"
- data-api-url='edit/quarantine_notification'
- data-api-attr='{"quarantine_notification":"hourly"}'>=$lang['user']['hourly'];?>
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="quarantine_notification"
- data-api-url='edit/quarantine_notification'
- data-api-attr='{"quarantine_notification":"daily"}'>=$lang['user']['daily'];?>
- "
- data-action="edit_selected"
- data-item="= htmlentities($username); ?>"
- data-id="quarantine_notification"
- data-api-url='edit/quarantine_notification'
- data-api-attr='{"quarantine_notification":"weekly"}'>=$lang['user']['weekly'];?>
-
-
=$lang['user']['quarantine_notification_info'];?>
-
-
-
-
-
-
=$lang['user']['eas_reset'];?>:
-
-
=$lang['user']['eas_reset_now'];?>
-
=$lang['user']['eas_reset_help'];?>
-
-
=$lang['user']['sogo_profile_reset'];?>:
-
-
=$lang['user']['sogo_profile_reset_now'];?>
-
=$lang['user']['sogo_profile_reset_help'];?>
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -320,7 +321,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
-
@@ -348,7 +348,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
-
@@ -376,10 +375,8 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
=$lang['user']['spamfilter_hint'];?>
-
-
@@ -419,7 +415,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
-
=$lang['user']['spamfilter_bl'];?>
@@ -451,7 +446,6 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
-
-
-
+
/etc/nginx/conf.d/server_name.active &&
envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active &&
+ . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.active &&
nginx -qt &&
until ping phpfpm -c1 > /dev/null; do sleep 1; done &&
until ping sogo -c1 > /dev/null; do sleep 1; done &&
@@ -274,14 +285,14 @@ services:
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- TZ=${TZ}
+ - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
volumes:
- ./data/web:/web:ro
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
- ./data/assets/ssl/:/etc/ssl/mail/:ro
- ./data/conf/nginx/:/etc/nginx/conf.d/:rw
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
- volumes_from:
- - sogo-mailcow
+ - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/
ports:
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
@@ -296,7 +307,7 @@ services:
acme-mailcow:
depends_on:
- nginx-mailcow
- image: mailcow/acme:1.48
+ image: mailcow/acme:1.51
build: ./data/Dockerfiles/acme
dns:
- ${IPV4_NETWORK:-172.22.1}.254
@@ -309,6 +320,7 @@ services:
- DBPASS=${DBPASS}
- SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n}
- SKIP_IP_CHECK=${SKIP_IP_CHECK:-n}
+ - SKIP_HTTP_VERIFICATION=${SKIP_HTTP_VERIFICATION:-n}
- LE_STAGING=${LE_STAGING:-n}
- TZ=${TZ}
volumes:
@@ -323,7 +335,7 @@ services:
- acme
netfilter-mailcow:
- image: mailcow/netfilter:1.22
+ image: mailcow/netfilter:1.23
build: ./data/Dockerfiles/netfilter
stop_grace_period: 30s
depends_on:
@@ -347,7 +359,7 @@ services:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
- image: mailcow/watchdog:1.34
+ image: mailcow/watchdog:1.39
# Debug
#command: /watchdog.sh
build: ./data/Dockerfiles/watchdog
@@ -395,11 +407,11 @@ services:
- dockerapi
solr-mailcow:
- image: mailcow/solr:1.1
+ image: mailcow/solr:1.4
build: ./data/Dockerfiles/solr
restart: always
volumes:
- - solr-vol-1:/opt/solr/server/solr/dovecot/data
+ - solr-vol-1:/opt/solr/server/solr/dovecot-fts/data
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
@@ -411,7 +423,7 @@ services:
aliases:
- solr
- ipv6nat:
+ ipv6nat-mailcow:
depends_on:
- unbound-mailcow
- mysql-mailcow
@@ -440,6 +452,8 @@ services:
networks:
mailcow-network:
driver: bridge
+ driver_opts:
+ com.docker.network.bridge.name: br-mailcow
enable_ipv6: true
ipam:
driver: default
@@ -459,3 +473,4 @@ volumes:
solr-vol-1:
postfix-vol-1:
crypt-vol-1:
+ sogo-web-vol-1:
diff --git a/generate_config.sh b/generate_config.sh
index a882ec08..d241a9ab 100755
--- a/generate_config.sh
+++ b/generate_config.sh
@@ -16,6 +16,7 @@ if [ -f mailcow.conf ]; then
case $response in
[yY][eE][sS]|[yY])
mv mailcow.conf mailcow.conf_backup
+ chmod 600 mailcow.conf_backup
;;
*)
exit 1
@@ -185,6 +186,10 @@ SKIP_LETS_ENCRYPT=n
SKIP_IP_CHECK=n
+# Skip HTTP verification in ACME container - y/n
+
+SKIP_HTTP_VERIFICATION=n
+
# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n
SKIP_CLAMD=${SKIP_CLAMD}
@@ -200,6 +205,10 @@ SOLR_HEAP=1024
USE_WATCHDOG=n
+# Allow admins to log into SOGo as email user (without any password)
+
+ALLOW_ADMIN_EMAIL_LOGIN=n
+
# Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME)
# Can by multiple rcpts, NO quotation marks
@@ -233,9 +242,14 @@ IPV6_NETWORK=fd4d:6169:6c63:6f77::/64
#API_KEY=
#API_ALLOW_FROM=127.0.0.1,1.2.3.4
+# mail_home is ~/Maildir
+MAILDIR_SUB=Maildir
+
EOF
mkdir -p data/assets/ssl
+chmod 600 mailcow.conf
+
# copy but don't overwrite existing certificate
cp -n data/assets/ssl-example/*.pem data/assets/ssl/
diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh
index d04f52d4..3ad9c9b4 100755
--- a/helper-scripts/nextcloud.sh
+++ b/helper-scripts/nextcloud.sh
@@ -76,12 +76,10 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/latest-15.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
&& rm nextcloud.tar.bz2 \
- && rm -rf ./data/web/nextcloud/updater \
&& mkdir -p ./data/web/nextcloud/data \
- && mkdir -p ./data/web/nextcloud/custom_apps \
- && chmod +x ./data/web/nextcloud/occ
- docker exec -it $(docker ps -f name=php-fpm-mailcow -q) bash -c "chown www-data:www-data -R /web/nextcloud"
- docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings upgrade"
+ && chmod +x ./data/web/nextcloud/occ \
+ docker exec -it $(docker ps -f name=php-fpm-mailcow -q) bash -c "chown www-data:www-data -R /web/nextcloud" \
+ docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings upgrade"
fi
elif [[ ${NC_INSTALL} == "y" ]]; then
@@ -106,12 +104,10 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/latest-15.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \
&& tar -xjf nextcloud.tar.bz2 -C ./data/web/ \
&& rm nextcloud.tar.bz2 \
- && rm -rf ./data/web/nextcloud/updater \
&& mkdir -p ./data/web/nextcloud/data \
- && mkdir -p ./data/web/nextcloud/custom_apps \
&& chmod +x ./data/web/nextcloud/occ
- docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud/data /web/nextcloud/config /web/nextcloud/apps /web/nextcloud/custom_apps"
+ docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud/data /web/nextcloud/config /web/nextcloud/apps"
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ --no-warnings maintenance:install \
--database mysql \
--database-host mysql \
diff --git a/update.sh b/update.sh
index 17e818f9..7fc65578 100755
--- a/update.sh
+++ b/update.sh
@@ -6,9 +6,12 @@ if [ "$(id -u)" -ne "0" ]; then
exit 1
fi
-#exit on error and pipefail
+# Exit on error and pipefail
set -o pipefail
+# Add /opt/bin to PATH
+PATH=$PATH:/opt/bin
+
umask 0022
for bin in curl docker-compose docker git awk sha1sum; do
@@ -68,8 +71,12 @@ while (($#)); do
case "${1}" in
--check|-c)
echo "Checking remote code for updates..."
- git fetch origin #${BRANCH}
- if [[ -z $(git log HEAD --pretty=format:"%H" | grep $(git rev-parse origin/${BRANCH})) ]]; then
+ LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized ${BRANCH} | cut -f1)
+ if [ $? -ne 0 ]; then
+ echo "A problem occurred while trying to fetch the latest revision from github."
+ exit 99
+ fi
+ if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
echo "Updated code is available."
exit 0
else
@@ -98,6 +105,7 @@ while (($#)); do
done
[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;}
+chmod 600 mailcow.conf
source mailcow.conf
DOTS=${MAILCOW_HOSTNAME//[^.]};
if [ ${#DOTS} -lt 2 ]; then
@@ -127,9 +135,12 @@ CONFIG_ARRAY=(
"API_KEY"
"API_ALLOW_FROM"
"MAILDIR_GC_TIME"
+ "MAILDIR_SUB"
"ACL_ANYONE"
"SOLR_HEAP"
"SKIP_SOLR"
+ "ALLOW_ADMIN_EMAIL_LOGIN"
+ "SKIP_HTTP_VERIFICATION"
)
sed -i '$a\' mailcow.conf
@@ -235,6 +246,13 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
echo "SKIP_SOLR=y" >> mailcow.conf
fi
+ elif [[ ${option} == "MAILDIR_SUB" ]]; then
+ if ! grep -q ${option} mailcow.conf; then
+ echo "Adding new option \"${option}\" to mailcow.conf"
+ echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
+ echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
+ echo "MAILDIR_SUB=" >> mailcow.conf
+ fi
elif ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=n" >> mailcow.conf
@@ -351,9 +369,8 @@ if grep -q 'SYSCTL_IPV6_DISABLED=1' mailcow.conf; then
read -p "Press any key to continue..." < /dev/tty
fi
-echo -e "Fixing project name... "
+# Checking for old project name bug
sed -i 's#COMPOSEPROJECT_NAME#COMPOSE_PROJECT_NAME#g' mailcow.conf
-sed -i '/COMPOSE_PROJECT_NAME=/s/-//g' mailcow.conf
echo -e "Fixing PHP-FPM worker ports for Nginx sites..."
sed -i 's#phpfpm:9000#phpfpm:9002#g' data/conf/nginx/*.conf