diff --git a/.gitignore b/.gitignore index 5782cad9..7d1c7c3e 100644 --- a/.gitignore +++ b/.gitignore @@ -59,7 +59,9 @@ data/web/inc/vars.local.inc.php data/web/inc/app_info.inc.php data/web/nextcloud*/ data/web/rc*/ +docker-compose.yml**.bak docker-compose.override.yml +!helper-scripts/docker-compose.override.yml.d/**/docker-compose.override.yml mailcow.conf mailcow.conf_backup rebuild-images.sh diff --git a/docker-compose.yml b/docker-compose.yml index 40d22ce0..99b555d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,10 @@ services: environment: - TZ=${TZ} volumes: - - ./data/hooks/unbound:/hooks:Z - - ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro,Z + - ./data/hooks/unbound:/hooks + - ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro + security_opt: + - label=disable restart: always tty: true networks: @@ -24,7 +26,9 @@ services: volumes: - mysql-vol-1:/var/lib/mysql/ - mysql-socket-vol-1:/var/run/mysqld/ - - ./data/conf/mysql/:/etc/mysql/conf.d/:ro,Z + - ./data/conf/mysql/:/etc/mysql/conf.d/:ro + security_opt: + - label=disable environment: - TZ=${TZ} - MYSQL_ROOT_PASSWORD=${DBROOT} @@ -44,6 +48,8 @@ services: image: redis:7-alpine volumes: - redis-vol-1:/data/ + security_opt: + - label=disable restart: always ports: - "${REDIS_PORT:-127.0.0.1:7654}:6379" @@ -68,8 +74,10 @@ services: - TZ=${TZ} - SKIP_CLAMD=${SKIP_CLAMD:-n} volumes: - - ./data/conf/clamav/:/etc/clamav/:Z + - ./data/conf/clamav/:/etc/clamav/ - clamd-db-vol-1:/var/lib/clamav + security_opt: + - label=disable networks: mailcow-network: aliases: @@ -87,15 +95,17 @@ services: - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} volumes: - - ./data/hooks/rspamd:/hooks:Z - - ./data/conf/rspamd/custom/:/etc/rspamd/custom:z - - ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:Z - - ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:Z - - ./data/conf/rspamd/plugins.d/:/etc/rspamd/plugins.d:Z - - ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro,Z - - ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local:Z - - ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override:Z + - ./data/hooks/rspamd:/hooks + - ./data/conf/rspamd/custom/:/etc/rspamd/custom + - ./data/conf/rspamd/override.d/:/etc/rspamd/override.d + - ./data/conf/rspamd/local.d/:/etc/rspamd/local.d + - ./data/conf/rspamd/plugins.d/:/etc/rspamd/plugins.d + - ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro + - ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local + - ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override - rspamd-vol-1:/var/lib/rspamd + security_opt: + - label=disable restart: always hostname: rspamd dns: @@ -111,23 +121,25 @@ services: depends_on: - redis-mailcow volumes: - - ./data/hooks/phpfpm:/hooks:Z - - ./data/web:/web:z - - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z - - ./data/conf/rspamd/custom/:/rspamd_custom_maps:z + - ./data/hooks/phpfpm:/hooks + - ./data/web:/web + - ./data/conf/rspamd/dynmaps:/dynmaps:ro + - ./data/conf/rspamd/custom/:/rspamd_custom_maps - rspamd-vol-1:/var/lib/rspamd - mysql-socket-vol-1:/var/run/mysqld/ - - ./data/conf/sogo/:/etc/sogo/:z - - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z - - ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/:z - - ./data/conf/phpfpm/php-fpm.d/pools.conf:/usr/local/etc/php-fpm.d/z-pools.conf:Z - - ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini:Z - - ./data/conf/phpfpm/php-conf.d/upload.ini:/usr/local/etc/php/conf.d/upload.ini:Z - - ./data/conf/phpfpm/php-conf.d/other.ini:/usr/local/etc/php/conf.d/zzz-other.ini:Z - - ./data/conf/dovecot/global_sieve_before:/global_sieve/before:z - - ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z - - ./data/assets/templates:/tpls:z - - ./data/conf/nginx/:/etc/nginx/conf.d/:z + - ./data/conf/sogo/:/etc/sogo/ + - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro + - ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/ + - ./data/conf/phpfpm/php-fpm.d/pools.conf:/usr/local/etc/php-fpm.d/z-pools.conf + - ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini + - ./data/conf/phpfpm/php-conf.d/upload.ini:/usr/local/etc/php/conf.d/upload.ini + - ./data/conf/phpfpm/php-conf.d/other.ini:/usr/local/etc/php/conf.d/zzz-other.ini + - ./data/conf/dovecot/global_sieve_before:/global_sieve/before + - ./data/conf/dovecot/global_sieve_after:/global_sieve/after + - ./data/assets/templates:/tpls + - ./data/conf/nginx/:/etc/nginx/conf.d/ + security_opt: + - label=disable dns: - ${IPV4_NETWORK:-172.22.1}.254 environment: @@ -189,15 +201,17 @@ services: dns: - ${IPV4_NETWORK:-172.22.1}.254 volumes: - - ./data/hooks/sogo:/hooks:Z - - ./data/conf/sogo/:/etc/sogo/:z - - ./data/web/inc/init_db.inc.php:/init_db.inc.php:Z - - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z - - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z - - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z + - ./data/hooks/sogo:/hooks + - ./data/conf/sogo/:/etc/sogo/ + - ./data/web/inc/init_db.inc.php:/init_db.inc.php + - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico + - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js + - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js - mysql-socket-vol-1:/var/run/mysqld/ - sogo-web-vol-1:/sogo_web - sogo-userdata-backup-vol-1:/sogo_backup + security_opt: + - label=disable labels: ofelia.enabled: "true" ofelia.job-exec.sogo_sessions.schedule: "@every 1m" @@ -224,18 +238,20 @@ services: cap_add: - NET_BIND_SERVICE volumes: - - ./data/hooks/dovecot:/hooks:Z - - ./data/conf/dovecot:/etc/dovecot:z - - ./data/assets/ssl:/etc/ssl/mail/:ro,z - - ./data/conf/sogo/:/etc/sogo/:z - - ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/:z + - ./data/hooks/dovecot:/hooks + - ./data/conf/dovecot:/etc/dovecot + - ./data/assets/ssl:/etc/ssl/mail/:ro + - ./data/conf/sogo/:/etc/sogo/ + - ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/ - vmail-vol-1:/var/vmail - vmail-index-vol-1:/var/vmail_index - crypt-vol-1:/mail_crypt/ - - ./data/conf/rspamd/custom/:/etc/rspamd/custom:z - - ./data/assets/templates:/templates:z + - ./data/conf/rspamd/custom/:/etc/rspamd/custom + - ./data/assets/templates:/templates - rspamd-vol-1:/var/lib/rspamd - mysql-socket-vol-1:/var/run/mysqld/ + security_opt: + - label=disable environment: - DOVECOT_MASTER_USER=${DOVECOT_MASTER_USER:-} - DOVECOT_MASTER_PASS=${DOVECOT_MASTER_PASS:-} @@ -300,13 +316,15 @@ services: depends_on: - mysql-mailcow volumes: - - ./data/hooks/postfix:/hooks:Z - - ./data/conf/postfix:/opt/postfix/conf:z - - ./data/assets/ssl:/etc/ssl/mail/:ro,z + - ./data/hooks/postfix:/hooks + - ./data/conf/postfix:/opt/postfix/conf + - ./data/assets/ssl:/etc/ssl/mail/:ro - postfix-vol-1:/var/spool/postfix - crypt-vol-1:/var/lib/zeyple - rspamd-vol-1:/var/lib/rspamd - mysql-socket-vol-1:/var/run/mysqld/ + security_opt: + - label=disable environment: - LOG_LINES=${LOG_LINES:-9999} - TZ=${TZ} @@ -334,6 +352,8 @@ services: memcached-mailcow: image: memcached:alpine restart: always + security_opt: + - label=disable environment: - TZ=${TZ} networks: @@ -371,12 +391,14 @@ services: - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} - ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-} volumes: - - ./data/web:/web:ro,z - - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z - - ./data/assets/ssl/:/etc/ssl/mail/:ro,z - - ./data/conf/nginx/:/etc/nginx/conf.d/:z - - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z + - ./data/web:/web:ro + - ./data/conf/rspamd/dynmaps:/dynmaps:ro + - ./data/assets/ssl/:/etc/ssl/mail/:ro + - ./data/conf/nginx/:/etc/nginx/conf.d/ + - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/ + security_opt: + - label=disable ports: - "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}" - "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}" @@ -414,10 +436,12 @@ services: - SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n} - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} volumes: - - ./data/web/.well-known/acme-challenge:/var/www/acme:z - - ./data/assets/ssl:/var/lib/acme/:z - - ./data/assets/ssl-example:/var/lib/ssl-example/:ro,Z + - ./data/web/.well-known/acme-challenge:/var/www/acme + - ./data/assets/ssl:/var/lib/acme/ + - ./data/assets/ssl-example:/var/lib/ssl-example/:ro - mysql-socket-vol-1:/var/run/mysqld/ + security_opt: + - label=disable restart: always networks: mailcow-network: @@ -446,6 +470,8 @@ services: network_mode: "host" volumes: - /lib/modules:/lib/modules:ro + security_opt: + - label=disable watchdog-mailcow: image: mailcow/watchdog:1.97 @@ -457,7 +483,9 @@ services: - rspamd-vol-1:/var/lib/rspamd - mysql-socket-vol-1:/var/run/mysqld/ - postfix-vol-1:/var/spool/postfix - - ./data/assets/ssl:/etc/ssl/mail/:ro,z + - ./data/assets/ssl:/etc/ssl/mail/:ro + security_opt: + - label=disable restart: always environment: - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} @@ -533,6 +561,8 @@ services: restart: always volumes: - solr-vol-1:/opt/solr/server/solr/dovecot-fts/data + security_opt: + - label=disable ports: - "${SOLR_PORT:-127.0.0.1:18983}:8983" environment: @@ -547,6 +577,8 @@ services: olefy-mailcow: image: mailcow/olefy:1.11 restart: always + security_opt: + - label=disable environment: - TZ=${TZ} - OLEFY_BINDADDRESS=0.0.0.0 diff --git a/generate_config.sh b/generate_config.sh index 89af0f64..458c8d03 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -21,39 +21,70 @@ if grep --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox grep if 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 if 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 -for bin in openssl curl docker git awk sha1sum; do - if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi +# Check which container engine is available. +# Check for podman first, because the 'podman-docker' package might be installed providing a dummy 'docker' command. +if command -v podman > /dev/null 2>&1; then + CONTAINER_ENGINE="podman" + echo -e "\e[32mFound Podman container engine.\e[0m" + echo -e "\e[31mNOTE: Support for Podman is experimental, consider this before deploying to production! \e[0m" + + if [[ -n "${DOCKER_HOST}" ]] && [[ "${DOCKER_HOST}" == "unix://"* ]]; then + CONTAINER_SOCKET="${DOCKER_HOST/"unix://"/}" + else + CONTAINER_SOCKET="/run/user/${UID}/podman/podman.sock" + fi + + # To patch the docker-compose file for use with podman + EXTRA_REQUIRED_PACKAGES="patch" +elif command -v docker > /dev/null 2>&1; then + CONTAINER_ENGINE="docker" + echo -e "\e[32mFound Docker container engine.\e[0m" + + CONTAINER_SOCKET="/var/run/docker.sock" + EXTRA_REQUIRED_PACKAGES="" +else + echo "Cannot find container engine (Docker or Podman), exiting..." + exit 1 +fi + +for bin in openssl curl git awk sha1sum ${EXTRA_REQUIRED_PACKAGES}; do + if [[ -z $(which ${bin}) ]]; then + echo "Cannot find ${bin}, exiting..." + exit 1 + fi done -if docker compose > /dev/null 2>&1; then +MAILCOW_DOCKER_COMPOSE=${MAILCOW_DOCKER_COMPOSE:-"docker-compose"} + +if [[ "${CONTAINER_ENGINE}" == "docker" ]] && docker compose > /dev/null 2>&1; then if docker compose version --short | grep "^2." > /dev/null 2>&1; then COMPOSE_VERSION=native echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m" echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m" sleep 2 - echo -e "\e[33mNotice: You´ll have to update this Compose Version via your Package Manager manually!\e[0m" + echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually! \e[0m" else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" exit 1 fi -elif docker-compose > /dev/null 2>&1; then - if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then - if docker-compose version --short | grep "^2." > /dev/null 2>&1; then +elif $MAILCOW_DOCKER_COMPOSE > /dev/null 2>&1; then + if ! [[ $(alias $MAILCOW_DOCKER_COMPOSE 2> /dev/null) ]] ; then + if $MAILCOW_DOCKER_COMPOSE version --short | grep "^2." > /dev/null 2>&1; then COMPOSE_VERSION=standalone echo -e "\e[31mFound Docker Compose Standalone.\e[0m" echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m" sleep 2 - echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" + echo -e "\e[33mNotice: For an automatic update of ${MAILCOW_DOCKER_COMPOSE} please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" echo -e "\e[31mPlease update/install manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" exit 1 fi fi else - echo -e "\e[31mCannot find Docker Compose.\e[0m" + echo -e "\e[31mCannot find Docker Compose.\e[0m" echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" exit 1 fi @@ -173,7 +204,7 @@ else echo -e "\033[31mCould not determine branch input..." echo -e "\033[31mExiting." exit 1 -fi +fi if [ ! -z "${MAILCOW_BRANCH}" ]; then git_branch=${MAILCOW_BRANCH} @@ -181,6 +212,11 @@ fi [ ! -f ./data/conf/rspamd/override.d/worker-controller-password.inc ] && echo '# Placeholder' > ./data/conf/rspamd/override.d/worker-controller-password.inc +if [[ "${CONTAINER_ENGINE}" == "podman" ]]; then + # Apply patches for usage with Podman + bash ./patches-for-podman.sh +fi + cat << EOF > mailcow.conf # ------------------------------ # mailcow web ui configuration @@ -196,6 +232,9 @@ MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} # see https://mailcow.github.io/mailcow-dockerized-docs/models/model-passwd/ MAILCOW_PASS_SCHEME=BLF-CRYPT +# The directory used to store the data of the used containers (used in case the CUSTOM_STORAGE_LOCATION override is included) +MAILCOW_STORAGE_DIR= + # ------------------------------ # SQL database configuration # ------------------------------ @@ -227,6 +266,43 @@ HTTP_BIND= HTTPS_PORT=443 HTTPS_BIND= +# ------------------------------ +# Container environment +# ------------------------------ + +# The container engine to use to run this project (docker or podman). + +MAILCOW_CONTAINER_ENGINE=${CONTAINER_ENGINE} + +# The location of the container socket to use for volume mounts. + +MAILCOW_CONTAINER_SOCKET=${CONTAINER_SOCKET} + +# Fixed project name +# Please use lowercase letters only + +COMPOSE_PROJECT_NAME=mailcowdockerized + +# Used Docker Compose version +# Switch here between native (compose plugin) and standalone +# For more information take a look at the mailcow docs regarding the configuration options. +# Normally this should be untouched but if you decided to use either of those you can switch it manually here. +# Please be aware that at least one of those variants should be installed on your machine or mailcow will fail. + +DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION} + +# Additional override files (relative to the repo root) that need to be taken care of when running mailcow scripts. +# Comma separated list without spaces! +# Example: DOCKER_COMPOSE_EXTRA_OVERRIDES=helper-scripts/docker-compose.override.yml.d/CONTAINER_ENGINE_PODMAN/docker-compose.override.yml,helper-scripts/docker-compose.override.yml.d/CUSTOM_STORAGE_LOCATION/docker-compose.override.yml + +DOCKER_COMPOSE_EXTRA_OVERRIDES= + +# The name of the docker-compose binary to use. This option can be used in case different versions of +# docker-compose are installed and another binary than 'docker-compose' (default) needs to be used. +# Example: docker-compose-v2 + +MAILCOW_DOCKER_COMPOSE=${MAILCOW_DOCKER_COMPOSE} + # ------------------------------ # Other bindings # ------------------------------ @@ -252,22 +328,9 @@ REDIS_PORT=127.0.0.1:7654 TZ=${MAILCOW_TZ} -# Fixed project name -# Please use lowercase letters only - -COMPOSE_PROJECT_NAME=mailcowdockerized - -# Used Docker Compose version -# Switch here between native (compose plugin) and standalone -# For more informations take a look at the mailcow docs regarding the configuration options. -# Normally this should be untouched but if you decided to use either of those you can switch it manually here. -# Please be aware that at least one of those variants should be installed on your maschine or mailcow will fail. - -DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION} - # Set this to "allow" to enable the anyone pseudo user. Disabled by default. # When enabled, ACL can be created, that apply to "All authenticated users" -# This should probably only be activated on mail hosts, that are used exclusivly by one organisation. +# This should probably only be activated on mail hosts, that are used exclusively by one organisation. # Otherwise a user might share data with too many other users. ACL_ANYONE=disallow diff --git a/helper-scripts/docker-compose.override.yml.d/CONTAINER_ENGINE_PODMAN/docker-compose.override.yml b/helper-scripts/docker-compose.override.yml.d/CONTAINER_ENGINE_PODMAN/docker-compose.override.yml new file mode 100644 index 00000000..54fc2c74 --- /dev/null +++ b/helper-scripts/docker-compose.override.yml.d/CONTAINER_ENGINE_PODMAN/docker-compose.override.yml @@ -0,0 +1,46 @@ +version: '3.9' + +# Pre-requisites on the host machine +# +# Since the sysctl option cannot be set and podman needs additional privileges to bind +# to ports lower than 1024, the following options need to be set: +# sudo sysctl net.ipv4.ip_unprivileged_port_start=25 +# sudo sysctl net.core.somaxconn=4096 +# +# Apparently compose extends instead of overrides (except for volumes), see +# https://github.com/docker/compose/issues/3729 +# Because of that, the patch-docker-compose-for-podman.sh script exists to remove these options from the original +# docker-compose.yml + +services: + + dovecot-mailcow: + cap_add: + - SYS_CHROOT + + postfix-mailcow: + cap_add: + - SYS_CHROOT + + dockerapi-mailcow: + volumes: + - ${MAILCOW_CONTAINER_SOCKET}:/var/run/docker.sock:ro + + ofelia-mailcow: + volumes: + - ${MAILCOW_CONTAINER_SOCKET}:/var/run/docker.sock:ro + + ipv6nat-mailcow: + image: bash:latest + restart: "no" + entrypoint: ["echo", "ipv6nat disabled in compose.override.yml"] + # Because docker-compose extends instead of overrides, the volume still needs to be overwritten, even when it is not used. + volumes: + - ${MAILCOW_CONTAINER_SOCKET}:/var/run/docker.sock:ro + +networks: + mailcow-network: + driver: bridge + ipam: + config: + - subnet: ${IPV4_NETWORK:-172.22.1}.0/24 diff --git a/helper-scripts/docker-compose.override.yml.d/CUSTOM_STORAGE_LOCATION/docker-compose.override.yml b/helper-scripts/docker-compose.override.yml.d/CUSTOM_STORAGE_LOCATION/docker-compose.override.yml new file mode 100644 index 00000000..b173fa80 --- /dev/null +++ b/helper-scripts/docker-compose.override.yml.d/CUSTOM_STORAGE_LOCATION/docker-compose.override.yml @@ -0,0 +1,66 @@ +version: '2.1' + +services: + + mysql-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/mysql:/var/lib/mysql/ + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + + redis-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/redis:/data/ + + clamd-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/clamd-db:/var/lib/clamav + + rspamd-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/rspamd:/var/lib/rspamd + + php-fpm-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/rspamd:/var/lib/rspamd + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + + sogo-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + - ${MAILCOW_STORAGE_DIR}/sogo-web:/sogo_web + - ${MAILCOW_STORAGE_DIR}/sogo-userdata-backup:/sogo_backup + + dovecot-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/vmail:/var/vmail + - ${MAILCOW_STORAGE_DIR}/vmail-index:/var/vmail_index + - ${MAILCOW_STORAGE_DIR}/crypt:/mail_crypt/ + - ${MAILCOW_STORAGE_DIR}/rspamd:/var/lib/rspamd + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + + postfix-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/postfix:/var/spool/postfix + - ${MAILCOW_STORAGE_DIR}/crypt:/var/lib/zeyple + - ${MAILCOW_STORAGE_DIR}/rspamd:/var/lib/rspamd + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + + nginx-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/sogo-web:/usr/lib/GNUstep/SOGo/ + + acme-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + + watchdog-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/rspamd:/var/lib/rspamd + - ${MAILCOW_STORAGE_DIR}/mysql-socket:/var/run/mysqld/ + - ${MAILCOW_STORAGE_DIR}/postfix:/var/spool/postfix + + solr-mailcow: + volumes: + - ${MAILCOW_STORAGE_DIR}/solr:/opt/solr/server/solr/dovecot-fts/data + +volumes: {} diff --git a/helper-scripts/mailcow-reset-admin.sh b/helper-scripts/mailcow-reset-admin.sh index ee95d3e9..0b0186ec 100755 --- a/helper-scripts/mailcow-reset-admin.sh +++ b/helper-scripts/mailcow-reset-admin.sh @@ -7,8 +7,10 @@ if [[ -z ${DBUSER} ]] || [[ -z ${DBPASS} ]] || [[ -z ${DBNAME} ]]; then exit 1 fi +_engine="${MAILCOW_CONTAINER_ENGINE}" + echo -n "Checking MySQL service... " -if [[ -z $(docker ps -qf name=mysql-mailcow) ]]; then +if [[ -z $(${_engine} ps -qf name=mysql-mailcow) ]]; then echo "failed" echo "MySQL (mysql-mailcow) is not up and running, exiting..." exit 1 @@ -20,11 +22,11 @@ response=${response,,} # tolower if [[ "$response" =~ ^(yes|y)$ ]]; then echo -e "\nWorking, please wait..." random=$( /dev/null; then GLIBC_V=$(ldd --version | grep -E '(GLIBC|GNU libc)' | rev | cut -d ' ' -f1 | rev | cut -d '.' -f2) if [ ! -z "${GLIBC_V}" ] && [ ${GLIBC_V} -gt 27 ]; then - DC_DL_SUFFIX= + DC_DL_SUFFIX= else - DC_DL_SUFFIX=legacy + DC_DL_SUFFIX=legacy fi else DC_DL_SUFFIX=legacy fi sleep 1 - if [[ $(command -v pip 2>&1) && $(pip list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 || $(command -v pip3 2>&1) && $(pip3 list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 ]]; then + if [[ "${_compose}" == "docker-compose" ]] && [[ $(command -v pip 2>&1) && $(pip list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 || $(command -v pip3 2>&1) && $(pip3 list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 ]]; then echo -e "\e[33mFound a docker-compose Version installed with pip!\e[0m" echo -e "\e[31mPlease uninstall the pip Version of docker-compose since it doesn't support Versions higher than 1.29.2.\e[0m" sleep 2 echo -e "\e[33mExiting...\e[0m" exit 1 #prevent breaking a working docker-compose installed with pip + #when ${_compose} is set to a non-default value, don't complain, since in some cases both v1 and v2 are used elif [[ $(curl -sL -w "%{http_code}" https://github.com/docker/compose/releases/latest -o /dev/null) == "200" ]]; then LATEST_COMPOSE=$(curl -Ls -w %{url_effective} -o /dev/null https://github.com/docker/compose/releases/latest) # redirect to latest release - LATEST_COMPOSE=${LATEST_COMPOSE##*/} #get the latest version from the redirect, inlcuding the "v" prefix - COMPOSE_VERSION=$(docker-compose version --short) + LATEST_COMPOSE=${LATEST_COMPOSE##*/} #get the latest version from the redirect, including the "v" prefix + COMPOSE_VERSION=$(${_compose} version --short) if [[ "$LATEST_COMPOSE" != "$COMPOSE_VERSION" ]]; then - COMPOSE_PATH=$(command -v docker-compose) - if [[ -w ${COMPOSE_PATH} ]]; then - curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $COMPOSE_PATH - chmod +x $COMPOSE_PATH - echo -e "\e[32mYour Docker Compose (standalone) has been updated to: $LATEST_COMPOSE\e[0m" - exit 0 - else - echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m" - exit 1 - fi + COMPOSE_PATH=$(command -v ${_compose}) + if [[ -w ${COMPOSE_PATH} ]]; then + curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $COMPOSE_PATH + RC=$? + if [[ $RC -eq 0 ]]; then + chmod +x $COMPOSE_PATH + echo -e "\e[32mYour Docker Compose (standalone) has been updated to: $LATEST_COMPOSE\e[0m" + exit 0 + else + echo -e "\e[31mError with updating $COMPOSE_PATH, please retry...\e[0m" + exit 1 + fi + else + echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m" + exit 1 + fi fi else echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m" @@ -69,4 +78,4 @@ elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then else echo -e "\e[31mCan not read DOCKER_COMPOSE_VERSION variable from mailcow.conf! Is your mailcow up to date? Exiting...\e[0m" exit 1 -fi \ No newline at end of file +fi diff --git a/mailcow-compose.sh b/mailcow-compose.sh new file mode 100755 index 00000000..c3df2b7f --- /dev/null +++ b/mailcow-compose.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# +# docker-compose wrapper script for mailcow + +#--------------------------------------- +# functions +#--------------------------------------- +function validate_input() +{ + if [[ "$#" -eq 0 ]]; then + echo -e "\e[31mNo arguments given. Call this script with e.g. 'up -d' or 'down'\e[0m" + exit 1 + fi + + if [[ ! -f mailcow.conf ]]; then + echo -e "\e[31mmailcow.conf is missing! Is mailcow installed?\e[0m" + exit 1 + fi + + source mailcow.conf +} + +function detect_docker_compose_command() +{ + if ! [[ "${DOCKER_COMPOSE_VERSION}" == "native" ]] && ! [[ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]]; then + if docker compose > /dev/null 2>&1; then + if docker compose version --short | grep "2." > /dev/null 2>&1; then + DOCKER_COMPOSE_VERSION=native + COMPOSE_COMMAND="docker compose" + echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m" + echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m" + sleep 2 + echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually! \e[0m" + else + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" + exit 1 + fi + elif docker-compose > /dev/null 2>&1; then + if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then + if docker-compose version --short | grep "^2." > /dev/null 2>&1; then + DOCKER_COMPOSE_VERSION=standalone + COMPOSE_COMMAND="docker-compose" + echo -e "\e[31mFound Docker Compose Standalone.\e[0m" + echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m" + sleep 2 + echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" + else + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mPlease update/install regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" + exit 1 + fi + fi + + else + echo -e "\e[31mCannot find Docker Compose.\e[0m" + echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" + exit 1 + fi + + elif [[ "${DOCKER_COMPOSE_VERSION}" == "native" ]]; then + COMPOSE_COMMAND="docker compose" + + elif [[ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]]; then + COMPOSE_COMMAND="${MAILCOW_DOCKER_COMPOSE:-"docker-compose"}" + fi +} + +function ensure_storage_directories_exist() +{ + # In case the user specified MAILCOW_STORAGE_DIR, create the required subdirectories. + if [[ -n "${MAILCOW_STORAGE_DIR}" ]]; then + mkdir -p "${MAILCOW_STORAGE_DIR}/clamd-db" + mkdir -p "${MAILCOW_STORAGE_DIR}/crypt" + mkdir -p "${MAILCOW_STORAGE_DIR}/mysql" + mkdir -p "${MAILCOW_STORAGE_DIR}/mysql-socket" + mkdir -p "${MAILCOW_STORAGE_DIR}/postfix" + mkdir -p "${MAILCOW_STORAGE_DIR}/redis" + mkdir -p "${MAILCOW_STORAGE_DIR}/rspamd" + mkdir -p "${MAILCOW_STORAGE_DIR}/sogo-userdata-backup" + mkdir -p "${MAILCOW_STORAGE_DIR}/sogo-web" + mkdir -p "${MAILCOW_STORAGE_DIR}/solr" + mkdir -p "${MAILCOW_STORAGE_DIR}/vmail" + mkdir -p "${MAILCOW_STORAGE_DIR}/vmail-index" + fi +} + +function run_compose() +{ + if [[ -n "${DOCKER_COMPOSE_EXTRA_OVERRIDES}" ]]; then + IFS=',' read -r -a overrides <<< "${DOCKER_COMPOSE_EXTRA_OVERRIDES}" + COMPOSE_ARGUMENTS="-f docker-compose.yml " + for override in "${overrides[@]}"; do + COMPOSE_ARGUMENTS+="-f ${override} " + done + else + COMPOSE_ARGUMENTS="" + fi + + echo -e "\e[32mExecuting: ${COMPOSE_COMMAND} ${COMPOSE_ARGUMENTS} $@ \e[0m" + ${COMPOSE_COMMAND} ${COMPOSE_ARGUMENTS} $@ +} + +#--------------------------------------- +# main +#--------------------------------------- +validate_input $@ +detect_docker_compose_command +ensure_storage_directories_exist +run_compose $@ diff --git a/patch-docker-compose-for-podman.patch b/patch-docker-compose-for-podman.patch new file mode 100644 index 00000000..670ef774 --- /dev/null +++ b/patch-docker-compose-for-podman.patch @@ -0,0 +1,125 @@ +--- docker-compose-original.yml 2023-01-09 13:46:07.792778709 +0100 ++++ docker-compose.yml 2023-01-09 13:45:20.828836564 +0100 +@@ -55,8 +55,8 @@ + - "${REDIS_PORT:-127.0.0.1:7654}:6379" + environment: + - TZ=${TZ} +- sysctls: +- - net.core.somaxconn=4096 ++# sysctls: ++# - net.core.somaxconn=4096 + networks: + mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.249 +@@ -68,8 +68,8 @@ + restart: always + depends_on: + - unbound-mailcow +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - TZ=${TZ} + - SKIP_CLAMD=${SKIP_CLAMD:-n} +@@ -108,8 +108,8 @@ + - label=disable + restart: always + hostname: rspamd +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + networks: + mailcow-network: + aliases: +@@ -140,8 +140,8 @@ + - ./data/conf/nginx/:/etc/nginx/conf.d/ + security_opt: + - label=disable +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} +@@ -198,8 +198,8 @@ + - MASTER=${MASTER:-y} + - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} + - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + volumes: + - ./data/hooks/sogo:/hooks + - ./data/conf/sogo/:/etc/sogo/ +@@ -233,8 +233,8 @@ + image: mailcow/dovecot:1.21 + depends_on: + - mysql-mailcow +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + cap_add: + - NET_BIND_SERVICE + volumes: +@@ -341,8 +341,8 @@ + - "${SMTPS_PORT:-465}:465" + - "${SUBMISSION_PORT:-587}:587" + restart: always +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + networks: + mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.253 +@@ -367,8 +367,8 @@ + - php-fpm-mailcow + - redis-mailcow + image: nginx:mainline-alpine +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active && + envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active && + envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active && +@@ -412,8 +412,8 @@ + depends_on: + - nginx-mailcow + image: mailcow/acme:1.83 +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - LOG_LINES=${LOG_LINES:-9999} + - ACME_CONTACT=${ACME_CONTACT:-} +@@ -475,8 +475,8 @@ + + watchdog-mailcow: + image: mailcow/watchdog:1.97 +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + tmpfs: + - /tmp + volumes: +@@ -542,9 +542,9 @@ + security_opt: + - label=disable + restart: always +- oom_kill_disable: true +- dns: +- - ${IPV4_NETWORK:-172.22.1}.254 ++# oom_kill_disable: true ++# dns: ++# - ${IPV4_NETWORK:-172.22.1}.254 + environment: + - DBROOT=${DBROOT} + - TZ=${TZ} diff --git a/patches-for-podman.sh b/patches-for-podman.sh new file mode 100755 index 00000000..96ff6f56 --- /dev/null +++ b/patches-for-podman.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# +# This script patches the docker-compose.yml for usage with podman. +# This is necessary because not all options (e.g. DNS) can be overwritten by docker-compose, see +# https://github.com/docker/compose/issues/3729 +# +# Next to that because IPv6 does not work (yet), patch the MySQL configuration file to bind to IPv4 + +set -e + +PATCH_FILE="patch-docker-compose-for-podman.patch" +TIMESTAMP="$(date +'%Y%m%d%H%M')" + +# Create a backup (in case custom changes are made) +cp docker-compose.yml docker-compose.yml.${TIMESTAMP}.bak + +# Detect whether the patch has been applied by trying to reverse the patch in a dry-run scenario +if ! patch -R -s -f --dry-run docker-compose.yml < ${PATCH_FILE} > /dev/null 2>&1; then + patch docker-compose.yml < ${PATCH_FILE} +else + echo "docker-compose.yml already patched (or custom changes prevent applying the patch)" +fi diff --git a/update.sh b/update.sh index 34d17354..8a728400 100755 --- a/update.sh +++ b/update.sh @@ -22,7 +22,7 @@ prefetch_images() { fi fi RET_C=0 - until docker pull ${image}; do + until ${MAILCOW_CONTAINER_ENGINE} pull ${image}; do RET_C=$((RET_C + 1)) echo -e "\e[33m\nError pulling $image, retrying...\e[0m" [ ${RET_C} -gt 3 ] && { echo -e "\e[31m\nToo many failed retries, exiting\e[0m"; exit 1; } @@ -38,7 +38,7 @@ docker_garbage() { TAG=${container/*:} V_MAIN=${container/*.} V_SUB=${container/*.} - EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }') + EXISTING_TAGS=$(${MAILCOW_CONTAINER_ENGINE} images | grep ${REPOSITORY} | awk '{ print $2 }') for existing_tag in ${EXISTING_TAGS[@]}; do V_MAIN_EXISTING=${existing_tag/*.} V_SUB_EXISTING=${existing_tag/*.} @@ -62,21 +62,21 @@ docker_garbage() { if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then echo "Run the following command to delete unused image tags:" echo - echo " docker rmi ${IMGS_TO_DELETE[*]}" + echo " ${MAILCOW_CONTAINER_ENGINE} rmi ${IMGS_TO_DELETE[*]}" echo if [ ! $FORCE ]; then read -r -p "Do you want to delete old image tags right now? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - docker rmi ${IMGS_TO_DELETE[*]} + ${MAILCOW_CONTAINER_ENGINE} rmi ${IMGS_TO_DELETE[*]} else echo "OK, skipped." fi else echo "Running image removal without extra confirmation due to force mode." - docker rmi ${IMGS_TO_DELETE[*]} + ${MAILCOW_CONTAINER_ENGINE} rmi ${IMGS_TO_DELETE[*]} fi echo -e "\e[32mFurther cleanup...\e[0m" - 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\"" + echo "If you want to cleanup further garbage collected by ${MAILCOW_CONTAINER_ENGINE}, please make sure all containers are up and running before cleaning your system by executing \"${MAILCOW_CONTAINER_ENGINE} system prune\"" fi } @@ -139,10 +139,10 @@ migrate_docker_nat() { # Removing legacy container sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.yml if [ -s docker-compose.override.yml ]; then - sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.override.yml - if [[ "$(cat docker-compose.override.yml | sed '/^\s*$/d' | wc -l)" == "2" ]]; then - mv docker-compose.override.yml docker-compose.override.yml_backup - fi + sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.override.yml + if [[ "$(cat docker-compose.override.yml | sed '/^\s*$/d' | wc -l)" == "2" ]]; then + mv docker-compose.override.yml docker-compose.override.yml_backup + fi fi echo -e "\e[32mGreat! \e[0mNative IPv6 NAT is active.\e[0m" else @@ -152,82 +152,47 @@ migrate_docker_nat() { } remove_obsolete_nginx_ports() { - # Removing obsolete docker-compose.override.yml - for override in docker-compose.override.yml docker-compose.override.yaml; do - if [ -s $override ] ; then - if cat $override | grep nginx-mailcow > /dev/null 2>&1; then - if cat $override | grep -E '(\[::])' > /dev/null 2>&1; then - if cat $override | grep -w 80:80 > /dev/null 2>&1 && cat $override | grep -w 443:443 > /dev/null 2>&1 ; then - echo -e "\e[33mBacking up ${override} to preserve custom changes...\e[0m" - echo -e "\e[33m!!! Manual Merge needed (if other overrides are set) !!!\e[0m" - sleep 3 - cp $override ${override}_backup - sed -i '/nginx-mailcow:$/,/^$/d' $override - echo -e "\e[33mRemoved obsolete NGINX IPv6 Bind from original override File.\e[0m" - if [[ "$(cat $override | sed '/^\s*$/d' | wc -l)" == "2" ]]; then - mv $override ${override}_empty - echo -e "\e[31m${override} is empty. Renamed it to ensure mailcow is startable.\e[0m" - fi + # Removing obsolete docker-compose.override.yml + for override in docker-compose.override.yml docker-compose.override.yaml; do + if [ -s $override ] ; then + if cat $override | grep nginx-mailcow > /dev/null 2>&1; then + if cat $override | grep -E '(\[::])' > /dev/null 2>&1; then + if cat $override | grep -w 80:80 > /dev/null 2>&1 && cat $override | grep -w 443:443 > /dev/null 2>&1 ; then + echo -e "\e[33mBacking up ${override} to preserve custom changes...\e[0m" + echo -e "\e[33m!!! Manual Merge needed (if other overrides are set) !!!\e[0m" + sleep 3 + cp $override ${override}_backup + sed -i '/nginx-mailcow:$/,/^$/d' $override + echo -e "\e[33mRemoved obsolete NGINX IPv6 Bind from original override File.\e[0m" + if [[ "$(cat $override | sed '/^\s*$/d' | wc -l)" == "2" ]]; then + mv $override ${override}_empty + echo -e "\e[31m${override} is empty. Renamed it to ensure mailcow is startable.\e[0m" fi - fi fi - fi - done -} - -detect_docker_compose_command(){ -if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then - if docker compose > /dev/null 2>&1; then - if docker compose version --short | grep "2." > /dev/null 2>&1; then - DOCKER_COMPOSE_VERSION=native - COMPOSE_COMMAND="docker compose" - echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m" - echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m" - sleep 2 - echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m" - else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" - echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" - exit 1 - fi - elif docker-compose > /dev/null 2>&1; then - if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then - if docker-compose version --short | grep "^2." > /dev/null 2>&1; then - DOCKER_COMPOSE_VERSION=standalone - COMPOSE_COMMAND="docker-compose" - echo -e "\e[31mFound Docker Compose Standalone.\e[0m" - echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m" - sleep 2 - echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" - else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" - echo -e "\e[31mPlease update/install regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" - exit 1 fi fi - - else - echo -e "\e[31mCannot find Docker Compose.\e[0m" - echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m" - exit 1 fi - -elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then - COMPOSE_COMMAND="docker compose" - -elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then - COMPOSE_COMMAND="docker-compose" -fi + done } ############## End Function Section ############## -# Check permissions -if [ "$(id -u)" -ne "0" ]; then - echo "You need to be root" +# Exit on error and pipefail +set -o pipefail + +if [[ ! -f mailcow.conf ]]; then + echo -e "\e[31mmailcow.conf is missing! Is mailcow installed?\e[0m" exit 1 fi +chmod 600 mailcow.conf +source mailcow.conf + +# Check permissions (only when docker is wanted) +if ([[ -z "${MAILCOW_CONTAINER_ENGINE}" ]] || [[ "${MAILCOW_CONTAINER_ENGINE}" == "docker" ]]) && [[ "$(id -u)" -ne "0" ]]; then + echo "You need to be root" +fi + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Run pre-update-hook @@ -251,9 +216,6 @@ if [[ "$(uname -r)" =~ ^4\.4\. ]]; then read -p "Press any key to continue..." < /dev/tty fi -# Exit on error and pipefail -set -o pipefail - # Setting high dc timeout export COMPOSE_HTTP_TIMEOUT=600 @@ -262,15 +224,38 @@ PATH=$PATH:/opt/bin umask 0022 -# Unset COMPOSE_COMMAND and DOCKER_COMPOSE_VERSION Variable to be on the newest state. -unset COMPOSE_COMMAND -unset DOCKER_COMPOSE_VERSION +# Check if the container engine flag is set, otherwise set it +if [[ -z "${MAILCOW_CONTAINER_ENGINE}" ]]; then + if command -v podman > /dev/null 2>&1; then + MAILCOW_CONTAINER_ENGINE="podman" + echo -e "\e[32mFound Podman container engine.\e[0m" + echo -e "\e[31mNOTE: Support for Podman is experimental, consider this before deploying to production! \e[0m" -for bin in curl docker git awk sha1sum; do - if [[ -z $(command -v ${bin}) ]]; then - echo "Cannot find ${bin}, exiting..." - exit 1; - fi + if [[ -n "${DOCKER_HOST}" ]] && [[ "${DOCKER_HOST}" == "unix://"* ]]; then + CONTAINER_SOCKET="${DOCKER_HOST/"unix://"/}" + else + CONTAINER_SOCKET="/run/user/${UID}/podman/podman.sock" + fi + + # To patch the docker-compose file for use with podman + EXTRA_REQUIRED_PACKAGES="patch" + elif command -v docker > /dev/null 2>&1; then + MAILCOW_CONTAINER_ENGINE="docker" + echo -e "\e[32mFound Docker container engine.\e[0m" + + CONTAINER_SOCKET="/var/run/docker.sock" + EXTRA_REQUIRED_PACKAGES="" + else + echo "Cannot find container engine (Docker or Podman), exiting..." + exit 1 + fi +fi + +for bin in curl git awk sha1sum ${EXTRA_REQUIRED_PACKAGES}; do + if [[ -z $(command -v ${bin}) ]]; then + echo "Cannot find ${bin}, exiting..." + exit 1 + fi done export LC_ALL=C @@ -344,16 +329,11 @@ while (($#)); do shift done -chmod 600 mailcow.conf -source mailcow.conf -detect_docker_compose_command - -[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing! Is mailcow installed?"; exit 1;} DOTS=${MAILCOW_HOSTNAME//[^.]}; if [ ${#DOTS} -lt 2 ]; then echo "MAILCOW_HOSTNAME (${MAILCOW_HOSTNAME}) is not a FQDN!" - echo "Please change it to a FQDN and run $COMPOSE_COMMAND down followed by $COMPOSE_COMMAND up -d" + echo "Please change it to a FQDN and run \"./mailcow-compose.sh down\" followed by \"./mailcow-compose.sh up -d\"" exit 1 fi @@ -381,6 +361,10 @@ CONFIG_ARRAY=( "SNAT6_TO_SOURCE" "COMPOSE_PROJECT_NAME" "DOCKER_COMPOSE_VERSION" + "MAILCOW_CONTAINER_ENGINE" + "MAILCOW_CONTAINER_SOCKET" + "DOCKER_COMPOSE_EXTRA_OVERRIDES" + "MAILCOW_DOCKER_COMPOSE" "SQL_PORT" "API_KEY" "API_KEY_READ_ONLY" @@ -402,6 +386,7 @@ CONFIG_ARRAY=( "ACME_CONTACT" "WATCHDOG_VERBOSE" "WEBAUTHN_ONLY_TRUSTED_VENDORS" + "MAILCOW_STORAGE_DIR" ) sed -i --follow-symlinks '$a\' mailcow.conf @@ -421,12 +406,44 @@ for option in ${CONFIG_ARRAY[@]}; do echo "Adding new option \"${option}\" to mailcow.conf" echo "# Used Docker Compose version" >> mailcow.conf echo "# Switch here between native (compose plugin) and standalone" >> mailcow.conf - echo "# For more informations take a look at the mailcow docs regarding the configuration options." >> mailcow.conf + echo "# For more information take a look at the mailcow docs regarding the configuration options." >> mailcow.conf echo "# Normally this should be untouched but if you decided to use either of those you can switch it manually here." >> mailcow.conf - echo "# Please be aware that at least one of those variants should be installed on your maschine or mailcow will fail." >> mailcow.conf + echo "# Please be aware that at least one of those variants should be installed on your machine or mailcow will fail." >> mailcow.conf echo "" >> mailcow.conf echo "DOCKER_COMPOSE_VERSION=${DOCKER_COMPOSE_VERSION}" >> mailcow.conf fi + elif [[ ${option} == "MAILCOW_CONTAINER_ENGINE" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "# The container engine to use to run this project (docker or podman)." >> mailcow.conf + echo "" >> mailcow.conf + echo "MAILCOW_CONTAINER_ENGINE=${MAILCOW_CONTAINER_ENGINE}" >> mailcow.conf + fi + elif [[ ${option} == "MAILCOW_CONTAINER_SOCKET" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "# The location of the container socket to use for volume mounts.." >> mailcow.conf + echo "" >> mailcow.conf + echo "MAILCOW_CONTAINER_SOCKET=${CONTAINER_SOCKET}" >> mailcow.conf + fi + elif [[ ${option} == "DOCKER_COMPOSE_EXTRA_OVERRIDES" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "# Additional override files (relative to the repo root) that need to be taken care of when running mailcow scripts." >> mailcow.conf + echo "# Comma separated list without spaces!" >> mailcow.conf + echo "# Example: DOCKER_COMPOSE_EXTRA_OVERRIDES=helper-scripts/docker-compose.override.yml.d/CONTAINER_ENGINE_PODMAN/docker-compose.override.yml,helper-scripts/docker-compose.override.yml.d/CUSTOM_STORAGE_LOCATION/docker-compose.override.yml" >> mailcow.conf + echo "" >> mailcow.conf + echo "DOCKER_COMPOSE_EXTRA_OVERRIDES=" >> mailcow.conf + fi + elif [[ ${option} == "MAILCOW_DOCKER_COMPOSE" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "# The name of the docker-compose binary to use. This option can be used in case different versions of" >> mailcow.conf + echo "# docker-compose are installed and another binary than 'docker-compose' (default) needs to be used." >> mailcow.conf + echo "# Example: docker-compose-v2" >> mailcow.conf + echo "" >> mailcow.conf + echo "MAILCOW_DOCKER_COMPOSE=${MAILCOW_DOCKER_COMPOSE:-"docker-compose"}" >> mailcow.conf + fi elif [[ ${option} == "DOVEADM_PORT" ]]; then if ! grep -q ${option} mailcow.conf; then echo "Adding new option \"${option}\" to mailcow.conf" @@ -602,6 +619,7 @@ for option in ${CONFIG_ARRAY[@]}; do fi elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" echo '# Additional server names for mailcow UI' >> mailcow.conf echo '#' >> mailcow.conf echo '# Specify alternative addresses for the mailcow UI to respond to' >> mailcow.conf @@ -613,6 +631,7 @@ for option in ${CONFIG_ARRAY[@]}; do fi elif [[ ${option} == "ACME_CONTACT" ]]; then if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" echo '# Lets Encrypt registration contact information' >> mailcow.conf echo '# Optional: Leave empty for none' >> mailcow.conf echo '# This value is only used on first order!' >> mailcow.conf @@ -622,16 +641,24 @@ for option in ${CONFIG_ARRAY[@]}; do fi elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" echo "# WebAuthn device manufacturer verification" >> mailcow.conf echo '# After setting WEBAUTHN_ONLY_TRUSTED_VENDORS=y only devices from trusted manufacturers are allowed' >> mailcow.conf echo '# root certificates can be placed for validation under mailcow-dockerized/data/web/inc/lib/WebAuthn/rootCertificates' >> mailcow.conf echo 'WEBAUTHN_ONLY_TRUSTED_VENDORS=n' >> mailcow.conf fi -elif [[ ${option} == "WATCHDOG_VERBOSE" ]]; then + elif [[ ${option} == "WATCHDOG_VERBOSE" ]]; then if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" echo '# Enable watchdog verbose logging' >> mailcow.conf echo 'WATCHDOG_VERBOSE=n' >> mailcow.conf - fi + fi + elif [[ ${option} == "MAILCOW_STORAGE_DIR" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo '# The directory used to store the data of the used containers (used in case the CUSTOM_STORAGE_LOCATION override is included)' >> mailcow.conf + echo 'MAILCOW_STORAGE_DIR=' >> mailcow.conf + fi elif ! grep -q ${option} mailcow.conf; then echo "Adding new option \"${option}\" to mailcow.conf" echo "${option}=n" >> mailcow.conf @@ -639,16 +666,16 @@ elif [[ ${option} == "WATCHDOG_VERBOSE" ]]; then done if [[( ${SKIP_PING_CHECK} == "y")]]; then -echo -e "\e[32mSkipping Ping Check...\e[0m" + echo -e "\e[32mSkipping Ping Check...\e[0m" else - echo -en "Checking internet connection... " - if ! check_online_status; then - echo -e "\e[31mfailed\e[0m" - exit 1 - else - echo -e "\e[32mOK\e[0m" - fi + echo -en "Checking internet connection... " + if ! check_online_status; then + echo -e "\e[31mfailed\e[0m" + exit 1 + else + echo -e "\e[32mOK\e[0m" + fi fi if ! [ $NEW_BRANCH ]; then @@ -679,9 +706,9 @@ elif [ $NEW_BRANCH == "master" ] && [ $CURRENT_BRANCH != "master" ]; then sleep 1 echo -e "\e[33mBefore you do: Please take a backup of all components to ensure that no Data is lost...\e[0m" sleep 1 - echo -e "\e[31mWARNING: Please see on GitHub or ask in the communitys if a switch to master is stable or not. + echo -e "\e[31mWARNING: Please see on GitHub or ask in the communities if a switch to master is stable or not. In some rear cases a Update back to master can destroy your mailcow configuration in case of Database Upgrades etc. - Normally a upgrade back to master should be safe during each full release. + Normally a upgrade back to master should be safe during each full release. Check GitHub for Database Changes and Update only if there similar to the full release!\e[0m" read -r -p "Are you sure you that want to continue upgrading to the stable (master) branch? [y/N] " response if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then @@ -744,7 +771,12 @@ if [ ! $FORCE ]; then echo "OK, exiting." exit 0 fi +fi + +if [[ "${MAILCOW_CONTAINER_ENGINE}" == "docker" ]]; then migrate_docker_nat +else + echo "Skipping migrating docker nat..." fi remove_obsolete_nginx_ports @@ -752,16 +784,20 @@ remove_obsolete_nginx_ports echo -e "\e[32mValidating docker-compose stack configuration...\e[0m" sed -i 's/HTTPS_BIND:-:/HTTPS_BIND:-/g' docker-compose.yml sed -i 's/HTTP_BIND:-:/HTTP_BIND:-/g' docker-compose.yml -if ! $COMPOSE_COMMAND config -q; then +if ! ./mailcow-compose.sh config -q; then echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m" exit 1 fi -echo -e "\e[32mChecking for conflicting bridges...\e[0m" -MAILCOW_BRIDGE=$($COMPOSE_COMMAND config | grep -i com.docker.network.bridge.name | cut -d':' -f2) -while read NAT_ID; do - iptables -t nat -D POSTROUTING $NAT_ID -done < <(iptables -L -vn -t nat --line-numbers | grep $IPV4_NETWORK | grep -E 'MASQUERADE.*all' | grep -v ${MAILCOW_BRIDGE} | cut -d' ' -f1) +if [[ "${MAILCOW_CONTAINER_ENGINE}" == "docker" ]]; then + echo -e "\e[32mChecking for conflicting bridges...\e[0m" + MAILCOW_BRIDGE=$(./mailcow-compose.sh config | grep -i com.docker.network.bridge.name | cut -d':' -f2) + while read NAT_ID; do + iptables -t nat -D POSTROUTING $NAT_ID + done < <(iptables -L -vn -t nat --line-numbers | grep $IPV4_NETWORK | grep -E 'MASQUERADE.*all' | grep -v ${MAILCOW_BRIDGE} | cut -d' ' -f1) +else + echo "Skipping check for conflicting bridges..." +fi DIFF_DIRECTORY=update_diffs DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S") @@ -778,12 +814,13 @@ prefetch_images echo -e "\e[32mStopping mailcow...\e[0m" sleep 2 -MAILCOW_CONTAINERS=($($COMPOSE_COMMAND ps -q)) -$COMPOSE_COMMAND down +MAILCOW_CONTAINERS=($(./mailcow-compose.sh ps -q)) +./mailcow-compose.sh down + echo -e "\e[32mChecking for remaining containers...\e[0m" sleep 2 for container in "${MAILCOW_CONTAINERS[@]}"; do - docker rm -f "$container" 2> /dev/null + ${MAILCOW_CONTAINER_ENGINE} rm -f "$container" 2> /dev/null done [[ -f data/conf/nginx/ZZZ-ejabberd.conf ]] && rm data/conf/nginx/ZZZ-ejabberd.conf @@ -807,7 +844,7 @@ if [[ ${MERGE_RETURN} == 128 ]]; then 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" exit 1 elif [[ ${MERGE_RETURN} == 1 ]]; then - echo -e "\e[93mPotenial conflict, trying to fix...\e[0m" + echo -e "\e[93mPotential conflict, trying to fix...\e[0m" git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v git add -A git commit -m "After update on ${DATE}" > /dev/null @@ -816,13 +853,18 @@ elif [[ ${MERGE_RETURN} == 1 ]]; then elif [[ ${MERGE_RETURN} != 0 ]]; then echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m" echo - echo "Run $COMPOSE_COMMAND up -d to restart your stack without updates or try again after fixing the mentioned errors." + echo "Run ./mailcow-compose.sh up -d to restart your stack without updates or try again after fixing the mentioned errors." exit 1 fi echo -e "\e[32mFetching new images, if any...\e[0m" sleep 2 -$COMPOSE_COMMAND pull +./mailcow-compose.sh pull + +if [[ "${MAILCOW_CONTAINER_ENGINE}" == "podman" ]]; then + # Apply patches for usage with Podman + bash ./patches-for-podman.sh +fi # Fix missing SSL, does not overwrite existing files [[ ! -d data/assets/ssl ]] && mkdir -p data/assets/ssl @@ -834,7 +876,7 @@ if grep -q 'SYSCTL_IPV6_DISABLED=1' mailcow.conf; then echo '!! IMPORTANT !!' echo 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".' - echo 'This setting will only be active after a complete shutdown of mailcow by running $COMPOSE_COMMAND down followed by $COMPOSE_COMMAND up -d".' + echo 'This setting will only be active after a complete shutdown of mailcow by running "./mailcow-compose.sh down" followed by "./mailcow-compose.sh up -d".' echo echo '!! IMPORTANT !!' echo @@ -872,8 +914,8 @@ else mailcow_last_git_version="" fi -mailcow_git_commit=$(git rev-parse origin/${BRANCH}) -mailcow_git_commit_date=$(git log -1 --format=%ci @{upstream} ) +mailcow_git_commit=$(git rev-parse HEAD) +mailcow_git_commit_date=$(git log -1 --format=%ci HEAD ) if [ $? -eq 0 ]; then echo ' data/web/inc/app_info.inc.php @@ -906,11 +948,11 @@ fi sed -i 's/^DOCKER_COMPOSE_VERSION=$/DOCKER_COMPOSE_VERSION='$DOCKER_COMPOSE_VERSION'/g' mailcow.conf if [[ ${SKIP_START} == "y" ]]; then - echo -e "\e[33mNot starting mailcow, please run \"$COMPOSE_COMMAND up -d --remove-orphans\" to start mailcow.\e[0m" + echo -e "\e[33mNot starting mailcow, please run \"./mailcow-compose.sh up -d --remove-orphans\" to start mailcow.\e[0m" else echo -e "\e[32mStarting mailcow...\e[0m" sleep 2 - $COMPOSE_COMMAND up -d --remove-orphans + ./mailcow-compose.sh up -d --remove-orphans fi echo -e "\e[32mCollecting garbage...\e[0m" @@ -925,4 +967,4 @@ fi # echo # git reflog --color=always | grep "Before update on " # echo -# echo "Use \"git reset --hard hash-on-the-left\" and run $COMPOSE_COMMAND up -d afterwards." \ No newline at end of file +# echo "Use \"git reset --hard hash-on-the-left\" and run ./mailcow-compose.sh up -d afterwards."