blob: 9b7a92efe91939adbd6952806ed2e6c80ea027ba [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001#!/usr/bin/env bash
2
3# Check permissions
4if [ "$(id -u)" -ne "0" ]; then
5 echo "You need to be root"
6 exit 1
7fi
8
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02009SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
10
11# Run pre-update-hook
12if [ -f "${SCRIPT_DIR}/pre_update_hook.sh" ]; then
13 bash "${SCRIPT_DIR}/pre_update_hook.sh"
14fi
15
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010016if [[ "$(uname -r)" =~ ^4\.15\.0-60 ]]; then
17 echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!";
18 echo "Please update to 5.x or use another distribution."
19 exit 1
20fi
21
22if [[ "$(uname -r)" =~ ^4\.4\. ]]; then
23 if grep -q Ubuntu <<< $(uname -a); then
24 echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!"
25 echo "Please update to linux-generic-hwe-16.04 by running \"apt-get install --install-recommends linux-generic-hwe-16.04\""
26 exit 1
27 fi
28 echo "mailcow on a 4.4.x kernel is not supported. It may or may not work, please upgrade your kernel or continue at your own risk."
29 read -p "Press any key to continue..." < /dev/tty
30fi
31
32# Exit on error and pipefail
33set -o pipefail
34
35# Setting high dc timeout
36export COMPOSE_HTTP_TIMEOUT=600
37
38# Add /opt/bin to PATH
39PATH=$PATH:/opt/bin
40
41umask 0022
42
43for bin in curl docker-compose docker git awk sha1sum; do
44 if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
45done
46
47export LC_ALL=C
48DATE=$(date +%Y-%m-%d_%H_%M_%S)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +010049BRANCH=$(cd ${SCRIPT_DIR}; git rev-parse --abbrev-ref HEAD)
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010050
51check_online_status() {
52 CHECK_ONLINE_IPS=(1.1.1.1 9.9.9.9 8.8.8.8)
53 for ip in "${CHECK_ONLINE_IPS[@]}"; do
54 if timeout 3 ping -c 1 ${ip} > /dev/null; then
55 return 0
56 fi
57 done
58 return 1
59}
60
61prefetch_images() {
62 [[ -z ${BRANCH} ]] && { echo -e "\e[33m\nUnknown branch...\e[0m"; exit 1; }
63 git fetch origin #${BRANCH}
64 while read image; do
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020065 if [[ "${image}" == "robbertkl/ipv6nat" ]]; then
66 if ! grep -qi "ipv6nat-mailcow" docker-compose.yml || grep -qi "enable_ipv6: false" docker-compose.yml; then
67 continue
68 fi
69 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010070 RET_C=0
71 until docker pull ${image}; do
72 RET_C=$((RET_C + 1))
73 echo -e "\e[33m\nError pulling $image, retrying...\e[0m"
74 [ ${RET_C} -gt 3 ] && { echo -e "\e[31m\nToo many failed retries, exiting\e[0m"; exit 1; }
75 sleep 1
76 done
77 done < <(git show origin/${BRANCH}:docker-compose.yml | grep "image:" | awk '{ gsub("image:","", $3); print $2 }')
78}
79
80docker_garbage() {
81 IMGS_TO_DELETE=()
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +010082 for container in $(grep -oP "image: \Kmailcow.+" "${SCRIPT_DIR}/docker-compose.yml"); do
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010083 REPOSITORY=${container/:*}
84 TAG=${container/*:}
85 V_MAIN=${container/*.}
86 V_SUB=${container/*.}
87 EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }')
88 for existing_tag in ${EXISTING_TAGS[@]}; do
89 V_MAIN_EXISTING=${existing_tag/*.}
90 V_SUB_EXISTING=${existing_tag/*.}
91 # Not an integer
92 [[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue
93 [[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue
94
95 if [[ $V_MAIN_EXISTING == "latest" ]]; then
96 echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted."
97 IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
98 elif [[ $V_MAIN_EXISTING -lt $V_MAIN ]]; then
99 echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
100 IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
101 elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then
102 echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
103 IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
104 fi
105 done
106 done
107
108 if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
109 echo "Run the following command to delete unused image tags:"
110 echo
111 echo " docker rmi ${IMGS_TO_DELETE[*]}"
112 echo
113 if [ ! $FORCE ]; then
114 read -r -p "Do you want to delete old image tags right now? [y/N] " response
115 if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
116 docker rmi ${IMGS_TO_DELETE[*]}
117 else
118 echo "OK, skipped."
119 fi
120 else
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200121 echo "Running image removal without extra confirmation due to force mode."
122 docker rmi ${IMGS_TO_DELETE[*]}
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100123 fi
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100124 echo -e "\e[32mFurther cleanup...\e[0m"
125 echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100126 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100127}
128
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200129in_array() {
130 local e match="$1"
131 shift
132 for e; do [[ "$e" == "$match" ]] && return 0; done
133 return 1
134}
135
136migrate_docker_nat() {
137 NAT_CONFIG='{"ipv6":true,"fixed-cidr-v6":"fd00:dead:beef:c0::/80","experimental":true,"ip6tables":true}'
138 # Min Docker version
139 DOCKERV_REQ=20.10.2
140 # Current Docker version
141 DOCKERV_CUR=$(docker version -f '{{.Server.Version}}')
142 if grep -qi "ipv6nat-mailcow" docker-compose.yml && grep -qi "enable_ipv6: true" docker-compose.yml; then
143 echo -e "\e[32mNative IPv6 implementation available.\e[0m"
144 echo "This will enable experimental features in the Docker daemon and configure Docker to do the IPv6 NATing instead of ipv6nat-mailcow."
145 echo '!!! This step is recommended !!!'
146 echo "mailcow will try to roll back the changes if starting Docker fails after modifying the daemon.json configuration file."
147 read -r -p "Should we try to enable the native IPv6 implementation in Docker now (recommended)? [y/N] " dockernatresponse
148 if [[ ! "${dockernatresponse}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
149 echo "OK, skipping this step."
150 return 0
151 fi
152 fi
153 # Sort versions and check if we are running a newer or equal version to req
154 if [ $(printf "${DOCKERV_REQ}\n${DOCKERV_CUR}" | sort -V | tail -n1) == "${DOCKERV_CUR}" ]; then
155 # If Dockerd daemon json exists
156 if [ -s /etc/docker/daemon.json ]; then
157 IFS=',' read -r -a dockerconfig <<< $(cat /etc/docker/daemon.json | tr -cd '[:alnum:],')
158 if ! in_array ipv6true "${dockerconfig[@]}" || \
159 ! in_array experimentaltrue "${dockerconfig[@]}" || \
160 ! in_array ip6tablestrue "${dockerconfig[@]}" || \
161 ! grep -qi "fixed-cidr-v6" /etc/docker/daemon.json; then
162 echo -e "\e[33mWarning:\e[0m You seem to have modified the /etc/docker/daemon.json configuration by yourself and not fully/correctly activated the native IPv6 NAT implementation."
163 echo "You will need to merge your existing configuration manually or fix/delete the existing daemon.json configuration before trying the update process again."
164 echo -e "Please merge the following content and restart the Docker daemon:\n"
165 echo ${NAT_CONFIG}
166 return 1
167 fi
168 else
169 echo "Working on IPv6 NAT, please wait..."
170 echo ${NAT_CONFIG} > /etc/docker/daemon.json
171 ip6tables -F -t nat
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100172 [[ -e /etc/alpine-release ]] && rc-service docker restart || systemctl restart docker.service
173 if [[ $? -ne 0 ]]; then
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200174 echo -e "\e[31mError:\e[0m Failed to activate IPv6 NAT! Reverting and exiting."
175 rm /etc/docker/daemon.json
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100176 if [[ -e /etc/alpine-release ]]; then
177 rc-service docker restart
178 else
179 systemctl reset-failed docker.service
180 systemctl restart docker.service
181 fi
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200182 return 1
183 fi
184 fi
185 # Removing legacy container
186 sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.yml
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100187 if [ -s docker-compose.override.yml ]; then
188 sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.override.yml
189 if [[ "$(cat docker-compose.override.yml | sed '/^\s*$/d' | wc -l)" == "2" ]]; then
190 mv docker-compose.override.yml docker-compose.override.yml_backup
191 fi
192 fi
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200193 echo -e "\e[32mGreat! \e[0mNative IPv6 NAT is active.\e[0m"
194 else
195 echo -e "\e[31mPlease upgrade Docker to version ${DOCKERV_REQ} or above.\e[0m"
196 return 0
197 fi
198}
199
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100200while (($#)); do
201 case "${1}" in
202 --check|-c)
203 echo "Checking remote code for updates..."
204 LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized ${BRANCH} | cut -f1)
205 if [ $? -ne 0 ]; then
206 echo "A problem occurred while trying to fetch the latest revision from github."
207 exit 99
208 fi
209 if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200210 echo -e "Updated code is available.\nThe changes can be found here: https://github.com/mailcow/mailcow-dockerized/commits/master"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100211 git log --date=short --pretty=format:"%ad - %s" $(git rev-parse --short HEAD)..origin/master
212 exit 0
213 else
214 echo "No updates available."
215 exit 3
216 fi
217 ;;
218 --ours)
219 MERGE_STRATEGY=ours
220 ;;
221 --skip-start)
222 SKIP_START=y
223 ;;
224 --gc)
225 echo -e "\e[32mCollecting garbage...\e[0m"
226 docker_garbage
227 exit 0
228 ;;
229 --prefetch)
230 echo -e "\e[32mPrefetching images...\e[0m"
231 prefetch_images
232 exit 0
233 ;;
234 -f|--force)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100235 echo -e "\e[32mRunning in forced mode...\e[0m"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100236 FORCE=y
237 ;;
238 --no-update-compose)
239 NO_UPDATE_COMPOSE=y
240 ;;
241 --help|-h)
242 echo './update.sh [-c|--check, --ours, --gc, --no-update-compose, --prefetch, --skip-start, -f|--force, -h|--help]
243
244 -c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
245 --ours - Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
246 --gc - Run garbage collector to delete old image tags
247 --no-update-compose - Do not update docker-compose
248 --prefetch - Only prefetch new images and exit (useful to prepare updates)
249 --skip-start - Do not start mailcow after update
250 -f|--force - Force update, do not ask questions
251'
252 exit 1
253 esac
254 shift
255done
256
257[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;}
258chmod 600 mailcow.conf
259source mailcow.conf
260DOTS=${MAILCOW_HOSTNAME//[^.]};
261if [ ${#DOTS} -lt 2 ]; then
262 echo "MAILCOW_HOSTNAME (${MAILCOW_HOSTNAME}) is not a FQDN!"
263 echo "Please change it to a FQDN and run docker-compose down followed by docker-compose up -d"
264 exit 1
265fi
266
267if grep --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox grep detected, please install gnu grep, \"apk add --no-cache --upgrade grep\""; exit 1; fi
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200268# This will also cover sort
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100269if cp --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox cp detected, please install coreutils, \"apk add --no-cache --upgrade coreutils\""; exit 1; fi
270if sed --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox sed detected, please install gnu sed, \"apk add --no-cache --upgrade sed\""; exit 1; fi
271
272CONFIG_ARRAY=(
273 "SKIP_LETS_ENCRYPT"
274 "SKIP_SOGO"
275 "USE_WATCHDOG"
276 "WATCHDOG_NOTIFY_EMAIL"
277 "WATCHDOG_NOTIFY_BAN"
278 "WATCHDOG_EXTERNAL_CHECKS"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200279 "WATCHDOG_SUBJECT"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100280 "SKIP_CLAMD"
281 "SKIP_IP_CHECK"
282 "ADDITIONAL_SAN"
283 "DOVEADM_PORT"
284 "IPV4_NETWORK"
285 "IPV6_NETWORK"
286 "LOG_LINES"
287 "SNAT_TO_SOURCE"
288 "SNAT6_TO_SOURCE"
289 "COMPOSE_PROJECT_NAME"
290 "SQL_PORT"
291 "API_KEY"
292 "API_KEY_READ_ONLY"
293 "API_ALLOW_FROM"
294 "MAILDIR_GC_TIME"
295 "MAILDIR_SUB"
296 "ACL_ANYONE"
297 "SOLR_HEAP"
298 "SKIP_SOLR"
299 "ENABLE_SSL_SNI"
300 "ALLOW_ADMIN_EMAIL_LOGIN"
301 "SKIP_HTTP_VERIFICATION"
302 "SOGO_EXPIRE_SESSION"
303 "REDIS_PORT"
304 "DOVECOT_MASTER_USER"
305 "DOVECOT_MASTER_PASS"
306 "MAILCOW_PASS_SCHEME"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200307 "ADDITIONAL_SERVER_NAMES"
308 "ACME_CONTACT"
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100309 "WATCHDOG_VERBOSE"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100310)
311
312sed -i --follow-symlinks '$a\' mailcow.conf
313for option in ${CONFIG_ARRAY[@]}; do
314 if [[ ${option} == "ADDITIONAL_SAN" ]]; then
315 if ! grep -q ${option} mailcow.conf; then
316 echo "Adding new option \"${option}\" to mailcow.conf"
317 echo "${option}=" >> mailcow.conf
318 fi
319 elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then
320 if ! grep -q ${option} mailcow.conf; then
321 echo "Adding new option \"${option}\" to mailcow.conf"
322 echo "COMPOSE_PROJECT_NAME=mailcowdockerized" >> mailcow.conf
323 fi
324 elif [[ ${option} == "DOVEADM_PORT" ]]; then
325 if ! grep -q ${option} mailcow.conf; then
326 echo "Adding new option \"${option}\" to mailcow.conf"
327 echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf
328 fi
329 elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then
330 if ! grep -q ${option} mailcow.conf; then
331 echo "Adding new option \"${option}\" to mailcow.conf"
332 echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf
333 fi
334 elif [[ ${option} == "LOG_LINES" ]]; then
335 if ! grep -q ${option} mailcow.conf; then
336 echo "Adding new option \"${option}\" to mailcow.conf"
337 echo '# Max log lines per service to keep in Redis logs' >> mailcow.conf
338 echo "LOG_LINES=9999" >> mailcow.conf
339 fi
340 elif [[ ${option} == "IPV4_NETWORK" ]]; then
341 if ! grep -q ${option} mailcow.conf; then
342 echo "Adding new option \"${option}\" to mailcow.conf"
343 echo '# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)' >> mailcow.conf
344 echo "IPV4_NETWORK=172.22.1" >> mailcow.conf
345 fi
346 elif [[ ${option} == "IPV6_NETWORK" ]]; then
347 if ! grep -q ${option} mailcow.conf; then
348 echo "Adding new option \"${option}\" to mailcow.conf"
349 echo '# Internal IPv6 subnet in fc00::/7' >> mailcow.conf
350 echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> mailcow.conf
351 fi
352 elif [[ ${option} == "SQL_PORT" ]]; then
353 if ! grep -q ${option} mailcow.conf; then
354 echo "Adding new option \"${option}\" to mailcow.conf"
355 echo '# Bind SQL to 127.0.0.1 on port 13306' >> mailcow.conf
356 echo "SQL_PORT=127.0.0.1:13306" >> mailcow.conf
357 fi
358 elif [[ ${option} == "API_KEY" ]]; then
359 if ! grep -q ${option} mailcow.conf; then
360 echo "Adding new option \"${option}\" to mailcow.conf"
361 echo '# Create or override API key for web UI' >> mailcow.conf
362 echo "#API_KEY=" >> mailcow.conf
363 fi
364 elif [[ ${option} == "API_KEY_READ_ONLY" ]]; then
365 if ! grep -q ${option} mailcow.conf; then
366 echo "Adding new option \"${option}\" to mailcow.conf"
367 echo '# Create or override read-only API key for web UI' >> mailcow.conf
368 echo "#API_KEY_READ_ONLY=" >> mailcow.conf
369 fi
370 elif [[ ${option} == "API_ALLOW_FROM" ]]; then
371 if ! grep -q ${option} mailcow.conf; then
372 echo "Adding new option \"${option}\" to mailcow.conf"
373 echo '# Must be set for API_KEY to be active' >> mailcow.conf
374 echo '# IPs only, no networks (networks can be set via UI)' >> mailcow.conf
375 echo "#API_ALLOW_FROM=" >> mailcow.conf
376 fi
377 elif [[ ${option} == "SNAT_TO_SOURCE" ]]; then
378 if ! grep -q ${option} mailcow.conf; then
379 echo "Adding new option \"${option}\" to mailcow.conf"
380 echo '# Use this IPv4 for outgoing connections (SNAT)' >> mailcow.conf
381 echo "#SNAT_TO_SOURCE=" >> mailcow.conf
382 fi
383 elif [[ ${option} == "SNAT6_TO_SOURCE" ]]; then
384 if ! grep -q ${option} mailcow.conf; then
385 echo "Adding new option \"${option}\" to mailcow.conf"
386 echo '# Use this IPv6 for outgoing connections (SNAT)' >> mailcow.conf
387 echo "#SNAT6_TO_SOURCE=" >> mailcow.conf
388 fi
389 elif [[ ${option} == "MAILDIR_GC_TIME" ]]; then
390 if ! grep -q ${option} mailcow.conf; then
391 echo "Adding new option \"${option}\" to mailcow.conf"
392 echo '# Garbage collector cleanup' >> mailcow.conf
393 echo '# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring' >> mailcow.conf
394 echo '# How long should objects remain in the garbage until they are being deleted? (value in minutes)' >> mailcow.conf
395 echo '# Check interval is hourly' >> mailcow.conf
396 echo 'MAILDIR_GC_TIME=1440' >> mailcow.conf
397 fi
398 elif [[ ${option} == "ACL_ANYONE" ]]; then
399 if ! grep -q ${option} mailcow.conf; then
400 echo "Adding new option \"${option}\" to mailcow.conf"
401 echo '# Set this to "allow" to enable the anyone pseudo user. Disabled by default.' >> mailcow.conf
402 echo '# When enabled, ACL can be created, that apply to "All authenticated users"' >> mailcow.conf
403 echo '# This should probably only be activated on mail hosts, that are used exclusivly by one organisation.' >> mailcow.conf
404 echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf
405 echo 'ACL_ANYONE=disallow' >> mailcow.conf
406 fi
407 elif [[ ${option} == "SOLR_HEAP" ]]; then
408 if ! grep -q ${option} mailcow.conf; then
409 echo "Adding new option \"${option}\" to mailcow.conf"
410 echo '# Solr heap size, there is no recommendation, please see Solr docs.' >> mailcow.conf
411 echo '# Solr is a prone to run OOM on large systems and should be monitored. Unmonitored Solr setups are not recommended.' >> mailcow.conf
412 echo '# Solr will refuse to start with total system memory below or equal to 2 GB.' >> mailcow.conf
413 echo "SOLR_HEAP=1024" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200414 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100415 elif [[ ${option} == "SKIP_SOLR" ]]; then
416 if ! grep -q ${option} mailcow.conf; then
417 echo "Adding new option \"${option}\" to mailcow.conf"
418 echo '# Solr is disabled by default after upgrading from non-Solr to Solr-enabled mailcows.' >> mailcow.conf
419 echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
420 echo "SKIP_SOLR=y" >> mailcow.conf
421 fi
422 elif [[ ${option} == "ENABLE_SSL_SNI" ]]; then
423 if ! grep -q ${option} mailcow.conf; then
424 echo "Adding new option \"${option}\" to mailcow.conf"
425 echo '# Create seperate certificates for all domains - y/n' >> mailcow.conf
426 echo '# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames' >> mailcow.conf
427 echo '# see https://wiki.dovecot.org/SSL/SNIClientSupport' >> mailcow.conf
428 echo "ENABLE_SSL_SNI=n" >> mailcow.conf
429 fi
430 elif [[ ${option} == "SKIP_SOGO" ]]; then
431 if ! grep -q ${option} mailcow.conf; then
432 echo "Adding new option \"${option}\" to mailcow.conf"
433 echo '# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n' >> mailcow.conf
434 echo "SKIP_SOGO=n" >> mailcow.conf
435 fi
436 elif [[ ${option} == "MAILDIR_SUB" ]]; then
437 if ! grep -q ${option} mailcow.conf; then
438 echo "Adding new option \"${option}\" to mailcow.conf"
439 echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
440 echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
441 echo "MAILDIR_SUB=" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200442 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100443 elif [[ ${option} == "WATCHDOG_NOTIFY_BAN" ]]; then
444 if ! grep -q ${option} mailcow.conf; then
445 echo "Adding new option \"${option}\" to mailcow.conf"
446 echo '# Notify about banned IP. Includes whois lookup.' >> mailcow.conf
447 echo "WATCHDOG_NOTIFY_BAN=y" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200448 fi
449 elif [[ ${option} == "WATCHDOG_SUBJECT" ]]; then
450 if ! grep -q ${option} mailcow.conf; then
451 echo "Adding new option \"${option}\" to mailcow.conf"
452 echo '# Subject for watchdog mails. Defaults to "Watchdog ALERT" followed by the error message.' >> mailcow.conf
453 echo "#WATCHDOG_SUBJECT=" >> mailcow.conf
454 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100455 elif [[ ${option} == "WATCHDOG_EXTERNAL_CHECKS" ]]; then
456 if ! grep -q ${option} mailcow.conf; then
457 echo "Adding new option \"${option}\" to mailcow.conf"
458 echo '# Checks if mailcow is an open relay. Requires a SAL. More checks will follow.' >> mailcow.conf
459 echo '# No data is collected. Opt-in and anonymous.' >> mailcow.conf
460 echo '# Will only work with unmodified mailcow setups.' >> mailcow.conf
461 echo "WATCHDOG_EXTERNAL_CHECKS=n" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200462 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100463 elif [[ ${option} == "SOGO_EXPIRE_SESSION" ]]; then
464 if ! grep -q ${option} mailcow.conf; then
465 echo "Adding new option \"${option}\" to mailcow.conf"
466 echo '# SOGo session timeout in minutes' >> mailcow.conf
467 echo "SOGO_EXPIRE_SESSION=480" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200468 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100469 elif [[ ${option} == "REDIS_PORT" ]]; then
470 if ! grep -q ${option} mailcow.conf; then
471 echo "Adding new option \"${option}\" to mailcow.conf"
472 echo "REDIS_PORT=127.0.0.1:7654" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200473 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100474 elif [[ ${option} == "DOVECOT_MASTER_USER" ]]; then
475 if ! grep -q ${option} mailcow.conf; then
476 echo "Adding new option \"${option}\" to mailcow.conf"
477 echo '# DOVECOT_MASTER_USER and _PASS must _both_ be provided. No special chars.' >> mailcow.conf
478 echo '# Empty by default to auto-generate master user and password on start.' >> mailcow.conf
479 echo '# User expands to DOVECOT_MASTER_USER@mailcow.local' >> mailcow.conf
480 echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
481 echo "DOVECOT_MASTER_USER=" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200482 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100483 elif [[ ${option} == "DOVECOT_MASTER_PASS" ]]; then
484 if ! grep -q ${option} mailcow.conf; then
485 echo "Adding new option \"${option}\" to mailcow.conf"
486 echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
487 echo "DOVECOT_MASTER_PASS=" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200488 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100489 elif [[ ${option} == "MAILCOW_PASS_SCHEME" ]]; then
490 if ! grep -q ${option} mailcow.conf; then
491 echo "Adding new option \"${option}\" to mailcow.conf"
492 echo '# Password hash algorithm' >> mailcow.conf
493 echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
494 echo '# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/' >> mailcow.conf
495 echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200496 fi
497 elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
498 if ! grep -q ${option} mailcow.conf; then
499 echo '# Additional server names for mailcow UI' >> mailcow.conf
500 echo '#' >> mailcow.conf
501 echo '# Specify alternative addresses for the mailcow UI to respond to' >> mailcow.conf
502 echo '# This is useful when you set mail.* as ADDITIONAL_SAN and want to make sure mail.maildomain.com will always point to the mailcow UI.' >> mailcow.conf
503 echo '# If the server name does not match a known site, Nginx decides by best-guess and may redirect users to the wrong web root.' >> mailcow.conf
504 echo '# You can understand this as server_name directive in Nginx.' >> mailcow.conf
505 echo '# Comma separated list without spaces! Example: ADDITIONAL_SERVER_NAMES=a.b.c,d.e.f' >> mailcow.conf
506 echo 'ADDITIONAL_SERVER_NAMES=' >> mailcow.conf
507 fi
508 elif [[ ${option} == "ACME_CONTACT" ]]; then
509 if ! grep -q ${option} mailcow.conf; then
510 echo '# Lets Encrypt registration contact information' >> mailcow.conf
511 echo '# Optional: Leave empty for none' >> mailcow.conf
512 echo '# This value is only used on first order!' >> mailcow.conf
513 echo '# Setting it at a later point will require the following steps:' >> mailcow.conf
514 echo '# https://mailcow.github.io/mailcow-dockerized-docs/debug-reset-tls/' >> mailcow.conf
515 echo 'ACME_CONTACT=' >> mailcow.conf
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100516 fi
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100517elif [[ ${option} == "WATCHDOG_VERBOSE" ]]; then
518 if ! grep -q ${option} mailcow.conf; then
519 echo '# Enable watchdog verbose logging' >> mailcow.conf
520 echo 'WATCHDOG_VERBOSE=n' >> mailcow.conf
521 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100522 elif ! grep -q ${option} mailcow.conf; then
523 echo "Adding new option \"${option}\" to mailcow.conf"
524 echo "${option}=n" >> mailcow.conf
525 fi
526done
527
528echo -en "Checking internet connection... "
529if ! check_online_status; then
530 echo -e "\e[31mfailed\e[0m"
531 exit 1
532else
533 echo -e "\e[32mOK\e[0m"
534fi
535
536echo -e "\e[32mChecking for newer update script...\e[0m"
537SHA1_1=$(sha1sum update.sh)
538git fetch origin #${BRANCH}
539git checkout origin/${BRANCH} update.sh
540SHA1_2=$(sha1sum update.sh)
541if [[ ${SHA1_1} != ${SHA1_2} ]]; then
542 echo "update.sh changed, please run this script again, exiting."
543 chmod +x update.sh
544 exit 2
545fi
546
547if [[ -f mailcow.conf ]]; then
548 source mailcow.conf
549else
550 echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m"
551 exit 1
552fi
553
554if [ ! $FORCE ]; then
555 read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200556 if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100557 echo "OK, exiting."
558 exit 0
559 fi
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200560 migrate_docker_nat
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100561fi
562
563echo -e "\e[32mValidating docker-compose stack configuration...\e[0m"
564if ! docker-compose config -q; then
565 echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
566 exit 1
567fi
568
569echo -e "\e[32mChecking for conflicting bridges...\e[0m"
570MAILCOW_BRIDGE=$(docker-compose config | grep -i com.docker.network.bridge.name | cut -d':' -f2)
571while read NAT_ID; do
572 iptables -t nat -D POSTROUTING $NAT_ID
573done < <(iptables -L -vn -t nat --line-numbers | grep $IPV4_NETWORK | grep -E 'MASQUERADE.*all' | grep -v ${MAILCOW_BRIDGE} | cut -d' ' -f1)
574
575DIFF_DIRECTORY=update_diffs
576DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S")
577mv diff_before_update* ${DIFF_DIRECTORY}/ 2> /dev/null
578if ! git diff-index --quiet HEAD; then
579 echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
580 mkdir -p ${DIFF_DIRECTORY}
581 git diff --stat > ${DIFF_FILE}
582 git diff >> ${DIFF_FILE}
583fi
584
585echo -e "\e[32mPrefetching images...\e[0m"
586prefetch_images
587
588echo -e "\e[32mStopping mailcow...\e[0m"
589sleep 2
590MAILCOW_CONTAINERS=($(docker-compose ps -q))
591docker-compose down
592echo -e "\e[32mChecking for remaining containers...\e[0m"
593sleep 2
594for container in "${MAILCOW_CONTAINERS[@]}"; do
595 docker rm -f "$container" 2> /dev/null
596done
597
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200598[[ -f data/conf/nginx/ZZZ-ejabberd.conf ]] && rm data/conf/nginx/ZZZ-ejabberd.conf
599
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100600# Silently fixing remote url from andryyy to mailcow
601git remote set-url origin https://github.com/mailcow/mailcow-dockerized
602echo -e "\e[32mCommitting current status...\e[0m"
603[[ -z "$(git config user.name)" ]] && git config user.name moo
604[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
605[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
606git add -u
607git commit -am "Before update on ${DATE}" > /dev/null
608echo -e "\e[32mFetching updated code from remote...\e[0m"
609git fetch origin #${BRANCH}
610echo -e "\e[32mMerging local with remote code (recursive, strategy: \"${MERGE_STRATEGY:-theirs}\", options: \"patience\"...\e[0m"
611git config merge.defaultToUpstream true
612git merge -X${MERGE_STRATEGY:-theirs} -Xpatience -m "After update on ${DATE}"
613# Need to use a variable to not pass return codes of if checks
614MERGE_RETURN=$?
615if [[ ${MERGE_RETURN} == 128 ]]; then
616 echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
617 exit 1
618elif [[ ${MERGE_RETURN} == 1 ]]; then
619 echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
620 git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
621 git add -A
622 git commit -m "After update on ${DATE}" > /dev/null
623 git checkout .
624 echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
625elif [[ ${MERGE_RETURN} != 0 ]]; then
626 echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
627 echo
628 echo "Run docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors."
629 exit 1
630fi
631
632if [[ ${NO_UPDATE_COMPOSE} == "y" ]]; then
633 echo -e "\e[33mNot fetching latest docker-compose, please check for updates manually!\e[0m"
634elif [[ -e /etc/alpine-release ]]; then
635 echo -e "\e[33mNot fetching latest docker-compose, because you are using Alpine Linux without glibc support. Please update docker-compose via apk!\e[0m"
636else
637 echo -e "\e[32mFetching new docker-compose version...\e[0m"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200638 echo -e "\e[32mTrying to determine GLIBC version...\e[0m"
639 if ldd --version > /dev/null; then
640 GLIBC_V=$(ldd --version | grep -E '(GLIBC|GNU libc)' | rev | cut -d ' ' -f1 | rev | cut -d '.' -f2)
641 if [ ! -z "${GLIBC_V}" ] && [ ${GLIBC_V} -gt 27 ]; then
642 DC_DL_SUFFIX=
643 else
644 DC_DL_SUFFIX=legacy
645 fi
646 else
647 DC_DL_SUFFIX=legacy
648 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100649 sleep 1
650 if [[ ! -z $(which pip) && $(pip list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 ]]; then
651 true
652 #prevent breaking a working docker-compose installed with pip
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200653 elif [[ $(curl -sL -w "%{http_code}" https://www.servercow.de/docker-compose/latest.php?vers=${DC_DL_SUFFIX} -o /dev/null) == "200" ]]; then
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100654 LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php)
655 COMPOSE_VERSION=$(docker-compose version --short)
656 if [[ "$LATEST_COMPOSE" != "$COMPOSE_VERSION" ]]; then
657 COMPOSE_PATH=$(which docker-compose)
658 if [[ -w ${COMPOSE_PATH} ]]; then
659 curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $COMPOSE_PATH
660 chmod +x $COMPOSE_PATH
661 else
662 echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m"
663 fi
664 fi
665 else
666 echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m"
667 fi
668fi
669
670echo -e "\e[32mFetching new images, if any...\e[0m"
671sleep 2
672docker-compose pull
673
674# Fix missing SSL, does not overwrite existing files
675[[ ! -d data/assets/ssl ]] && mkdir -p data/assets/ssl
676cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/
677
678echo -e "Checking IPv6 settings... "
679if grep -q 'SYSCTL_IPV6_DISABLED=1' mailcow.conf; then
680 echo
681 echo '!! IMPORTANT !!'
682 echo
683 echo 'SYSCTL_IPV6_DISABLED was removed due to complications. IPv6 can be disabled by editing "docker-compose.yml" and setting "enable_ipv6: true" to "enable_ipv6: false".'
684 echo 'This setting will only be active after a complete shutdown of mailcow by running "docker-compose down" followed by "docker-compose up -d".'
685 echo
686 echo '!! IMPORTANT !!'
687 echo
688 read -p "Press any key to continue..." < /dev/tty
689fi
690
691# Checking for old project name bug
692sed -i --follow-symlinks 's#COMPOSEPROJECT_NAME#COMPOSE_PROJECT_NAME#g' mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200693# Checking old, wrong bindings
694sed -i --follow-symlinks 's/HTTP_BIND=0.0.0.0/HTTP_BIND=/g' mailcow.conf
695sed -i --follow-symlinks 's/HTTPS_BIND=0.0.0.0/HTTPS_BIND=/g' mailcow.conf
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100696
697# Fix Rspamd maps
698if [ -f data/conf/rspamd/custom/global_from_blacklist.map ]; then
699 mv data/conf/rspamd/custom/global_from_blacklist.map data/conf/rspamd/custom/global_smtp_from_blacklist.map
700fi
701if [ -f data/conf/rspamd/custom/global_from_whitelist.map ]; then
702 mv data/conf/rspamd/custom/global_from_whitelist.map data/conf/rspamd/custom/global_smtp_from_whitelist.map
703fi
704
705# Fix deprecated metrics.conf
706if [ -f "data/conf/rspamd/local.d/metrics.conf" ]; then
707 if [ ! -z "$(git diff --name-only origin/master data/conf/rspamd/local.d/metrics.conf)" ]; then
708 echo -e "\e[33mWARNING\e[0m - Please migrate your customizations of data/conf/rspamd/local.d/metrics.conf to actions.conf and groups.conf after this update."
709 echo "The deprecated configuration file metrics.conf will be moved to metrics.conf_deprecated after updating mailcow."
710 fi
711 mv data/conf/rspamd/local.d/metrics.conf data/conf/rspamd/local.d/metrics.conf_deprecated
712fi
713
714if [[ ${SKIP_START} == "y" ]]; then
715 echo -e "\e[33mNot starting mailcow, please run \"docker-compose up -d --remove-orphans\" to start mailcow.\e[0m"
716else
717 echo -e "\e[32mStarting mailcow...\e[0m"
718 sleep 2
719 docker-compose up -d --remove-orphans
720fi
721
722echo -e "\e[32mCollecting garbage...\e[0m"
723docker_garbage
724
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200725# Run post-update-hook
726if [ -f "${SCRIPT_DIR}/post_update_hook.sh" ]; then
727 bash "${SCRIPT_DIR}/post_update_hook.sh"
728fi
729
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100730#echo "In case you encounter any problem, hard-reset to a state before updating mailcow:"
731#echo
732#git reflog --color=always | grep "Before update on "
733#echo
734#echo "Use \"git reset --hard hash-on-the-left\" and run docker-compose up -d afterwards."