Compare commits

...

5 Commits

Author SHA1 Message Date
DerLinkman
d8a856d1cb update: backport compose v5 fix for legacy branch 2025-12-08 14:48:55 +01:00
FreddleSpl0it
11356674ba [Redis] Update to 7.4.6 2025-10-15 09:25:01 +02:00
FreddleSpl0it
8c5f6c0321 [Dovecot] Use Jinja2 sandbox for rendering quota and quarantine notifications 2025-07-15 10:46:50 +02:00
FreddleSpl0it
6a16a4886c Merge branch 'staging' into legacy 2025-03-24 11:48:11 +01:00
FreddleSpl0it
4222f73ea0 Add switch to legacy version 2025-03-20 14:40:12 +01:00
6 changed files with 99 additions and 36 deletions

View File

@@ -8,7 +8,8 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
import jinja2
from jinja2 import Template
from jinja2 import TemplateError
from jinja2.sandbox import SandboxedEnvironment
import json
import redis
import time
@@ -80,17 +81,22 @@ try:
if len(meta_query) == 0:
return
msg_count = len(meta_query)
env = SandboxedEnvironment()
if r.get('Q_HTML'):
try:
template = Template(r.get('Q_HTML'))
except:
print("Error: Cannot parse quarantine template, falling back to default template.")
with open('/templates/quarantine.tpl') as file_:
template = Template(file_.read())
try:
template = env.from_string(r.get('Q_HTML'))
except Exception:
print("Error: Cannot parse quarantine template, falling back to default template.")
with open('/templates/quarantine.tpl') as file_:
template = env.from_string(file_.read())
else:
with open('/templates/quarantine.tpl') as file_:
template = Template(file_.read())
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
with open('/templates/quarantine.tpl') as file_:
template = env.from_string(file_.read())
try:
html = template.render(meta=meta_query, username=rcpt, counter=msg_count, hostname=mailcow_hostname, quarantine_acl=quarantine_acl)
except (jinja2.exceptions.SecurityError, TemplateError) as ex:
print(f"SecurityError or TemplateError in template rendering: {ex}")
return
text = html2text.html2text(html)
count = 0
while count < 15:
@@ -165,4 +171,4 @@ try:
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
finally:
os.unlink(pidfile)
os.unlink(pidfile)

View File

@@ -6,7 +6,7 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
import jinja2
from jinja2 import Template
from jinja2.sandbox import SandboxedEnvironment
import redis
import time
import json
@@ -33,16 +33,24 @@ while True:
if r.get('QW_HTML'):
try:
template = Template(r.get('QW_HTML'))
except:
print("Error: Cannot parse quarantine template, falling back to default template.")
env = SandboxedEnvironment()
template = env.from_string(r.get('QW_HTML'))
except Exception:
print("Error: Cannot parse quota template, falling back to default template.")
with open('/templates/quota.tpl') as file_:
template = Template(file_.read())
env = SandboxedEnvironment()
template = env.from_string(file_.read())
else:
with open('/templates/quota.tpl') as file_:
template = Template(file_.read())
env = SandboxedEnvironment()
template = env.from_string(file_.read())
try:
html = template.render(username=username, percent=percent)
except (jinja2.exceptions.SecurityError, jinja2.TemplateError) as ex:
print(f"SecurityError or TemplateError in template rendering: {ex}")
sys.exit(1)
html = template.render(username=username, percent=percent)
text = html2text.html2text(html)
try:
@@ -91,4 +99,4 @@ except:
try:
sys.stderr.close()
except:
pass
pass

View File

@@ -235,7 +235,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
$(".totp-authenticator-selection").click(function(){
$(".totp-authenticator-selection").removeClass("active");
$(this).addClass("active");
var id = $(this).children('input').first().val();
$("#totp_selected_id").val(id);
@@ -244,7 +244,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
if ($('.totp-authenticator-selection').length == 1 &&
$('#pending_tfa_tab_yubi_otp').length == 0 &&
$('.webauthn-authenticator-selection').length == 0){
// select default if only one authenticator exists
$('.totp-authenticator-selection').addClass("active");
@@ -257,7 +257,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
$('#pending_tfa_tab_totp').on('shown.bs.tab', function() {
// autofocus
setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 200);
});
});
// validate Yubi OTP tfa
if ($('.webauthn-authenticator-selection').length == 0){
// autofocus
@@ -276,10 +276,10 @@ function recursiveBase64StrToArrayBuffer(obj) {
$(".webauthn-authenticator-selection").click(function(){
$(".webauthn-authenticator-selection").removeClass("active");
$(this).addClass("active");
var id = $(this).children('input').first().val();
$("#webauthn_selected_id").val(id);
var webauthn_status_auth = document.getElementById('webauthn_status_auth');
webauthn_status_auth.style.setProperty('display', 'flex', 'important');
var webauthn_return_code = document.getElementById('webauthn_return_code');
@@ -302,7 +302,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
console.log(json);
if (json.success === false) throw new Error();
if (json.type === "error") throw new Error(json.msg);
recursiveBase64StrToArrayBuffer(json);
return json;
}).then(getCredentialArgs => {
@@ -329,7 +329,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
webauthn_return_code.style.setProperty('display', 'block', 'important');
webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry;
});
}
}
});
$('#ConfirmTFAModal').on('hidden.bs.modal', function(){
// cancel pending login
@@ -540,7 +540,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
Version: <a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">{{ mailcow_info.version_tag }}
</a>
</span>
{% endif %}
{% endif %}
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %}
<span class="version">
🛠️🐮 + 🐋 = 💕
@@ -549,6 +549,14 @@ function recursiveBase64StrToArrayBuffer(obj) {
<span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span>
</span>
{% endif %}
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "legacy" and mailcow_info.version_tag|default %}
<span class="version">
⚰️🐮 + 🐋 = 💕
Legacy: <a href="{{ mailcow_info.git_project_url }}/commit/{{ mailcow_info.git_commit }}" target="_blank">{{ mailcow_info.version_tag }}
</a><br>
<span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span>
</span>
{% endif %}
</div>
</body>
</html>

View File

@@ -42,7 +42,7 @@ services:
- mysql
redis-mailcow:
image: redis:7.4.2-alpine
image: redis:7.4.6-alpine
entrypoint: ["/bin/sh","/redis-conf.sh"]
volumes:
- redis-vol-1:/data/
@@ -234,7 +234,7 @@ services:
- sogo
dovecot-mailcow:
image: ghcr.io/mailcow/dovecot:2.32
image: ghcr.io/mailcow/dovecot:2.34-legacy
depends_on:
- mysql-mailcow
- netfilter-mailcow

View File

@@ -181,11 +181,15 @@ if [[ ${SKIP_BRANCH} != y ]]; then
echo "Available Branches:"
echo "- master branch (stable updates) | default, recommended [1]"
echo "- nightly branch (unstable updates, testing) | not-production ready [2]"
echo "- legacy branch (supported until February 2026) | deprecated, security updates only [3]"
sleep 1
while [ -z "${MAILCOW_BRANCH}" ]; do
read -r -p "Choose the Branch with it's number [1/2] " branch
read -r -p "Choose the Branch with it's number [1/2/3] " branch
case $branch in
[3])
MAILCOW_BRANCH="legacy"
;;
[2])
MAILCOW_BRANCH="nightly"
;;
@@ -534,6 +538,10 @@ case ${git_branch} in
mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
mailcow_last_git_version=""
;;
legacy)
mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
mailcow_last_git_version=""
;;
*)
mailcow_git_version=$(git rev-parse --short HEAD)
mailcow_last_git_version=""

