From 1e07b85fa132a22da11aa5603764f4fc3df729d4 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 24 Sep 2021 10:20:21 +0200 Subject: [PATCH 01/26] doh --- core/admin/mailu/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index 34f52d8c..a77cef47 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -58,7 +58,7 @@ def has_dane_record(domain, timeout=10): # we will receive this non-specific exception. The safe behaviour is to # accept to defer the email. flask.current_app.logger.warn(f'Unable to lookup the TLSA record for {domain}. Is the DNSSEC zone okay on https://dnsviz.net/d/{domain}/dnssec/?') - return app.config['DEFER_ON_TLS_ERROR'] + return flask.current_app.config['DEFER_ON_TLS_ERROR'] except dns.exception.Timeout: flask.current_app.logger.warn(f'Timeout while resolving the TLSA record for {domain} ({timeout}s).') except dns.resolver.NXDOMAIN: From 7083b3f7c61c398e5fa41e8bd966014f9919ce3f Mon Sep 17 00:00:00 2001 From: Diman0 Date: Fri, 24 Sep 2021 12:10:21 +0200 Subject: [PATCH 02/26] Fix roundcube sso header issue Removed apache rewrite module. --- webmails/roundcube/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index f4399f70..df83bc83 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -39,7 +39,7 @@ RUN apt-get update && apt-get install -y \ && sed -i 's,^php_value.*upload_max_filesize,#&,g' .htaccess \ && chown -R www-data: logs temp \ && rm -rf /var/lib/apt/lists \ - && a2enmod rewrite deflate expires headers + && a2enmod deflate expires headers COPY php.ini /php.ini COPY config.inc.php /var/www/html/config/ From e3fa74768ab94881e99187b0eb5c87d9d6add332 Mon Sep 17 00:00:00 2001 From: Diman0 Date: Fri, 24 Sep 2021 12:16:42 +0200 Subject: [PATCH 03/26] Add newsfragment. --- towncrier/newsfragments/1990.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/newsfragments/1990.bugfix diff --git a/towncrier/newsfragments/1990.bugfix b/towncrier/newsfragments/1990.bugfix new file mode 100644 index 00000000..394fc05b --- /dev/null +++ b/towncrier/newsfragments/1990.bugfix @@ -0,0 +1 @@ +Fixed roundcube sso login not working. From 16691e83adcdcae01948cfa3b6601dd9f0450894 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 24 Sep 2021 18:15:00 +0200 Subject: [PATCH 04/26] re-enable mod_rewrite in roundcube moved chown/mkdir/symlink from start.py to Dockerfile --- webmails/roundcube/Dockerfile | 10 +++++++--- webmails/roundcube/start.py | 6 +----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index df83bc83..32f71ea5 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -33,13 +33,17 @@ RUN apt-get update && apt-get install -y \ && mv roundcubemail-* html \ && mv carddav html/plugins/ \ && cd html \ - && rm -rf CHANGELOG INSTALL LICENSE README.md UPGRADING composer.json-dist installer \ + && rm -rf CHANGELOG INSTALL LICENSE README.md UPGRADING composer.json-dist installer composer.* \ && sed -i 's,mod_php5.c,mod_php7.c,g' .htaccess \ && sed -i 's,^php_value.*post_max_size,#&,g' .htaccess \ && sed -i 's,^php_value.*upload_max_filesize,#&,g' .htaccess \ - && chown -R www-data: logs temp \ + && ln -sf index.php /var/www/html/sso.php \ + && chown -R root:root . \ + && touch logs/errors.log \ + && chown -R www-data:www-data logs temp \ + && chmod -R a+rX . \ && rm -rf /var/lib/apt/lists \ - && a2enmod deflate expires headers + && a2enmod rewrite deflate expires headers COPY php.ini /php.ini COPY config.inc.php /var/www/html/config/ diff --git a/webmails/roundcube/start.py b/webmails/roundcube/start.py index cd42ba06..82c4c26f 100755 --- a/webmails/roundcube/start.py +++ b/webmails/roundcube/start.py @@ -34,11 +34,7 @@ else: conf.jinja("/php.ini", os.environ, "/usr/local/etc/php/conf.d/roundcube.ini") # Create dirs, setup permissions -os.system("mkdir -p /data/gpg /var/www/html/logs") -os.system("touch /var/www/html/logs/errors.log") -os.system("chown -R www-data:www-data /var/www/html/logs") -os.system("chmod -R a+rX /var/www/html/") -os.system("ln -sf /var/www/html/index.php /var/www/html/sso.php") +os.system("mkdir -p /data/gpg") try: print("Initializing database") From e9f84d7d994656162e41136e375d6cdfa86446ed Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 25 Sep 2021 16:25:59 +0200 Subject: [PATCH 05/26] Improve the unbound configuration --- optional/unbound/unbound.conf | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/optional/unbound/unbound.conf b/optional/unbound/unbound.conf index 6c8fc64d..42b2d4d8 100644 --- a/optional/unbound/unbound.conf +++ b/optional/unbound/unbound.conf @@ -1,19 +1,21 @@ server: verbosity: 1 interface: 0.0.0.0 - interface: ::0 + {{ 'interface: ::0' if SUBNET6 }} logfile: "" do-ip4: yes - do-ip6: yes + do-ip6: {{ 'yes' if SUBNET6 else 'no' }} do-udp: yes do-tcp: yes do-daemonize: no access-control: {{ SUBNET }} allow + {{ 'access-control: {{ SUBNET6 }} allow' if SUBNET6 }} directory: "/etc/unbound" username: unbound auto-trust-anchor-file: trusted-key.key root-hints: "/etc/unbound/root.hints" hide-identity: yes hide-version: yes - max-udp-size: 4096 - msg-buffer-size: 65552 + cache-min-ttl: 300 + qname-minimisation: yes + From 739702a0349d099bf70cb71f18a2bd12180897ab Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 25 Sep 2021 16:31:11 +0200 Subject: [PATCH 06/26] doc --- towncrier/newsfragments/1992.enhancement | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 towncrier/newsfragments/1992.enhancement diff --git a/towncrier/newsfragments/1992.enhancement b/towncrier/newsfragments/1992.enhancement new file mode 100644 index 00000000..56a11538 --- /dev/null +++ b/towncrier/newsfragments/1992.enhancement @@ -0,0 +1,3 @@ +Make unbound work with ipv6 +Add a cache-min-ttl of 5minutes +Enable qname minimisation (privacy) From 1cf0f76b529389b47eff0c7e9800c53263e073af Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 27 Sep 2021 09:04:15 +0200 Subject: [PATCH 07/26] not required anymore --- optional/unbound/unbound.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/optional/unbound/unbound.conf b/optional/unbound/unbound.conf index 42b2d4d8..df0c76ff 100644 --- a/optional/unbound/unbound.conf +++ b/optional/unbound/unbound.conf @@ -17,5 +17,4 @@ server: hide-identity: yes hide-version: yes cache-min-ttl: 300 - qname-minimisation: yes From 65133a960af543687c00c09068cdbdf10c2f7ff1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 28 Sep 2021 10:38:37 +0200 Subject: [PATCH 08/26] Prevent traceback when using non-email in login There's a traceback when the username used to log via SMTPAUTH in is not an email address: === before === ``` [...] ERROR in app: Exception on /internal/auth/email [GET] Traceback (most recent call last): File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1179, in _execute_context context = constructor(dialect, self, conn, *args) File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 719, in _init_compiled param.append(processors[key](compiled_params[key])) File "/usr/lib/python3.9/site-packages/sqlalchemy/sql/type_api.py", line 1201, in process return process_param(value, dialect) File "/app/mailu/models.py", line 60, in process_bind_param localpart, domain_name = value.lower().rsplit('@', 1) ValueError: not enough values to unpack (expected 2, got 1) [...] [parameters: [{'%(140657157923216 param)s': 'foobar'}]] ``` === after === ``` [...] WARNING in nginx: Invalid user 'foobar': (builtins.ValueError) invalid email address (no "@") ``` --- core/admin/mailu/internal/nginx.py | 23 ++++++++++++++--------- core/admin/mailu/models.py | 2 ++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index 167341e2..f011bf5a 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -5,6 +5,7 @@ import re import urllib import ipaddress import socket +import sqlalchemy.exc import tenacity SUPPORTED_AUTH_METHODS = ["none", "plain"] @@ -90,15 +91,19 @@ def handle_authentication(headers): except: app.logger.warn(f'Received undecodable user/password from nginx: {raw_user_email!r}/{raw_password!r}') else: - user = models.User.query.get(user_email) - ip = urllib.parse.unquote(headers["Client-Ip"]) - if check_credentials(user, password, ip, protocol): - server, port = get_server(headers["Auth-Protocol"], True) - return { - "Auth-Status": "OK", - "Auth-Server": server, - "Auth-Port": port - } + try: + user = models.User.query.get(user_email) + except sqlalchemy.exc.StatementError as exc: + exc = str(exc).split('\n', 1)[0] + app.logger.warn(f'Invalid user {user_email!r}: {exc}') + else: + if check_credentials(user, password, urllib.parse.unquote(headers["Client-Ip"]), protocol): + server, port = get_server(headers["Auth-Protocol"], True) + return { + "Auth-Status": "OK", + "Auth-Server": server, + "Auth-Port": port + } status, code = get_status(protocol, "authentication") return { "Auth-Status": status, diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index f93b158f..01711a60 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -57,6 +57,8 @@ class IdnaEmail(db.TypeDecorator): def process_bind_param(self, value, dialect): """ encode unicode domain part of email address to punycode """ + if not '@' in value: + raise ValueError('invalid email address (no "@")') localpart, domain_name = value.lower().rsplit('@', 1) if '@' in localpart: raise ValueError('email local part must not contain "@"') From cd17aa0c43f33821a36fbc6fca50bf9cd059a3ff Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 28 Sep 2021 11:06:59 +0200 Subject: [PATCH 09/26] repair failing health-check --- webmails/roundcube/Dockerfile | 2 +- webmails/roundcube/mailu.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index 32f71ea5..2905d30e 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -55,4 +55,4 @@ VOLUME ["/data"] CMD /start.py -HEALTHCHECK CMD curl -f -L http://localhost/ || exit 1 +HEALTHCHECK CMD curl -f -L -H 'User-Agent: health' http://localhost/ || exit 1 diff --git a/webmails/roundcube/mailu.php b/webmails/roundcube/mailu.php index bb4d65e9..f5079e98 100644 --- a/webmails/roundcube/mailu.php +++ b/webmails/roundcube/mailu.php @@ -52,6 +52,12 @@ class mailu extends rcube_plugin } function login_failed($args) { + $ua = $_SERVER['HTTP_USER_AGENT']; + $ra = $_SERVER['REMOTE_ADDR']; + if ($ua == 'health' and ($ra == '127.0.0.1' or $ra == '::1')) { + echo "OK"; + exit; + } header('Location: sso.php'); exit(); } From 7380b248cf9cca970f7cb26e2cc54ff8d2553888 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 28 Sep 2021 11:16:40 +0200 Subject: [PATCH 10/26] direct logging of php errors to stderr --- webmails/roundcube/Dockerfile | 4 ++-- webmails/roundcube/start.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index 2905d30e..c751b6bf 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -38,9 +38,9 @@ RUN apt-get update && apt-get install -y \ && sed -i 's,^php_value.*post_max_size,#&,g' .htaccess \ && sed -i 's,^php_value.*upload_max_filesize,#&,g' .htaccess \ && ln -sf index.php /var/www/html/sso.php \ + && ln -sf /dev/stderr /var/www/html/logs/errors.log \ && chown -R root:root . \ - && touch logs/errors.log \ - && chown -R www-data:www-data logs temp \ + && chown www-data:www-data logs temp \ && chmod -R a+rX . \ && rm -rf /var/lib/apt/lists \ && a2enmod rewrite deflate expires headers diff --git a/webmails/roundcube/start.py b/webmails/roundcube/start.py index 82c4c26f..efaac357 100755 --- a/webmails/roundcube/start.py +++ b/webmails/roundcube/start.py @@ -57,8 +57,5 @@ except subprocess.CalledProcessError as e: # Setup database permissions os.system("chown -R www-data:www-data /data") -# Tail roundcube logs -subprocess.Popen(["tail", "-f", "-n", "0", "/var/www/html/logs/errors.log"]) - # Run apache os.execv("/usr/local/bin/apache2-foreground", ["apache2-foreground"]) From ef9e1ac27997128ee982fb92c5183ade34accf4c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 28 Sep 2021 12:29:57 +0200 Subject: [PATCH 11/26] remove health check from log --- webmails/roundcube/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile index c751b6bf..1f788918 100644 --- a/webmails/roundcube/Dockerfile +++ b/webmails/roundcube/Dockerfile @@ -11,7 +11,8 @@ FROM build_${QEMU} RUN apt-get update && apt-get install -y \ python3 curl python3-pip git python3-multidict \ && rm -rf /var/lib/apt/lists \ - && echo "ServerSignature Off" >> /etc/apache2/apache2.conf + && echo "ServerSignature Off\nServerName roundcube" >> /etc/apache2/apache2.conf \ + && sed -i 's,CustomLog.*combined$,\0 "'"expr=!(%{HTTP_USER_AGENT}=='health'\&\&(-R '127.0.0.1/8' || -R '::1'))"'",' /etc/apache2/sites-available/000-default.conf # Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube RUN pip3 install socrate From 995ce8d4374e501e3733ea2cf2f1d96070b172ef Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 1 Oct 2021 14:54:04 +0200 Subject: [PATCH 12/26] Remove OUTCLEAN_ADDRESS I believe that this isn't relevant anymore as we don't use OpenDKIM anymore Background on: https://bofhskull.wordpress.com/2014/03/25/postfix-opendkim-and-missing-from-header/ --- core/postfix/conf/outclean_header_filter.cf | 5 +---- core/postfix/start.py | 9 --------- towncrier/newsfragments/446.feature | 1 + 3 files changed, 2 insertions(+), 13 deletions(-) create mode 100644 towncrier/newsfragments/446.feature diff --git a/core/postfix/conf/outclean_header_filter.cf b/core/postfix/conf/outclean_header_filter.cf index 7e0e92d3..6a5d6b5b 100644 --- a/core/postfix/conf/outclean_header_filter.cf +++ b/core/postfix/conf/outclean_header_filter.cf @@ -1,10 +1,7 @@ # This configuration was copied from Mailinabox. The original version is available at: # https://raw.githubusercontent.com/mail-in-a-box/mailinabox/master/conf/postfix_outgoing_mail_header_filters -# Remove the first line of the Received: header. Note that we cannot fully remove the Received: header -# because OpenDKIM requires that a header be present when signing outbound mail. The first line is -# where the user's home IP address would be. -/^\s*Received:[^\n]*(.*)/ REPLACE Received: from authenticated-user ({{OUTCLEAN}} [{{OUTCLEAN_ADDRESS}}])$1 +/^\s*Received:[^\n]*(.*)/ IGNORE # Remove other typically private information. /^\s*User-Agent:/ IGNORE diff --git a/core/postfix/start.py b/core/postfix/start.py index 12610bd0..c889dce1 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -46,15 +46,6 @@ os.environ["FRONT_ADDRESS"] = system.get_host_address_from_environment("FRONT", os.environ["ADMIN_ADDRESS"] = system.get_host_address_from_environment("ADMIN", "admin") os.environ["ANTISPAM_MILTER_ADDRESS"] = system.get_host_address_from_environment("ANTISPAM_MILTER", "antispam:11332") os.environ["LMTP_ADDRESS"] = system.get_host_address_from_environment("LMTP", "imap:2525") -os.environ["OUTCLEAN"] = os.environ["HOSTNAMES"].split(",")[0] -try: - _to_lookup = os.environ["OUTCLEAN"] - # Ensure we lookup a FQDN: @see #1884 - if not _to_lookup.endswith('.'): - _to_lookup += '.' - os.environ["OUTCLEAN_ADDRESS"] = system.resolve_hostname(_to_lookup) -except: - os.environ["OUTCLEAN_ADDRESS"] = "10.10.10.10" for postfix_file in glob.glob("/conf/*.cf"): conf.jinja(postfix_file, os.environ, os.path.join("/etc/postfix", os.path.basename(postfix_file))) diff --git a/towncrier/newsfragments/446.feature b/towncrier/newsfragments/446.feature new file mode 100644 index 00000000..12049b94 --- /dev/null +++ b/towncrier/newsfragments/446.feature @@ -0,0 +1 @@ +Remove the Received header with PRIMARY_HOSTNAME [PUBLIC_IP] From 10d78a888bf4a509e36bb96cd0548cd11ee2b585 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 1 Oct 2021 15:00:10 +0200 Subject: [PATCH 13/26] Derive a new subkey for SRS --- core/admin/mailu/__init__.py | 1 + core/admin/mailu/internal/views/postfix.py | 4 ++-- towncrier/newsfragments/1999.enhancement | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 towncrier/newsfragments/1999.enhancement diff --git a/core/admin/mailu/__init__.py b/core/admin/mailu/__init__.py index 9b712512..51532968 100644 --- a/core/admin/mailu/__init__.py +++ b/core/admin/mailu/__init__.py @@ -29,6 +29,7 @@ def create_app_from_config(config): utils.migrate.init_app(app, models.db) app.temp_token_key = hmac.new(bytearray(app.secret_key, 'utf-8'), bytearray('WEBMAIL_TEMP_TOKEN_KEY', 'utf-8'), 'sha256').digest() + app.srs_key = hmac.new(bytearray(app.secret_key, 'utf-8'), bytearray('SRS_KEY', 'utf-8'), 'sha256').digest() # Initialize list of translations config.translations = { diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 330fed5b..928f4faf 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -108,7 +108,7 @@ def postfix_recipient_map(recipient): This is meant for bounces to go back to the original sender. """ - srs = srslib.SRS(flask.current_app.config["SECRET_KEY"]) + srs = srslib.SRS(flask.current_app.srs_key) if srslib.SRS.is_srs_address(recipient): try: return flask.jsonify(srs.reverse(recipient)) @@ -123,7 +123,7 @@ def postfix_sender_map(sender): This is for bounces to come back the reverse path properly. """ - srs = srslib.SRS(flask.current_app.config["SECRET_KEY"]) + srs = srslib.SRS(flask.current_app.srs_key) domain = flask.current_app.config["DOMAIN"] try: localpart, domain_name = models.Email.resolve_domain(sender) diff --git a/towncrier/newsfragments/1999.enhancement b/towncrier/newsfragments/1999.enhancement new file mode 100644 index 00000000..bd025141 --- /dev/null +++ b/towncrier/newsfragments/1999.enhancement @@ -0,0 +1 @@ +Derive a new subkey (from SECRET_KEY) for SRS From 65ee1c1ef27eb7fe3e8c6c6f8b116180c6fcd7bc Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 1 Oct 2021 15:04:45 +0200 Subject: [PATCH 14/26] doh --- towncrier/newsfragments/{446.feature => 466.feature} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename towncrier/newsfragments/{446.feature => 466.feature} (100%) diff --git a/towncrier/newsfragments/446.feature b/towncrier/newsfragments/466.feature similarity index 100% rename from towncrier/newsfragments/446.feature rename to towncrier/newsfragments/466.feature From 4a78d646db1648a0277c4ffe4684cdc435da0391 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 1 Oct 2021 15:05:38 +0200 Subject: [PATCH 15/26] doh --- towncrier/newsfragments/{1999.enhancement => 2002.enhancement} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename towncrier/newsfragments/{1999.enhancement => 2002.enhancement} (100%) diff --git a/towncrier/newsfragments/1999.enhancement b/towncrier/newsfragments/2002.enhancement similarity index 100% rename from towncrier/newsfragments/1999.enhancement rename to towncrier/newsfragments/2002.enhancement From a349190e5232fddd927b460215f71325caf76b05 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 2 Oct 2021 10:19:57 +0200 Subject: [PATCH 16/26] simplify --- core/postfix/conf/outclean_header_filter.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/postfix/conf/outclean_header_filter.cf b/core/postfix/conf/outclean_header_filter.cf index 6a5d6b5b..35b90ff5 100644 --- a/core/postfix/conf/outclean_header_filter.cf +++ b/core/postfix/conf/outclean_header_filter.cf @@ -1,7 +1,7 @@ # This configuration was copied from Mailinabox. The original version is available at: # https://raw.githubusercontent.com/mail-in-a-box/mailinabox/master/conf/postfix_outgoing_mail_header_filters -/^\s*Received:[^\n]*(.*)/ IGNORE +/^\s*Received:[^\n]*/ IGNORE # Remove other typically private information. /^\s*User-Agent:/ IGNORE From 502affbe66eafb6a8eba4f8d36ab8fef94e5eeb7 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 3 Oct 2021 10:14:49 +0200 Subject: [PATCH 17/26] Use the regexp engine since we have one --- core/postfix/conf/outclean_header_filter.cf | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/core/postfix/conf/outclean_header_filter.cf b/core/postfix/conf/outclean_header_filter.cf index 35b90ff5..9c880843 100644 --- a/core/postfix/conf/outclean_header_filter.cf +++ b/core/postfix/conf/outclean_header_filter.cf @@ -1,14 +1,8 @@ # This configuration was copied from Mailinabox. The original version is available at: # https://raw.githubusercontent.com/mail-in-a-box/mailinabox/master/conf/postfix_outgoing_mail_header_filters -/^\s*Received:[^\n]*/ IGNORE - -# Remove other typically private information. -/^\s*User-Agent:/ IGNORE -/^\s*X-Enigmail:/ IGNORE -/^\s*X-Mailer:/ IGNORE -/^\s*X-Originating-IP:/ IGNORE -/^\s*X-Pgp-Agent:/ IGNORE +# Remove typically private information. +/^\s*(Received|User-Agent|X-(Enigmail|Mailer|Originating-IP|Pgp-Agent)):/ IGNORE # The Mime-Version header can leak the user agent too, e.g. in Mime-Version: 1.0 (Mac OS X Mail 8.1 \(2010.6\)). /^\s*(Mime-Version:\s*[0-9\.]+)\s.+/ REPLACE $1 From b48779ea7084278d494e436386d06fd5e55161d6 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 8 Oct 2021 10:17:03 +0200 Subject: [PATCH 18/26] SESSION_COOKIE_SECURE and HTTP won't work --- core/admin/mailu/ui/templates/login.html | 12 ++++++++++++ towncrier/newsfragments/1996.enhancement | 1 + 2 files changed, 13 insertions(+) create mode 100644 towncrier/newsfragments/1996.enhancement diff --git a/core/admin/mailu/ui/templates/login.html b/core/admin/mailu/ui/templates/login.html index fb8e5bd4..118173cb 100644 --- a/core/admin/mailu/ui/templates/login.html +++ b/core/admin/mailu/ui/templates/login.html @@ -7,3 +7,15 @@ {%- block subtitle %} {% trans %}to access the administration tools{% endtrans %} {%- endblock %} + +{%+ block content %} +{% if config["SESSION_COOKIE_SECURE"] %} + +{% endif %} +{{ super() }} +{%+ endblock %} diff --git a/towncrier/newsfragments/1996.enhancement b/towncrier/newsfragments/1996.enhancement new file mode 100644 index 00000000..d1bc2ccf --- /dev/null +++ b/towncrier/newsfragments/1996.enhancement @@ -0,0 +1 @@ +Disable the login page if SESSION_COOKIE_SECURE is incompatible with how Mailu is accessed as this seems to be a common misconfiguration. From aaf3ddd002b87dfccd09e82a00830364a19aeed1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 8 Oct 2021 19:54:31 +0200 Subject: [PATCH 19/26] moved javascript to app.js --- core/admin/assets/app.js | 7 +++++++ core/admin/mailu/ui/templates/login.html | 15 ++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/admin/assets/app.js b/core/admin/assets/app.js index dc3414f2..5df8052c 100644 --- a/core/admin/assets/app.js +++ b/core/admin/assets/app.js @@ -66,5 +66,12 @@ $('document').ready(function() { // init clipboard.js new ClipboardJS('.btn-clip'); + // disable login if not possible + var l = $('#login_needs_https'); + if (l.length && window.location.protocol != 'https:') { + l.removeClass("d-none"); + $('form :input').prop('disabled', true); + } + }); diff --git a/core/admin/mailu/ui/templates/login.html b/core/admin/mailu/ui/templates/login.html index 118173cb..4c38d134 100644 --- a/core/admin/mailu/ui/templates/login.html +++ b/core/admin/mailu/ui/templates/login.html @@ -8,14 +8,11 @@ {% trans %}to access the administration tools{% endtrans %} {%- endblock %} -{%+ block content %} {% if config["SESSION_COOKIE_SECURE"] %} - -{% endif %} +{%- block content %} + {{ super() }} -{%+ endblock %} +{%- endblock %} +{% endif %} From d131d863baca9616bb2251aa44dd658167888966 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 9 Oct 2021 15:44:56 +0200 Subject: [PATCH 20/26] The if needs to be inside the block --- core/admin/mailu/ui/templates/login.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/admin/mailu/ui/templates/login.html b/core/admin/mailu/ui/templates/login.html index 4c38d134..d4d115db 100644 --- a/core/admin/mailu/ui/templates/login.html +++ b/core/admin/mailu/ui/templates/login.html @@ -8,11 +8,11 @@ {% trans %}to access the administration tools{% endtrans %} {%- endblock %} -{% if config["SESSION_COOKIE_SECURE"] %} {%- block content %} +{% if config["SESSION_COOKIE_SECURE"] %} +{% endif %} {{ super() }} {%- endblock %} -{% endif %} From 1d571dedfc5df67dd9b47d1f235edbc1198afbca Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 9 Oct 2021 17:11:12 +0200 Subject: [PATCH 21/26] split localpart into user and tag --- core/admin/mailu/internal/views/postfix.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 928f4faf..d2c9f877 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -140,7 +140,8 @@ def postfix_sender_login(sender): localpart, domain_name = models.Email.resolve_domain(sender) if localpart is None: return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404) - destination = models.Email.resolve_destination(localpart, domain_name, True) + user, plus = localpart.split("+", 1) + destination = models.Email.resolve_destination(user, domain_name, True) destination = [*destination, *wildcard_senders] if destination else [*wildcard_senders] return flask.jsonify(",".join(destination)) if destination else flask.abort(404) From 22ed2b7f904271d2a7b524b390ccbbc00536fdfd Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 9 Oct 2021 17:17:40 +0200 Subject: [PATCH 22/26] add newsfragment --- towncrier/newsfragments/2006.enhancement | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/newsfragments/2006.enhancement diff --git a/towncrier/newsfragments/2006.enhancement b/towncrier/newsfragments/2006.enhancement new file mode 100644 index 00000000..802e6d36 --- /dev/null +++ b/towncrier/newsfragments/2006.enhancement @@ -0,0 +1 @@ +allow sending emails as user+detail@domain.tld From 6a8066c0ae1597fc0b85d9130978ff1af778d70f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sat, 9 Oct 2021 17:18:53 +0200 Subject: [PATCH 23/26] renamed newsfragment --- towncrier/newsfragments/{2006.enhancement => 2007.enhancement} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename towncrier/newsfragments/{2006.enhancement => 2007.enhancement} (100%) diff --git a/towncrier/newsfragments/2006.enhancement b/towncrier/newsfragments/2007.enhancement similarity index 100% rename from towncrier/newsfragments/2006.enhancement rename to towncrier/newsfragments/2007.enhancement From 8c59f3569760513774ad8a48d1a30d336ce2f259 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 9 Oct 2021 17:43:09 +0200 Subject: [PATCH 24/26] use RECIPIENT_DELIMITER for splitting --- core/admin/mailu/configuration.py | 1 + core/admin/mailu/internal/views/postfix.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 4401888a..8d4d334a 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -49,6 +49,7 @@ DEFAULT_CONFIG = { 'DKIM_PATH': '/dkim/{domain}.{selector}.key', 'DEFAULT_QUOTA': 1000000000, 'MESSAGE_RATELIMIT': '200/day', + 'RECIPIENT_DELIMITER': None, # Web settings 'SITENAME': 'Mailu', 'WEBSITE': 'https://mailu.io', diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index d2c9f877..44a685c9 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -140,8 +140,9 @@ def postfix_sender_login(sender): localpart, domain_name = models.Email.resolve_domain(sender) if localpart is None: return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404) - user, plus = localpart.split("+", 1) - destination = models.Email.resolve_destination(user, domain_name, True) + if delim := flask.current_app.config.get('RECIPIENT_DELIMITER'): + localpart = localpart.split(delim, 1)[0] + destination = models.Email.resolve_destination(localpart, domain_name, True) destination = [*destination, *wildcard_senders] if destination else [*wildcard_senders] return flask.jsonify(",".join(destination)) if destination else flask.abort(404) From 14360f8926461c62c019c62338fef1be2308f133 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 9 Oct 2021 18:28:50 +0200 Subject: [PATCH 25/26] RECIPIENT_DELIMITER can have several characters --- core/admin/mailu/configuration.py | 2 +- core/admin/mailu/internal/views/postfix.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 8d4d334a..1f2a9239 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -49,7 +49,7 @@ DEFAULT_CONFIG = { 'DKIM_PATH': '/dkim/{domain}.{selector}.key', 'DEFAULT_QUOTA': 1000000000, 'MESSAGE_RATELIMIT': '200/day', - 'RECIPIENT_DELIMITER': None, + 'RECIPIENT_DELIMITER': '', # Web settings 'SITENAME': 'Mailu', 'WEBSITE': 'https://mailu.io', diff --git a/core/admin/mailu/internal/views/postfix.py b/core/admin/mailu/internal/views/postfix.py index 44a685c9..ab965967 100644 --- a/core/admin/mailu/internal/views/postfix.py +++ b/core/admin/mailu/internal/views/postfix.py @@ -140,8 +140,7 @@ def postfix_sender_login(sender): localpart, domain_name = models.Email.resolve_domain(sender) if localpart is None: return flask.jsonify(",".join(wildcard_senders)) if wildcard_senders else flask.abort(404) - if delim := flask.current_app.config.get('RECIPIENT_DELIMITER'): - localpart = localpart.split(delim, 1)[0] + localpart = localpart[:next((i for i, ch in enumerate(localpart) if ch in flask.current_app.config.get('RECIPIENT_DELIMITER')), None)] destination = models.Email.resolve_destination(localpart, domain_name, True) destination = [*destination, *wildcard_senders] if destination else [*wildcard_senders] return flask.jsonify(",".join(destination)) if destination else flask.abort(404) From e127e6b32f8cf83123a5bcfa9431f4caeab80e22 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 9 Oct 2021 18:58:51 +0200 Subject: [PATCH 26/26] clarify the documentation --- docs/configuration.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 5f17b57e..5d8e87b1 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -93,9 +93,10 @@ go and fetch new email if available. Do not use too short delays if you do not want to be blacklisted by external services, but not too long delays if you want to receive your email in time. -The ``RECIPIENT_DELIMITER`` is a character used to delimit localpart from a -custom address part. For instance, if set to ``+``, users can use addresses -like ``localpart+custom@domain.tld`` to deliver mail to ``localpart@domain.tld``. +The ``RECIPIENT_DELIMITER`` is a list of characters used to delimit localpart +from a custom address part. For instance, if set to ``+-``, users can use +addresses like ``localpart+custom@example.com`` or ``localpart-custom@example.com`` +to deliver mail to ``localpart@example.com``. This is useful to provide external parties with different email addresses and later classify incoming mail based on the custom part.