From cac67db20314e6a023ef4b8a059d82765bb3023e Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Sat, 23 Feb 2019 17:59:18 +0100 Subject: [PATCH 01/22] add config ALLOW_ADMIN_EMAIL_LOGIN and implement password-less SOGo login admins --- .gitignore | 1 + data/Dockerfiles/dovecot/docker-entrypoint.sh | 11 + data/conf/dovecot/dovecot.conf | 1 + data/conf/nginx/site.conf | 11 + .../templates/sogo.auth_request.template.sh | 7 + data/conf/sogo/sogo.conf | 2 + data/web/js/site/mailbox.js | 1983 +++++++++-------- data/web/mailbox.php | 5 + data/web/sogo-auth.php | 62 + docker-compose.yml | 7 +- generate_config.sh | 4 + update.sh | 1 + 12 files changed, 1104 insertions(+), 991 deletions(-) create mode 100644 data/conf/nginx/templates/sogo.auth_request.template.sh create mode 100644 data/web/sogo-auth.php diff --git a/.gitignore b/.gitignore index 624e1c06..5c6cd161 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ data/conf/nginx/*.custom data/conf/nginx/*.bak data/conf/dovecot/acl_anyone data/conf/dovecot/mail_plugins* +data/conf/dovecot/sogo-sso.conf data/conf/dovecot/extra.conf data/conf/rspamd/custom/* data/conf/portainer/ diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 0589579d..179ffb65 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -118,6 +118,17 @@ default_pass_scheme = SSHA256 password_query = SELECT password FROM mailbox WHERE active = '1' AND username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1') AND JSON_EXTRACT(attributes, '$.force_pw_update') NOT LIKE '%%1%%' EOF +if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + cat < /usr/local/etc/dovecot/sogo-sso.conf +passdb { + driver = static + args = password= allow_real_nets=${IPV4_NETWORK}.248/32 +} +EOF +else + rm -f /usr/local/etc/dovecot/sogo-sso.conf +fi + # Create global sieve_after script cat /usr/local/etc/dovecot/sieve_after > /var/vmail/sieve/global.sieve diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf index 80422599..f28f9b5c 100644 --- a/data/conf/dovecot/dovecot.conf +++ b/data/conf/dovecot/dovecot.conf @@ -389,4 +389,5 @@ auth_cache_negative_ttl = 0 auth_cache_ttl = 30 s auth_cache_size = 2 M !include_try /usr/local/etc/dovecot/extra.conf +!include_try /usr/local/etc/dovecot/sogo-sso.conf default_client_limit = 10400 diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index 8b8959d5..c19310ae 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -164,6 +164,17 @@ server { client_max_body_size 0; } + # auth_request endpoint if ALLOW_ADMIN_EMAIL_LOGIN is set + location /sogo-auth-verify { + internal; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header Content-Length ""; + proxy_pass http://127.0.0.1:80/sogo-auth; + proxy_pass_request_body off; + } + location ^~ /SOGo { include /etc/nginx/conf.d/sogo.active; proxy_set_header X-Real-IP $remote_addr; diff --git a/data/conf/nginx/templates/sogo.auth_request.template.sh b/data/conf/nginx/templates/sogo.auth_request.template.sh new file mode 100644 index 00000000..3139d5f8 --- /dev/null +++ b/data/conf/nginx/templates/sogo.auth_request.template.sh @@ -0,0 +1,7 @@ +if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then + echo ' +auth_request /sogo-auth-verify; +auth_request_set $user $upstream_http_x_username; +proxy_set_header x-webobjects-remote-user $user; +' +fi diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf index aa1a86ec..a8befc2b 100644 --- a/data/conf/sogo/sogo.conf +++ b/data/conf/sogo/sogo.conf @@ -83,4 +83,6 @@ //SOGoUIxDebugEnabled = YES; //WODontZipResponse = YES; WOLogFile = "/dev/sogo_log"; + + SOGoTrustProxyAuthentication = YES; } diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js index 00a815e6..eb04590a 100644 --- a/data/web/js/site/mailbox.js +++ b/data/web/js/site/mailbox.js @@ -1,990 +1,993 @@ -$(document).ready(function() { - acl_data = JSON.parse(acl); - FooTable.domainFilter = FooTable.Filtering.extend({ - construct: function(instance){ - this._super(instance); - var domain_list = []; - $.ajax({ - dataType: 'json', - url: '/api/v1/get/domain/all', - jsonp: false, - async: true, - error: function () { - domain_list.push('Cannot read domain list'); - }, - success: function (data) { - $.each(data, function (i, item) { - domain_list.push(item.domain_name); - }); - } - }); - this.domains = domain_list; - this.def = 'All Domains'; - this.$domain = null; - }, - $create: function(){ - this._super(); - var self = this, - $form_grp = $('
', {'class': 'form-group'}) - .append($('
'; - item.chkbox = ''; - }); - } - }), - "paging": { - "enabled": true, - "limit": 5, - "size": pagination_size - }, - "state": { - "enabled": true - }, - "filtering": { - "enabled": true, - "delay": 100, - "position": "left", - "connectors": false, - "placeholder": lang.filter_table - }, - "sorting": { - "enabled": true - }, - "on": { - "ready.ft.table": function(e, ft){ - table_mailbox_ready(ft, 'aliasdomain_table'); - } - } - }); - } - - function draw_sync_job_table() { - ft_syncjob_table = FooTable.init('#sync_job_table', { - "columns": [ - {"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"}, - {"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}}, - {"name":"user2","title":lang.owner}, - {"name":"server_w_port","title":"Server","breakpoints":"xs","style":{"word-break":"break-all"}}, - {"name":"exclude","title":lang.excludes,"breakpoints":"all"}, - {"name":"mins_interval","title":lang.mins_interval,"breakpoints":"all"}, - {"name":"last_run","title":lang.last_run,"breakpoints":"sm"}, - {"name":"log","title":"Log"}, - {"name":"active","filterable": false,"style":{"maxWidth":"70px","width":"70px"},"title":lang.active}, - {"name":"is_running","filterable": false,"style":{"maxWidth":"120px","width":"100px"},"title":lang.status}, - {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} - ], - "empty": lang.empty, - "rows": $.ajax({ - dataType: 'json', - url: '/api/v1/get/syncjobs/all/no_log', - jsonp: false, - error: function () { - console.log('Cannot draw sync job table'); - }, - success: function (data) { - $.each(data, function (i, item) { - item.log = 'Open logs' - item.user2 = escapeHtml(item.user2); - if (!item.exclude > 0) { - item.exclude = '-'; - } else { - item.exclude = '' + item.exclude + ''; - } - item.server_w_port = escapeHtml(item.user1) + '@' + item.host1 + ':' + item.port1; - item.action = ''; - item.chkbox = ''; - if (item.is_running == 1) { - item.is_running = '' + lang.running + ''; - } else { - item.is_running = '' + lang.waiting + ''; - } - if (!item.last_run > 0) { - item.last_run = lang.waiting; - } - }); - } - }), - "paging": { - "enabled": true, - "limit": 5, - "size": pagination_size - }, - "state": { - "enabled": true - }, - "filtering": { - "enabled": true, - "delay": 100, - "position": "left", - "connectors": false, - "placeholder": lang.filter_table - }, - "sorting": { - "enabled": true - }, - "on": { - "ready.ft.table": function(e, ft){ - table_mailbox_ready(ft, 'sync_job_table'); - } - } - }); - } - - function draw_filter_table() { - ft_filter_table = FooTable.init('#filter_table', { - "columns": [ - {"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"}, - {"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}}, - {"name":"active","style":{"maxWidth":"80px","width":"80px"},"title":lang.active}, - {"name":"filter_type","style":{"maxWidth":"80px","width":"80px"},"title":"Type"}, - {"sorted": true,"name":"username","title":lang.owner,"style":{"maxWidth":"550px","width":"350px"}}, - {"name":"script_desc","title":lang.description,"breakpoints":"xs"}, - {"name":"script_data","title":"Script","breakpoints":"all"}, - {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} - ], - "empty": lang.empty, - "rows": $.ajax({ - dataType: 'json', - url: '/api/v1/get/filters/all', - jsonp: false, - error: function () { - console.log('Cannot draw filter table'); - }, - success: function (data) { - $.each(data, function (i, item) { - if (item.active_int == 1) { - item.active = '' + lang.active + ''; - } else { - item.active = '' + lang.inactive + ''; - } - item.script_data = '
' + escapeHtml(item.script_data) + '
' - item.filter_type = '
' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '
' - item.action = ''; - item.chkbox = '' - }); - } - }), - "paging": { - "enabled": true, - "limit": 5, - "size": pagination_size - }, - "state": { - "enabled": true - }, - "filtering": { - "enabled": true, - "delay": 100, - "position": "left", - "connectors": false, - "placeholder": lang.filter_table - }, - "sorting": { - "enabled": true - }, - "on": { - "ready.ft.table": function(e, ft){ - table_mailbox_ready(ft, 'filter_table'); - } - } - }); - }; - - draw_domain_table(); - draw_mailbox_table(); - draw_resource_table(); - draw_alias_table(); - draw_aliasdomain_table(); - draw_sync_job_table(); - draw_filter_table(); - draw_bcc_table(); - draw_recipient_map_table(); - draw_tls_policy_table(); - draw_transport_maps_table(); - -}); +$(document).ready(function() { + acl_data = JSON.parse(acl); + FooTable.domainFilter = FooTable.Filtering.extend({ + construct: function(instance){ + this._super(instance); + var domain_list = []; + $.ajax({ + dataType: 'json', + url: '/api/v1/get/domain/all', + jsonp: false, + async: true, + error: function () { + domain_list.push('Cannot read domain list'); + }, + success: function (data) { + $.each(data, function (i, item) { + domain_list.push(item.domain_name); + }); + } + }); + this.domains = domain_list; + this.def = 'All Domains'; + this.$domain = null; + }, + $create: function(){ + this._super(); + var self = this, + $form_grp = $('
', {'class': 'form-group'}) + .append($('
'; + item.chkbox = ''; + }); + } + }), + "paging": { + "enabled": true, + "limit": 5, + "size": pagination_size + }, + "state": { + "enabled": true + }, + "filtering": { + "enabled": true, + "delay": 100, + "position": "left", + "connectors": false, + "placeholder": lang.filter_table + }, + "sorting": { + "enabled": true + }, + "on": { + "ready.ft.table": function(e, ft){ + table_mailbox_ready(ft, 'aliasdomain_table'); + } + } + }); + } + + function draw_sync_job_table() { + ft_syncjob_table = FooTable.init('#sync_job_table', { + "columns": [ + {"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"}, + {"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}}, + {"name":"user2","title":lang.owner}, + {"name":"server_w_port","title":"Server","breakpoints":"xs","style":{"word-break":"break-all"}}, + {"name":"exclude","title":lang.excludes,"breakpoints":"all"}, + {"name":"mins_interval","title":lang.mins_interval,"breakpoints":"all"}, + {"name":"last_run","title":lang.last_run,"breakpoints":"sm"}, + {"name":"log","title":"Log"}, + {"name":"active","filterable": false,"style":{"maxWidth":"70px","width":"70px"},"title":lang.active}, + {"name":"is_running","filterable": false,"style":{"maxWidth":"120px","width":"100px"},"title":lang.status}, + {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + ], + "empty": lang.empty, + "rows": $.ajax({ + dataType: 'json', + url: '/api/v1/get/syncjobs/all/no_log', + jsonp: false, + error: function () { + console.log('Cannot draw sync job table'); + }, + success: function (data) { + $.each(data, function (i, item) { + item.log = 'Open logs' + item.user2 = escapeHtml(item.user2); + if (!item.exclude > 0) { + item.exclude = '-'; + } else { + item.exclude = '' + item.exclude + ''; + } + item.server_w_port = escapeHtml(item.user1) + '@' + item.host1 + ':' + item.port1; + item.action = ''; + item.chkbox = ''; + if (item.is_running == 1) { + item.is_running = '' + lang.running + ''; + } else { + item.is_running = '' + lang.waiting + ''; + } + if (!item.last_run > 0) { + item.last_run = lang.waiting; + } + }); + } + }), + "paging": { + "enabled": true, + "limit": 5, + "size": pagination_size + }, + "state": { + "enabled": true + }, + "filtering": { + "enabled": true, + "delay": 100, + "position": "left", + "connectors": false, + "placeholder": lang.filter_table + }, + "sorting": { + "enabled": true + }, + "on": { + "ready.ft.table": function(e, ft){ + table_mailbox_ready(ft, 'sync_job_table'); + } + } + }); + } + + function draw_filter_table() { + ft_filter_table = FooTable.init('#filter_table', { + "columns": [ + {"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"}, + {"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}}, + {"name":"active","style":{"maxWidth":"80px","width":"80px"},"title":lang.active}, + {"name":"filter_type","style":{"maxWidth":"80px","width":"80px"},"title":"Type"}, + {"sorted": true,"name":"username","title":lang.owner,"style":{"maxWidth":"550px","width":"350px"}}, + {"name":"script_desc","title":lang.description,"breakpoints":"xs"}, + {"name":"script_data","title":"Script","breakpoints":"all"}, + {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} + ], + "empty": lang.empty, + "rows": $.ajax({ + dataType: 'json', + url: '/api/v1/get/filters/all', + jsonp: false, + error: function () { + console.log('Cannot draw filter table'); + }, + success: function (data) { + $.each(data, function (i, item) { + if (item.active_int == 1) { + item.active = '' + lang.active + ''; + } else { + item.active = '' + lang.inactive + ''; + } + item.script_data = '
' + escapeHtml(item.script_data) + '
' + item.filter_type = '
' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '
' + item.action = ''; + item.chkbox = '' + }); + } + }), + "paging": { + "enabled": true, + "limit": 5, + "size": pagination_size + }, + "state": { + "enabled": true + }, + "filtering": { + "enabled": true, + "delay": 100, + "position": "left", + "connectors": false, + "placeholder": lang.filter_table + }, + "sorting": { + "enabled": true + }, + "on": { + "ready.ft.table": function(e, ft){ + table_mailbox_ready(ft, 'filter_table'); + } + } + }); + }; + + draw_domain_table(); + draw_mailbox_table(); + draw_resource_table(); + draw_alias_table(); + draw_aliasdomain_table(); + draw_sync_job_table(); + draw_filter_table(); + draw_bcc_table(); + draw_recipient_map_table(); + draw_tls_policy_table(); + draw_transport_maps_table(); + +}); diff --git a/data/web/mailbox.php b/data/web/mailbox.php index 96c2e16d..392e9adf 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -348,6 +348,11 @@ $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? 'true' : 'false'; echo "var role = '". $role . "';\n"; echo "var is_dual = " . $is_dual . ";\n"; echo "var pagination_size = '". $PAGINATION_SIZE . "';\n"; +$ALLOW_ADMIN_EMAIL_LOGIN = (preg_match( + "/^([yY][eE][sS]|[yY])+$/", + $_ENV["ALLOW_ADMIN_EMAIL_LOGIN"] +)) ? "true" : "false"; +echo "var ALLOW_ADMIN_EMAIL_LOGIN = " . $ALLOW_ADMIN_EMAIL_LOGIN . ";\n"; ?> /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/server_name.template > /etc/nginx/conf.d/server_name.active && - envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active && + . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo.active && + envsubst < /etc/nginx/conf.d/templates/sogo.template >> /etc/nginx/conf.d/sogo.active && envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active && nginx -qt && until ping phpfpm -c1 > /dev/null; do sleep 1; done && @@ -274,6 +278,7 @@ services: - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} - TZ=${TZ} + - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} volumes: - ./data/web:/web:ro - ./data/conf/rspamd/dynmaps:/dynmaps:ro diff --git a/generate_config.sh b/generate_config.sh index a882ec08..e6005b72 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -200,6 +200,10 @@ SOLR_HEAP=1024 USE_WATCHDOG=n +# Allow admins to log into SOGo as email user (without any password) + +ALLOW_ADMIN_EMAIL_LOGIN=n + # Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME) # Can by multiple rcpts, NO quotation marks diff --git a/update.sh b/update.sh index 17e818f9..9c18e0cc 100755 --- a/update.sh +++ b/update.sh @@ -130,6 +130,7 @@ CONFIG_ARRAY=( "ACL_ANYONE" "SOLR_HEAP" "SKIP_SOLR" + "ALLOW_ADMIN_EMAIL_LOGIN" ) sed -i '$a\' mailcow.conf From 0c8f217f49d0a332b1da5bf49fe480bdc2af6b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Peters?= Date: Sat, 23 Feb 2019 22:20:09 +0100 Subject: [PATCH 02/22] Update sogo.auth_request.template.sh Don't want to split hairs! Just consistency. :) --- data/conf/nginx/templates/sogo.auth_request.template.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/data/conf/nginx/templates/sogo.auth_request.template.sh b/data/conf/nginx/templates/sogo.auth_request.template.sh index 3139d5f8..1a7b3f72 100644 --- a/data/conf/nginx/templates/sogo.auth_request.template.sh +++ b/data/conf/nginx/templates/sogo.auth_request.template.sh @@ -1,6 +1,5 @@ -if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then - echo ' -auth_request /sogo-auth-verify; +if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + echo 'auth_request /sogo-auth-verify; auth_request_set $user $upstream_http_x_username; proxy_set_header x-webobjects-remote-user $user; ' From 88fbc6bf1675957ce40642fe62e56f4d2ce20529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Peters?= Date: Sat, 23 Feb 2019 22:26:41 +0100 Subject: [PATCH 03/22] Update sogo-auth.php Consistency again. :) I moved the prerequisites require_once to the top, ok? --- data/web/sogo-auth.php | 85 +++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 15cbeab8..98cafac5 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -1,4 +1,5 @@ Date: Sat, 23 Feb 2019 22:29:14 +0100 Subject: [PATCH 04/22] Update sogo.auth_request.template.sh --- data/conf/nginx/templates/sogo.auth_request.template.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/nginx/templates/sogo.auth_request.template.sh b/data/conf/nginx/templates/sogo.auth_request.template.sh index 1a7b3f72..ae1a3879 100644 --- a/data/conf/nginx/templates/sogo.auth_request.template.sh +++ b/data/conf/nginx/templates/sogo.auth_request.template.sh @@ -1,4 +1,4 @@ -if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then +if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then echo 'auth_request /sogo-auth-verify; auth_request_set $user $upstream_http_x_username; proxy_set_header x-webobjects-remote-user $user; From 4482aee747934f062b4c3825f7b3c24083489396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Peters?= Date: Sun, 24 Feb 2019 00:15:09 +0100 Subject: [PATCH 05/22] Update sogo-auth.php --- data/web/sogo-auth.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 98cafac5..77c73590 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -1,10 +1,9 @@ Date: Mon, 25 Feb 2019 00:00:32 +0100 Subject: [PATCH 06/22] [Compose] Add ALLOW_ADMIN_EMAIL_LOGIN to sogo-mailcow to trigger bootstrap on change [Compose] Static IPv4 for Dovecot [SOGo] Remove SOGoIMAPServer from sogo.conf [SOGo] Add SOGoIMAPServer to bootstrap process [Nginx] Disallow editAccount for other accounts than 0 (own) --- data/Dockerfiles/sogo/bootstrap-sogo.sh | 5 +++++ data/conf/nginx/templates/sogo.auth_request.template.sh | 4 +++- data/conf/sogo/sogo.conf | 1 - docker-compose.yml | 7 +++++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 5072a306..84176ebd 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -85,6 +85,9 @@ done mkdir -p /var/lib/sogo/GNUstep/Defaults/ +# Force-remove lines from sogo.conf +sed -i '/SOGoIMAPServer/d' /etc/sogo/sogo.conf + # Generate plist header with timezone data cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist @@ -93,6 +96,8 @@ cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist OCSAclURL mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl + SOGoIMAPServer + imap://${IPV4_NETWORK}.250:143/?tls=YES OCSCacheFolderURL mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_cache_folder OCSEMailAlarmsFolderURL diff --git a/data/conf/nginx/templates/sogo.auth_request.template.sh b/data/conf/nginx/templates/sogo.auth_request.template.sh index ae1a3879..d885d9f5 100644 --- a/data/conf/nginx/templates/sogo.auth_request.template.sh +++ b/data/conf/nginx/templates/sogo.auth_request.template.sh @@ -2,5 +2,7 @@ if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' echo 'auth_request /sogo-auth-verify; auth_request_set $user $upstream_http_x_username; proxy_set_header x-webobjects-remote-user $user; -' +if ($args ~* (.*)(account=(?!0))(.*)) { + return 401; +}' fi diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf index a8befc2b..b115d75d 100644 --- a/data/conf/sogo/sogo.conf +++ b/data/conf/sogo/sogo.conf @@ -26,7 +26,6 @@ // (domain3.tld, domain2.tld) // ); - SOGoIMAPServer = "imap://dovecot:143/?tls=YES"; SOGoSieveServer = "sieve://dovecot:4190/?tls=YES"; SOGoSMTPServer = "postfix:588"; WOPort = "0.0.0.0:20000"; diff --git a/docker-compose.yml b/docker-compose.yml index a4a8b9be..c2394909 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -140,7 +140,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.52 + image: mailcow/sogo:1.53 build: ./data/Dockerfiles/sogo environment: - DBNAME=${DBNAME} @@ -150,6 +150,8 @@ services: - LOG_LINES=${LOG_LINES:-9999} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} - ACL_ANYONE=${ACL_ANYONE:-disallow} + - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} volumes: - ./data/conf/sogo/:/etc/sogo/ - ./data/web/inc/init_db.inc.php:/init_db.inc.php @@ -165,7 +167,7 @@ services: - sogo dovecot-mailcow: - image: mailcow/dovecot:1.63 + image: mailcow/dovecot:1.64 build: ./data/Dockerfiles/dovecot cap_add: - NET_BIND_SERVICE @@ -210,6 +212,7 @@ services: hostname: ${MAILCOW_HOSTNAME} networks: mailcow-network: + ipv4_address: ${IPV4_NETWORK:-172.22.1}.250 aliases: - dovecot From dd6d253ac07e40bc8e4b6e9f5f53ef208c0b18cb Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Tue, 26 Feb 2019 09:02:35 +0100 Subject: [PATCH 07/22] add random masterpass for sogo admin login add required headers for sogo proxy auth with password add SOGoEncryptionKey add SOGoTrustProxyAuthentication only conditionally if feature is enabled --- data/Dockerfiles/dovecot/docker-entrypoint.sh | 26 +++++++++++-------- data/Dockerfiles/sogo/bootstrap-sogo.sh | 11 ++++++++ .../templates/sogo.auth_request.template.sh | 12 +++++---- data/conf/phpfpm/sogo-sso/.gitkeep | 0 data/conf/sogo/sogo.conf | 2 -- data/web/sogo-auth.php | 23 +++++++++++----- docker-compose.yml | 2 ++ 7 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 data/conf/phpfpm/sogo-sso/.gitkeep diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 179ffb65..28b31420 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -118,17 +118,6 @@ default_pass_scheme = SSHA256 password_query = SELECT password FROM mailbox WHERE active = '1' AND username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1') AND JSON_EXTRACT(attributes, '$.force_pw_update') NOT LIKE '%%1%%' EOF -if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - cat < /usr/local/etc/dovecot/sogo-sso.conf -passdb { - driver = static - args = password= allow_real_nets=${IPV4_NETWORK}.248/32 -} -EOF -else - rm -f /usr/local/etc/dovecot/sogo-sso.conf -fi - # Create global sieve_after script cat /usr/local/etc/dovecot/sieve_after > /var/vmail/sieve/global.sieve @@ -146,6 +135,21 @@ echo ${RAND_USER}@mailcow.local:{SHA1}$(echo -n ${RAND_PASS} | sha1sum | awk '{p echo ${RAND_USER}@mailcow.local::5000:5000:::: > /usr/local/etc/dovecot/dovecot-master.userdb echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds +if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + # Create random master Password for SOGo 'login as user' via proxy auth + RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1) + echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass + cat < /usr/local/etc/dovecot/sogo-sso.conf +passdb { + driver = static + args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS} +} +EOF +else + rm -f /usr/local/etc/dovecot/sogo-sso.pass + rm -f /usr/local/etc/dovecot/sogo-sso.conf +fi + # 401 is user dovecot if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 84176ebd..51e1eea3 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -88,6 +88,13 @@ mkdir -p /var/lib/sogo/GNUstep/Defaults/ # Force-remove lines from sogo.conf sed -i '/SOGoIMAPServer/d' /etc/sogo/sogo.conf +if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + TRUST_PROXY="YES" +else + TRUST_PROXY="NO" +fi +RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1) + # Generate plist header with timezone data cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist @@ -98,6 +105,10 @@ cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl SOGoIMAPServer imap://${IPV4_NETWORK}.250:143/?tls=YES + SOGoTrustProxyAuthentication + ${TRUST_PROXY} + SOGoEncryptionKey + ${RAND_PASS} OCSCacheFolderURL mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_cache_folder OCSEMailAlarmsFolderURL diff --git a/data/conf/nginx/templates/sogo.auth_request.template.sh b/data/conf/nginx/templates/sogo.auth_request.template.sh index d885d9f5..f6d2d98e 100644 --- a/data/conf/nginx/templates/sogo.auth_request.template.sh +++ b/data/conf/nginx/templates/sogo.auth_request.template.sh @@ -1,8 +1,10 @@ if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then echo 'auth_request /sogo-auth-verify; -auth_request_set $user $upstream_http_x_username; -proxy_set_header x-webobjects-remote-user $user; -if ($args ~* (.*)(account=(?!0))(.*)) { - return 401; -}' +auth_request_set $user $upstream_http_x_user; +auth_request_set $auth $upstream_http_x_auth; +auth_request_set $auth_type $upstream_http_x_auth_type; +proxy_set_header x-webobjects-remote-user "$user"; +proxy_set_header Authorization "$auth"; +proxy_set_header x-webobjects-auth-type "$auth_type"; +' fi diff --git a/data/conf/phpfpm/sogo-sso/.gitkeep b/data/conf/phpfpm/sogo-sso/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf index b115d75d..f9e9e077 100644 --- a/data/conf/sogo/sogo.conf +++ b/data/conf/sogo/sogo.conf @@ -82,6 +82,4 @@ //SOGoUIxDebugEnabled = YES; //WODontZipResponse = YES; WOLogFile = "/dev/sogo_log"; - - SOGoTrustProxyAuthentication = YES; } diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 77c73590..d9c10557 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -30,7 +30,8 @@ $ALLOW_ADMIN_EMAIL_LOGIN = (preg_match( $_ENV["ALLOW_ADMIN_EMAIL_LOGIN"] )); -$session_variable = 'sogo-sso-user'; +$session_var_user = 'sogo-sso-user'; +$session_var_pass = 'sogo-sso-pass'; if (!$ALLOW_ADMIN_EMAIL_LOGIN) { header("Location: /"); @@ -42,7 +43,9 @@ elseif (isset($_GET['login'])) { $login = html_entity_decode(rawurldecode($_GET["login"])); if (filter_var($login, FILTER_VALIDATE_EMAIL)) { if (!empty(mailbox('get', 'mailbox_details', $login))) { - $_SESSION[$session_variable] = $login; + $sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass"); + $_SESSION[$session_var_user] = $login; + $_SESSION[$session_var_pass] = $sogo_sso_pass; header("Location: /SOGo/"); exit; } @@ -54,11 +57,17 @@ elseif (isset($_GET['login'])) { else { // this is an nginx auth_request call, we check for an existing sogo-sso-user session variable session_start(); - $username = ""; - if (isset($_SESSION[$session_variable]) && filter_var($_SESSION[$session_variable], FILTER_VALIDATE_EMAIL)) { - $username = $_SESSION[$session_variable]; + if (isset($_SESSION[$session_var_user]) && filter_var($_SESSION[$session_var_user], FILTER_VALIDATE_EMAIL)) { + $username = $_SESSION[$session_var_user]; + $password = $_SESSION[$session_var_pass]; + header("X-User: $username"); + header("X-Auth: Basic ".base64_encode("$username:$password")); + header("X-Auth-Type: Basic"); + } else { + // if username is empty, SOGo will display the normal login form + header("X-User: "); + header("X-Auth: "); + header("X-Auth-Type: "); } - // if username is empty, SOGo will display the normal login form - header("X-Username: $username"); exit; } diff --git a/docker-compose.yml b/docker-compose.yml index c2394909..4d4b9022 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -106,6 +106,7 @@ services: - mysql-socket-vol-1:/var/run/mysqld/ - ./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 @@ -175,6 +176,7 @@ services: - ./data/conf/dovecot:/usr/local/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-attachments-vol-1:/var/attachments - crypt-vol-1:/mail_crypt/ From 9f3b79b361788061cd9530e920d4c64be782b755 Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Tue, 26 Feb 2019 09:03:21 +0100 Subject: [PATCH 08/22] ignore sogo master pass file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c6cd161..5fd3c0f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ rebuild-images.sh data/conf/sogo/sieve.creds +data/conf/phpfpm/sogo-sso/sogo-sso.pass data/conf/dovecot/dovecot-master.passwd data/conf/dovecot/dovecot-master.userdb mailcow.conf From e2f39df7d84c96885e807c2216b8b062dcc75ebe Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Tue, 26 Feb 2019 20:44:53 +0100 Subject: [PATCH 09/22] remove obsolete code, use openssl instead of `cat /dev/urandom` --- data/Dockerfiles/sogo/bootstrap-sogo.sh | 9 ++---- data/web/sogo-auth.php | 37 ++++++------------------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 51e1eea3..5290f9dc 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -83,19 +83,16 @@ EOF done -mkdir -p /var/lib/sogo/GNUstep/Defaults/ - -# Force-remove lines from sogo.conf -sed -i '/SOGoIMAPServer/d' /etc/sogo/sogo.conf - if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then TRUST_PROXY="YES" else TRUST_PROXY="NO" fi -RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1) +# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl +RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9) # Generate plist header with timezone data +mkdir -p /var/lib/sogo/GNUstep/Defaults/ cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index d9c10557..b08ca4e7 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -1,30 +1,5 @@ Date: Tue, 26 Feb 2019 22:13:37 +0100 Subject: [PATCH 10/22] Don't break DAV --- data/conf/nginx/site.conf | 17 ++++++++++++++++- docker-compose.yml | 9 +++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index c19310ae..a5dc0222 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -176,7 +176,22 @@ server { } location ^~ /SOGo { - include /etc/nginx/conf.d/sogo.active; + include /etc/nginx/conf.d/sogo_main.active; + 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; + proxy_set_header x-webobjects-server-protocol HTTP/1.0; + proxy_set_header x-webobjects-remote-host $remote_addr; + proxy_set_header x-webobjects-server-name $server_name; + proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host; + proxy_set_header x-webobjects-server-port $server_port; + client_body_buffer_size 128k; + client_max_body_size 0; + break; + } + + location ^~ /SOGo/dav { + include /etc/nginx/conf.d/sogo_dav.active; 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; diff --git a/docker-compose.yml b/docker-compose.yml index 4d4b9022..724c5c6b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -141,7 +141,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.53 + image: mailcow/sogo:1.54 build: ./data/Dockerfiles/sogo environment: - DBNAME=${DBNAME} @@ -168,7 +168,7 @@ services: - sogo dovecot-mailcow: - image: mailcow/dovecot:1.64 + image: mailcow/dovecot:1.65 build: ./data/Dockerfiles/dovecot cap_add: - NET_BIND_SERVICE @@ -268,8 +268,9 @@ services: 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/server_name.template > /etc/nginx/conf.d/server_name.active && - . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo.active && - envsubst < /etc/nginx/conf.d/templates/sogo.template >> /etc/nginx/conf.d/sogo.active && + . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_main.active && + envsubst < /etc/nginx/conf.d/templates/sogo.template >> /etc/nginx/conf.d/sogo_main.active && + envsubst < /etc/nginx/conf.d/templates/sogo.template >> /etc/nginx/conf.d/sogo_dav.active && envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active && nginx -qt && until ping phpfpm -c1 > /dev/null; do sleep 1; done && From a110378000168848f97947a5d88f7ed9db7373be Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Wed, 27 Feb 2019 23:06:19 +0100 Subject: [PATCH 11/22] always check basic auth against user database for EAS and SOGo if ALLOW_ADMIN_EMAIL_LOGIN is enabled --- data/conf/nginx/site.conf | 41 +++++++++++++-------------------------- data/web/sogo-auth.php | 30 +++++++++++++++++++++++++--- docker-compose.yml | 5 ++--- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index a5dc0222..4c6d1daa 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -142,7 +142,19 @@ server { try_files /autoconfig.php =404; } + # auth_request endpoint if ALLOW_ADMIN_EMAIL_LOGIN is set + location /sogo-auth-verify { + internal; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header Content-Length ""; + proxy_pass http://127.0.0.1:80/sogo-auth; + proxy_pass_request_body off; + } + location ^~ /Microsoft-Server-ActiveSync { + include /etc/nginx/conf.d/sogo_proxy_auth.active; include /etc/nginx/conf.d/sogo_eas.active; proxy_connect_timeout 4000; proxy_next_upstream timeout error; @@ -164,34 +176,9 @@ server { client_max_body_size 0; } - # auth_request endpoint if ALLOW_ADMIN_EMAIL_LOGIN is set - location /sogo-auth-verify { - internal; - proxy_set_header X-Original-URI $request_uri; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $http_host; - proxy_set_header Content-Length ""; - proxy_pass http://127.0.0.1:80/sogo-auth; - proxy_pass_request_body off; - } - location ^~ /SOGo { - include /etc/nginx/conf.d/sogo_main.active; - 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; - proxy_set_header x-webobjects-server-protocol HTTP/1.0; - proxy_set_header x-webobjects-remote-host $remote_addr; - proxy_set_header x-webobjects-server-name $server_name; - proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host; - proxy_set_header x-webobjects-server-port $server_port; - client_body_buffer_size 128k; - client_max_body_size 0; - break; - } - - location ^~ /SOGo/dav { - include /etc/nginx/conf.d/sogo_dav.active; + include /etc/nginx/conf.d/sogo_proxy_auth.active; + include /etc/nginx/conf.d/sogo.active; 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; diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index b08ca4e7..ae882f26 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -8,11 +8,31 @@ $ALLOW_ADMIN_EMAIL_LOGIN = (preg_match( $session_var_user = 'sogo-sso-user'; $session_var_pass = 'sogo-sso-pass'; +// prevent if feature is disabled if (!$ALLOW_ADMIN_EMAIL_LOGIN) { - header('HTTP/1.0 401 Forbidden'); + header('HTTP/1.0 403 Forbidden'); echo "this feature is disabled"; exit; } +// validate credentials for basic auth requests +elseif (isset($_SERVER['PHP_AUTH_USER'])) { + // load prerequisites only when required + require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; + $username = $_SERVER['PHP_AUTH_USER']; + $password = $_SERVER['PHP_AUTH_PW']; + $login_check = check_login($username, $password); + if ($login_check === 'user') { + header("X-User: $username"); + header("X-Auth: Basic ".base64_encode("$username:$password")); + header("X-Auth-Type: Basic"); + exit; + } else { + header('HTTP/1.0 401 Unauthorized'); + echo 'Invalid login'; + exit; + } +} +// check permissions and redirect for direct GET ?login=xy requests elseif (isset($_GET['login'])) { // load prerequisites only when required require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; @@ -32,10 +52,14 @@ elseif (isset($_GET['login'])) { } } } - header('HTTP/1.0 401 Forbidden'); + header('HTTP/1.0 403 Forbidden'); exit; } -else { +// do not check for admin-login / sogo-sso for EAS and DAV requests, SOGo can check auth itself if no authorization header is set +elseif ( + substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 28) !== "/Microsoft-Server-ActiveSync" && + substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9) !== "/SOGo/dav" +) { // this is an nginx auth_request call, we check for existing sogo-sso session variables session_start(); if (isset($_SESSION[$session_var_user]) && filter_var($_SESSION[$session_var_user], FILTER_VALIDATE_EMAIL)) { diff --git a/docker-compose.yml b/docker-compose.yml index 724c5c6b..efee7ade 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -268,10 +268,9 @@ services: 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/server_name.template > /etc/nginx/conf.d/server_name.active && - . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_main.active && - envsubst < /etc/nginx/conf.d/templates/sogo.template >> /etc/nginx/conf.d/sogo_main.active && - envsubst < /etc/nginx/conf.d/templates/sogo.template >> /etc/nginx/conf.d/sogo_dav.active && + envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active && envsubst < /etc/nginx/conf.d/templates/sogo_eas.template > /etc/nginx/conf.d/sogo_eas.active && + . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.active && nginx -qt && until ping phpfpm -c1 > /dev/null; do sleep 1; done && until ping sogo -c1 > /dev/null; do sleep 1; done && From fa80d66d6c855e75de14b298103828a0a9a5d469 Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Wed, 27 Feb 2019 23:14:30 +0100 Subject: [PATCH 12/22] match EAS and SOGO/dav case insensitive --- data/web/sogo-auth.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index ae882f26..37e6f75f 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -57,8 +57,8 @@ elseif (isset($_GET['login'])) { } // do not check for admin-login / sogo-sso for EAS and DAV requests, SOGo can check auth itself if no authorization header is set elseif ( - substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 28) !== "/Microsoft-Server-ActiveSync" && - substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9) !== "/SOGo/dav" + strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 28), "/Microsoft-Server-ActiveSync") == 0 && + strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/dav") == 0 ) { // this is an nginx auth_request call, we check for existing sogo-sso session variables session_start(); From 965577c5d820975d1c24cb11ab7b85f9d9f73c99 Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Wed, 27 Feb 2019 23:16:23 +0100 Subject: [PATCH 13/22] fix path check --- data/web/sogo-auth.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 37e6f75f..0cc4beee 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -57,8 +57,8 @@ elseif (isset($_GET['login'])) { } // do not check for admin-login / sogo-sso for EAS and DAV requests, SOGo can check auth itself if no authorization header is set elseif ( - strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 28), "/Microsoft-Server-ActiveSync") == 0 && - strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/dav") == 0 + strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 28), "/Microsoft-Server-ActiveSync") !== 0 && + strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/dav") !== 0 ) { // this is an nginx auth_request call, we check for existing sogo-sso session variables session_start(); From fcbcc117d27d4a607f1a083e5e6c635eeb3c20f5 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 28 Feb 2019 20:22:16 +0100 Subject: [PATCH 14/22] [Netfilter] Detect SOGo 403 [Compose] Update Netfilter --- data/Dockerfiles/netfilter/server.py | 3 ++- docker-compose.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/Dockerfiles/netfilter/server.py b/data/Dockerfiles/netfilter/server.py index f43122ea..910679c6 100644 --- a/data/Dockerfiles/netfilter/server.py +++ b/data/Dockerfiles/netfilter/server.py @@ -31,7 +31,8 @@ RULES[2] = '-login: Disconnected \(auth failed, .+\): user=.*, method=.+, rip=([ RULES[3] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+' RULES[4] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked' RULES[5] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)' -#RULES[6] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+' +RULES[6] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+' +#RULES[7] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+' bans = {} log = {} diff --git a/docker-compose.yml b/docker-compose.yml index efee7ade..46317570 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -333,7 +333,7 @@ services: - acme netfilter-mailcow: - image: mailcow/netfilter:1.22 + image: mailcow/netfilter:1.23 build: ./data/Dockerfiles/netfilter stop_grace_period: 30s depends_on: From 8fd9db88b6c8276018fc3724c234357ccd655f88 Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 1 Mar 2019 14:15:25 +0100 Subject: [PATCH 15/22] [Compose] New SOGo image --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 46317570..1bb087b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -141,7 +141,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.54 + image: mailcow/sogo:1.55 build: ./data/Dockerfiles/sogo environment: - DBNAME=${DBNAME} From 752a7c418b3f842d7ea473861094362ea066b104 Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 1 Mar 2019 14:17:09 +0100 Subject: [PATCH 16/22] Revert SOGo image version --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1bb087b7..46317570 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -141,7 +141,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.55 + image: mailcow/sogo:1.54 build: ./data/Dockerfiles/sogo environment: - DBNAME=${DBNAME} From 6a7b4387ebd54180decc3e3ea6b2c0c9bf44fc09 Mon Sep 17 00:00:00 2001 From: Marcel Hofer Date: Sat, 2 Mar 2019 12:32:10 +0100 Subject: [PATCH 17/22] allow multiple concurrent admin logins --- data/web/sogo-auth.php | 44 +++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 0cc4beee..08fb1b0b 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -5,7 +5,7 @@ $ALLOW_ADMIN_EMAIL_LOGIN = (preg_match( $_ENV["ALLOW_ADMIN_EMAIL_LOGIN"] )); -$session_var_user = 'sogo-sso-user'; +$session_var_user_allowed = 'sogo-sso-user-allowed'; $session_var_pass = 'sogo-sso-pass'; // prevent if feature is disabled @@ -44,10 +44,10 @@ elseif (isset($_GET['login'])) { // load master password $sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass"); // register username and password in session - $_SESSION[$session_var_user] = $login; + $_SESSION[$session_var_user_allowed][] = $login; $_SESSION[$session_var_pass] = $sogo_sso_pass; // redirect to sogo (sogo will get the correct credentials via nginx auth_request - header("Location: /SOGo/"); + header("Location: /SOGo/so/${login}"); exit; } } @@ -55,24 +55,32 @@ elseif (isset($_GET['login'])) { header('HTTP/1.0 403 Forbidden'); exit; } -// do not check for admin-login / sogo-sso for EAS and DAV requests, SOGo can check auth itself if no authorization header is set +// only check for admin-login on sogo GUI requests elseif ( - strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 28), "/Microsoft-Server-ActiveSync") !== 0 && - strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/dav") !== 0 + strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0 ) { // this is an nginx auth_request call, we check for existing sogo-sso session variables session_start(); - if (isset($_SESSION[$session_var_user]) && filter_var($_SESSION[$session_var_user], FILTER_VALIDATE_EMAIL)) { - $username = $_SESSION[$session_var_user]; - $password = $_SESSION[$session_var_pass]; - header("X-User: $username"); - header("X-Auth: Basic ".base64_encode("$username:$password")); - header("X-Auth-Type: Basic"); - } else { - // if username is empty, SOGo will display the normal login form - header("X-User: "); - header("X-Auth: "); - header("X-Auth-Type: "); + // extract email address from "/SOGo/so/user@domain/xy" + $url_parts = explode("/", $_SERVER['HTTP_X_ORIGINAL_URI']); + $email = $url_parts[3]; + // check if this email is in session allowed list + if ( + !empty($email) && + filter_var($email, FILTER_VALIDATE_EMAIL) && + is_array($_SESSION[$session_var_user_allowed]) && + in_array($email, $_SESSION[$session_var_user_allowed]) + ) { + $username = $email; + $password = $_SESSION[$session_var_pass]; + header("X-User: $username"); + header("X-Auth: Basic ".base64_encode("$username:$password")); + header("X-Auth-Type: Basic"); + exit; } - exit; } + +// if username is empty, SOGo will use the normal login methods / login form +header("X-User: "); +header("X-Auth: "); +header("X-Auth-Type: "); From 5754c6e2aa5251f09e3d95641daf0d76a4af4002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Peters?= Date: Sun, 10 Mar 2019 09:52:31 +0100 Subject: [PATCH 18/22] Update docker-compose.yml --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 3e2b6e1d..d67f603b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -239,6 +239,7 @@ services: ports: - "${SMTP_PORT:-25}:25" - "${SMTPS_PORT:-465}:465" + - "${SUBMISSION_PORT:-587}:587" restart: always dns: - ${IPV4_NETWORK:-172.22.1}.254 From eb2b26699c2375d202dcce319a962d926749e9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Peters?= Date: Wed, 27 Mar 2019 16:37:15 +0100 Subject: [PATCH 19/22] [Dovcot] Cleanup random user maildirs --- data/Dockerfiles/dovecot/docker-entrypoint.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 231c7e54..7034fc08 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -127,6 +127,10 @@ if [[ $(stat -c %U /var/vmail/) != "vmail" ]] ; then chown -R vmail:vmail /var/v if [[ $(stat -c %U /var/vmail/_garbage) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail/_garbage ; fi if [[ $(stat -c %U /var/attachments) != "vmail" ]] ; then chown -R vmail:vmail /var/attachments ; fi +# Cleanup random user maildirs +rm -rf /var/vmail/mailcow.local/* + + # Create random master for SOGo sieve features RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1) RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1) From 220cda444900322d5058d8ca4174d895026ce0fa Mon Sep 17 00:00:00 2001 From: andryyy Date: Wed, 27 Mar 2019 16:49:26 +0100 Subject: [PATCH 20/22] [Compose] Update Dovecot image --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index f5265f89..75b46082 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -168,7 +168,7 @@ services: - sogo dovecot-mailcow: - image: mailcow/dovecot:1.65 + image: mailcow/dovecot:1.66 build: ./data/Dockerfiles/dovecot cap_add: - NET_BIND_SERVICE From f28b58a5bcec8e485e34ac6f45f264a1e568cdf2 Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 31 Mar 2019 15:58:45 +0200 Subject: [PATCH 21/22] [Compose] Update PHP iamge --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1a9a1ba5..c4111f2b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -94,7 +94,7 @@ services: - rspamd php-fpm-mailcow: - image: mailcow/phpfpm:1.35 + image: mailcow/phpfpm:1.36 build: ./data/Dockerfiles/phpfpm command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" depends_on: From a86f9e0120b3f3b9d50045a2a76cb15840636baf Mon Sep 17 00:00:00 2001 From: andryyy Date: Sun, 31 Mar 2019 19:07:39 +0200 Subject: [PATCH 22/22] [Compose] New Dovecot image [Dovecot] Update Dovecot to v2.3.5.1 --- data/Dockerfiles/dovecot/Dockerfile | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile index bc4fca5c..c5517499 100644 --- a/data/Dockerfiles/dovecot/Dockerfile +++ b/data/Dockerfiles/dovecot/Dockerfile @@ -3,7 +3,7 @@ LABEL maintainer "Andre Peters " ARG DEBIAN_FRONTEND=noninteractive ENV LC_ALL C -ENV DOVECOT_VERSION 2.3.5 +ENV DOVECOT_VERSION 2.3.5.1 ENV PIGEONHOLE_VERSION 0.5.5 RUN apt-get update && apt-get -y --no-install-recommends install \ diff --git a/docker-compose.yml b/docker-compose.yml index c4111f2b..fbd90b00 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -169,7 +169,7 @@ services: - sogo dovecot-mailcow: - image: mailcow/dovecot:1.66 + image: mailcow/dovecot:1.67 build: ./data/Dockerfiles/dovecot cap_add: - NET_BIND_SERVICE