blob: dc578f9b46f1986f108958decbb8d81bab31e8c2 [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)
49BRANCH=$(git rev-parse --abbrev-ref HEAD)
50
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=()
82 for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do
83 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
124 fi
125 echo -e "\e[32mFurther cleanup...\e[0m"
126 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\""
127}
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
172 if ! systemctl restart docker.service; then
173 echo -e "\e[31mError:\e[0m Failed to activate IPv6 NAT! Reverting and exiting."
174 rm /etc/docker/daemon.json
175 systemctl reset-failed docker.service
176 systemctl restart docker.service
177 return 1
178 fi
179 fi
180 # Removing legacy container
181 sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.yml
182 echo -e "\e[32mGreat! \e[0mNative IPv6 NAT is active.\e[0m"
183 else
184 echo -e "\e[31mPlease upgrade Docker to version ${DOCKERV_REQ} or above.\e[0m"
185 return 0
186 fi
187}
188
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100189while (($#)); do
190 case "${1}" in
191 --check|-c)
192 echo "Checking remote code for updates..."
193 LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized ${BRANCH} | cut -f1)
194 if [ $? -ne 0 ]; then
195 echo "A problem occurred while trying to fetch the latest revision from github."
196 exit 99
197 fi
198 if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200199 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 +0100200 git log --date=short --pretty=format:"%ad - %s" $(git rev-parse --short HEAD)..origin/master
201 exit 0
202 else
203 echo "No updates available."
204 exit 3
205 fi
206 ;;
207 --ours)
208 MERGE_STRATEGY=ours
209 ;;
210 --skip-start)
211 SKIP_START=y
212 ;;
213 --gc)
214 echo -e "\e[32mCollecting garbage...\e[0m"
215 docker_garbage
216 exit 0
217 ;;
218 --prefetch)
219 echo -e "\e[32mPrefetching images...\e[0m"
220 prefetch_images
221 exit 0
222 ;;
223 -f|--force)
224 echo -e "\e[32mForcing Update...\e[0m"
225 FORCE=y
226 ;;
227 --no-update-compose)
228 NO_UPDATE_COMPOSE=y
229 ;;
230 --help|-h)
231 echo './update.sh [-c|--check, --ours, --gc, --no-update-compose, --prefetch, --skip-start, -f|--force, -h|--help]
232
233 -c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
234 --ours - Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
235 --gc - Run garbage collector to delete old image tags
236 --no-update-compose - Do not update docker-compose
237 --prefetch - Only prefetch new images and exit (useful to prepare updates)
238 --skip-start - Do not start mailcow after update
239 -f|--force - Force update, do not ask questions
240'
241 exit 1
242 esac
243 shift
244done
245
246[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;}
247chmod 600 mailcow.conf
248source mailcow.conf
249DOTS=${MAILCOW_HOSTNAME//[^.]};
250if [ ${#DOTS} -lt 2 ]; then
251 echo "MAILCOW_HOSTNAME (${MAILCOW_HOSTNAME}) is not a FQDN!"
252 echo "Please change it to a FQDN and run docker-compose down followed by docker-compose up -d"
253 exit 1
254fi
255
256if 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 +0200257# This will also cover sort
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100258if 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
259if 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
260
261CONFIG_ARRAY=(
262 "SKIP_LETS_ENCRYPT"
263 "SKIP_SOGO"
264 "USE_WATCHDOG"
265 "WATCHDOG_NOTIFY_EMAIL"
266 "WATCHDOG_NOTIFY_BAN"
267 "WATCHDOG_EXTERNAL_CHECKS"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200268 "WATCHDOG_SUBJECT"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100269 "SKIP_CLAMD"
270 "SKIP_IP_CHECK"
271 "ADDITIONAL_SAN"
272 "DOVEADM_PORT"
273 "IPV4_NETWORK"
274 "IPV6_NETWORK"
275 "LOG_LINES"
276 "SNAT_TO_SOURCE"
277 "SNAT6_TO_SOURCE"
278 "COMPOSE_PROJECT_NAME"
279 "SQL_PORT"
280 "API_KEY"
281 "API_KEY_READ_ONLY"
282 "API_ALLOW_FROM"
283 "MAILDIR_GC_TIME"
284 "MAILDIR_SUB"
285 "ACL_ANYONE"
286 "SOLR_HEAP"
287 "SKIP_SOLR"
288 "ENABLE_SSL_SNI"
289 "ALLOW_ADMIN_EMAIL_LOGIN"
290 "SKIP_HTTP_VERIFICATION"
291 "SOGO_EXPIRE_SESSION"
292 "REDIS_PORT"
293 "DOVECOT_MASTER_USER"
294 "DOVECOT_MASTER_PASS"
295 "MAILCOW_PASS_SCHEME"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200296 "ADDITIONAL_SERVER_NAMES"
297 "ACME_CONTACT"
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100298)
299
300sed -i --follow-symlinks '$a\' mailcow.conf
301for option in ${CONFIG_ARRAY[@]}; do
302 if [[ ${option} == "ADDITIONAL_SAN" ]]; then
303 if ! grep -q ${option} mailcow.conf; then
304 echo "Adding new option \"${option}\" to mailcow.conf"
305 echo "${option}=" >> mailcow.conf
306 fi
307 elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then
308 if ! grep -q ${option} mailcow.conf; then
309 echo "Adding new option \"${option}\" to mailcow.conf"
310 echo "COMPOSE_PROJECT_NAME=mailcowdockerized" >> mailcow.conf
311 fi
312 elif [[ ${option} == "DOVEADM_PORT" ]]; then
313 if ! grep -q ${option} mailcow.conf; then
314 echo "Adding new option \"${option}\" to mailcow.conf"
315 echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf
316 fi
317 elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then
318 if ! grep -q ${option} mailcow.conf; then
319 echo "Adding new option \"${option}\" to mailcow.conf"
320 echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf
321 fi
322 elif [[ ${option} == "LOG_LINES" ]]; then
323 if ! grep -q ${option} mailcow.conf; then
324 echo "Adding new option \"${option}\" to mailcow.conf"
325 echo '# Max log lines per service to keep in Redis logs' >> mailcow.conf
326 echo "LOG_LINES=9999" >> mailcow.conf
327 fi
328 elif [[ ${option} == "IPV4_NETWORK" ]]; then
329 if ! grep -q ${option} mailcow.conf; then
330 echo "Adding new option \"${option}\" to mailcow.conf"
331 echo '# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)' >> mailcow.conf
332 echo "IPV4_NETWORK=172.22.1" >> mailcow.conf
333 fi
334 elif [[ ${option} == "IPV6_NETWORK" ]]; then
335 if ! grep -q ${option} mailcow.conf; then
336 echo "Adding new option \"${option}\" to mailcow.conf"
337 echo '# Internal IPv6 subnet in fc00::/7' >> mailcow.conf
338 echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> mailcow.conf
339 fi
340 elif [[ ${option} == "SQL_PORT" ]]; then
341 if ! grep -q ${option} mailcow.conf; then
342 echo "Adding new option \"${option}\" to mailcow.conf"
343 echo '# Bind SQL to 127.0.0.1 on port 13306' >> mailcow.conf
344 echo "SQL_PORT=127.0.0.1:13306" >> mailcow.conf
345 fi
346 elif [[ ${option} == "API_KEY" ]]; then
347 if ! grep -q ${option} mailcow.conf; then
348 echo "Adding new option \"${option}\" to mailcow.conf"
349 echo '# Create or override API key for web UI' >> mailcow.conf
350 echo "#API_KEY=" >> mailcow.conf
351 fi
352 elif [[ ${option} == "API_KEY_READ_ONLY" ]]; then
353 if ! grep -q ${option} mailcow.conf; then
354 echo "Adding new option \"${option}\" to mailcow.conf"
355 echo '# Create or override read-only API key for web UI' >> mailcow.conf
356 echo "#API_KEY_READ_ONLY=" >> mailcow.conf
357 fi
358 elif [[ ${option} == "API_ALLOW_FROM" ]]; then
359 if ! grep -q ${option} mailcow.conf; then
360 echo "Adding new option \"${option}\" to mailcow.conf"
361 echo '# Must be set for API_KEY to be active' >> mailcow.conf
362 echo '# IPs only, no networks (networks can be set via UI)' >> mailcow.conf
363 echo "#API_ALLOW_FROM=" >> mailcow.conf
364 fi
365 elif [[ ${option} == "SNAT_TO_SOURCE" ]]; then
366 if ! grep -q ${option} mailcow.conf; then
367 echo "Adding new option \"${option}\" to mailcow.conf"
368 echo '# Use this IPv4 for outgoing connections (SNAT)' >> mailcow.conf
369 echo "#SNAT_TO_SOURCE=" >> mailcow.conf
370 fi
371 elif [[ ${option} == "SNAT6_TO_SOURCE" ]]; then
372 if ! grep -q ${option} mailcow.conf; then
373 echo "Adding new option \"${option}\" to mailcow.conf"
374 echo '# Use this IPv6 for outgoing connections (SNAT)' >> mailcow.conf
375 echo "#SNAT6_TO_SOURCE=" >> mailcow.conf
376 fi
377 elif [[ ${option} == "MAILDIR_GC_TIME" ]]; then
378 if ! grep -q ${option} mailcow.conf; then
379 echo "Adding new option \"${option}\" to mailcow.conf"
380 echo '# Garbage collector cleanup' >> mailcow.conf
381 echo '# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring' >> mailcow.conf
382 echo '# How long should objects remain in the garbage until they are being deleted? (value in minutes)' >> mailcow.conf
383 echo '# Check interval is hourly' >> mailcow.conf
384 echo 'MAILDIR_GC_TIME=1440' >> mailcow.conf
385 fi
386 elif [[ ${option} == "ACL_ANYONE" ]]; then
387 if ! grep -q ${option} mailcow.conf; then
388 echo "Adding new option \"${option}\" to mailcow.conf"
389 echo '# Set this to "allow" to enable the anyone pseudo user. Disabled by default.' >> mailcow.conf
390 echo '# When enabled, ACL can be created, that apply to "All authenticated users"' >> mailcow.conf
391 echo '# This should probably only be activated on mail hosts, that are used exclusivly by one organisation.' >> mailcow.conf
392 echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf
393 echo 'ACL_ANYONE=disallow' >> mailcow.conf
394 fi
395 elif [[ ${option} == "SOLR_HEAP" ]]; then
396 if ! grep -q ${option} mailcow.conf; then
397 echo "Adding new option \"${option}\" to mailcow.conf"
398 echo '# Solr heap size, there is no recommendation, please see Solr docs.' >> mailcow.conf
399 echo '# Solr is a prone to run OOM on large systems and should be monitored. Unmonitored Solr setups are not recommended.' >> mailcow.conf
400 echo '# Solr will refuse to start with total system memory below or equal to 2 GB.' >> mailcow.conf
401 echo "SOLR_HEAP=1024" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200402 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100403 elif [[ ${option} == "SKIP_SOLR" ]]; then
404 if ! grep -q ${option} mailcow.conf; then
405 echo "Adding new option \"${option}\" to mailcow.conf"
406 echo '# Solr is disabled by default after upgrading from non-Solr to Solr-enabled mailcows.' >> mailcow.conf
407 echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
408 echo "SKIP_SOLR=y" >> mailcow.conf
409 fi
410 elif [[ ${option} == "ENABLE_SSL_SNI" ]]; then
411 if ! grep -q ${option} mailcow.conf; then
412 echo "Adding new option \"${option}\" to mailcow.conf"
413 echo '# Create seperate certificates for all domains - y/n' >> mailcow.conf
414 echo '# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames' >> mailcow.conf
415 echo '# see https://wiki.dovecot.org/SSL/SNIClientSupport' >> mailcow.conf
416 echo "ENABLE_SSL_SNI=n" >> mailcow.conf
417 fi
418 elif [[ ${option} == "SKIP_SOGO" ]]; then
419 if ! grep -q ${option} mailcow.conf; then
420 echo "Adding new option \"${option}\" to mailcow.conf"
421 echo '# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n' >> mailcow.conf
422 echo "SKIP_SOGO=n" >> mailcow.conf
423 fi
424 elif [[ ${option} == "MAILDIR_SUB" ]]; then
425 if ! grep -q ${option} mailcow.conf; then
426 echo "Adding new option \"${option}\" to mailcow.conf"
427 echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
428 echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
429 echo "MAILDIR_SUB=" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200430 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100431 elif [[ ${option} == "WATCHDOG_NOTIFY_BAN" ]]; then
432 if ! grep -q ${option} mailcow.conf; then
433 echo "Adding new option \"${option}\" to mailcow.conf"
434 echo '# Notify about banned IP. Includes whois lookup.' >> mailcow.conf
435 echo "WATCHDOG_NOTIFY_BAN=y" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200436 fi
437 elif [[ ${option} == "WATCHDOG_SUBJECT" ]]; then
438 if ! grep -q ${option} mailcow.conf; then
439 echo "Adding new option \"${option}\" to mailcow.conf"
440 echo '# Subject for watchdog mails. Defaults to "Watchdog ALERT" followed by the error message.' >> mailcow.conf
441 echo "#WATCHDOG_SUBJECT=" >> mailcow.conf
442 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100443 elif [[ ${option} == "WATCHDOG_EXTERNAL_CHECKS" ]]; then
444 if ! grep -q ${option} mailcow.conf; then
445 echo "Adding new option \"${option}\" to mailcow.conf"
446 echo '# Checks if mailcow is an open relay. Requires a SAL. More checks will follow.' >> mailcow.conf
447 echo '# No data is collected. Opt-in and anonymous.' >> mailcow.conf
448 echo '# Will only work with unmodified mailcow setups.' >> mailcow.conf
449 echo "WATCHDOG_EXTERNAL_CHECKS=n" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200450 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100451 elif [[ ${option} == "SOGO_EXPIRE_SESSION" ]]; then
452 if ! grep -q ${option} mailcow.conf; then
453 echo "Adding new option \"${option}\" to mailcow.conf"
454 echo '# SOGo session timeout in minutes' >> mailcow.conf
455 echo "SOGO_EXPIRE_SESSION=480" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200456 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100457 elif [[ ${option} == "REDIS_PORT" ]]; then
458 if ! grep -q ${option} mailcow.conf; then
459 echo "Adding new option \"${option}\" to mailcow.conf"
460 echo "REDIS_PORT=127.0.0.1:7654" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200461 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100462 elif [[ ${option} == "DOVECOT_MASTER_USER" ]]; then
463 if ! grep -q ${option} mailcow.conf; then
464 echo "Adding new option \"${option}\" to mailcow.conf"
465 echo '# DOVECOT_MASTER_USER and _PASS must _both_ be provided. No special chars.' >> mailcow.conf
466 echo '# Empty by default to auto-generate master user and password on start.' >> mailcow.conf
467 echo '# User expands to DOVECOT_MASTER_USER@mailcow.local' >> mailcow.conf
468 echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
469 echo "DOVECOT_MASTER_USER=" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200470 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100471 elif [[ ${option} == "DOVECOT_MASTER_PASS" ]]; then
472 if ! grep -q ${option} mailcow.conf; then
473 echo "Adding new option \"${option}\" to mailcow.conf"
474 echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
475 echo "DOVECOT_MASTER_PASS=" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200476 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100477 elif [[ ${option} == "MAILCOW_PASS_SCHEME" ]]; then
478 if ! grep -q ${option} mailcow.conf; then
479 echo "Adding new option \"${option}\" to mailcow.conf"
480 echo '# Password hash algorithm' >> mailcow.conf
481 echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
482 echo '# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/' >> mailcow.conf
483 echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200484 fi
485 elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
486 if ! grep -q ${option} mailcow.conf; then
487 echo '# Additional server names for mailcow UI' >> mailcow.conf
488 echo '#' >> mailcow.conf
489 echo '# Specify alternative addresses for the mailcow UI to respond to' >> mailcow.conf
490 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
491 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
492 echo '# You can understand this as server_name directive in Nginx.' >> mailcow.conf
493 echo '# Comma separated list without spaces! Example: ADDITIONAL_SERVER_NAMES=a.b.c,d.e.f' >> mailcow.conf
494 echo 'ADDITIONAL_SERVER_NAMES=' >> mailcow.conf
495 fi
496 elif [[ ${option} == "ACME_CONTACT" ]]; then
497 if ! grep -q ${option} mailcow.conf; then
498 echo '# Lets Encrypt registration contact information' >> mailcow.conf
499 echo '# Optional: Leave empty for none' >> mailcow.conf
500 echo '# This value is only used on first order!' >> mailcow.conf
501 echo '# Setting it at a later point will require the following steps:' >> mailcow.conf
502 echo '# https://mailcow.github.io/mailcow-dockerized-docs/debug-reset-tls/' >> mailcow.conf
503 echo 'ACME_CONTACT=' >> mailcow.conf
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100504 fi
505 elif ! grep -q ${option} mailcow.conf; then
506 echo "Adding new option \"${option}\" to mailcow.conf"
507 echo "${option}=n" >> mailcow.conf
508 fi
509done
510
511echo -en "Checking internet connection... "
512if ! check_online_status; then
513 echo -e "\e[31mfailed\e[0m"
514 exit 1
515else
516 echo -e "\e[32mOK\e[0m"
517fi
518
519echo -e "\e[32mChecking for newer update script...\e[0m"
520SHA1_1=$(sha1sum update.sh)
521git fetch origin #${BRANCH}
522git checkout origin/${BRANCH} update.sh
523SHA1_2=$(sha1sum update.sh)
524if [[ ${SHA1_1} != ${SHA1_2} ]]; then
525 echo "update.sh changed, please run this script again, exiting."
526 chmod +x update.sh
527 exit 2
528fi
529
530if [[ -f mailcow.conf ]]; then
531 source mailcow.conf
532else
533 echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m"
534 exit 1
535fi
536
537if [ ! $FORCE ]; then
538 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 +0200539 if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100540 echo "OK, exiting."
541 exit 0
542 fi
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200543 migrate_docker_nat
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100544fi
545
546echo -e "\e[32mValidating docker-compose stack configuration...\e[0m"
547if ! docker-compose config -q; then
548 echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
549 exit 1
550fi
551
552echo -e "\e[32mChecking for conflicting bridges...\e[0m"
553MAILCOW_BRIDGE=$(docker-compose config | grep -i com.docker.network.bridge.name | cut -d':' -f2)
554while read NAT_ID; do
555 iptables -t nat -D POSTROUTING $NAT_ID
556done < <(iptables -L -vn -t nat --line-numbers | grep $IPV4_NETWORK | grep -E 'MASQUERADE.*all' | grep -v ${MAILCOW_BRIDGE} | cut -d' ' -f1)
557
558DIFF_DIRECTORY=update_diffs
559DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S")
560mv diff_before_update* ${DIFF_DIRECTORY}/ 2> /dev/null
561if ! git diff-index --quiet HEAD; then
562 echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
563 mkdir -p ${DIFF_DIRECTORY}
564 git diff --stat > ${DIFF_FILE}
565 git diff >> ${DIFF_FILE}
566fi
567
568echo -e "\e[32mPrefetching images...\e[0m"
569prefetch_images
570
571echo -e "\e[32mStopping mailcow...\e[0m"
572sleep 2
573MAILCOW_CONTAINERS=($(docker-compose ps -q))
574docker-compose down
575echo -e "\e[32mChecking for remaining containers...\e[0m"
576sleep 2
577for container in "${MAILCOW_CONTAINERS[@]}"; do
578 docker rm -f "$container" 2> /dev/null
579done
580
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200581[[ -f data/conf/nginx/ZZZ-ejabberd.conf ]] && rm data/conf/nginx/ZZZ-ejabberd.conf
582
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100583# Silently fixing remote url from andryyy to mailcow
584git remote set-url origin https://github.com/mailcow/mailcow-dockerized
585echo -e "\e[32mCommitting current status...\e[0m"
586[[ -z "$(git config user.name)" ]] && git config user.name moo
587[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
588[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
589git add -u
590git commit -am "Before update on ${DATE}" > /dev/null
591echo -e "\e[32mFetching updated code from remote...\e[0m"
592git fetch origin #${BRANCH}
593echo -e "\e[32mMerging local with remote code (recursive, strategy: \"${MERGE_STRATEGY:-theirs}\", options: \"patience\"...\e[0m"
594git config merge.defaultToUpstream true
595git merge -X${MERGE_STRATEGY:-theirs} -Xpatience -m "After update on ${DATE}"
596# Need to use a variable to not pass return codes of if checks
597MERGE_RETURN=$?
598if [[ ${MERGE_RETURN} == 128 ]]; then
599 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"
600 exit 1
601elif [[ ${MERGE_RETURN} == 1 ]]; then
602 echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
603 git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
604 git add -A
605 git commit -m "After update on ${DATE}" > /dev/null
606 git checkout .
607 echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
608elif [[ ${MERGE_RETURN} != 0 ]]; then
609 echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
610 echo
611 echo "Run docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors."
612 exit 1
613fi
614
615if [[ ${NO_UPDATE_COMPOSE} == "y" ]]; then
616 echo -e "\e[33mNot fetching latest docker-compose, please check for updates manually!\e[0m"
617elif [[ -e /etc/alpine-release ]]; then
618 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"
619else
620 echo -e "\e[32mFetching new docker-compose version...\e[0m"
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200621 echo -e "\e[32mTrying to determine GLIBC version...\e[0m"
622 if ldd --version > /dev/null; then
623 GLIBC_V=$(ldd --version | grep -E '(GLIBC|GNU libc)' | rev | cut -d ' ' -f1 | rev | cut -d '.' -f2)
624 if [ ! -z "${GLIBC_V}" ] && [ ${GLIBC_V} -gt 27 ]; then
625 DC_DL_SUFFIX=
626 else
627 DC_DL_SUFFIX=legacy
628 fi
629 else
630 DC_DL_SUFFIX=legacy
631 fi
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100632 sleep 1
633 if [[ ! -z $(which pip) && $(pip list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 ]]; then
634 true
635 #prevent breaking a working docker-compose installed with pip
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200636 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 +0100637 LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php)
638 COMPOSE_VERSION=$(docker-compose version --short)
639 if [[ "$LATEST_COMPOSE" != "$COMPOSE_VERSION" ]]; then
640 COMPOSE_PATH=$(which docker-compose)
641 if [[ -w ${COMPOSE_PATH} ]]; then
642 curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $COMPOSE_PATH
643 chmod +x $COMPOSE_PATH
644 else
645 echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m"
646 fi
647 fi
648 else
649 echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m"
650 fi
651fi
652
653echo -e "\e[32mFetching new images, if any...\e[0m"
654sleep 2
655docker-compose pull
656
657# Fix missing SSL, does not overwrite existing files
658[[ ! -d data/assets/ssl ]] && mkdir -p data/assets/ssl
659cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/
660
661echo -e "Checking IPv6 settings... "
662if grep -q 'SYSCTL_IPV6_DISABLED=1' mailcow.conf; then
663 echo
664 echo '!! IMPORTANT !!'
665 echo
666 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".'
667 echo 'This setting will only be active after a complete shutdown of mailcow by running "docker-compose down" followed by "docker-compose up -d".'
668 echo
669 echo '!! IMPORTANT !!'
670 echo
671 read -p "Press any key to continue..." < /dev/tty
672fi
673
674# Checking for old project name bug
675sed -i --follow-symlinks 's#COMPOSEPROJECT_NAME#COMPOSE_PROJECT_NAME#g' mailcow.conf
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200676# Checking old, wrong bindings
677sed -i --follow-symlinks 's/HTTP_BIND=0.0.0.0/HTTP_BIND=/g' mailcow.conf
678sed -i --follow-symlinks 's/HTTPS_BIND=0.0.0.0/HTTPS_BIND=/g' mailcow.conf
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100679
680# Fix Rspamd maps
681if [ -f data/conf/rspamd/custom/global_from_blacklist.map ]; then
682 mv data/conf/rspamd/custom/global_from_blacklist.map data/conf/rspamd/custom/global_smtp_from_blacklist.map
683fi
684if [ -f data/conf/rspamd/custom/global_from_whitelist.map ]; then
685 mv data/conf/rspamd/custom/global_from_whitelist.map data/conf/rspamd/custom/global_smtp_from_whitelist.map
686fi
687
688# Fix deprecated metrics.conf
689if [ -f "data/conf/rspamd/local.d/metrics.conf" ]; then
690 if [ ! -z "$(git diff --name-only origin/master data/conf/rspamd/local.d/metrics.conf)" ]; then
691 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."
692 echo "The deprecated configuration file metrics.conf will be moved to metrics.conf_deprecated after updating mailcow."
693 fi
694 mv data/conf/rspamd/local.d/metrics.conf data/conf/rspamd/local.d/metrics.conf_deprecated
695fi
696
697if [[ ${SKIP_START} == "y" ]]; then
698 echo -e "\e[33mNot starting mailcow, please run \"docker-compose up -d --remove-orphans\" to start mailcow.\e[0m"
699else
700 echo -e "\e[32mStarting mailcow...\e[0m"
701 sleep 2
702 docker-compose up -d --remove-orphans
703fi
704
705echo -e "\e[32mCollecting garbage...\e[0m"
706docker_garbage
707
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200708# Run post-update-hook
709if [ -f "${SCRIPT_DIR}/post_update_hook.sh" ]; then
710 bash "${SCRIPT_DIR}/post_update_hook.sh"
711fi
712
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100713#echo "In case you encounter any problem, hard-reset to a state before updating mailcow:"
714#echo
715#git reflog --color=always | grep "Before update on "
716#echo
717#echo "Use \"git reset --hard hash-on-the-left\" and run docker-compose up -d afterwards."