Compare commits

...

8 Commits

Author SHA1 Message Date
FreddleSpl0it
ef0f366d1c
Merge pull request #6738 from mailcow/staging
Update 2025-09b
2025-09-12 11:29:26 +02:00
patr_
84e230de8f
[Nginx] fix: Disable IPv6 support in Nginx configuration (#6736)
Co-authored-by: patr_ <patbernh@gmail.com>
2025-09-12 11:17:18 +02:00
FreddleSpl0it
f67a12d157
Merge pull request #6726 from mailcow/fix/6135
[Web] remove unused bcc dest column from alias table
2025-09-11 13:50:25 +02:00
FreddleSpl0it
34b48eedfc
Merge pull request #6727 from mailcow/fix/6423
[SOGo] Drop deprecated `sogo_update_password` sql trigger if it still exists
2025-09-11 13:50:05 +02:00
FreddleSpl0it
0d900d4fc8
[SOGo] Drop deprecated sogo_update_password sql trigger if it still exists 2025-09-11 11:01:50 +02:00
FreddleSpl0it
642ac6d02c
[Web] remove unused bcc dest column from alias table 2025-09-11 10:34:35 +02:00
DerLinkman
4db1569c93
Squashed commit of the following:
commit 94c1a6c4e1f8901e38e4562dd208cc026277fab8
Author: DerLinkman <niklas.meyer@servercow.de>
Date:   Wed Sep 10 16:20:58 2025 +0200

    scripts:  ipv6_controller improvement + fix modules handling (#6722)

    * Fix subscript handling for modules

    * ipv6: detect case when link local is present

    * v6-controller: removed fixed-cidr for docker 28+
2025-09-10 16:22:19 +02:00
DerLinkman
94c1a6c4e1
scripts: ipv6_controller improvement + fix modules handling (#6722)
* Fix subscript handling for modules

* ipv6: detect case when link local is present

* v6-controller: removed fixed-cidr for docker 28+
2025-09-10 16:20:58 +02:00
8 changed files with 154 additions and 52 deletions

View File

@ -5,14 +5,65 @@
# 1) Check if the host supports IPv6 # 1) Check if the host supports IPv6
get_ipv6_support() { get_ipv6_support() {
if grep -qs '^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null \ # ---- helper: probe external IPv6 connectivity without DNS ----
|| ! ip -6 route show default &>/dev/null; then _probe_ipv6_connectivity() {
# Use literal, always-on IPv6 echo responders (no DNS required)
local PROBE_IPS=("2001:4860:4860::8888" "2606:4700:4700::1111")
local ip rc=1
for ip in "${PROBE_IPS[@]}"; do
if command -v ping6 &>/dev/null; then
ping6 -c1 -W2 "$ip" &>/dev/null || ping6 -c1 -w2 "$ip" &>/dev/null
rc=$?
elif command -v ping &>/dev/null; then
ping -6 -c1 -W2 "$ip" &>/dev/null || ping -6 -c1 -w2 "$ip" &>/dev/null
rc=$?
else
rc=1
fi
[[ $rc -eq 0 ]] && return 0
done
return 1
}
if [[ ! -f /proc/net/if_inet6 ]] || grep -qs '^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null; then
DETECTED_IPV6=false DETECTED_IPV6=false
echo -e "${YELLOW}IPv6 not detected on host ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" echo -e "${YELLOW}IPv6 not detected on host ${LIGHT_RED}IPv6 is administratively disabled${YELLOW}.${NC}"
else return
DETECTED_IPV6=true
echo -e "IPv6 detected on host ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
fi fi
if ip -6 route show default 2>/dev/null | grep -qE '^default'; then
echo -e "${YELLOW}Default IPv6 route found testing external IPv6 connectivity...${NC}"
if _probe_ipv6_connectivity; then
DETECTED_IPV6=true
echo -e "IPv6 detected on host ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
else
DETECTED_IPV6=false
echo -e "${YELLOW}Default IPv6 route present but external IPv6 connectivity failed ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
fi
return
fi
if ip -6 addr show scope global 2>/dev/null | grep -q 'inet6'; then
DETECTED_IPV6=false
echo -e "${YELLOW}Global IPv6 address present but no default route ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
return
fi
if ip -6 addr show scope link 2>/dev/null | grep -q 'inet6'; then
echo -e "${YELLOW}Only link-local IPv6 addresses found testing external IPv6 connectivity...${NC}"
if _probe_ipv6_connectivity; then
DETECTED_IPV6=true
echo -e "External IPv6 connectivity available ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
else
DETECTED_IPV6=false
echo -e "${YELLOW}Only link-local IPv6 present and no external connectivity ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
fi
return
fi
DETECTED_IPV6=false
echo -e "${YELLOW}IPv6 not detected on host ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
} }
# 2) Ensure Docker daemon.json has (or create) the required IPv6 settings # 2) Ensure Docker daemon.json has (or create) the required IPv6 settings
@ -21,7 +72,7 @@ docker_daemon_edit(){
DOCKER_MAJOR=$(docker version --format '{{.Server.Version}}' 2>/dev/null | cut -d. -f1) DOCKER_MAJOR=$(docker version --format '{{.Server.Version}}' 2>/dev/null | cut -d. -f1)
MISSING=() MISSING=()
_has_kv() { grep -Eq "\"$1\"\s*:\s*$2" "$DOCKER_DAEMON_CONFIG" 2>/dev/null; } _has_kv() { grep -Eq "\"$1\"[[:space:]]*:[[:space:]]*$2" "$DOCKER_DAEMON_CONFIG" 2>/dev/null; }
if [[ -f "$DOCKER_DAEMON_CONFIG" ]]; then if [[ -f "$DOCKER_DAEMON_CONFIG" ]]; then
@ -38,12 +89,18 @@ docker_daemon_edit(){
fi fi
# Gather missing keys # Gather missing keys
! _has_kv ipv6 true && MISSING+=("ipv6: true") ! _has_kv ipv6 true && MISSING+=("ipv6: true")
! grep -Eq '"fixed-cidr-v6"\s*:\s*".+"' "$DOCKER_DAEMON_CONFIG" \
&& MISSING+=('fixed-cidr-v6: "fd00:dead:beef:c0::/80"') # For Docker < 28, keep requiring fixed-cidr-v6 (default bridge needs it on old engines)
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -le 27 ]]; then if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
! grep -Eq '"fixed-cidr-v6"[[:space:]]*:[[:space:]]*".+"' "$DOCKER_DAEMON_CONFIG" \
&& MISSING+=('fixed-cidr-v6: "fd00:dead:beef:c0::/80"')
fi
# For Docker < 27, ip6tables needed and was tied to experimental in older releases
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then
_has_kv ipv6 true && ! _has_kv ip6tables true && MISSING+=("ip6tables: true") _has_kv ipv6 true && ! _has_kv ip6tables true && MISSING+=("ip6tables: true")
! _has_kv experimental true && MISSING+=("experimental: true") ! _has_kv experimental true && MISSING+=("experimental: true")
fi fi
# Fix if needed # Fix if needed
@ -60,9 +117,19 @@ docker_daemon_edit(){
cp "$DOCKER_DAEMON_CONFIG" "${DOCKER_DAEMON_CONFIG}.bak" cp "$DOCKER_DAEMON_CONFIG" "${DOCKER_DAEMON_CONFIG}.bak"
if command -v jq &>/dev/null; then if command -v jq &>/dev/null; then
TMP=$(mktemp) TMP=$(mktemp)
JQ_FILTER='.ipv6 = true | .["fixed-cidr-v6"] = "fd00:dead:beef:c0::/80"' # Base filter: ensure ipv6 = true
[[ "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]] \ JQ_FILTER='.ipv6 = true'
&& JQ_FILTER+=' | .ip6tables = true | .experimental = true'
# Add fixed-cidr-v6 only for Docker < 28
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
JQ_FILTER+=' | .["fixed-cidr-v6"] = (.["fixed-cidr-v6"] // "fd00:dead:beef:c0::/80")'
fi
# Add ip6tables/experimental only for Docker < 27
if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then
JQ_FILTER+=' | .ip6tables = true | .experimental = true'
fi
jq "$JQ_FILTER" "$DOCKER_DAEMON_CONFIG" >"$TMP" && mv "$TMP" "$DOCKER_DAEMON_CONFIG" jq "$JQ_FILTER" "$DOCKER_DAEMON_CONFIG" >"$TMP" && mv "$TMP" "$DOCKER_DAEMON_CONFIG"
echo -e "${LIGHT_GREEN}daemon.json updated. Restarting Docker...${NC}" echo -e "${LIGHT_GREEN}daemon.json updated. Restarting Docker...${NC}"
(command -v systemctl &>/dev/null && systemctl restart docker) || service docker restart (command -v systemctl &>/dev/null && systemctl restart docker) || service docker restart
@ -97,12 +164,19 @@ docker_daemon_edit(){
"experimental": true "experimental": true
} }
EOF EOF
else elif [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then
cat > "$DOCKER_DAEMON_CONFIG" <<EOF cat > "$DOCKER_DAEMON_CONFIG" <<EOF
{ {
"ipv6": true, "ipv6": true,
"fixed-cidr-v6": "fd00:dead:beef:c0::/80" "fixed-cidr-v6": "fd00:dead:beef:c0::/80"
} }
EOF
else
# Docker 28+: ipv6 works without fixed-cidr-v6
cat > "$DOCKER_DAEMON_CONFIG" <<EOF
{
"ipv6": true
}
EOF EOF
fi fi
echo -e "${GREEN}Created $DOCKER_DAEMON_CONFIG with IPv6 settings.${NC}" echo -e "${GREEN}Created $DOCKER_DAEMON_CONFIG with IPv6 settings.${NC}"
@ -122,7 +196,7 @@ configure_ipv6() {
# detect manual override if mailcow.conf is present # detect manual override if mailcow.conf is present
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]] && grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]] && grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
MANUAL_SETTING=$(grep '^ENABLE_IPV6=' "$MAILCOW_CONF" | cut -d= -f2) MANUAL_SETTING=$(grep '^ENABLE_IPV6=' "$MAILCOW_CONF" | cut -d= -f2)
elif [[ -z "$MAILCOW_CONF" ]] && [[ ! -z "${ENABLE_IPV6:-}" ]]; then elif [[ -z "$MAILCOW_CONF" ]] && [[ -n "${ENABLE_IPV6:-}" ]]; then
MANUAL_SETTING="$ENABLE_IPV6" MANUAL_SETTING="$ENABLE_IPV6"
else else
MANUAL_SETTING="" MANUAL_SETTING=""
@ -131,38 +205,34 @@ configure_ipv6() {
get_ipv6_support get_ipv6_support
# if user manually set it, check for mismatch # if user manually set it, check for mismatch
if [[ -n "$MANUAL_SETTING" ]]; then if [[ "$DETECTED_IPV6" != "true" ]]; then
if [[ "$MANUAL_SETTING" == "false" && "$DETECTED_IPV6" == "true" ]]; then if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
echo -e "${RED}ERROR: You have ENABLE_IPV6=false but your host and Docker support IPv6.${NC}" if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
echo -e "${RED}This can create an open relay. Please set ENABLE_IPV6=true in your mailcow.conf and re-run.${NC}" sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=false/' "$MAILCOW_CONF"
exit 1 else
elif [[ "$MANUAL_SETTING" == "true" && "$DETECTED_IPV6" == "false" ]]; then echo "ENABLE_IPV6=false" >> "$MAILCOW_CONF"
echo -e "${RED}ERROR: You have ENABLE_IPV6=true but your host does not support IPv6.${NC}" fi
echo -e "${RED}Please disable or fix your host/Docker IPv6 support, or set ENABLE_IPV6=false.${NC}"
exit 1
else else
return export IPV6_BOOL=false
fi fi
fi
# no manual override: proceed to set or export
if [[ "$DETECTED_IPV6" == "true" ]]; then
docker_daemon_edit
else
echo "Skipping Docker IPv6 configuration because host does not support IPv6." echo "Skipping Docker IPv6 configuration because host does not support IPv6."
echo "Make sure to check if your docker daemon.json does not include \"enable_ipv6\": true if you do not want IPv6."
echo "IPv6 configuration complete: ENABLE_IPV6=false"
sleep 2
return
fi fi
# now write into mailcow.conf or export docker_daemon_edit
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
LINE="ENABLE_IPV6=$DETECTED_IPV6"
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
sed -i "s/^ENABLE_IPV6=.*/$LINE/" "$MAILCOW_CONF" sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=true/' "$MAILCOW_CONF"
else else
echo "$LINE" >> "$MAILCOW_CONF" echo "ENABLE_IPV6=true" >> "$MAILCOW_CONF"
fi fi
else else
export IPV6_BOOL="$DETECTED_IPV6" export IPV6_BOOL=true
fi fi
echo "IPv6 configuration complete: ENABLE_IPV6=$DETECTED_IPV6" echo "IPv6 configuration complete: ENABLE_IPV6=true"
} }

View File

@ -24,6 +24,10 @@ while [[ "${DBV_NOW}" != "${DBV_NEW}" ]]; do
done done
echo "DB schema is ${DBV_NOW}" echo "DB schema is ${DBV_NOW}"
if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TRIGGER IF EXISTS sogo_update_password"
fi
# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl # 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) RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)

View File

@ -78,7 +78,7 @@ http {
{%endif%} {%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %} {% if ENABLE_IPV6 %}
{% if not HTTP_REDIRECT %} {% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%} {%endif%}
@ -105,7 +105,7 @@ http {
{%endif%} {%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %} {% if ENABLE_IPV6 %}
{% if not HTTP_REDIRECT %} {% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%} {%endif%}
@ -126,7 +126,7 @@ http {
# rspamd dynmaps: # rspamd dynmaps:
server { server {
listen 8081; listen 8081;
{% if not DISABLE_IPv6 %} {% if ENABLE_IPV6 %}
listen [::]:8081; listen [::]:8081;
{%endif%} {%endif%}
index index.php index.html; index index.php index.html;
@ -199,7 +199,7 @@ http {
{%endif%} {%endif%}
listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl;
{% if not DISABLE_IPv6 %} {% if ENABLE_IPV6 %}
{% if not HTTP_REDIRECT %} {% if not HTTP_REDIRECT %}
listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%};
{%endif%} {%endif%}

View File

@ -1949,11 +1949,6 @@ jQuery(function($){
defaultContent: '', defaultContent: '',
responsivePriority: 5, responsivePriority: 5,
}, },
{
title: lang.bcc_destinations,
data: 'bcc_dest',
defaultContent: ''
},
{ {
title: lang.sogo_visible, title: lang.sogo_visible,
data: 'sogo_visible', data: 'sogo_visible',

View File

@ -200,7 +200,7 @@ services:
- phpfpm - phpfpm
sogo-mailcow: sogo-mailcow:
image: ghcr.io/mailcow/sogo:1.134 image: ghcr.io/mailcow/sogo:1.135
environment: environment:
- DBNAME=${DBNAME} - DBNAME=${DBNAME}
- DBUSER=${DBUSER} - DBUSER=${DBUSER}

View File

@ -1,5 +1,26 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Ensure the script is run from the directory that contains a link of .env
# Resolve the directory this script lives in for consistent behavior when invoked from elsewhere
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" >/dev/null 2>&1 && pwd)"
# Ensure script is executed in the mailcow installation directory by checking for a .env symlink that points to mailcow.conf
if [ ! -L "${PWD}/.env" ]; then
echo -e "\e[33mPlease run this script from the mailcow installation directory.\e[0m"
echo -e " \e[36mcd /path/to/mailcow && ./generate_config.sh\e[0m"
exit 1
fi
# Verify the .env symlink points to a mailcow.conf file
env_target="$(readlink -f "${PWD}/.env" 2>/dev/null || true)"
if [ -z "$env_target" ] || [ "$(basename "$env_target")" != "mailcow.conf" ]; then
echo -e "\e[31mThe found .env symlink does not point to a mailcow.conf file.\e[0m"
echo -e "\e[33mPlease create a symbolic link .env -> mailcow.conf inside the mailcow directory and run this script there.\e[0m"
echo -e "\e[33mNote: 'ln -s mailcow.conf .env' will create the symlink even if mailcow.conf does not yet exist.\e[0m"
echo -e " \e[36mcd /path/to/mailcow && ln -s mailcow.conf .env && ./generate_config.sh\e[0m"
exit 1
fi
# Load mailcow Generic Scripts # Load mailcow Generic Scripts
source _modules/scripts/core.sh source _modules/scripts/core.sh
source _modules/scripts/ipv6_controller.sh source _modules/scripts/ipv6_controller.sh

View File

@ -293,7 +293,7 @@ if ! ssh -o StrictHostKeyChecking=no \
-i "${REMOTE_SSH_KEY}" \ -i "${REMOTE_SSH_KEY}" \
${REMOTE_SSH_HOST} \ ${REMOTE_SSH_HOST} \
-p ${REMOTE_SSH_PORT} \ -p ${REMOTE_SSH_PORT} \
${SCRIPT_DIR}/../update.sh -f --gc ; then "cd \"${SCRIPT_DIR}/../\" && ./update.sh -f --gc" ; then
>&2 echo -e "\e[31m[ERR]\e[0m - Could not cleanup old images on remote" >&2 echo -e "\e[31m[ERR]\e[0m - Could not cleanup old images on remote"
fi fi

View File

@ -3,6 +3,20 @@
############## Begin Function Section ############## ############## Begin Function Section ##############
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
MAILCOW_CONF="${SCRIPT_DIR}/mailcow.conf"
# Ensure the script is run from the directory that contains mailcow.conf
if [ ! -f "${PWD}/mailcow.conf" ]; then
if [ -f "${SCRIPT_DIR}/mailcow.conf" ]; then
echo -e "\e[33mPlease run this script directly from the mailcow installation directory:\e[0m"
echo -e " \e[36mcd ${SCRIPT_DIR} && ./update.sh\e[0m"
exit 1
else
echo -e "\e[31mmailcow.conf not found in current directory or script directory (\e[36m${SCRIPT_DIR}\e[31m).\e[0m"
echo -e "\e[33mRun this script directly from your mailcow installation directory.\e[0m"
exit 1
fi
fi
BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)" BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)"
MODULE_DIR="${SCRIPT_DIR}/_modules" MODULE_DIR="${SCRIPT_DIR}/_modules"
@ -27,8 +41,6 @@ if [ "$(id -u)" -ne "0" ]; then
exit 1 exit 1
fi fi
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Run pre-update-hook # Run pre-update-hook
if [ -f "${SCRIPT_DIR}/pre_update_hook.sh" ]; then if [ -f "${SCRIPT_DIR}/pre_update_hook.sh" ]; then
bash "${SCRIPT_DIR}/pre_update_hook.sh" bash "${SCRIPT_DIR}/pre_update_hook.sh"