From e2d4e3eb2ef3499da2e994541e9b10ed074b6db3 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 19 Nov 2022 17:59:31 +0100 Subject: [PATCH] Implement header authentication via external proxy --- core/admin/mailu/configuration.py | 4 ++++ core/admin/mailu/sso/views/base.py | 35 ++++++++++++++++++++++++++++ docs/configuration.rst | 12 ++++++++++ towncrier/newsfragments/1972.feature | 1 + 4 files changed, 52 insertions(+) create mode 100644 towncrier/newsfragments/1972.feature diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index d282bfc2..d447e570 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -80,6 +80,9 @@ DEFAULT_CONFIG = { 'TLS_PERMISSIVE': True, 'TZ': 'Etc/UTC', 'DEFAULT_SPAM_THRESHOLD': 80, + 'PROXY_AUTH_WHITELIST': '', + 'PROXY_AUTH_HEADER': 'X-Auth-Email', + 'PROXY_AUTH_CREATE': False, # Host settings 'HOST_IMAP': 'imap', 'HOST_LMTP': 'imap:2525', @@ -171,6 +174,7 @@ class ConfigManager: self.config['HOSTNAMES'] = ','.join(hostnames) self.config['HOSTNAME'] = hostnames[0] self.config['DEFAULT_SPAM_THRESHOLD'] = int(self.config['DEFAULT_SPAM_THRESHOLD']) + self.config['PROXY_AUTH_WHITELIST'] = set(ipaddress.ip_network(cidr, False) for cidr in (cidr.strip() for cidr in self.config['PROXY_AUTH_WHITELIST'].split(',')) if cidr) # update the app config app.config.update(self.config) diff --git a/core/admin/mailu/sso/views/base.py b/core/admin/mailu/sso/views/base.py index 6fa9403f..600f62f4 100644 --- a/core/admin/mailu/sso/views/base.py +++ b/core/admin/mailu/sso/views/base.py @@ -6,6 +6,8 @@ from mailu.ui import access from flask import current_app as app import flask import flask_login +import secrets +import ipaddress @sso.route('/login', methods=['GET', 'POST']) def login(): @@ -57,3 +59,36 @@ def logout(): flask.session.destroy() return flask.redirect(flask.url_for('.login')) + +@sso.route('/proxy', methods=['GET']) +@sso.route('/proxy/', methods=['GET']) +def proxy(target='webmail'): + ip = ipaddress.ip_address(flask.request.remote_addr) + if not any(ip in cidr for cidr in app.config['PROXY_AUTH_WHITELIST']): + return flask.abort(500, '%s is not on PROXY_AUTH_WHITELIST' % flask.request.remote_addr) + + email = flask.request.headers.get(app.config['PROXY_AUTH_HEADER'], None) + if not email: + return flask.abort(500, 'No %s header' % app.config['PROXY_AUTH_HEADER']) + + user = models.User.get(email) + if user: + flask.session.regenerate() + flask_login.login_user(user) + return flask.redirect(app.config['WEB_ADMIN'] if target=='admin' else app.config['WEB_WEBMAIL']) + elif app.config['PROXY_AUTH_CREATE']: + localpart, desireddomain = email.split('@') + domain = models.Domain.query.get(desireddomain) or flask.abort(500, 'You don\'t exist. Go away! (domain=%s)' % desireddomain) + if not domain.max_users == -1 and len(domain.users) >= domain.max_users: + flask.current_app.logger.warning('Too many users for domain %s' % domain) + return flask.abort(500, 'Too many users in (domain=%s)' % domain) + user = models.User(localpart=localpart, domain=domain) + user.set_password(secrets.token_urlsafe()) + models.db.session.add(user) + models.db.session.commit() + user.send_welcome() + client_ip = flask.request.headers.get('X-Real-IP', flask.request.remote_addr) + flask.current_app.logger.info(f'Login succeeded by proxy created user: {user} from {client_ip} through {flask.request.remote_addr}.') + return flask.redirect(app.config['WEB_ADMIN'] if target=='admin' else app.config['WEB_WEBMAIL']) + else: + return flask.abort(500, 'You don\'t exist. Go away! (%s)' % email) diff --git a/docs/configuration.rst b/docs/configuration.rst index 1a40bf65..b5affad6 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -362,3 +362,15 @@ It can be configured with the following option: When ``POSTFIX_LOG_FILE`` is enabled, the logrotate program will automatically rotate the logs every week and keep 52 logs. To override the logrotate configuration, create the file logrotate.conf with the desired configuration in the :ref:`Postfix overrides folder`. + + +Header authentication using an external proxy +--------------------------------------------- + +The ``PROXY_AUTH_WHITELIST`` (default: unset/disabled) option allows you to configure a comma separated list of CIDRs of proxies to trust for authentication. This list is separate from ``REAL_IP_FROM`` and any entry in ``PROXY_AUTH_WHITELIST`` should also appear in ``REAL_IP_FROM``. + +Use ``PROXY_AUTH_HEADER`` (default: 'X-Auth-Email') to customize which HTTP header the email address of the user to authenticate as should be and ``PROXY_AUTH_CREATE`` (default: False) to control whether non-existing accounts should be auto-created. Please note that Mailu doesn't currently support creating new users for non-existing domains; you do need to create all the domains that may be used manually. + +Once configured, any request to /sso/proxy will be redirected to the webmail and /sso/proxy/admin to the admin panel. Please check issue `1972` for more details. + +.. _`1972`: https://github.com/Mailu/Mailu/issues/1972 diff --git a/towncrier/newsfragments/1972.feature b/towncrier/newsfragments/1972.feature new file mode 100644 index 00000000..4efe45c9 --- /dev/null +++ b/towncrier/newsfragments/1972.feature @@ -0,0 +1 @@ +Implement Header authentication via external proxy