55
66# 1) Check if the host supports IPv6
77get_ipv6_support () {
8- if grep -qs ' ^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2> /dev/null \
9- || ! ip -6 route show default & > /dev/null; then
8+ # ---- helper: probe external IPv6 connectivity without DNS ----
9+ _probe_ipv6_connectivity () {
10+ # Use literal, always-on IPv6 echo responders (no DNS required)
11+ local PROBE_IPS=(" 2001:4860:4860::8888" " 2606:4700:4700::1111" )
12+ local ip rc=1
13+
14+ for ip in " ${PROBE_IPS[@]} " ; do
15+ if command -v ping6 & > /dev/null; then
16+ ping6 -c1 -W2 " $ip " & > /dev/null || ping6 -c1 -w2 " $ip " & > /dev/null
17+ rc=$?
18+ elif command -v ping & > /dev/null; then
19+ ping -6 -c1 -W2 " $ip " & > /dev/null || ping -6 -c1 -w2 " $ip " & > /dev/null
20+ rc=$?
21+ else
22+ rc=1
23+ fi
24+ [[ $rc -eq 0 ]] && return 0
25+ done
26+ return 1
27+ }
28+
29+ if [[ ! -f /proc/net/if_inet6 ]] || grep -qs ' ^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2> /dev/null; then
1030 DETECTED_IPV6=false
11- echo -e " ${YELLOW} IPv6 not detected on host – ${LIGHT_RED} disabling IPv6 support${YELLOW} .${NC} "
12- else
13- DETECTED_IPV6=true
14- echo -e " IPv6 detected on host – ${LIGHT_GREEN} leaving IPv6 support enabled${YELLOW} .${NC} "
31+ echo -e " ${YELLOW} IPv6 not detected on host – ${LIGHT_RED} IPv6 is administratively disabled${YELLOW} .${NC} "
32+ return
33+ fi
34+
35+ if ip -6 route show default 2> /dev/null | grep -qE ' ^default' ; then
36+ echo -e " ${YELLOW} Default IPv6 route found – testing external IPv6 connectivity...${NC} "
37+ if _probe_ipv6_connectivity; then
38+ DETECTED_IPV6=true
39+ echo -e " IPv6 detected on host – ${LIGHT_GREEN} leaving IPv6 support enabled${YELLOW} .${NC} "
40+ else
41+ DETECTED_IPV6=false
42+ echo -e " ${YELLOW} Default IPv6 route present but external IPv6 connectivity failed – ${LIGHT_RED} disabling IPv6 support${YELLOW} .${NC} "
43+ fi
44+ return
45+ fi
46+
47+ if ip -6 addr show scope global 2> /dev/null | grep -q ' inet6' ; then
48+ DETECTED_IPV6=false
49+ echo -e " ${YELLOW} Global IPv6 address present but no default route – ${LIGHT_RED} disabling IPv6 support${YELLOW} .${NC} "
50+ return
1551 fi
52+
53+ if ip -6 addr show scope link 2> /dev/null | grep -q ' inet6' ; then
54+ echo -e " ${YELLOW} Only link-local IPv6 addresses found – testing external IPv6 connectivity...${NC} "
55+ if _probe_ipv6_connectivity; then
56+ DETECTED_IPV6=true
57+ echo -e " External IPv6 connectivity available – ${LIGHT_GREEN} leaving IPv6 support enabled${YELLOW} .${NC} "
58+ else
59+ DETECTED_IPV6=false
60+ echo -e " ${YELLOW} Only link-local IPv6 present and no external connectivity – ${LIGHT_RED} disabling IPv6 support${YELLOW} .${NC} "
61+ fi
62+ return
63+ fi
64+
65+ DETECTED_IPV6=false
66+ echo -e " ${YELLOW} IPv6 not detected on host – ${LIGHT_RED} disabling IPv6 support${YELLOW} .${NC} "
1667}
1768
1869# 2) Ensure Docker daemon.json has (or create) the required IPv6 settings
@@ -21,7 +72,7 @@ docker_daemon_edit(){
2172 DOCKER_MAJOR=$( docker version --format ' {{.Server.Version}}' 2> /dev/null | cut -d. -f1)
2273 MISSING=()
2374
24- _has_kv () { grep -Eq " \" $1 \" \s*:\s *$2 " " $DOCKER_DAEMON_CONFIG " 2> /dev/null; }
75+ _has_kv () { grep -Eq " \" $1 \" [[:space:]]*:[[:space:]] *$2 " " $DOCKER_DAEMON_CONFIG " 2> /dev/null; }
2576
2677 if [[ -f " $DOCKER_DAEMON_CONFIG " ]]; then
2778
@@ -38,12 +89,18 @@ docker_daemon_edit(){
3889 fi
3990
4091 # Gather missing keys
41- ! _has_kv ipv6 true && MISSING+=(" ipv6: true" )
42- ! grep -Eq ' "fixed-cidr-v6"\s*:\s*".+"' " $DOCKER_DAEMON_CONFIG " \
43- && MISSING+=(' fixed-cidr-v6: "fd00:dead:beef:c0::/80"' )
44- if [[ -n " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -le 27 ]]; then
92+ ! _has_kv ipv6 true && MISSING+=(" ipv6: true" )
93+
94+ # For Docker < 28, keep requiring fixed-cidr-v6 (default bridge needs it on old engines)
95+ if [[ -n " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -lt 28 ]]; then
96+ ! grep -Eq ' "fixed-cidr-v6"[[:space:]]*:[[:space:]]*".+"' " $DOCKER_DAEMON_CONFIG " \
97+ && MISSING+=(' fixed-cidr-v6: "fd00:dead:beef:c0::/80"' )
98+ fi
99+
100+ # For Docker < 27, ip6tables needed and was tied to experimental in older releases
101+ if [[ -n " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -lt 27 ]]; then
45102 _has_kv ipv6 true && ! _has_kv ip6tables true && MISSING+=(" ip6tables: true" )
46- ! _has_kv experimental true && MISSING+=(" experimental: true" )
103+ ! _has_kv experimental true && MISSING+=(" experimental: true" )
47104 fi
48105
49106 # Fix if needed
@@ -60,9 +117,19 @@ docker_daemon_edit(){
60117 cp " $DOCKER_DAEMON_CONFIG " " ${DOCKER_DAEMON_CONFIG} .bak"
61118 if command -v jq & > /dev/null; then
62119 TMP=$( mktemp)
63- JQ_FILTER=' .ipv6 = true | .["fixed-cidr-v6"] = "fd00:dead:beef:c0::/80"'
64- [[ " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -lt 27 ]] \
65- && JQ_FILTER+=' | .ip6tables = true | .experimental = true'
120+ # Base filter: ensure ipv6 = true
121+ JQ_FILTER=' .ipv6 = true'
122+
123+ # Add fixed-cidr-v6 only for Docker < 28
124+ if [[ -n " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -lt 28 ]]; then
125+ JQ_FILTER+=' | .["fixed-cidr-v6"] = (.["fixed-cidr-v6"] // "fd00:dead:beef:c0::/80")'
126+ fi
127+
128+ # Add ip6tables/experimental only for Docker < 27
129+ if [[ -n " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -lt 27 ]]; then
130+ JQ_FILTER+=' | .ip6tables = true | .experimental = true'
131+ fi
132+
66133 jq " $JQ_FILTER " " $DOCKER_DAEMON_CONFIG " > " $TMP " && mv " $TMP " " $DOCKER_DAEMON_CONFIG "
67134 echo -e " ${LIGHT_GREEN} daemon.json updated. Restarting Docker...${NC} "
68135 (command -v systemctl & > /dev/null && systemctl restart docker) || service docker restart
@@ -97,12 +164,19 @@ docker_daemon_edit(){
97164 "experimental": true
98165}
99166EOF
100- else
167+ elif [[ -n " $DOCKER_MAJOR " && " $DOCKER_MAJOR " -lt 28 ]] ; then
101168 cat > " $DOCKER_DAEMON_CONFIG " << EOF
102169{
103170 "ipv6": true,
104171 "fixed-cidr-v6": "fd00:dead:beef:c0::/80"
105172}
173+ EOF
174+ else
175+ # Docker 28+: ipv6 works without fixed-cidr-v6
176+ cat > " $DOCKER_DAEMON_CONFIG " << EOF
177+ {
178+ "ipv6": true
179+ }
106180EOF
107181 fi
108182 echo -e " ${GREEN} Created $DOCKER_DAEMON_CONFIG with IPv6 settings.${NC} "
@@ -122,7 +196,7 @@ configure_ipv6() {
122196 # detect manual override if mailcow.conf is present
123197 if [[ -n " $MAILCOW_CONF " && -f " $MAILCOW_CONF " ]] && grep -q ' ^ENABLE_IPV6=' " $MAILCOW_CONF " ; then
124198 MANUAL_SETTING=$( grep ' ^ENABLE_IPV6=' " $MAILCOW_CONF " | cut -d= -f2)
125- elif [[ -z " $MAILCOW_CONF " ]] && [[ ! -z " ${ENABLE_IPV6:- } " ]]; then
199+ elif [[ -z " $MAILCOW_CONF " ]] && [[ -n " ${ENABLE_IPV6:- } " ]]; then
126200 MANUAL_SETTING=" $ENABLE_IPV6 "
127201 else
128202 MANUAL_SETTING=" "
@@ -131,38 +205,34 @@ configure_ipv6() {
131205 get_ipv6_support
132206
133207 # if user manually set it, check for mismatch
134- if [[ -n " $MANUAL_SETTING " ]]; then
135- if [[ " $MANUAL_SETTING " == " false" && " $DETECTED_IPV6 " == " true" ]]; then
136- echo -e " ${RED} ERROR: You have ENABLE_IPV6=false but your host and Docker support IPv6.${NC} "
137- echo -e " ${RED} This can create an open relay. Please set ENABLE_IPV6=true in your mailcow.conf and re-run.${NC} "
138- exit 1
139- elif [[ " $MANUAL_SETTING " == " true" && " $DETECTED_IPV6 " == " false" ]]; then
140- echo -e " ${RED} ERROR: You have ENABLE_IPV6=true but your host does not support IPv6.${NC} "
141- echo -e " ${RED} Please disable or fix your host/Docker IPv6 support, or set ENABLE_IPV6=false.${NC} "
142- exit 1
208+ if [[ " $DETECTED_IPV6 " != " true" ]]; then
209+ if [[ -n " $MAILCOW_CONF " && -f " $MAILCOW_CONF " ]]; then
210+ if grep -q ' ^ENABLE_IPV6=' " $MAILCOW_CONF " ; then
211+ sed -i ' s/^ENABLE_IPV6=.*/ENABLE_IPV6=false/' " $MAILCOW_CONF "
212+ else
213+ echo " ENABLE_IPV6=false" >> " $MAILCOW_CONF "
214+ fi
143215 else
144- return
216+ export IPV6_BOOL=false
145217 fi
146- fi
147-
148- # no manual override: proceed to set or export
149- if [[ " $DETECTED_IPV6 " == " true" ]]; then
150- docker_daemon_edit
151- else
152218 echo " Skipping Docker IPv6 configuration because host does not support IPv6."
219+ echo " Make sure to check if your docker daemon.json does not include \" enable_ipv6\" : true if you do not want IPv6."
220+ echo " IPv6 configuration complete: ENABLE_IPV6=false"
221+ sleep 2
222+ return
153223 fi
154224
155- # now write into mailcow.conf or export
225+ docker_daemon_edit
226+
156227 if [[ -n " $MAILCOW_CONF " && -f " $MAILCOW_CONF " ]]; then
157- LINE=" ENABLE_IPV6=$DETECTED_IPV6 "
158228 if grep -q ' ^ENABLE_IPV6=' " $MAILCOW_CONF " ; then
159- sed -i " s/^ENABLE_IPV6=.*/$LINE / " " $MAILCOW_CONF "
229+ sed -i ' s/^ENABLE_IPV6=.*/ENABLE_IPV6=true/ ' " $MAILCOW_CONF "
160230 else
161- echo " $LINE " >> " $MAILCOW_CONF "
231+ echo " ENABLE_IPV6=true " >> " $MAILCOW_CONF "
162232 fi
163233 else
164- export IPV6_BOOL=" $DETECTED_IPV6 "
234+ export IPV6_BOOL=true
165235 fi
166236
167- echo " IPv6 configuration complete: ENABLE_IPV6=$DETECTED_IPV6 "
237+ echo " IPv6 configuration complete: ENABLE_IPV6=true "
168238}
0 commit comments