From 2e876bda9a6c66222a38b80bc80b87a1427198f5 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Mon, 26 May 2025 15:19:42 +0200 Subject: [PATCH] ipv6: added ipv6 detection + removed ip6 nat container --- docker-compose.yml | 33 +------------- generate_config.sh | 104 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 31 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a67475316..3f0c9cafe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -405,7 +405,7 @@ services: - TZ=${TZ} - SKIP_SOGO=${SKIP_SOGO:-n} - SKIP_RSPAMD=${SKIP_RSPAMD:-n} - - DISABLE_IPv6=${DISABLE_IPv6:-n} + - ENABLE_IPV6=${ENABLE_IPV6:-true} - HTTP_REDIRECT=${HTTP_REDIRECT:-n} - PHPFPMHOST=${PHPFPMHOST:-} - SOGOHOST=${SOGOHOST:-} @@ -630,41 +630,12 @@ services: aliases: - ofelia - ipv6nat-mailcow: - depends_on: - - unbound-mailcow - - mysql-mailcow - - redis-mailcow - - clamd-mailcow - - rspamd-mailcow - - php-fpm-mailcow - - sogo-mailcow - - dovecot-mailcow - - postfix-mailcow - - memcached-mailcow - - nginx-mailcow - - acme-mailcow - - netfilter-mailcow - - watchdog-mailcow - - dockerapi-mailcow - environment: - - TZ=${TZ} - image: robbertkl/ipv6nat - security_opt: - - label=disable - restart: always - privileged: true - network_mode: "host" - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - /lib/modules:/lib/modules:ro - networks: mailcow-network: driver: bridge driver_opts: com.docker.network.bridge.name: br-mailcow - enable_ipv6: true + enable_ipv6: ${ENABLE_IPV6:-true} ipam: driver: default config: diff --git a/generate_config.sh b/generate_config.sh index c4396a9ce..adaa4a231 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -217,6 +217,104 @@ if [ ! -z "${MAILCOW_BRANCH}" ]; then git_branch=${MAILCOW_BRANCH} fi +# Check IPv6 support on the host +if grep -qs '^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null \ + || ! ip -6 route show default &>/dev/null; then + ENABLE_IPV6_LINE="ENABLE_IPV6=false" + echo "IPv6 not detected on host — disabling IPv6 support." +else + ENABLE_IPV6_LINE="ENABLE_IPV6=true" + echo "IPv6 detected on host — leaving IPv6 support enabled." +fi + +# Check Docker daemon IPv6 settings +# We require in /etc/docker/daemon.json: +# "ipv6": true +# "fixed-cidr-v6": "fd00:dead:beef:c0::/80" +# For Docker < 27: +# "ip6tables": true +# "experimental": true +DOCKER_DAEMON_CONFIG="/etc/docker/daemon.json" +MISSING=() + +_has_kv() { + grep -Eq "\"$1\"\s*:\s*$2" "$DOCKER_DAEMON_CONFIG" 2>/dev/null +} + +if [[ -f "$DOCKER_DAEMON_CONFIG" ]]; then + # ---- JSON validation ---- + if command -v jq &>/dev/null; then + if ! jq empty "$DOCKER_DAEMON_CONFIG" 2>/dev/null; then + echo "ERROR: $DOCKER_DAEMON_CONFIG contains invalid JSON." + echo "Please fix the syntax (e.g. missing commas/braces) and rerun this script." + exit 1 + fi + else + echo "WARNING: jq not found — cannot validate JSON syntax. Continuing anyway." + fi + + # require "ipv6": true + if ! _has_kv ipv6 true; then + MISSING+=("ipv6: true") + fi + + # require "fixed-cidr-v6": "fd00:dead:beef:c0::/80" + if ! grep -Eq '"fixed-cidr-v6"\s*:\s*".+"' "$DOCKER_DAEMON_CONFIG"; then + MISSING+=('fixed-cidr-v6: "fd00:dead:beef:c0::/80"') + fi + + # determine Docker major version + DOCKER_MAJOR=$(docker version --format '{{.Server.Version}}' 2>/dev/null | cut -d. -f1) + + if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then + # for Docker < 27, also require ip6tables and experimental + if _has_kv ipv6 true && ! _has_kv ip6tables true; then + MISSING+=("ip6tables: true") + fi + if ! _has_kv experimental true; then + MISSING+=("experimental: true") + fi + else + echo "Docker >= 27 detected — no ip6tables/experimental flags required." + fi + + if (( ${#MISSING[@]} > 0 )); then + echo "Your Docker daemon.json is missing: ${MISSING[*]}" + read -p "Would you like to update $DOCKER_DAEMON_CONFIG now? [Y/n] " answer + answer=${answer:-Y} + if [[ $answer =~ ^[Yy]$ ]]; then + cp "$DOCKER_DAEMON_CONFIG" "${DOCKER_DAEMON_CONFIG}.bak" + echo "Backed up original to ${DOCKER_DAEMON_CONFIG}.bak" + if command -v jq &>/dev/null; then + TMP=$(mktemp) + # build jq filter + JQ_FILTER='.ipv6 = true | .["fixed-cidr-v6"] = "fd00:dead:beef:c0::/80"' + 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" + echo "Updated $DOCKER_DAEMON_CONFIG." + echo "Restarting Docker daemon..." + if command -v systemctl &>/dev/null; then + systemctl restart docker + else + service docker restart + fi + echo "Docker restarted. Please rerun this script." + exit 1 + else + echo "Please install jq or edit $DOCKER_DAEMON_CONFIG manually (add ipv6:true, fixed-cidr-v6:\"fd00:dead:beef:c0::/80\", plus ip6tables/experimental if Docker<27), then restart Docker and rerun this script." + exit 1 + fi + else + ENABLE_IPV6_LINE="ENABLE_IPV6=false" + echo "User declined Docker config update — disabling IPv6 support." + fi + fi +else + echo "Warning: $DOCKER_DAEMON_CONFIG not found — cannot verify Docker IPv6 settings." +fi + [ ! -f ./data/conf/rspamd/override.d/worker-controller-password.inc ] && echo '# Placeholder' > ./data/conf/rspamd/override.d/worker-controller-password.inc cat << EOF > mailcow.conf @@ -517,6 +615,12 @@ WEBAUTHN_ONLY_TRUSTED_VENDORS=n # Otherwise it will work normally. SPAMHAUS_DQS_KEY= +# IPv6 Controller Section +# This variable controls the usage of IPv6 within mailcow. +# Defaults to true +# WARNING: MAKE SURE TO PROPERLY CONFIGURE IPv6 ON YOUR HOST FIRST BEFORE ENABLING THIS AS FAULTY CONFIGURATIONS CAN LEAD TO OPEN RELAYS! +$ENABLE_IPV6_LINE + # Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n # CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost DISABLE_NETFILTER_ISOLATION_RULE=n