View File

@@ -177,7 +177,7 @@ remove_obsolete_nginx_ports() {
detect_docker_compose_command(){
if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
if docker compose > /dev/null 2>&1; then
if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then
if docker compose version --short | grep -e "^[2-9]\." -e "^v[2-9]\." -e "^[1-9][0-9]\." -e "^v[1-9][0-9]\." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=native
COMPOSE_COMMAND="docker compose"
echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m"
@@ -187,12 +187,12 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
elif docker-compose > /dev/null 2>&1; then
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
if docker-compose version --short | grep -e "^[2-9]\." -e "^[1-9][0-9]\." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=standalone
COMPOSE_COMMAND="docker-compose"
echo -e "\e[33mFound Docker Compose Standalone.\e[0m"
@@ -202,7 +202,7 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
else
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m"
exit 1
fi
fi
@@ -895,8 +895,12 @@ while (($#)); do
echo -e "\e[32mRunning in Developer mode...\e[0m"
DEV=y
;;
--legacy)
CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"
NEW_BRANCH="legacy"
;;
--help|-h)
echo './update.sh [-c|--check, --check-tags, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help]
echo './update.sh [-c|--check, --check-tags, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, --legacy, -f|--force, -d|--dev, -h|--help]
-c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
--check-tags - Check for newer tags and exit (exit codes => 0: newer tag available, 3: no newer tag)
@@ -906,7 +910,8 @@ while (($#)); do
--prefetch - Only prefetch new images and exit (useful to prepare updates)
--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.
--stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly or --legacy.
--legacy - Switch your mailcow updates to the legacy branch. The legacy branch will only recieve security updates until February 2026.
-f|--force - Force update, do not ask questions
-d|--dev - Enables Developer Mode (No Checkout of update.sh for tests)
'
@@ -1312,6 +1317,11 @@ if ! [ "$NEW_BRANCH" ]; then
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"
elif [ "${BRANCH}" == "legacy" ]; then
echo -e "\e[31mYou are receiving legacy updates. The legacy branch will only recieve security updates until February 2026.\e[0m"
sleep 1
echo -e "\e[33mTo change that run the update.sh Script one time with the --stable parameter to switch to stable builds.\e[0m"
else
echo -e "\e[33mYou are receiving updates from an unsupported branch.\e[0m"
sleep 1
@@ -1374,6 +1384,29 @@ elif [ "$NEW_BRANCH" == "nightly" ] && [ "$CURRENT_BRANCH" != "nightly" ]; then
fi
git fetch origin
git checkout -f "${BRANCH}"
elif [ "$NEW_BRANCH" == "legacy" ] && [ "$CURRENT_BRANCH" != "legacy" ]; then
echo -e "\e[33mYou are about to switch your mailcow Updates to the legacy branch.\e[0m"
sleep 1
echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no Data is lost...\e[0m"
sleep 1
echo -e "\e[31mWARNING: A switch to stable or nightly is possible any time.\e[0m"
read -r -p "Are you sure you want to continue upgrading to the legacy branch? [y/N] " response
if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "OK. If you prepared yourself for that please run the update.sh Script with the --legacy parameter again to trigger this process here."
exit 0
fi
BRANCH=$NEW_BRANCH
DIFF_DIRECTORY=update_diffs
DIFF_FILE=${DIFF_DIRECTORY}/diff_before_upgrade_to_legacy_$(date +"%Y-%m-%d-%H-%M-%S")
mv diff_before_upgrade* ${DIFF_DIRECTORY}/ 2> /dev/null
if ! git diff-index --quiet HEAD; then
echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
mkdir -p ${DIFF_DIRECTORY}
git diff "${BRANCH}" --stat > "${DIFF_FILE}"
git diff "${BRANCH}" >> "${DIFF_FILE}"
fi
git fetch origin
git checkout -f "${BRANCH}"
fi
if [ ! "$DEV" ]; then