From b8a20b3a23fc888d6a6c2237df8b3346f44a07d8 Mon Sep 17 00:00:00 2001 From: apoc4lyps Date: Thu, 4 Jan 2018 15:57:43 +0100 Subject: [PATCH 01/43] [Helper] Nextcloud: fix subdomain --- data/assets/nextcloud/nextcloud.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/assets/nextcloud/nextcloud.conf b/data/assets/nextcloud/nextcloud.conf index 49a585d2..c2dc8943 100644 --- a/data/assets/nextcloud/nextcloud.conf +++ b/data/assets/nextcloud/nextcloud.conf @@ -25,7 +25,7 @@ server { add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; - server_name NC_SERVER_SUB; + server_name NC_SUBD; root /web/nextcloud/; @@ -111,7 +111,7 @@ server { add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; - server_name NC_SERVER_SUB; + server_name NC_SUBD; root /web/nextcloud/; From c3aaae2a764ddd1d7ac5052a30fa68df07ad592b Mon Sep 17 00:00:00 2001 From: apoc4lyps Date: Thu, 4 Jan 2018 15:59:27 +0100 Subject: [PATCH 02/43] [Helper] Nextcloud: fix subdomain and background --- helper-scripts/nextcloud.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh index 781e5133..0e3504ad 100755 --- a/helper-scripts/nextcloud.sh +++ b/helper-scripts/nextcloud.sh @@ -78,10 +78,12 @@ elif [[ ${NC_INSTALL} == "y" ]]; then /web/nextcloud/occ config:system:set redis port --value=6379 --type=integer; \ /web/nextcloud/occ config:system:set memcache.locking --value='\OC\Memcache\Redis' --type=string; \ /web/nextcloud/occ config:system:set memcache.local --value='\OC\Memcache\Redis' --type=string; \ + /web/nextcloud/occ config:system:set trusted_domains 1 --value=${MAILCOW_HOSTNAME}; \ /web/nextcloud/occ config:system:set trusted_proxies 0 --value=fd4d:6169:6c63:6f77::1; \ /web/nextcloud/occ config:system:set trusted_proxies 1 --value=172.22.1.0/24; \ /web/nextcloud/occ config:system:set overwritewebroot --value=/nextcloud; \ /web/nextcloud/occ config:system:set overwritehost --value=${MAILCOW_HOSTNAME}; \ + /web/nextcloud/occ config:system:set overwriteprotocol --value=https \ /web/nextcloud/occ config:system:set mail_smtpmode --value=smtp; \ /web/nextcloud/occ config:system:set mail_smtpauthtype --value=LOGIN; \ /web/nextcloud/occ config:system:set mail_from_address --value=nextcloud; \ @@ -93,10 +95,11 @@ elif [[ ${NC_INSTALL} == "y" ]]; then /web/nextcloud/occ config:system:set user_backends 0 class --value=OC_User_IMAP" if [[ ${NC_TYPE} == "subdomain" ]]; then + docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set trusted_domains 1 --value=${NC_SUBD} docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritewebroot --value=/ - docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritehost --value=nextcloud.develcow.de + docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritehost --value=${NC_SUBD} cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/ - sed -i 's/NC_SUBD/${NC_SUBD}/g' ./data/conf/nginx/nextcloud.conf + sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf elif [[ ${NC_TYPE} == "subfolder" ]]; then cp ./data/assets/nextcloud/site.nextcloud.custom ./data/conf/nginx/ fi From 06955f53127451750ecb8cd9e0d455fe246206aa Mon Sep 17 00:00:00 2001 From: apoc4lyps Date: Thu, 4 Jan 2018 16:58:02 +0100 Subject: [PATCH 03/43] Update nextcloud.sh fix typo --- helper-scripts/nextcloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh index 0e3504ad..bec53eb5 100755 --- a/helper-scripts/nextcloud.sh +++ b/helper-scripts/nextcloud.sh @@ -83,7 +83,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then /web/nextcloud/occ config:system:set trusted_proxies 1 --value=172.22.1.0/24; \ /web/nextcloud/occ config:system:set overwritewebroot --value=/nextcloud; \ /web/nextcloud/occ config:system:set overwritehost --value=${MAILCOW_HOSTNAME}; \ - /web/nextcloud/occ config:system:set overwriteprotocol --value=https \ + /web/nextcloud/occ config:system:set overwriteprotocol --value=https; \ /web/nextcloud/occ config:system:set mail_smtpmode --value=smtp; \ /web/nextcloud/occ config:system:set mail_smtpauthtype --value=LOGIN; \ /web/nextcloud/occ config:system:set mail_from_address --value=nextcloud; \ From a6bcde3ee0b4cc8175dcbc08e2561890d6bf2488 Mon Sep 17 00:00:00 2001 From: Kristian Date: Thu, 18 Jan 2018 19:14:18 +0100 Subject: [PATCH 04/43] [WEB] Make quarantine action column more wider --- data/web/js/quarantaine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/web/js/quarantaine.js b/data/web/js/quarantaine.js index 8c8ee3a0..09a3b2df 100644 --- a/data/web/js/quarantaine.js +++ b/data/web/js/quarantaine.js @@ -15,7 +15,7 @@ jQuery(function($){ {"name":"sender","title":lang.sender,"breakpoints":"xs sm"}, {"name":"rcpt","title":lang.rcpt, "type": "text"}, {"name":"created","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.received,"style":{"width":"170px"}}, - {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"205px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} ], "rows": $.ajax({ dataType: 'json', @@ -81,4 +81,4 @@ jQuery(function($){ } // Initial table drawings draw_quarantaine_table(); -}); \ No newline at end of file +}); From 08c8976a9543c357251aba2edb09e39ab9e053e4 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 14:58:16 +0100 Subject: [PATCH 05/43] [SOGo] Show shared aliases and "allow to send as" addresses as FROM fields in SOGo --- data/Dockerfiles/sogo/bootstrap-sogo.sh | 11 +++++++---- data/web/inc/init_db.inc.php | 9 +++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 4bd9d570..59877927 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -18,11 +18,13 @@ done mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view" mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF -CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, home, kind, multiple_bookings) AS -SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(ga.aliases, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox -LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username = mailbox.username +CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, sa_aliases, ad_aliases, home, kind, multiple_bookings) AS +SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(GROUP_CONCAT(ga.aliases SEPARATOR ' '), ''), IFNULL(gsa.send_as_acl, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox +LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)') +LEFT OUTER JOIN grouped_sender_acl gsa ON gsa.username = mailbox.username LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username -WHERE mailbox.active = '1'; +WHERE mailbox.active = '1' +GROUP BY mailbox.username; EOF @@ -67,6 +69,7 @@ while read line MailFieldNames aliases + sa_aliases ad_aliases KindFieldName diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 6c0ef937..4f93a9d0 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -3,7 +3,7 @@ function init_db_schema() { try { global $pdo; - $db_version = "02012018_1515"; + $db_version = "21012018_1317"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -21,13 +21,14 @@ function init_db_schema() { AND active = '1' AND address NOT LIKE '@%' GROUP BY goto;", - "grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as) AS - SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as FROM sender_acl + "grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as_acl) AS + SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as_acl FROM sender_acl WHERE send_as NOT LIKE '@%' GROUP BY logged_in_as;", "grouped_domain_alias_address" => "CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox - LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;", + LEFT OUTER JOIN alias_domain ON target_domain=domain + GROUP BY username;", "sieve_before" => "CREATE VIEW sieve_before (id, username, script_name, script_data) AS SELECT md5(script_data), username, script_name, script_data FROM sieve_filters WHERE filter_type = 'prefilter';", From 83fb8c0fd8d18c297cdfd11be9c32ef28c77d5fb Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 14:59:45 +0100 Subject: [PATCH 06/43] [Nginx] Use names instead of IPs --- data/conf/nginx/dynmaps.conf | 2 +- data/conf/nginx/site.conf | 35 +++++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/data/conf/nginx/dynmaps.conf b/data/conf/nginx/dynmaps.conf index 3ff8692f..99c0c6aa 100644 --- a/data/conf/nginx/dynmaps.conf +++ b/data/conf/nginx/dynmaps.conf @@ -10,7 +10,7 @@ server { location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9000; + fastcgi_pass phpfpm:9001; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index 89572eff..ede9fe4a 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -7,6 +7,13 @@ map $http_x_forwarded_proto $client_req_scheme { https https; } +server { + listen 80 default_server; + listen [::]:80 default_server; + include /etc/nginx/conf.d/server_name.active; + return 301 https://$host$request_uri; +} + server { include /etc/nginx/mime.types; charset utf-8; @@ -65,7 +72,7 @@ server { } location /rspamd/ { - proxy_pass http://172.22.1.253:11334/; + proxy_pass http://rspamd:11334/; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; @@ -97,7 +104,7 @@ server { } location ^~ /Microsoft-Server-ActiveSync { - proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync; + proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync; proxy_connect_timeout 1000; proxy_next_upstream timeout error; proxy_send_timeout 1000; @@ -119,7 +126,7 @@ server { } location ^~ /SOGo { - proxy_pass http://172.22.1.252:20000; + proxy_pass http://sogo:20000; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; @@ -134,7 +141,7 @@ server { } location /SOGo.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -144,7 +151,7 @@ server { } location /.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -154,7 +161,7 @@ server { } location /SOGo/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -164,7 +171,7 @@ server { } location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { - proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2; + proxy_pass http://sogo:9192/$1.SOGo/Resources/$2; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -243,7 +250,7 @@ server { } location /rspamd/ { - proxy_pass http://172.22.1.253:11334/; + proxy_pass http://rspamd:11334/; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; @@ -275,7 +282,7 @@ server { } location ^~ /Microsoft-Server-ActiveSync { - proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync; + proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync; proxy_connect_timeout 1000; proxy_next_upstream timeout error; proxy_send_timeout 1000; @@ -297,7 +304,7 @@ server { } location ^~ /SOGo { - proxy_pass http://172.22.1.252:20000; + proxy_pass http://sogo:20000; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; @@ -312,7 +319,7 @@ server { } location /SOGo.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -322,7 +329,7 @@ server { } location /.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -332,7 +339,7 @@ server { } location /SOGo/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -342,7 +349,7 @@ server { } location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { - proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2; + proxy_pass http://sogo:9192/$1.SOGo/Resources/$2; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; From 83a21259f7269cc5eb1882590263f36455fd3754 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:00:05 +0100 Subject: [PATCH 07/43] [Rspamd] Use names instead of IPs --- data/Dockerfiles/rspamd/settings.conf | 2 +- data/conf/rspamd/local.d/greylist.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/Dockerfiles/rspamd/settings.conf b/data/Dockerfiles/rspamd/settings.conf index 3c1c9c16..4449f091 100644 --- a/data/Dockerfiles/rspamd/settings.conf +++ b/data/Dockerfiles/rspamd/settings.conf @@ -1 +1 @@ -settings = "http://172.22.1.251:8081/settings.php"; +settings = "http://nginx:8081/settings.php"; diff --git a/data/conf/rspamd/local.d/greylist.conf b/data/conf/rspamd/local.d/greylist.conf index 9acf6f28..ee0220d7 100644 --- a/data/conf/rspamd/local.d/greylist.conf +++ b/data/conf/rspamd/local.d/greylist.conf @@ -1 +1 @@ -whitelisted_ip = "http://172.22.1.251:8081/forwardinghosts.php"; +whitelisted_ip = "http://nginx:8081/forwardinghosts.php"; From a50f85026ac33e965da1322528e67b4d91c28f51 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:00:28 +0100 Subject: [PATCH 08/43] [PHP-FPM] Mount php configs into container --- data/conf/phpfpm/php-conf.d/opcache-recommended.ini | 7 +++++++ data/conf/phpfpm/php-fpm.d/system.conf | 12 ++++++++++++ data/conf/phpfpm/php-fpm.d/www.conf | 12 ++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 data/conf/phpfpm/php-conf.d/opcache-recommended.ini create mode 100644 data/conf/phpfpm/php-fpm.d/system.conf create mode 100644 data/conf/phpfpm/php-fpm.d/www.conf diff --git a/data/conf/phpfpm/php-conf.d/opcache-recommended.ini b/data/conf/phpfpm/php-conf.d/opcache-recommended.ini new file mode 100644 index 00000000..104f2425 --- /dev/null +++ b/data/conf/phpfpm/php-conf.d/opcache-recommended.ini @@ -0,0 +1,7 @@ +opcache.enable=1 +opcache.enable_cli=1 +opcache.interned_strings_buffer=8 +opcache.max_accelerated_files=10000 +opcache.memory_consumption=128 +opcache.save_comments=1 +opcache.revalidate_freq=1 diff --git a/data/conf/phpfpm/php-fpm.d/system.conf b/data/conf/phpfpm/php-fpm.d/system.conf new file mode 100644 index 00000000..52edd73c --- /dev/null +++ b/data/conf/phpfpm/php-fpm.d/system.conf @@ -0,0 +1,12 @@ +[system] +user = www-data +group = www-data +pm = dynamic +pm.max_children = 10 +pm.start_servers = 2 +pm.min_spare_servers = 2 +pm.max_spare_servers = 4 +listen = [::]:9001 +access.log = /proc/self/fd/2 +clear_env = no +catch_workers_output = yes diff --git a/data/conf/phpfpm/php-fpm.d/www.conf b/data/conf/phpfpm/php-fpm.d/www.conf new file mode 100644 index 00000000..7ef3ce77 --- /dev/null +++ b/data/conf/phpfpm/php-fpm.d/www.conf @@ -0,0 +1,12 @@ +[www] +user = www-data +group = www-data +listen = 127.0.0.1:9000 +pm = ondemand +pm.max_children = 20 +pm.process_idle_timeout = 20s +pm.max_requests = 800 +listen = [::]:9001 +access.log = /proc/self/fd/2 +clear_env = no +catch_workers_output = yes From 737c41379f0a0cb6a33b0a239197e147d98e9b01 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:01:02 +0100 Subject: [PATCH 09/43] [PHP-FPM] Move opcache config to local config file, define new PECL versions --- data/Dockerfiles/phpfpm/Dockerfile | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile index 5259e70d..45d2fa84 100644 --- a/data/Dockerfiles/phpfpm/Dockerfile +++ b/data/Dockerfiles/phpfpm/Dockerfile @@ -1,10 +1,11 @@ FROM php:7.1-fpm-alpine LABEL maintainer "Andre Peters " -ENV REDIS_PECL 3.1.4 -ENV MEMCACHED_PECL 3.0.3 -ENV APCU_PECL 5.1.8 +ENV REDIS_PECL 3.1.6 +ENV MEMCACHED_PECL 3.0.4 +ENV APCU_PECL 5.1.9 ENV IMAGICK_PECL 3.4.3 +ENV MAILPARSE_PECL 3.0.2 RUN apk add -U --no-cache libxml2-dev \ icu-dev \ @@ -41,27 +42,16 @@ RUN apk add -U --no-cache libxml2-dev \ Net_Sieve \ NET_SMTP \ Mail_mime \ - && pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse \ + && pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse-${MAILPARSE_PECL} \ && docker-php-ext-enable redis apcu memcached imagick mailparse \ && pecl clear-cache \ && docker-php-ext-configure intl \ && docker-php-ext-install -j 4 intl gettext ldap sockets soap pdo pdo_mysql xmlrpc gd zip pcntl opcache \ && docker-php-ext-configure imap --with-imap --with-imap-ssl \ && docker-php-ext-install -j 4 imap \ - && apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev \ - && { \ - echo 'opcache.enable=1'; \ - echo 'opcache.enable_cli=1'; \ - echo 'opcache.interned_strings_buffer=8'; \ - echo 'opcache.max_accelerated_files=10000'; \ - echo 'opcache.memory_consumption=128'; \ - echo 'opcache.save_comments=1'; \ - echo 'opcache.revalidate_freq=1'; \ -} > /usr/local/etc/php/conf.d/opcache-recommended.ini + && apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev COPY ./docker-entrypoint.sh / -EXPOSE 9000 - ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["php-fpm"] From d24bb169471d9fbc44e0f2c5f1b28eb888f1ed5f Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:01:35 +0100 Subject: [PATCH 10/43] [Watchdog] Check PHP-FPM port 9000 and 9001 --- data/Dockerfiles/watchdog/watchdog.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh index 1302f49a..bed11907 100755 --- a/data/Dockerfiles/watchdog/watchdog.sh +++ b/data/Dockerfiles/watchdog/watchdog.sh @@ -191,6 +191,7 @@ phpfpm_checks() { host_ip=$(get_container_ip php-fpm-mailcow) err_c_cur=${err_count} cgi-fcgi -bind -connect ${host_ip}:9000 | grep "Content-type" 1>&2; err_count=$(( ${err_count} + ($? * 2))) + cgi-fcgi -bind -connect ${host_ip}:9001 | grep "Content-type" 1>&2; err_count=$(( ${err_count} + ($? * 2))) /usr/lib/nagios/plugins/check_ping -4 -H ${host_ip} -w 2000,10% -c 4000,100% -p2 1>&2; err_count=$(( ${err_count} + $? )) [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) From a7a7b3f3fdf359d69dbb098dd1eef3f6ecaddb36 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:01:51 +0100 Subject: [PATCH 11/43] [Postfix] Use name instead of IP --- data/Dockerfiles/postfix/whitelist_forwardinghosts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh b/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh index ab066d89..4ad5ab32 100755 --- a/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh +++ b/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh @@ -6,7 +6,7 @@ while read QUERY; do echo "500 dunno" continue fi - result=$(curl -s http://172.22.1.251:8081/forwardinghosts.php?host=${QUERY[1]}) + result=$(curl -s http://nginx:8081/forwardinghosts.php?host=${QUERY[1]}) logger -t whitelist_forwardinghosts -p mail.info "Look up ${QUERY[1]} on whitelist, result $result" echo ${result} done From 43770434b37829f507f407080d95604b91fca54b Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:02:56 +0100 Subject: [PATCH 12/43] [Web] Alias by alias domain is now in 'direct aliases', added desc to direct and shared aliases --- data/web/inc/functions.inc.php | 11 +++++++++-- data/web/lang/lang.de.php | 3 +++ data/web/lang/lang.en.php | 3 +++ data/web/user.php | 16 +++++++--------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index b19792e2..f7f08403 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -459,8 +459,9 @@ function user_get_alias_details($username) { while ($row = array_shift($run)) { $data['shared_aliases'] = $row['shared_aliases']; } - $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') AS `direct_aliases` FROM `alias` + $stmt = $pdo->prepare("SELECT GROUP_CONCAT(`address` SEPARATOR ', ') AS `direct_aliases` FROM `alias` WHERE `goto` = :username_goto + AND `address` NOT LIKE '@%' AND `address` != :username_address"); $stmt->execute( array( @@ -477,7 +478,13 @@ function user_get_alias_details($username) { $stmt->execute(array(':username' => $username)); $run = $stmt->fetchAll(PDO::FETCH_ASSOC); while ($row = array_shift($run)) { - $data['ad_alias'] = $row['ad_alias']; + if (empty($data['direct_aliases'])) { + $data['direct_aliases'] = $row['ad_alias']; + } + else { + // Probably faster than imploding + $data['direct_aliases'] .= ', ' . $row['ad_alias']; + } } $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '✘') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';"); $stmt->execute(array(':username' => $username)); diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 33d5ce33..edf97908 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -101,6 +101,7 @@ $lang['danger']['spam_alias_max_exceeded'] = 'Maximale Anzahl an Spam-Alias-Adre $lang['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an'; $lang['user']['loading'] = "Lade..."; $lang['user']['active_sieve'] = "Aktiver Filter"; +$lang['user']['show_sieve_filters'] = "Zeige aktiven Filter des Benutzers"; $lang['user']['no_active_filter'] = "Kein aktiver Filter vorhanden"; $lang['user']['on'] = 'Ein'; $lang['user']['off'] = 'Aus'; @@ -122,7 +123,9 @@ $lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse'; $lang['user']['alias'] = 'Alias'; $lang['user']['aliases'] = 'Aliasse'; $lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen'; +$lang['user']['shared_aliases_desc'] = 'Geteilte Alias-Adressen werden nicht bei benutzerdefinierten Einstellungen wie die des Spam-Filters oder der Verschlüsselungsrichtlinie berücksichtigt. Entsprechende Spam-Filter können lediglich von einem Administrator vorgenommen werden.'; $lang['user']['direct_aliases'] = 'Direkte Alias-Adressen'; +$lang['user']['direct_aliases_desc'] = 'Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.'; $lang['user']['domain_aliases'] = 'Domain-Alias Adressen'; $lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)'; $lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer'; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index ef60bd22..b945ea4c 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -101,6 +101,7 @@ $lang['danger']['spam_alias_max_exceeded'] = "Max. allowed spam alias addresses $lang['danger']['validity_missing'] = 'Please assign a period of validity'; $lang['user']['loading'] = "Loading..."; $lang['user']['active_sieve'] = "Active filter"; +$lang['user']['show_sieve_filters'] = "Show active user sieve filter"; $lang['user']['no_active_filter'] = "No active filter available"; $lang['user']['on'] = "On"; $lang['user']['off'] = "Off"; @@ -122,7 +123,9 @@ $lang['user']['spam_aliases'] = 'Temporary email aliases'; $lang['user']['alias'] = 'Alias'; $lang['user']['aliases'] = 'Aliases'; $lang['user']['shared_aliases'] = 'Shared alias addresses'; +$lang['user']['shared_aliases_desc'] = 'A shared alias address is not affected by any user specific settings. A custom spam filter setting can be archived by a domain-wide policy set by an administrator..'; $lang['user']['direct_aliases'] = 'Direct alias addresses'; +$lang['user']['direct_aliases_desc'] = 'Direct alias addresses are affected by spam filter and TLS policy settings.'; $lang['user']['domain_aliases'] = 'Domain alias addresses'; $lang['user']['is_catch_all'] = 'Catch-all for domain/s'; $lang['user']['aliases_also_send_as'] = 'Also allowed to send as user'; diff --git a/data/web/user.php b/data/web/user.php index 5ccd0f8f..0fb0875f 100644 --- a/data/web/user.php +++ b/data/web/user.php @@ -97,7 +97,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
@@ -105,24 +105,22 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == ' $user_get_alias_details = user_get_alias_details($username); ?>
-
:
+
: +

+

-
:
+
: +

+


-
-
:
-
-

-
-
:
From 76573da3b9f312a022f53954f2e1a0e4626495b2 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:03:24 +0100 Subject: [PATCH 13/43] [Compose] Removed some static IPs, added local configs, pushed new images --- docker-compose.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 59bc7cd2..77143bfa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,6 @@ services: - 172.22.1.254 networks: mailcow-network: - ipv4_address: 172.22.1.250 aliases: - mysql @@ -66,7 +65,7 @@ services: - clamd rspamd-mailcow: - image: mailcow/rspamd:1.15 + image: mailcow/rspamd:1.16 build: ./data/Dockerfiles/rspamd stop_grace_period: 30s depends_on: @@ -86,12 +85,11 @@ services: hostname: rspamd networks: mailcow-network: - ipv4_address: 172.22.1.253 aliases: - rspamd php-fpm-mailcow: - image: mailcow/phpfpm:1.8 + image: mailcow/phpfpm:1.9 build: ./data/Dockerfiles/phpfpm command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" depends_on: @@ -101,6 +99,9 @@ services: - ./data/conf/rspamd/dynmaps:/dynmaps:ro - dkim-vol-1:/data/dkim - ./data/conf/rspamd/meta_exporter:/meta_exporter:ro + - ./data/conf/phpfpm/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf + - ./data/conf/phpfpm/php-fpm.d/system.conf:/usr/local/etc/php-fpm.d/system.conf + - ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini environment: - LOG_LINES=${LOG_LINES} - TZ=${TZ} @@ -125,7 +126,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.13 + image: mailcow/sogo:1.14 build: ./data/Dockerfiles/sogo environment: - DBNAME=${DBNAME} @@ -141,7 +142,6 @@ services: - 172.22.1.254 networks: mailcow-network: - ipv4_address: 172.22.1.252 aliases: - sogo @@ -184,7 +184,7 @@ services: - dovecot postfix-mailcow: - image: mailcow/postfix:1.11 + image: mailcow/postfix:1.12 build: ./data/Dockerfiles/postfix volumes: - ./data/conf/postfix:/opt/postfix/conf @@ -252,7 +252,6 @@ services: - 172.22.1.254 networks: mailcow-network: - ipv4_address: 172.22.1.251 aliases: - nginx @@ -305,7 +304,7 @@ services: - /lib/modules:/lib/modules:ro watchdog-mailcow: - image: mailcow/watchdog:1.12 + image: mailcow/watchdog:1.13 # Debug #command: /watchdog.sh build: ./data/Dockerfiles/watchdog @@ -323,7 +322,6 @@ services: - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} networks: mailcow-network: - ipv4_address: 172.22.1.248 aliases: - watchdog From 1c3a52172a2fb5440ebeaac495635886293ceb81 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Sun, 21 Jan 2018 15:03:46 +0100 Subject: [PATCH 14/43] [Update] Added --no-start parameter to update.sh --- update.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/update.sh b/update.sh index f7016512..647071ac 100755 --- a/update.sh +++ b/update.sh @@ -53,7 +53,9 @@ set -o pipefail export LC_ALL=C DATE=$(date +%Y-%m-%d_%H_%M_%S) BRANCH=$(git rev-parse --abbrev-ref HEAD) +declare -a DC_PARAMS +while (($#)); do case "${1}" in --check|-c) echo "Checking remote code for updates..." @@ -66,7 +68,11 @@ case "${1}" in exit 3 fi ;; + --no-start) + DC_PARAMS=(${DC_PARAMS[@]} "--no-start") + ;; esac +done echo -e "\e[32mChecking for newer update script...\e[0m" SHA1_1=$(sha1sum update.sh) @@ -146,7 +152,7 @@ cp -n data/assets/ssl-example/*.pem data/assets/ssl/ echo -e "\e[32mStarting mailcow...\e[0m" sleep 2 -docker-compose up -d --remove-orphans +docker-compose up -d --remove-orphans ${DC_PARAMS[@]} echo -e "\e[32mCollecting garbage...\e[0m" IMGS_TO_DELETE=() From 40a9389295c9d81defd13f2e2da8b89bb9561af1 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:30:25 +0100 Subject: [PATCH 15/43] [SOGo] Reduce workers to 7 by default --- data/conf/sogo/sogo.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf index bf79358a..0acb8a80 100644 --- a/data/conf/sogo/sogo.conf +++ b/data/conf/sogo/sogo.conf @@ -5,7 +5,7 @@ PrivateDAndTViewer ); - WOWorkersCount = "20"; + WOWorkersCount = "7"; SOGoACLsSendEMailNotifications = YES; SOGoAppointmentSendEMailNotifications = YES; SOGoDraftsFolderName = "Drafts"; @@ -36,7 +36,7 @@ SOGoMailingMechanism = smtp; SOGoSMTPAuthenticationType = plain; - SxVMemLimit = 512; + SxVMemLimit = 384; SOGoMaximumPingInterval = 354; From 67ddc710a7f1f6c911e1c8a2196571fa9fca8f78 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:36:19 +0100 Subject: [PATCH 16/43] [Nginx] Set real IP from internal networks --- data/conf/nginx/site.conf | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index ede9fe4a..8a896264 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -24,6 +24,7 @@ server { add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; + add_header X-Frame-Options "SAMEORIGIN"; add_header X-Permitted-Cross-Domain-Policies none; index index.php index.html; @@ -46,7 +47,10 @@ server { } # If behind reverse proxy, forwards the correct IP - set_real_ip_from 172.22.1.1; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + set_real_ip_from fd00::/8; real_ip_header X-Forwarded-For; real_ip_recursive on; @@ -202,6 +206,7 @@ server { add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; + add_header X-Frame-Options "SAMEORIGIN"; add_header X-Permitted-Cross-Domain-Policies none; index index.php index.html; @@ -224,7 +229,10 @@ server { } # If behind reverse proxy, forwards the correct IP - set_real_ip_from 172.22.1.1; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + set_real_ip_from fd00::/8; real_ip_header X-Forwarded-For; real_ip_recursive on; From 696b52b5eba8e4dcb09bf202e06126555d9e9c8d Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:36:37 +0100 Subject: [PATCH 17/43] [Unbound] Allow internal networks in access-control --- data/conf/unbound/unbound.conf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data/conf/unbound/unbound.conf b/data/conf/unbound/unbound.conf index a668d0d8..16952ff2 100644 --- a/data/conf/unbound/unbound.conf +++ b/data/conf/unbound/unbound.conf @@ -8,8 +8,11 @@ server: do-udp: yes do-tcp: yes do-daemonize: no - access-control: 172.22.1.0/24 allow - access-control: fd4d:6169:6c63:6f77::/64 allow + access-control: 10.0.0.0/8 allow + access-control: 172.16.0.0/12 allow + access-control: 192.168.0.0/16 allow + access-control: fd00::/8 allow + access-control: fe80::/10 allow directory: "/etc/unbound" username: unbound auto-trust-anchor-file: trusted-key.key @@ -19,7 +22,6 @@ server: private-address: 169.254.0.0/16 private-address: fd00::/8 private-address: fe80::/10 - private-address: fd4d:6169:6c63:6f77::/64 root-hints: "/etc/unbound/root.hints" hide-identity: yes hide-version: yes From 7efe67daafdc6b3ab5bdd55bf214f0be2394dfe8 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:36:56 +0100 Subject: [PATCH 18/43] [ClamAV] Mount ClamAV config files --- data/conf/clamav/clamd.conf | 47 +++++++++++++++++++++++++++++++++ data/conf/clamav/freshclam.conf | 17 ++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 data/conf/clamav/clamd.conf create mode 100644 data/conf/clamav/freshclam.conf diff --git a/data/conf/clamav/clamd.conf b/data/conf/clamav/clamd.conf new file mode 100644 index 00000000..362ae5a5 --- /dev/null +++ b/data/conf/clamav/clamd.conf @@ -0,0 +1,47 @@ +LogFile /tmp/logpipe_clamd +LogTime yes +LogClean yes +ExtendedDetectionInfo yes +PidFile /run/clamav/clamd.pid +OfficialDatabaseOnly no +LocalSocket /run/clamav/clamd.sock +TCPSocket 3310 +StreamMaxLength 25M +MaxThreads 10 +ReadTimeout 10 +CommandReadTimeout 3 +SendBufTimeout 200 +MaxQueue 80 +IdleTimeout 20 +SelfCheck 3600 +User clamav +AllowSupplementaryGroups yes +Foreground yes +DetectPUA yes +# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md +#ExcludePUA NetTool +#ExcludePUA PWTool +#IncludePUA Spy +#IncludePUA Scanner +#IncludePUA RAT +AlgorithmicDetection yes +ScanOLE2 yes +OLE2BlockMacros yes +ScanPDF yes +ScanSWF yes +ScanXMLDOCS yes +ScanHWP3 yes +ScanMail yes +PhishingSignatures no +PhishingScanURLs no +HeuristicScanPrecedence yes +ScanHTML yes +ScanArchive yes +MaxScanSize 50M +MaxFileSize 25M +MaxRecursion 5 +MaxFiles 200 +ScanOnAccess no +Bytecode yes +BytecodeSecurity TrustSigned +BytecodeTimeout 1000 diff --git a/data/conf/clamav/freshclam.conf b/data/conf/clamav/freshclam.conf new file mode 100644 index 00000000..382befbc --- /dev/null +++ b/data/conf/clamav/freshclam.conf @@ -0,0 +1,17 @@ +UpdateLogFile /tmp/logpipe_freshclam +LogTime yes +PidFile /run/clamav/freshclam.pid +DatabaseOwner clamav +AllowSupplementaryGroups yes +DNSDatabaseInfo current.cvd.clamav.net +DatabaseMirror database.clamav.net +MaxAttempts 4 +ScriptedUpdates yes +Checks 6 +NotifyClamd /etc/clamav/clamd.conf +Foreground yes +ConnectTimeout 20 +ReceiveTimeout 20 +TestDatabases yes +Bytecode yes + From c9b3044d5df1cb7096bb2f5ba4a1bbbed614d0bf Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:37:27 +0100 Subject: [PATCH 19/43] [Postfix] Allow internal IPv6 networks --- data/conf/postfix/main.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf index 4e8c577f..b3cbbb49 100644 --- a/data/conf/postfix/main.cf +++ b/data/conf/postfix/main.cf @@ -9,7 +9,7 @@ smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_una alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases relayhost = -mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fd4d:6169:6c63:6f77::]/64 +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fd::]/8 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all From 7149350973e8435555a547d73ba9e94a00ce66c4 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:37:49 +0100 Subject: [PATCH 20/43] [Rspamd] Allow internal IPv6 networks --- data/conf/rspamd/override.d/worker-controller.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/rspamd/override.d/worker-controller.inc b/data/conf/rspamd/override.d/worker-controller.inc index 22d9a024..7f17485d 100644 --- a/data/conf/rspamd/override.d/worker-controller.inc +++ b/data/conf/rspamd/override.d/worker-controller.inc @@ -4,6 +4,6 @@ secure_ip = "172.16.0.0/12"; secure_ip = "10.0.0.0/8"; secure_ip = "127.0.0.1"; secure_ip = "::1"; -secure_ip = "fd4d:6169:6c63:6f77::/64" +secure_ip = "fd00::/8" .include(try=true; priority=10) "$CONFDIR/override.d/worker-controller-password.inc" .include(try=true; priority=20) "$CONFDIR/override.d/worker-controller.custom.inc" From 2bf136945375f1e3c2536382cabe84f19a467043 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:38:03 +0100 Subject: [PATCH 21/43] [Nginx] Set real IP from internal networks --- data/assets/nextcloud/nextcloud.conf | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/data/assets/nextcloud/nextcloud.conf b/data/assets/nextcloud/nextcloud.conf index bde2a2e8..6e1080e2 100644 --- a/data/assets/nextcloud/nextcloud.conf +++ b/data/assets/nextcloud/nextcloud.conf @@ -48,8 +48,10 @@ server { gzip_min_length 256; gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; - - set_real_ip_from 172.22.1.1; + set_real_ip_from fd00::/8; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on; @@ -135,8 +137,10 @@ server { gzip_min_length 256; gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; - - set_real_ip_from 172.22.1.1; + set_real_ip_from fd00::/8; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on; From 46aafff627dd867e40894888db683cc2a4ab482d Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 08:40:13 +0100 Subject: [PATCH 22/43] [ClamAV] Outsource config --- data/Dockerfiles/clamd/Dockerfile | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/data/Dockerfiles/clamd/Dockerfile b/data/Dockerfiles/clamd/Dockerfile index ec56bf1d..e2d8ae81 100644 --- a/data/Dockerfiles/clamd/Dockerfile +++ b/data/Dockerfiles/clamd/Dockerfile @@ -12,14 +12,7 @@ RUN apk add --update \ && set -ex; /bin/bash /dl_files.sh \ && mkdir /run/clamav \ && chown clamav:clamav /run/clamav \ - && chmod 750 /run/clamav \ - && sed -i '/Foreground yes/s/^#//g' /etc/clamav/clamd.conf \ - && sed -i '/TCPSocket 3310/s/^#//g' /etc/clamav/clamd.conf \ - && sed -i 's#LogFile /var/log/clamav/clamd.log#LogFile /tmp/logpipe_clamd#g' /etc/clamav/clamd.conf \ - && sed -i 's/#PhishingSignatures yes/PhishingSignatures no/g' /etc/clamav/clamd.conf \ - && sed -i 's/#PhishingScanURLs yes/PhishingScanURLs no/g' /etc/clamav/clamd.conf \ - && sed -i 's#UpdateLogFile /var/log/clamav/freshclam.log#UpdateLogFile /tmp/logpipe_freshclam#g' /etc/clamav/freshclam.conf \ - && sed -i '/Foreground yes/s/^#//g' /etc/clamav/freshclam.conf + && chmod 750 /run/clamav # Port provision EXPOSE 3310 From f2f4dabce413be4e9b0f63d3f8442b535a197b84 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 09:10:43 +0100 Subject: [PATCH 23/43] [Postfix] postconf wrapper for correct config location, fixes #949 --- data/Dockerfiles/postfix/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/Dockerfiles/postfix/Dockerfile b/data/Dockerfiles/postfix/Dockerfile index 2e0e73df..43ed1c27 100644 --- a/data/Dockerfiles/postfix/Dockerfile +++ b/data/Dockerfiles/postfix/Dockerfile @@ -26,7 +26,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ syslog-ng-core \ syslog-ng-mod-redis \ && rm -rf /var/lib/apt/lists/* \ - && touch /etc/default/locale + && touch /etc/default/locale \ + && printf '#!/bin/bash\n/usr/sbin/postconf -c /opt/postfix/conf "$@"' > /usr/local/sbin/postconf \ + && chmod +x /usr/local/sbin/postconf RUN addgroup --system --gid 600 zeyple RUN adduser --system --home /var/lib/zeyple --no-create-home --uid 600 --gid 600 --disabled-login zeyple From 1aaa5682b4707a3b4e9e627f04d4d8866f0f045f Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 09:11:33 +0100 Subject: [PATCH 24/43] [Fail2ban] Allow to set subnet size for banned networks --- data/Dockerfiles/fail2ban/logwatch.py | 120 +++++++++++++------------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/data/Dockerfiles/fail2ban/logwatch.py b/data/Dockerfiles/fail2ban/logwatch.py index 10024772..e6f6d099 100644 --- a/data/Dockerfiles/fail2ban/logwatch.py +++ b/data/Dockerfiles/fail2ban/logwatch.py @@ -14,11 +14,11 @@ import json yes_regex = re.compile(r'([yY][eE][sS]|[yY])+$') if re.search(yes_regex, os.getenv('SKIP_FAIL2BAN', 0)): - print "SKIP_FAIL2BAN=y, Skipping Fail2ban container..." + print 'SKIP_FAIL2BAN=y, Skipping Fail2ban container...' time.sleep(31536000) raise SystemExit -r = redis.StrictRedis(host='172.22.1.249', decode_responses=True, port=6379, db=0) +r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0) pubsub = r.pubsub() RULES = {} @@ -29,19 +29,23 @@ RULES[4] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=( RULES[5] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked' RULES[6] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)' -r.setnx("F2B_BAN_TIME", "1800") -r.setnx("F2B_MAX_ATTEMPTS", "10") -r.setnx("F2B_RETRY_WINDOW", "600") +r.setnx('F2B_BAN_TIME', '1800') +r.setnx('F2B_MAX_ATTEMPTS', '10') +r.setnx('F2B_RETRY_WINDOW', '600') +r.setnx('F2B_NETBAN_IPV6', '64') +r.setnx('F2B_NETBAN_IPV4', '24') bans = {} log = {} quit_now = False def ban(address): - BAN_TIME = int(r.get("F2B_BAN_TIME")) - MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) - RETRY_WINDOW = int(r.get("F2B_RETRY_WINDOW")) - WHITELIST = r.hgetall("F2B_WHITELIST") + BAN_TIME = int(r.get('F2B_BAN_TIME')) + MAX_ATTEMPTS = int(r.get('F2B_MAX_ATTEMPTS')) + RETRY_WINDOW = int(r.get('F2B_RETRY_WINDOW')) + WHITELIST = r.hgetall('F2B_WHITELIST') + NETBAN_IPV6 = '/' + str(r.get('F2B_NETBAN_IPV6')) + NETBAN_IPV4 = '/' + str(r.get('F2B_NETBAN_IPV4')) ip = ipaddress.ip_address(address.decode('ascii')) if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped: @@ -56,13 +60,13 @@ def ban(address): wl_net = ipaddress.ip_network(wl_key.decode('ascii'), False) if wl_net.overlaps(self_network): log['time'] = int(round(time.time())) - log['priority'] = "info" - log['message'] = "Address %s is whitelisted by rule %s" % (self_network, wl_net) - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Address %s is whitelisted by rule %s" % (self_network, wl_net) + log['priority'] = 'info' + log['message'] = 'Address %s is whitelisted by rule %s' % (self_network, wl_net) + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Address %s is whitelisted by rule %s' % (self_network, wl_net) return - net = ipaddress.ip_network((address + ('/24' if type(ip) is ipaddress.IPv4Address else '/64')).decode('ascii'), strict=False) + net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)).decode('ascii'), strict=False) net = str(net) if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW: @@ -78,45 +82,45 @@ def ban(address): if bans[net]['attempts'] >= MAX_ATTEMPTS: log['time'] = int(round(time.time())) - log['priority'] = "crit" - log['message'] = "Banning %s" % net - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Banning %s for %d minutes" % (net, BAN_TIME / 60) + log['priority'] = 'crit' + log['message'] = 'Banning %s' % net + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Banning %s for %d minutes' % (net, BAN_TIME / 60) if type(ip) is ipaddress.IPv4Address: - subprocess.call(["iptables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["iptables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) + subprocess.call(['iptables', '-I', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['iptables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT']) else: - subprocess.call(["ip6tables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["ip6tables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) - r.hset("F2B_ACTIVE_BANS", "%s" % net, log['time'] + BAN_TIME) + subprocess.call(['ip6tables', '-I', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['ip6tables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT']) + r.hset('F2B_ACTIVE_BANS', '%s' % net, log['time'] + BAN_TIME) else: log['time'] = int(round(time.time())) - log['priority'] = "warn" - log['message'] = "%d more attempts in the next %d seconds until %s is banned" % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "%d more attempts in the next %d seconds until %s is banned" % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) + log['priority'] = 'warn' + log['message'] = '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) def unban(net): log['time'] = int(round(time.time())) - log['priority'] = "info" - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + log['priority'] = 'info' + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) if not net in bans: - log['message'] = "%s is not banned, skipping unban and deleting from queue (if any)" % net - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "%s is not banned, skipping unban and deleting from queue (if any)" % net - r.hdel("F2B_QUEUE_UNBAN", "%s" % net) + log['message'] = '%s is not banned, skipping unban and deleting from queue (if any)' % net + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print '%s is not banned, skipping unban and deleting from queue (if any)' % net + r.hdel('F2B_QUEUE_UNBAN', '%s' % net) return - log['message'] = "Unbanning %s" % net - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Unbanning %s" % net + log['message'] = 'Unbanning %s' % net + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Unbanning %s' % net if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network: - subprocess.call(["iptables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["iptables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) + subprocess.call(['iptables', '-D', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['iptables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT']) else: - subprocess.call(["ip6tables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["ip6tables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) - r.hdel("F2B_ACTIVE_BANS", "%s" % net) - r.hdel("F2B_QUEUE_UNBAN", "%s" % net) + subprocess.call(['ip6tables', '-D', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['ip6tables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT']) + r.hdel('F2B_ACTIVE_BANS', '%s' % net) + r.hdel('F2B_QUEUE_UNBAN', '%s' % net) del bans[net] def quit(signum, frame): @@ -125,21 +129,21 @@ def quit(signum, frame): def clear(): log['time'] = int(round(time.time())) - log['priority'] = "info" - log['message'] = "Clearing all bans" - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Clearing all bans" + log['priority'] = 'info' + log['message'] = 'Clearing all bans' + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Clearing all bans' for net in bans.copy(): unban(net) pubsub.unsubscribe() def watch(): log['time'] = int(round(time.time())) - log['priority'] = "info" - log['message'] = "Watching Redis channel F2B_CHANNEL" - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - pubsub.subscribe("F2B_CHANNEL") - print "Subscribing to Redis channel F2B_CHANNEL" + log['priority'] = 'info' + log['message'] = 'Watching Redis channel F2B_CHANNEL' + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + pubsub.subscribe('F2B_CHANNEL') + print 'Subscribing to Redis channel F2B_CHANNEL' while True: for item in pubsub.listen(): for rule_id, rule_regex in RULES.iteritems(): @@ -150,18 +154,18 @@ def watch(): ip = ipaddress.ip_address(addr.decode('ascii')) if ip.is_private or ip.is_loopback: continue - print "%s matched rule id %d" % (addr, rule_id) + print '%s matched rule id %d' % (addr, rule_id) log['time'] = int(round(time.time())) - log['priority'] = "warn" - log['message'] = "%s matched rule id %d" % (addr, rule_id) - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + log['priority'] = 'warn' + log['message'] = '%s matched rule id %d' % (addr, rule_id) + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) ban(addr) def autopurge(): while not quit_now: - BAN_TIME = int(r.get("F2B_BAN_TIME")) - MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) - QUEUE_UNBAN = r.hgetall("F2B_QUEUE_UNBAN") + BAN_TIME = int(r.get('F2B_BAN_TIME')) + MAX_ATTEMPTS = int(r.get('F2B_MAX_ATTEMPTS')) + QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN') if QUEUE_UNBAN: for net in QUEUE_UNBAN: unban(str(net)) From a569fb343575f88f5deb52c0bb8025e057a605a8 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 09:19:03 +0100 Subject: [PATCH 25/43] [Helper] Set Nextcloud trusted proxies by ipv6/4 network vars --- helper-scripts/nextcloud.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh index 6d68d191..a1b9eb5d 100755 --- a/helper-scripts/nextcloud.sh +++ b/helper-scripts/nextcloud.sh @@ -80,8 +80,8 @@ elif [[ ${NC_INSTALL} == "y" ]]; then /web/nextcloud/occ config:system:set memcache.locking --value='\OC\Memcache\Redis' --type=string; \ /web/nextcloud/occ config:system:set memcache.local --value='\OC\Memcache\Redis' --type=string; \ /web/nextcloud/occ config:system:set trusted_domains 1 --value=${MAILCOW_HOSTNAME}; \ - /web/nextcloud/occ config:system:set trusted_proxies 0 --value=fd4d:6169:6c63:6f77::1; \ - /web/nextcloud/occ config:system:set trusted_proxies 1 --value=172.22.1.0/24; \ + /web/nextcloud/occ config:system:set trusted_proxies 0 --value=${IPV6_NETWORK}; \ + /web/nextcloud/occ config:system:set trusted_proxies 1 --value=${IPV4_NETWORK}.0/24; \ /web/nextcloud/occ config:system:set overwritewebroot --value=/nextcloud; \ /web/nextcloud/occ config:system:set overwritehost --value=${MAILCOW_HOSTNAME}; \ /web/nextcloud/occ config:system:set overwriteprotocol --value=https; \ From 1f1ab0960c7d118d15fcd86a348885e399f4dd47 Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 09:24:40 +0100 Subject: [PATCH 26/43] [Update] Formatting; Added new options --- update.sh | 222 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 117 insertions(+), 105 deletions(-) diff --git a/update.sh b/update.sh index 647071ac..b81847fa 100755 --- a/update.sh +++ b/update.sh @@ -1,7 +1,7 @@ #!/bin/bash for bin in curl docker-compose docker git awk sha1sum; do - if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi + if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi done [[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;} @@ -9,44 +9,57 @@ done CONFIG_ARRAY=("SKIP_LETS_ENCRYPT" "USE_WATCHDOG" "WATCHDOG_NOTIFY_EMAIL" "SKIP_CLAMD" "SKIP_IP_CHECK" "SKIP_FAIL2BAN" "ADDITIONAL_SAN" "DOVEADM_PORT") sed -i '$a\' mailcow.conf for option in ${CONFIG_ARRAY[@]}; do - if [[ ${option} == "ADDITIONAL_SAN" ]]; then - if ! grep -q ${option} mailcow.conf; then - echo "Adding new option \"${option}\" to mailcow.conf" - echo "${option}=" >> mailcow.conf - fi - elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then - if ! grep -q ${option} mailcow.conf; then - echo "Adding new option \"${option}\" to mailcow.conf" - echo "COMPOSE_PROJECT_NAME=mailcow-dockerized" >> mailcow.conf - fi - elif [[ ${option} == "DOVEADM_PORT" ]]; then - if ! grep -q ${option} mailcow.conf; then - echo "Adding new option \"${option}\" to mailcow.conf" - echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf - fi - elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then - if ! grep -q ${option} mailcow.conf; then - echo "Adding new option \"${option}\" to mailcow.conf" - echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf - fi + if [[ ${option} == "ADDITIONAL_SAN" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "${option}=" >> mailcow.conf + fi + elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "COMPOSE_PROJECT_NAME=mailcow-dockerized" >> mailcow.conf + fi + elif [[ ${option} == "DOVEADM_PORT" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf + fi + elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf + fi elif [[ ${option} == "LOG_LINES" ]]; then if ! grep -q ${option} mailcow.conf; then echo "Adding new option \"${option}\" to mailcow.conf" + echo '# Max log lines per service to keep in Redis logs' >> mailcow.conf echo "LOG_LINES=9999" >> mailcow.conf fi - elif ! grep -q ${option} mailcow.conf; then - echo "Adding new option \"${option}\" to mailcow.conf" - echo "${option}=n" >> mailcow.conf - fi + elif [[ ${option} == "IPV4_NETWORK" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo '# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)' >> mailcow.conf + echo "IPV4_NETWORK=172.22.1" >> mailcow.conf + fi + elif [[ ${option} == "IPV6_NETWORK" ]]; then + if ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo '# Internal IPv6 subnet in fd00::/8' >> mailcow.conf + echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> mailcow.conf + fi + elif ! grep -q ${option} mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo "${option}=n" >> mailcow.conf + fi done echo -en "Checking internet connection... " curl -o /dev/null google.com -sm3 if [[ $? != 0 ]]; then - echo -e "\e[31mfailed\e[0m" - exit 1 -else - echo -e "\e[32mOK\e[0m" + echo -e "\e[31mfailed\e[0m" + exit 1 + else + echo -e "\e[32mOK\e[0m" fi set -o pipefail @@ -56,22 +69,22 @@ BRANCH=$(git rev-parse --abbrev-ref HEAD) declare -a DC_PARAMS while (($#)); do -case "${1}" in - --check|-c) - echo "Checking remote code for updates..." - git fetch origin ${BRANCH} - if ! git diff origin/${BRANCH} --quiet; then - echo "Updated code is available." - exit 0 - else - echo "No updates available." - exit 3 - fi - ;; - --no-start) - DC_PARAMS=(${DC_PARAMS[@]} "--no-start") - ;; -esac + case "${1}" in + --check|-c) + echo "Checking remote code for updates..." + git fetch origin ${BRANCH} + if ! git diff origin/${BRANCH} --quiet; then + echo "Updated code is available." + exit 0 + else + echo "No updates available." + exit 3 + fi + ;; + --no-start) + DC_PARAMS=(${DC_PARAMS[@]} "--no-start") + ;; + esac done echo -e "\e[32mChecking for newer update script...\e[0m" @@ -80,22 +93,22 @@ git fetch origin ${BRANCH} git checkout origin/${BRANCH} update.sh SHA1_2=$(sha1sum update.sh) if [[ ${SHA1_1} != ${SHA1_2} ]]; then - echo "update.sh changed, please run this script again, exiting." - chmod +x update.sh - exit 0 + echo "update.sh changed, please run this script again, exiting." + chmod +x update.sh + exit 0 fi if [[ -f mailcow.conf ]]; then - source mailcow.conf -else - echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m" - exit 1 + source mailcow.conf + else + echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m" + exit 1 fi read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - echo "OK, exiting." - exit 0 + echo "OK, exiting." + exit 0 fi echo -e "Stopping mailcow... " @@ -115,31 +128,31 @@ git merge -Xtheirs -Xpatience -m "After update on ${DATE}" # Need to use a variable to not pass return codes of if checks MERGE_RETURN=$? 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 + 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" - 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 - git checkout . - echo -e "\e[32mRemoved and recreated files if necessary.\e[0m" + echo -e "\e[93mPotenial 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 + git checkout . + echo -e "\e[32mRemoved and recreated files if necessary.\e[0m" 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 docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors." - exit 1 + echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m" + echo + echo "Run docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors." + exit 1 fi echo -e "\e[32mFetching new docker-compose version...\e[0m" sleep 2 if [[ $(curl -sL -w "%{http_code}" https://www.servercow.de/docker-compose/latest.php -o /dev/null) == "200" ]]; then - LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php) - curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $(which docker-compose) - chmod +x $(which docker-compose) -else - echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m" + LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php) + curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $(which docker-compose) + chmod +x $(which docker-compose) + else + echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m" fi echo -e "\e[32mFetching new images, if any...\e[0m" @@ -157,43 +170,42 @@ docker-compose up -d --remove-orphans ${DC_PARAMS[@]} echo -e "\e[32mCollecting garbage...\e[0m" IMGS_TO_DELETE=() for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do - REPOSITORY=${container/:*} - TAG=${container/*:} - V_MAIN=${container/*.} - V_SUB=${container/*.} + REPOSITORY=${container/:*} + TAG=${container/*:} + V_MAIN=${container/*.} + V_SUB=${container/*.} + EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }') + for existing_tag in ${EXISTING_TAGS[@]}; do + V_MAIN_EXISTING=${existing_tag/*.} + V_SUB_EXISTING=${existing_tag/*.} + # Not an integer + [[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue + [[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue - EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }') - for existing_tag in ${EXISTING_TAGS[@]}; do - V_MAIN_EXISTING=${existing_tag/*.} - V_SUB_EXISTING=${existing_tag/*.} - - # Not an integer - [[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue - [[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue - - if [[ $V_MAIN_EXISTING == "latest" ]]; then - echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted." - IMGS_TO_DELETE+=($REPOSITORY:$existing_tag) - elif [[ $V_MAIN_EXISTING -lt $V_MAIN ]]; then - echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted." - IMGS_TO_DELETE+=($REPOSITORY:$existing_tag) - elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then - echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted." - IMGS_TO_DELETE+=($REPOSITORY:$existing_tag) - fi - done + if [[ $V_MAIN_EXISTING == "latest" ]]; then + echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted." + IMGS_TO_DELETE+=($REPOSITORY:$existing_tag) + elif [[ $V_MAIN_EXISTING -lt $V_MAIN ]]; then + echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted." + IMGS_TO_DELETE+=($REPOSITORY:$existing_tag) + elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then + echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted." + IMGS_TO_DELETE+=($REPOSITORY:$existing_tag) + fi + done done + if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then - echo "Run the following command to delete unused image tags:" - echo - echo " docker rmi ${IMGS_TO_DELETE[*]}" - echo - 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[*]} - else - echo "OK, skipped." - fi + echo "Run the following command to delete unused image tags:" + echo + echo " docker rmi ${IMGS_TO_DELETE[*]}" + echo + 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[*]} + else + echo "OK, skipped." + fi 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\"" From 4f2d9bc48e8c83db06c10a7075016d24f0db785e Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 09:25:13 +0100 Subject: [PATCH 27/43] [Compose] New images; Allow to set networks; mount ClamAV configs --- docker-compose.yml | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 77143bfa..39fb10dd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,7 @@ services: restart: always networks: mailcow-network: - ipv4_address: 172.22.1.254 + ipv4_address: ${IPV4_NETWORK}.254 aliases: - unbound @@ -29,7 +29,7 @@ services: - MYSQL_PASSWORD=${DBPASS} restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: aliases: @@ -43,22 +43,24 @@ services: environment: - TZ=${TZ} dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: - ipv4_address: 172.22.1.249 + ipv4_address: ${IPV4_NETWORK}.249 aliases: - redis clamd-mailcow: - image: mailcow/clamd:1.6 + image: mailcow/clamd:1.7 build: ./data/Dockerfiles/clamd restart: always environment: - - SKIP_CLAMD=${SKIP_CLAMD:-n} - TZ=${TZ} + - SKIP_CLAMD=${SKIP_CLAMD:-n} + volumes: + - ./data/conf/clamav/:/etc/clamav/ dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: aliases: @@ -81,7 +83,7 @@ services: - rspamd-vol-1:/var/lib/rspamd restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 hostname: rspamd networks: mailcow-network: @@ -119,7 +121,7 @@ services: - SMTP_PORT=${SMTP_PORT:-25} restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: aliases: @@ -139,7 +141,7 @@ services: - ./data/conf/sogo/:/etc/sogo/ restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: aliases: @@ -176,7 +178,7 @@ services: soft: 20000 hard: 40000 dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 hostname: ${MAILCOW_HOSTNAME} networks: mailcow-network: @@ -184,7 +186,7 @@ services: - dovecot postfix-mailcow: - image: mailcow/postfix:1.12 + image: mailcow/postfix:1.13 build: ./data/Dockerfiles/postfix volumes: - ./data/conf/postfix:/opt/postfix/conf @@ -203,7 +205,7 @@ services: - "${SUBMISSION_PORT:-587}:587" restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 hostname: ${MAILCOW_HOSTNAME} networks: mailcow-network: @@ -214,7 +216,7 @@ services: image: memcached:alpine restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: aliases: @@ -249,7 +251,7 @@ services: - "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}" restart: always dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 networks: mailcow-network: aliases: @@ -262,7 +264,7 @@ services: image: mailcow/acme:1.28 build: ./data/Dockerfiles/acme dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 environment: - LOG_LINES=${LOG_LINES} - ADDITIONAL_SAN=${ADDITIONAL_SAN} @@ -283,7 +285,7 @@ services: - acme fail2ban-mailcow: - image: mailcow/fail2ban:1.10 + image: mailcow/fail2ban:1.11 build: ./data/Dockerfiles/fail2ban stop_grace_period: 30s depends_on: @@ -297,16 +299,17 @@ services: environment: - TZ=${TZ} - SKIP_FAIL2BAN=${SKIP_FAIL2BAN:-n} + - IPV4_NETWORK=${IPV4_NETWORK} network_mode: "host" dns: - - 172.22.1.254 + - ${IPV4_NETWORK}.254 volumes: - /lib/modules:/lib/modules:ro watchdog-mailcow: image: mailcow/watchdog:1.13 # Debug - #command: /watchdog.sh + command: /watchdog.sh build: ./data/Dockerfiles/watchdog volumes: - vmail-vol-1:/vmail:ro @@ -356,8 +359,8 @@ networks: ipam: driver: default config: - - subnet: 172.22.1.0/24 - - subnet: fd4d:6169:6c63:6f77::/64 + - subnet: ${IPV4_NETWORK}.0/24 + - subnet: ${IPV6_NETWORK} volumes: vmail-vol-1: From 1c6d3c16b6f348a06c5da609021505d77c70985b Mon Sep 17 00:00:00 2001 From: "andre.peters" Date: Wed, 24 Jan 2018 09:25:28 +0100 Subject: [PATCH 28/43] [Web] Set Fail2ban subnet sizes --- data/web/admin.php | 14 ++++++++++++++ data/web/inc/functions.fail2ban.inc.php | 12 ++++++++++++ data/web/inc/header.inc.php | 1 + data/web/lang/lang.de.php | 2 ++ data/web/lang/lang.en.php | 2 ++ 5 files changed, 31 insertions(+) diff --git a/data/web/admin.php b/data/web/admin.php index 79bce8e3..fb92345e 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -346,6 +346,20 @@ $tfa_data = get_tfa();
+
+ +
+ / + +
+
+
+ +
+ / + +
+
diff --git a/data/web/inc/functions.fail2ban.inc.php b/data/web/inc/functions.fail2ban.inc.php index e1801be7..b78fb36a 100644 --- a/data/web/inc/functions.fail2ban.inc.php +++ b/data/web/inc/functions.fail2ban.inc.php @@ -12,6 +12,8 @@ function fail2ban($_action, $_data = null) { $data['ban_time'] = $redis->Get('F2B_BAN_TIME'); $data['max_attempts'] = $redis->Get('F2B_MAX_ATTEMPTS'); $data['retry_window'] = $redis->Get('F2B_RETRY_WINDOW'); + $data['netban_ipv4'] = $redis->Get('F2B_NETBAN_IPV4'); + $data['netban_ipv6'] = $redis->Get('F2B_NETBAN_IPV6'); $wl = $redis->hGetAll('F2B_WHITELIST'); if (is_array($wl)) { foreach ($wl as $key => $value) { @@ -50,6 +52,8 @@ function fail2ban($_action, $_data = null) { $ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']); $max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['active_int']); $retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']); + $netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']); + $netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']); } else { $_SESSION['return'] = array( @@ -60,12 +64,20 @@ function fail2ban($_action, $_data = null) { } $wl = $_data['whitelist']; $ban_time = ($ban_time < 60) ? 60 : $ban_time; + + $netban_ipv4 = ($netban_ipv4 < 8) ? 8 : $netban_ipv4; + $netban_ipv6 = ($netban_ipv6 < 8) ? 8 : $netban_ipv6; + $netban_ipv4 = ($netban_ipv4 > 32) ? 32 : $netban_ipv4; + $netban_ipv6 = ($netban_ipv6 > 128) ? 128 : $netban_ipv6; + $max_attempts = ($max_attempts < 1) ? 1 : $max_attempts; $retry_window = ($retry_window < 1) ? 1 : $retry_window; try { $redis->Set('F2B_BAN_TIME', $ban_time); $redis->Set('F2B_MAX_ATTEMPTS', $max_attempts); $redis->Set('F2B_RETRY_WINDOW', $retry_window); + $redis->Set('F2B_NETBAN_IPV4', $netban_ipv4); + $redis->Set('F2B_NETBAN_IPV6', $netban_ipv6); $redis->Del('F2B_WHITELIST'); if(!empty($wl)) { $wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl)); diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index e7f4a6a1..f928f620 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -4,6 +4,7 @@ + <?=$UI_TEXTS['title_name'];?>