Compare commits

...

39 Commits

Author SHA1 Message Date
FreddleSpl0it
3f493e043d Merge pull request #6468 from mailcow/staging
Update 2025-03b
2025-04-07 09:09:39 +02:00
FreddleSpl0it
3ddad9dee8 Merge pull request #6460 from mailcow/ui/improve-ldap-ssl-labels
[Web] Improve clarity of LDAP SSL/TLS settings
2025-04-07 08:58:19 +02:00
FreddleSpl0it
2c10c39bc4 [Web] Update 2FA Info tooltip 2025-04-07 08:06:43 +02:00
FreddleSpl0it
0eb8f38792 [Web] Update LDAP SSL/TLS tooltips 2025-04-07 07:59:43 +02:00
FreddleSpl0it
402bf53a5c [Web] Improve clarity of LDAP SSL/TLS settings 2025-04-04 13:18:42 +02:00
FreddleSpl0it
428a59dd3f Merge branch 'fix/dovecot-lua-timeout' into staging 2025-04-03 14:18:33 +02:00
FreddleSpl0it
153890b283 Merge pull request #6439 from mailcow/fix/6430
[SOGo] Use JS for mailcow logout
2025-04-03 12:57:24 +02:00
FreddleSpl0it
a741c2ba4a Merge pull request #6426 from sardaukar/fix/typo-on-backup-and-restore-script
Fix tiny typo
2025-04-03 12:41:46 +02:00
FreddleSpl0it
741e5c719f Merge pull request #6438 from mailcow/fix/6405
[Netfilter] Downgrade to 1.61
2025-04-03 12:39:48 +02:00
FreddleSpl0it
34e4f93db9 Merge pull request #6451 from mailcow/fix/6437
[Web] Fix transport routing test
2025-04-03 12:38:43 +02:00
FreddleSpl0it
3758135dc3 Merge pull request #6450 from mailcow/fix/sasl_logs
Fix sasl_logs
2025-04-03 12:38:13 +02:00
FreddleSpl0it
6794e6ff43 [Dovecot] Add service for authentication cache_key 2025-04-03 12:31:43 +02:00
FreddleSpl0it
62f816e64a [Web] Check app password before user password on web login 2025-04-03 12:19:04 +02:00
FreddleSpl0it
e65478076b [Web] Prevent user sync for mismatched authsource 2025-04-03 11:58:35 +02:00
FreddleSpl0it
ceeabded73 [Web] Fix transport routing test 2025-04-03 10:29:47 +02:00
FreddleSpl0it
805634f9a9 Fix sasl_logs 2025-04-03 10:19:30 +02:00
DerLinkman
a92832d115 update README.md to include first 50 and 100$ monthly sponsors 2025-04-02 14:39:24 +02:00
milkmaker
4c5f485587 update postscreen_access.cidr (#6443) 2025-04-01 22:00:11 +02:00
FreddleSpl0it
db3a577ae3 [Web] Fix password reset 2025-04-01 16:39:15 +02:00
FreddleSpl0it
e452917de9 [SOGo] Show mailcow Settings Button to SOGoSuperUsers 2025-03-31 12:14:43 +02:00
FreddleSpl0it
f37961b7d0 [SOGo] Use JS for mailcow logout 2025-03-31 11:32:01 +02:00
FreddleSpl0it
0157cbddaf [Netfilter] Downgrade to 1.61 2025-03-31 10:36:20 +02:00
Bruno Antunes
65d872cc14 Fix tiny typo 2025-03-27 20:21:25 +00:00
FreddleSpl0it
4ad2422810 [Dovecot] Increase Timeout for HTTP Login Request 2025-03-27 16:52:15 +01:00
FreddleSpl0it
2c47145dee Merge pull request #6419 from mailcow/staging
Update 2025-03a
2025-03-27 09:19:29 +01:00
FreddleSpl0it
9b41b24522 Merge pull request #6402 from marvinruder/fix/long-dropdown-label
fix(ui): Swap translations for oversized dropdown
2025-03-27 08:07:51 +01:00
FreddleSpl0it
1c9d80f554 Merge pull request #6406 from mailcow/fix/6392
[Web] Fix SOGo access after Passwordless auth
2025-03-27 07:42:07 +01:00
FreddleSpl0it
7172cad257 Merge pull request #6407 from mailcow/fix/6396
[Web] Fix oauth2 redirect after user login
2025-03-27 07:41:08 +01:00
FreddleSpl0it
b550c6f88e Merge pull request #6408 from mailcow/fix/6373
[Swagger] Fix type property for /api/v1/add/bcc endpoint
2025-03-27 07:40:19 +01:00
FreddleSpl0it
5baf9eb375 Merge pull request #6409 from mailcow/fix/6372
[Web] Check if mailbox is active before renaming
2025-03-27 07:40:03 +01:00
FreddleSpl0it
4eb89f67ed Merge pull request #6410 from mailcow/fix/6395
[Web] Use absolute paths for flag SVGs
2025-03-27 07:39:34 +01:00
FreddleSpl0it
efdc798238 Merge pull request #6411 from mailcow/fix/6340
[Nginx] Move conf.d include before SNI vhosts
2025-03-27 07:39:06 +01:00
FreddleSpl0it
65fb4c2aa8 [Nginx] Move conf.d include before SNI vhosts 2025-03-26 13:04:43 +01:00
FreddleSpl0it
a5ca3353da [Web] Use absolute paths for flag SVGs 2025-03-26 10:59:56 +01:00
FreddleSpl0it
95aa35e133 [Web] Check if mailbox is active before renaming 2025-03-26 10:10:22 +01:00
FreddleSpl0it
21b11ed999 [Swagger] Fix type property for /api/v1/add/bcc endpoint 2025-03-26 09:24:03 +01:00
FreddleSpl0it
348107dae8 [Web] Fix oauth2 redirect after user login 2025-03-26 09:13:05 +01:00
FreddleSpl0it
fcb1b29c89 [Web] Fix SOGo access after Passwordless auth 2025-03-26 08:32:34 +01:00
Marvin A. Ruder
05fc4f7aba fix(ui): Swap translations for oversized dropdown
* Fix other typos
* Fixes #6400

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2025-03-25 21:24:22 +01:00
35 changed files with 309 additions and 124 deletions

View File

@@ -13,6 +13,22 @@ You can also [get a SAL](https://www.servercow.de/mailcow?lang=en#sal) which is
Or just spread the word: moo.
## Many thanks to our GitHub Sponsors ❤️
A big thank you to everyone supporting us on GitHub Sponsors—your contributions mean the world to us! Special thanks to the following amazing supporters:
### 100$/Month Sponsors
<a href="https://www.colba.net/" target=_blank><img
src="https://avatars.githubusercontent.com/u/204464723" height="58"
/></a>
<a href="https://www.maehdros.com/" target=_blank><img
src="https://avatars.githubusercontent.com/u/173894712" height="58"
/></a>
### 50$/Month Sponsors
<a href="https://github.com/vnukhr" target=_blank><img
src="https://avatars.githubusercontent.com/u/7805987?s=52&v=4" height="58"
/></a>
## Info, documentation and support
Please see [the official documentation](https://docs.mailcow.email/) for installation and support instructions. 🐄

View File

@@ -241,9 +241,9 @@ async def handle_pubsub_messages(channel: aioredis.client.PubSub):
else:
dockerapi.logger.error("api call: missing container_name, post_action or request")
else:
dockerapi.logger.error("Unknwon PubSub recieved - %s" % json.dumps(data_json))
dockerapi.logger.error("Unknown PubSub received - %s" % json.dumps(data_json))
else:
dockerapi.logger.error("Unknwon PubSub recieved - %s" % json.dumps(data_json))
dockerapi.logger.error("Unknown PubSub received - %s" % json.dumps(data_json))
await asyncio.sleep(0.0)
except asyncio.TimeoutError:

View File

@@ -1,20 +1,15 @@
59,65d58
< ng-show="::!activeUser.isSuperUser"
60,65d58
< var:ng-click="navButtonClick"
< ng-href="/user">
< <md-icon>build</md-icon>
< <md-tooltip><var:string label:value="mailcow"/></md-tooltip>
< <md-tooltip>mailcow <var:string label:value="Preferences"/></md-tooltip>
< </md-button>
< <md-button class="md-icon-button"
83c76
< onclick="document.getElementById('mc_logout').setAttribute('action', '/'); document.getElementById('mc_logout').submit();"
< onclick="mc_logout();"
---
> ng-show="::activeUser.path.logoff.length"
85c78
< ng-href="#">
---
> ng-href="{{::activeUser.path.logoff}}">
89,91d81
< <form method="POST" id="mc_logout" action="user">
< <input type="hidden" name="logout" value="1">
< </form>

View File

@@ -49,7 +49,7 @@
# 2013101601 Optical clean up #
# 2013101602 Rewrite help output #
# 2013101700 Handle Slave IO in 'Connecting' state #
# 2013101701 Minor changes in output, handling UNKWNON situations now #
# 2013101701 Minor changes in output, handling UNKNOWN situations now #
# 2013101702 Exit CRITICAL when Slave IO in Connecting state #
# 2013123000 Slave_SQL_Running also matched Slave_SQL_Running_State #
# 2015011600 Added 'moving' check to catch possible connection issues #
@@ -131,7 +131,7 @@ elif [[ -n "${socket}" && (-z "${user}" || -z "${password}") ]]; then
fi
# Connect to the DB server and store output in vars
if [[ -n $socket ]]; then
if [[ -n $socket ]]; then
ConnectionResult=$(mariadb --skip-ssl ${optfile} ${socket} ${user} -e "show slave ${connection} status\G" 2>&1)
else
ConnectionResult=$(mariadb --skip-ssl ${optfile} ${host} ${port} ${user} -e "show slave ${connection} status\G" 2>&1)
@@ -178,33 +178,33 @@ if [ ${check} = ${ok} ] && [ ${checkio} = ${ok} ]; then
then echo "CRITICAL: Slave is ${delayinfo} seconds behind Master | delay=${delayinfo}s"; exit ${STATE_CRITICAL}
elif [[ ${delayinfo} -ge ${warn_delay} ]]
then echo "WARNING: Slave is ${delayinfo} seconds behind Master | delay=${delayinfo}s"; exit ${STATE_WARNING}
else
else
# Everything looks OK here but now let us check if the replication is moving
if [[ -n ${moving} ]] && [[ -n ${tmpfile} ]] && [[ $readpos -eq $execpos ]]
then
#echo "Debug: Read pos is $readpos - Exec pos is $execpos"
then
#echo "Debug: Read pos is $readpos - Exec pos is $execpos"
# Check if tmp file exists
curtime=`date +%s`
if [[ -w $tmpfile ]]
then
if [[ -w $tmpfile ]]
then
tmpfiletime=`date +%s -r $tmpfile`
if [[ `expr $curtime - $tmpfiletime` -gt ${moving} ]]
then
exectmp=`cat $tmpfile`
#echo "Debug: Exec pos in tmpfile is $exectmp"
if [[ $exectmp -eq $execpos ]]
then
then
# The value read from the tmp file and from db are the same. Replication hasnt moved!
echo "WARNING: Slave replication has not moved in ${moving} seconds. Manual check required."; exit ${STATE_WARNING}
else
else
# Replication has moved since the tmp file was written. Delete tmp file and output OK.
rm $tmpfile
echo "OK: Slave SQL running: ${check} Slave IO running: ${checkio} / master: ${masterinfo} / slave is ${delayinfo} seconds behind master | delay=${delayinfo}s"; exit ${STATE_OK};
fi
else
else
echo "OK: Slave SQL running: ${check} Slave IO running: ${checkio} / master: ${masterinfo} / slave is ${delayinfo} seconds behind master | delay=${delayinfo}s"; exit ${STATE_OK};
fi
else
else
echo "$execpos" > $tmpfile
echo "OK: Slave SQL running: ${check} Slave IO running: ${checkio} / master: ${masterinfo} / slave is ${delayinfo} seconds behind master | delay=${delayinfo}s"; exit ${STATE_OK};
fi

View File

@@ -69,29 +69,34 @@ require_once 'functions.acl.inc.php';
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
$result = false;
$protocol = $post['protocol'];
if ($isSOGoRequest) {
$protocol = null;
// This is a SOGo Auth request. First check for SSO password.
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
if ($sogo_sso_pass === $post['password']){
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], "SOGO");
$result = true;
}
}
if ($result === false){
$result = apppass_login($post['username'], $post['password'], $protocol, array(
$result = apppass_login($post['username'], $post['password'], array($post['service'] => true), array(
'is_internal' => true,
'remote_addr' => $post['real_rip']
));
if ($result) error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
if ($result) {
error_log('MAILCOWAUTH: App auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
}
}
if ($result === false){
// Init Identity Provider
$iam_provider = identity_provider('init');
$iam_settings = identity_provider('get');
$result = user_login($post['username'], $post['password'], array('is_internal' => true));
if ($result) error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
if ($result) {
error_log('MAILCOWAUTH: User auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
}
}
if ($result) {

View File

@@ -3,21 +3,20 @@ function auth_password_verify(request, password)
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
end
json = require "cjson"
ltn12 = require "ltn12"
https = require "ssl.https"
https.TIMEOUT = 5
local json = require "cjson"
local ltn12 = require "ltn12"
local https = require "ssl.https"
https.TIMEOUT = 30
local req = {
username = request.user,
password = password,
real_rip = request.real_rip,
protocol = {}
service = request.service
}
req.protocol[request.service] = true
local req_json = json.encode(req)
local res = {}
local res = {}
local b, c = https.request {
method = "POST",
url = "https://nginx:9082",
@@ -29,11 +28,17 @@ function auth_password_verify(request, password)
sink = ltn12.sink.table(res),
insecure = true
}
if c ~= 200 then
dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
return dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE, "Upstream error"
end
local api_response = json.decode(table.concat(res))
if api_response.success == true then
return dovecot.auth.PASSDB_RESULT_OK, ""
end
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
end

View File

@@ -53,7 +53,7 @@ mail_shared_explicit_inbox = yes
mail_prefetch_count = 30
passdb {
driver = lua
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%u:%w
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%s:%u:%w
result_success = return-ok
result_failure = continue
result_internalfail = continue

View File

@@ -182,6 +182,8 @@ http {
}
}
include /etc/nginx/conf.d/*.conf;
{% for cert in valid_cert_dirs %}
server {
{% if not HTTP_REDIRECT %}
@@ -206,6 +208,4 @@ http {
include /etc/nginx/includes/sites-default.conf;
}
{% endfor %}
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -130,7 +130,7 @@ while (true) {
curl_close($ch);
if ($code != 200){
logMsg("err", "Recieved HTTP {$code}");
logMsg("err", "Received HTTP {$code}");
session_destroy();
exit;
}
@@ -141,7 +141,7 @@ while (true) {
break;
}
if (!is_array($response)){
logMsg("err", "Recieved malformed response from keycloak api");
logMsg("err", "Received malformed response from keycloak api");
break;
}
if (count($response) == 0) {
@@ -196,7 +196,7 @@ while (true) {
logMsg("err", "Could not create user " . $user['email']);
continue;
}
} else if ($row && intval($iam_settings['periodic_sync']) == 1) {
} else if ($row && intval($iam_settings['periodic_sync']) == 1 && $row['authsource'] == "keycloak") {
if ($mapper_key === false){
logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
continue;

View File

@@ -168,7 +168,7 @@ foreach ($response as $user) {
logMsg("err", "Could not create user " . $user[$iam_settings['username_field']][0]);
continue;
}
} else if ($row && intval($iam_settings['periodic_sync']) == 1) {
} else if ($row && intval($iam_settings['periodic_sync']) == 1 && $row['authsource'] == "ldap") {
if ($mapper_key === false){
logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
continue;

View File

@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Sat Mar 1 00:19:29 UTC 2025
# Whitelist generated by Postwhite v3.4 on Tue Apr 1 00:20:51 UTC 2025
# https://github.com/stevejenkins/postwhite/
# 2000 total rules
# 2067 total rules
2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit
@@ -26,7 +26,12 @@
8.20.114.31 permit
8.25.194.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
12.130.86.238 permit
13.107.246.59 permit
13.110.208.0/21 permit
13.110.209.0/24 permit
13.110.216.0/22 permit
@@ -60,8 +65,6 @@
20.59.80.4/30 permit
20.63.210.192/28 permit
20.69.8.108/30 permit
20.70.246.20 permit
20.76.201.171 permit
20.83.222.104/30 permit
20.88.157.184/30 permit
20.94.180.64/28 permit
@@ -70,14 +73,11 @@
20.98.194.68/30 permit
20.105.209.76/30 permit
20.107.239.64/30 permit
20.112.250.133 permit
20.118.139.208/30 permit
20.141.10.196 permit
20.185.214.0/27 permit
20.185.214.32/27 permit
20.185.214.64/27 permit
20.231.239.246 permit
20.236.44.162 permit
23.103.224.0/19 permit
23.249.208.0/20 permit
23.251.224.0/19 permit
@@ -103,6 +103,24 @@
27.123.206.80/28 permit
31.25.48.222 permit
31.47.251.17 permit
34.2.64.0/22 permit
34.2.68.0/23 permit
34.2.70.0/23 permit
34.2.71.64/26 permit
34.2.72.0/22 permit
34.2.75.0/26 permit
34.2.78.0/23 permit
34.2.80.0/23 permit
34.2.82.0/23 permit
34.2.84.0/24 permit
34.2.84.64/26 permit
34.2.85.0/24 permit
34.2.85.64/26 permit
34.2.86.0/23 permit
34.2.88.0/23 permit
34.2.90.0/23 permit
34.2.92.0/23 permit
34.2.94.0/23 permit
34.195.217.107 permit
34.212.163.75 permit
34.215.104.144 permit
@@ -215,7 +233,6 @@
52.95.49.88/29 permit
52.96.91.34 permit
52.96.111.82 permit
52.96.172.98 permit
52.96.214.50 permit
52.96.222.194 permit
52.96.222.226 permit
@@ -255,6 +272,7 @@
54.244.54.130 permit
54.244.242.0/24 permit
54.255.61.23 permit
56.124.6.228 permit
57.103.64.0/18 permit
62.13.128.0/24 permit
62.13.129.128/25 permit
@@ -341,6 +359,7 @@
65.110.161.77 permit
65.123.29.213 permit
65.123.29.220 permit
65.154.166.0/24 permit
65.212.180.36 permit
66.102.0.0/20 permit
66.119.150.192/26 permit
@@ -1304,6 +1323,9 @@
117.120.16.0/21 permit
119.42.242.52/31 permit
119.42.242.156 permit
121.244.91.48 permit
121.244.91.52 permit
122.15.156.182 permit
123.126.78.64/29 permit
124.108.96.24/31 permit
124.108.96.28/31 permit
@@ -1366,7 +1388,21 @@
134.170.141.64/26 permit
134.170.143.0/24 permit
134.170.174.0/24 permit
135.84.80.0/24 permit
135.84.81.0/24 permit
135.84.82.0/24 permit
135.84.83.0/24 permit
135.84.216.0/22 permit
136.143.160.0/24 permit
136.143.161.0/24 permit
136.143.162.0/24 permit
136.143.176.0/24 permit
136.143.177.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit
136.143.184.0/24 permit
136.143.188.0/24 permit
136.143.190.0/23 permit
136.147.128.0/20 permit
136.147.135.0/24 permit
136.147.176.0/20 permit
@@ -1381,6 +1417,7 @@
139.138.46.219 permit
139.138.57.55 permit
139.138.58.119 permit
139.167.79.86 permit
139.180.17.0/24 permit
140.238.148.191 permit
141.148.159.229 permit
@@ -1498,6 +1535,9 @@
163.114.135.16 permit
164.152.23.32 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.221 permit
166.78.69.169 permit
@@ -1526,6 +1566,12 @@
168.245.12.252 permit
168.245.46.9 permit
168.245.127.231 permit
169.148.129.0/24 permit
169.148.131.0/24 permit
169.148.142.10 permit
169.148.144.0/25 permit
169.148.144.10 permit
169.148.146.0/23 permit
170.10.128.0/24 permit
170.10.129.0/24 permit
170.10.132.56/29 permit
@@ -1667,6 +1713,14 @@
198.61.254.231 permit
198.178.234.57 permit
198.244.48.0/20 permit
198.244.56.107 permit
198.244.56.108 permit
198.244.56.109 permit
198.244.56.111 permit
198.244.56.112 permit
198.244.56.113 permit
198.244.56.114 permit
198.244.56.115 permit
198.244.59.30 permit
198.244.59.33 permit
198.244.59.35 permit
@@ -1679,7 +1733,15 @@
199.16.156.0/22 permit
199.33.145.1 permit
199.33.145.32 permit
199.34.22.36 permit
199.59.148.0/22 permit
199.67.80.2 permit
199.67.80.20 permit
199.67.82.2 permit
199.67.82.20 permit
199.67.84.0/24 permit
199.67.86.0/24 permit
199.67.88.0/24 permit
199.101.161.130 permit
199.101.162.0/25 permit
199.122.120.0/21 permit
@@ -1736,6 +1798,8 @@
204.92.114.187 permit
204.92.114.203 permit
204.92.114.204/31 permit
204.141.32.0/23 permit
204.141.42.0/23 permit
204.220.160.0/21 permit
204.220.168.0/21 permit
204.220.176.0/20 permit
@@ -1988,6 +2052,9 @@
2603:1030:20e:3::23c permit
2603:1030:b:3::152 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
2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit

View File

@@ -5,6 +5,16 @@ document.addEventListener('DOMContentLoaded', function () {
window.location.href = '/user';
}
});
// logout function
function mc_logout() {
fetch("/", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "logout=1"
}).then(() => window.location.href = '/');
}
// Custom SOGo JS

View File

@@ -346,7 +346,8 @@ paths:
description: the domain which emails should be forwarded
type: string
type:
description: the type of bcc map can be `sender` or `recipient`
description: the type of bcc map can be `sender` or `rcpt`
enum: [sender, rcpt]
type: string
type: object
summary: Create BCC Map

File diff suppressed because one or more lines are too long

View File

@@ -9,25 +9,52 @@ function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
// Try validate admin
if (!isset($role) || $role == "admin") {
$result = admin_login($user, $pass);
if ($result !== false) return $result;
if ($result !== false){
return $result;
}
}
// Try validate domain admin
if (!isset($role) || $role == "domain_admin") {
$result = domainadmin_login($user, $pass);
if ($result !== false) return $result;
if ($result !== false) {
return $result;
}
}
// Try validate app password
if (!isset($role) || $role == "app") {
$result = apppass_login($user, $pass, $app_passwd_data);
if ($result !== false) {
if ($app_passwd_data['eas'] === true) {
$service = 'EAS';
} elseif ($app_passwd_data['dav'] === true) {
$service = 'DAV';
} else {
$service = 'NONE';
}
$real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
set_sasl_log($user, $real_rip, $service, $pass);
return $result;
}
}
// Try validate user
if (!isset($role) || $role == "user") {
$result = user_login($user, $pass);
if ($result !== false) return $result;
}
// Try validate app password
if (!isset($role) || $role == "app") {
$result = apppass_login($user, $pass, $app_passwd_data);
if ($result !== false) return $result;
if ($result !== false) {
if ($app_passwd_data['eas'] === true) {
$service = 'EAS';
} elseif ($app_passwd_data['dav'] === true) {
$service = 'DAV';
} else {
$service = 'MAILCOWUI';
}
$real_rip = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
set_sasl_log($user, $real_rip, $service);
return $result;
}
}
// skip log and only return false if it's an internal request
@@ -415,21 +442,7 @@ function apppass_login($user, $pass, $app_passwd_data, $extra = null){
// verify password
if (verify_hash($row['password'], $pass) !== false) {
if ($is_internal){
$remote_addr = $extra['remote_addr'];
} else {
$remote_addr = ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']);
}
$service = strtoupper($is_app_passwd);
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
$stmt->execute(array(
':service' => $service,
':app_id' => $row['app_passwd_id'],
':username' => $user,
':remote_addr' => $remote_addr
));
$_SESSION['app_passwd_id'] = $row['app_passwd_id'];
unset($_SESSION['ldelay']);
return "user";
}

View File

@@ -350,6 +350,34 @@ function last_login($action, $username, $sasl_limit_days = 7, $ui_offset = 1) {
}
}
function set_sasl_log($username, $real_rip, $service){
global $pdo;
try {
if (!empty($_SESSION['app_passwd_id'])) {
$app_password = $_SESSION['app_passwd_id'];
} else {
$app_password = 0;
}
$stmt = $pdo->prepare('REPLACE INTO `sasl_log` (`username`, `real_rip`, `service`, `app_password`) VALUES (:username, :real_rip, :service, :app_password)');
$stmt->execute(array(
':username' => $username,
':real_rip' => $real_rip,
':service' => $service,
':app_password' => $app_password
));
} catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_data_log),
'msg' => array('mysql_error', $e)
);
return false;
}
return true;
}
function flush_memcached() {
try {
$m = new Memcached();
@@ -1385,6 +1413,7 @@ function fido2($_data) {
);
break;
case "verify":
$role = "";
$tokenData = json_decode($_data['token']);
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
$authenticatorData = base64_decode($tokenData->authenticatorData);
@@ -1418,17 +1447,17 @@ function fido2($_data) {
$stmt->execute(array(':username' => $process_fido2['username']));
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
if ($obj_props['superadmin'] === 1 && (!$_data['user'] || $_data['user'] == "admin")) {
$_SESSION["mailcow_cc_role"] = "admin";
$role = "admin";
}
elseif ($obj_props['superadmin'] === 0 && (!$_data['user'] || $_data['user'] == "domainadmin")) {
$_SESSION["mailcow_cc_role"] = "domainadmin";
$role = "domainadmin";
}
elseif (!isset($obj_props['superadmin']) && (!$_data['user'] || $_data['user'] == "user")) {
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
$stmt->execute(array(':username' => $process_fido2['username']));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row['username'] == $process_fido2['username']) {
$_SESSION["mailcow_cc_role"] = "user";
$role = "user";
}
}
else {
@@ -1439,7 +1468,7 @@ function fido2($_data) {
);
return false;
}
if (empty($_SESSION["mailcow_cc_role"])) {
if (empty($role)) {
session_unset();
session_destroy();
$_SESSION['return'][] = array(
@@ -1449,15 +1478,17 @@ function fido2($_data) {
);
return false;
}
$_SESSION["mailcow_cc_username"] = $process_fido2['username'];
$_SESSION["fido2_cid"] = $process_fido2['cid'];
unset($_SESSION["challenge"]);
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array("fido2_login", $_data['user'], $process_fido2['username']),
'msg' => array('logged_in_as', $process_fido2['username'])
);
return true;
return array(
"role" => $role,
"username" => $process_fido2['username'],
"cid" => $process_fido2['cid']
);
break;
}
}

View File

@@ -3324,7 +3324,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
$is_now = mailbox('get', 'mailbox_details', $old_username);
if (empty($is_now)) {
if (empty($is_now) || ($is_now['active'] != '1' && $is_now['active'] != '2')) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),

View File

@@ -19,11 +19,16 @@ if (isset($_POST["verify_tfa_login"])) {
unset($_SESSION['pending_tfa_methods']);
}
if (isset($_POST["verify_fido2_login"])) {
fido2(array(
$res = fido2(array(
"action" => "verify",
"token" => $_POST["token"],
"user" => "admin"
));
if (is_array($res) && $res['role'] == "admin" && !empty($res['username'])){
$_SESSION["mailcow_cc_username"] = $res['username'];
$_SESSION["mailcow_cc_role"] = $res['role'];
$_SESSION["fido2_cid"] = $res['cid'];
}
exit;
}

View File

@@ -30,11 +30,16 @@ if (isset($_POST["verify_tfa_login"])) {
unset($_SESSION['pending_tfa_methods']);
}
if (isset($_POST["verify_fido2_login"])) {
fido2(array(
$res = fido2(array(
"action" => "verify",
"token" => $_POST["token"],
"user" => "domainadmin"
));
if (is_array($res) && $res['role'] == "domainadmin" && !empty($res['username'])){
$_SESSION["mailcow_cc_username"] = $res['username'];
$_SESSION["mailcow_cc_role"] = $res['role'];
$_SESSION["fido2_cid"] = $res['cid'];
}
exit;
}

View File

@@ -21,7 +21,7 @@ if ($iam_provider){
}
} elseif ($_GET['code'] && $_GET['state'] === $_SESSION['oauth2state']) {
// Check given state against previously stored one to mitigate CSRF attack
// Recieved access token in $_GET['code']
// Received access token in $_GET['code']
// extract info and verify user
identity_provider('verify-sso');
}
@@ -66,6 +66,14 @@ if (isset($_POST["verify_tfa_login"])) {
die();
} else {
set_user_loggedin_session($_SESSION['pending_mailcow_cc_username']);
if (isset($_SESSION['oauth2_request'])) {
$oauth2_request = $_SESSION['oauth2_request'];
unset($_SESSION['oauth2_request']);
header('Location: ' . $oauth2_request);
die();
}
$user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']);
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;
if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual) {
@@ -84,11 +92,15 @@ if (isset($_POST["verify_tfa_login"])) {
unset($_SESSION['pending_tfa_methods']);
}
if (isset($_POST["verify_fido2_login"])) {
fido2(array(
$res = fido2(array(
"action" => "verify",
"token" => $_POST["token"],
"user" => "user"
));
if (is_array($res) && $res['role'] == "user" && !empty($res['username'])){
set_user_loggedin_session($res['username']);
$_SESSION["fido2_cid"] = $res['cid'];
}
exit;
}
@@ -118,6 +130,12 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
header("Location: /mobileconfig.php");
die();
}
if (isset($_SESSION['oauth2_request'])) {
$oauth2_request = $_SESSION['oauth2_request'];
unset($_SESSION['oauth2_request']);
header('Location: ' . $oauth2_request);
die();
}
$user_details = mailbox("get", "mailbox_details", $login_user);
$is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false;

View File

@@ -681,7 +681,7 @@ jQuery(function($){
$(this).html('<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> ');
$.ajax({
type: 'GET',
url: 'inc/ajax/transport_check.php',
url: '/inc/ajax/transport_check.php',
dataType: 'text',
data: $('#test_transport_form').serialize(),
complete: function (data) {

View File

@@ -90,13 +90,7 @@ jQuery(function($){
console.log('error reading last logins');
},
success: function (data) {
$('.last-ui-login').html('');
$('.last-sasl-login').html('');
if (data.ui.time) {
$('.last-ui-login').html('<i class="bi bi-person-fill"></i> ' + lang.last_ui_login + ': ' + unix_time_format(data.ui.time));
} else {
$('.last-ui-login').text(lang.no_last_login);
}
if (data.sasl) {
$('.last-sasl-login').append('<ul class="list-group">');
$.each(data.sasl, function (i, item) {

View File

@@ -238,7 +238,9 @@
"iam_username_field": "Username Feld",
"iam_binddn": "Bind DN",
"iam_use_ssl": "Benutze SSL",
"iam_use_tls": "Benutze TLS",
"iam_use_ssl_info": "Wenn SSL aktiviert ist und der Port auf 389 gesetzt wurde, wird dieser automatisch auf 636 geändert.",
"iam_use_tls": "Benutze StartTLS",
"iam_use_tls_info": "Wenn TLS aktiviert wird, muss der Standardport deines LDAP-Servers (389) verwendet werden. SSL-Ports können dabei nicht verwendet werden.",
"iam_version": "Version",
"ignore_ssl_error": "Ignoriere SSL Fehler",
"import": "Importieren",
@@ -1333,7 +1335,7 @@
"tag_in_subfolder": "In Unterordner",
"tag_in_subject": "In Betreff",
"text": "Text",
"tfa_info": "Zwei-Faktor-Authentifizierung hilft dabei, Ihr Konto zu schützen. Wenn Sie sie aktivieren, benötigen Sie möglicherweise App-Passwörter, um sich bei Apps oder Diensten anzumelden, die die Zwei-Faktor-Authentifizierung nicht unterstützen (z.B. Mailclients).",
"tfa_info": "Zwei-Faktor-Authentifizierung hilft dabei, Ihr Konto zu schützen. Wenn Sie sie aktivieren, benötigen Sie App-Passwörter, um sich bei Apps oder Diensten anzumelden, die die Zwei-Faktor-Authentifizierung nicht unterstützen (z.B. Mailclients).",
"title": "Title",
"tls_enforce_in": "TLS eingehend erzwingen",
"tls_enforce_out": "TLS ausgehend erzwingen",

View File

@@ -245,7 +245,9 @@
"iam_username_field": "Username Field",
"iam_binddn": "Bind DN",
"iam_use_ssl": "Use SSL",
"iam_use_tls": "Use TLS",
"iam_use_ssl_info": "If enabling SSL, and port is set to 389, it will be automatically overridden to use 636.",
"iam_use_tls": "Use StartTLS",
"iam_use_tls_info": "If enabling TLS, you must use the default port for your LDAP server (389). SSL ports cannot be used.",
"iam_version": "Version",
"ignore_ssl_error": "Ignore SSL Errors",
"import": "Import",
@@ -624,7 +626,7 @@
"alias": "Edit alias",
"allow_from_smtp": "Only allow these IPs to use <b>SMTP</b>",
"allow_from_smtp_info": "Leave empty to allow all senders.<br>IPv4/IPv6 addresses and networks.",
"allowed_protocols": "Allowed protocols",
"allowed_protocols": "Allowed protocols for direct user access (does not affect app password protocols)",
"app_name": "App name",
"app_passwd": "App password",
"app_passwd_protocols": "Allowed protocols for app password",
@@ -844,7 +846,7 @@
"all_domains": "All Domains",
"allow_from_smtp": "Only allow these IPs to use <b>SMTP</b>",
"allow_from_smtp_info": "Leave empty to allow all senders.<br>IPv4/IPv6 addresses and networks.",
"allowed_protocols": "Allowed protocols for direct user access (does not affect app password protocols)",
"allowed_protocols": "Allowed protocols",
"backup_mx": "Relay domain",
"bcc": "BCC",
"bcc_destination": "BCC destination",
@@ -1355,7 +1357,7 @@
"tag_in_subfolder": "In subfolder",
"tag_in_subject": "In subject",
"text": "Text",
"tfa_info": "Two-factor authentication helps protect your account. If you enable it, you may need app passwords to log in to apps or services that don't support two-factor authentication (e.g. Mailclients).",
"tfa_info": "Two-factor authentication helps protect your account. If you enable it, you need app passwords to log in to apps or services that don't support two-factor authentication (e.g. Mailclients).",
"title": "Title",
"tls_enforce_in": "Enforce TLS incoming",
"tls_enforce_out": "Enforce TLS outgoing",

View File

@@ -581,7 +581,7 @@
"alias": "エイリアスを編集",
"allow_from_smtp": "<b>SMTP</b>を使用するこれらのIPのみを許可",
"allow_from_smtp_info": "すべての送信者を許可するには空欄にしてください。<br>IPv4/IPv6アドレスおよびネットワークを指定できます。",
"allowed_protocols": "許可されプロトコル",
"allowed_protocols": "直接ユーザーアクセスで許可されプロトコル(アプリパスワードプロトコルには影響しません)",
"app_name": "アプリ名",
"app_passwd": "アプリパスワード",
"app_passwd_protocols": "アプリパスワードで許可されるプロトコル",
@@ -798,7 +798,7 @@
"all_domains": "すべてのドメイン",
"allow_from_smtp": "<b>SMTP</b>を使用するこれらのIPのみを許可",
"allow_from_smtp_info": "すべての送信者を許可するには空欄にしてください。<br>IPv4/IPv6アドレスおよびネットワークを指定可能。",
"allowed_protocols": "直接ユーザーアクセスで許可されるプロトコル(アプリパスワードプロトコルには影響しません)",
"allowed_protocols": "許可されたプロトコル",
"backup_mx": "リレードメイン",
"bcc": "BCC",
"bcc_destination": "BCC送信先",

View File

@@ -581,7 +581,7 @@
"alias": "Editar alias",
"allow_from_smtp": "<b>Permita que esses IPs usem apenas SMTP</b>",
"allow_from_smtp_info": "Deixe em branco para permitir todos os remetentes. Endereços e <br>redes IPv4/IPv6.",
"allowed_protocols": "Protocolos permitidos",
"allowed_protocols": "Protocolos permitidos para acesso direto do usuário (não afeta os protocolos de senha do aplicativo)",
"app_name": "Nome do aplicativo",
"app_passwd": "Senha do aplicativo",
"app_passwd_protocols": "Protocolos permitidos para a senha do aplicativo",
@@ -793,7 +793,7 @@
"all_domains": "Todos os domínios",
"allow_from_smtp": "<b>Permita que esses IPs usem apenas SMTP</b>",
"allow_from_smtp_info": "Deixe em branco para permitir todos os remetentes. Endereços e <br>redes IPv4/IPv6.",
"allowed_protocols": "Protocolos permitidos para acesso direto do usuário (não afeta os protocolos de senha do aplicativo)",
"allowed_protocols": "Protocolos permitidos",
"backup_mx": "Domínio de retransmissão",
"bcc": "BCC",
"bcc_destination": "Destino BCC",

View File

@@ -482,7 +482,7 @@
"sender_acl_disabled": "<span class=\\\"label label-danger\\\">Gönderen denetimi devre dışı</span>",
"allow_from_smtp": "Yalnızca bu IP'lerin <b>SMTP</b> kullanmasına izin verin",
"allow_from_smtp_info": "Tüm gönderenlere izin vermek için boş bırakın.<br>IPv4/IPv6 adresleri ve ağları.",
"allowed_protocols": "İzin verilen protokoller",
"allowed_protocols": "Doğrudan kullanıcı erişimi için izin verilen protokoller (uygulama parola protokollerini etkilemez)",
"app_name": "Uygulama adı",
"app_passwd": "Uygulama şifresi",
"app_passwd_protocols": "Uygulama şifresi için izin verilen protokoller",
@@ -782,7 +782,7 @@
"aliases": "Takma Adlar",
"all_domains": "Tüm Alan Adları",
"allow_from_smtp": "Yalnızca bu IP'lerin <b>SMTP</b> kullanmasına izin verin",
"allowed_protocols": "Doğrudan kullanıcı erişimi için izin verilen protokoller (uygulama parola protokollerini etkilemez)",
"allowed_protocols": "İzin verilen protokoller",
"backup_mx": "Geçiş alanı",
"bcc": "BCC",
"bcc_destination": "Gizli hedef",

View File

@@ -554,7 +554,7 @@
"alias": "编辑别名",
"allow_from_smtp": "只允许这些 IP 使用 <b>SMTP</b>",
"allow_from_smtp_info": "留空以允许所有发送者。<br>IPv4/IPv6 地址和网络。",
"allowed_protocols": "允许的协议",
"allowed_protocols": "允许用户直接访问的协议 (不会影响应用的密码协议)",
"app_name": "应用名称",
"app_passwd": "应用密码",
"app_passwd_protocols": "应用密码允许的协议",
@@ -770,7 +770,7 @@
"all_domains": "全部域名",
"allow_from_smtp": "只允许这些 IP 使用 <b>SMTP</b>",
"allow_from_smtp_info": "留空以允许所有发送者。<br>IPv4/IPv6 地址或网络。",
"allowed_protocols": "允许用户直接访问的协议 (不会影响应用的密码协议)",
"allowed_protocols": "允许的协议",
"backup_mx": "中继域名",
"bcc": "BCC",
"bcc_destination": "BCC 目标地址",

View File

@@ -553,7 +553,7 @@
"alias": "編輯別名",
"allow_from_smtp": "只允許這些 IP 使用 <b>SMTP</b>",
"allow_from_smtp_info": "留空將允許所有寄件人<br>IPv4/IPv6 地址或網路",
"allowed_protocols": "允許的協定",
"allowed_protocols": "使用者直接存取時允許的協定 (不影響應用程式密碼所能使用的協定)",
"app_name": "應用程式名稱",
"app_passwd": "應用程式密碼",
"app_passwd_protocols": "應用程式密碼允許的協定",
@@ -763,7 +763,7 @@
"all_domains": "所有域名",
"allow_from_smtp": "只允許這些 IP 使用<b>SMTP</b>",
"allow_from_smtp_info": "留空以允許所有發送者<br>IPv4/IPv6 地址或網路",
"allowed_protocols": "使用者直接存取時允許的協定 (不影響應用程式密碼所能使用的協定)",
"allowed_protocols": "允許的協定",
"backup_mx": "中繼域名",
"bcc": "密件副本",
"bcc_destination": "密件副本目標地址",

View File

@@ -1,5 +1,6 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.user.inc.php';
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') {
header('Location: /admin/dashboard');

View File

@@ -392,11 +392,11 @@
<input type="hidden" name="authsource" value="ldap">
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<i style="font-size: 16px; cursor: pointer;" class="bi bi-patch-question-fill m-2 ms-0" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom" title="{{ lang.admin.iam_host_info }}"></i>
<i style="font-size: 16px; cursor: pointer;" class="bi bi-patch-question-fill mx-2 ms-0" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom" title="{{ lang.admin.iam_host_info }}"></i>
<label class="control-label" for="iam_ldap_host">{{ lang.admin.iam_host }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-4 d-flex">
<input type="text" class="form-control" id="iam_ldap_host" name="host" value="{{ iam_settings.host }}" required>
<input type="text" class="form-control" id="iam_ldap_host" name="host" value="{{ iam_settings.host }}" required>
</div>
</div>
<div class="row mb-2">
@@ -409,21 +409,37 @@
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<i style="font-size: 16px; cursor: pointer;" class="bi bi-patch-question-fill mx-2 ms-0" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom" title="{{ lang.admin.iam_use_ssl_info }}"></i>
<label class="control-label">{{ lang.admin.iam_use_ssl }}</label>
</div>
<div class="col-12 col-md-9">
<div class="col-12 col-md-9 d-flex align-items-center">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="use_ssl" value="1" {% if iam_settings.use_ssl == 1 %}checked{% endif %}>
<input class="form-check-input"
type="checkbox"
role="switch"
id="use_ssl"
name="use_ssl"
value="1"
onchange="if(this.checked) document.getElementById('use_tls').checked = false"
{% if iam_settings.use_ssl == 1 %}checked{% endif %}>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<i style="font-size: 16px; cursor: pointer;" class="bi bi-patch-question-fill mx-2 ms-0" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom" title="{{ lang.admin.iam_use_tls_info }}"></i>
<label class="control-label">{{ lang.admin.iam_use_tls }}</label>
</div>
<div class="col-12 col-md-9">
<div class="col-12 col-md-9 d-flex align-items-center">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" name="use_tls" value="1" {% if iam_settings.use_tls == 1 %}checked{% endif %}>
<input class="form-check-input"
type="checkbox"
role="switch"
id="use_tls"
name="use_tls"
value="1"
onchange="if(this.checked) document.getElementById('use_ssl').checked = false"
{% if iam_settings.use_tls == 1 %}checked{% endif %}>
</div>
</div>
</div>

View File

@@ -184,7 +184,7 @@
</div>
{% endif %}
</div>
<div class="ms-auto col-xl-3 col-lg-5 col-md-12 col-12 d-flex flex-column well flex-grow-1">
<div class="ms-auto col-xl-3 col-lg-5 col-md-12 col-12 d-flex flex-column well flex-grow-1" id="recent-logins">
<legend class="d-flex">
<span>{{ lang.user.recent_successful_connections }}</span>
<div id="spinner-last-login" class="ms-auto my-auto spinner-border spinner-border-sm d-none" role="status">
@@ -192,7 +192,6 @@
</div>
</legend>
<hr>
<h6 class="last-ui-login"></h6>
<div class="d-flex">
<span class="clear-last-logins mt-auto mb-2">
{{ lang.user.clear_recent_successful_connections }}

View File

@@ -199,7 +199,7 @@ services:
- phpfpm
sogo-mailcow:
image: ghcr.io/mailcow/sogo:1.131
image: ghcr.io/mailcow/sogo:1.133
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -477,7 +477,7 @@ services:
- acme
netfilter-mailcow:
image: ghcr.io/mailcow/netfilter:1.62
image: ghcr.io/mailcow/netfilter:1.61
stop_grace_period: 30s
restart: always
privileged: true

View File

@@ -236,7 +236,7 @@ function restore() {
if [[ $(find "${RESTORE_LOCATION}" \( -name '*x86*' -o -name '*aarch*' \) -exec basename {} \; | sed 's/^\.//' | sed 's/^\.//') == "" ]]; then
echo -e "\e[33mCould not find a architecture signature of the loaded backup... Maybe the backup was done before the multiarch update?"
sleep 2
echo -e "Continuing anyhow. If rspamd is crashing opon boot try remove the rspamd volume with docker volume rm ${CMPS_PRJ}_rspamd-vol-1 after you've stopped the stack.\e[0m"
echo -e "Continuing anyhow. If rspamd is crashing upon boot try remove the rspamd volume with docker volume rm ${CMPS_PRJ}_rspamd-vol-1 after you've stopped the stack.\e[0m"
sleep 2
docker stop $(docker ps -qf name=rspamd-mailcow)
docker run -i --name mailcow-backup --rm \

View File

@@ -911,7 +911,7 @@ while (($#)); do
--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)
--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.
--legacy - Switch your mailcow updates to the legacy branch. The legacy branch will only receive security updates until February 2026.
-f|--force - Force update, do not ask questions
-d|--dev - Enables Developer Mode (No Checkout of update.sh for tests)
'
@@ -1318,7 +1318,7 @@ if ! [ "$NEW_BRANCH" ]; then
echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
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"
echo -e "\e[31mYou are receiving legacy updates. The legacy branch will only receive 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"