From 9fac3d7ad3895c1e03016dd3eadbff186c875711 Mon Sep 17 00:00:00 2001 From: Diman0 Date: Thu, 2 Sep 2021 13:36:42 +0200 Subject: [PATCH 01/48] Initial implementation for standalone sso page --- core/admin/Dockerfile | 1 + core/admin/mailu/__init__.py | 4 +- core/admin/mailu/sso/__init__.py | 6 +++ core/admin/mailu/sso/forms.py | 14 ++++++ core/admin/mailu/sso/templates/base_sso.html | 51 ++++++++++++++++++++ core/admin/mailu/sso/templates/form_sso.html | 7 +++ core/admin/mailu/sso/templates/login.html | 9 ++++ core/admin/mailu/sso/views/__init__.py | 3 ++ core/admin/mailu/sso/views/base.py | 30 ++++++++++++ core/admin/mailu/sso/views/hello.py | 6 +++ core/admin/mailu/ui/forms.py | 4 +- core/admin/mailu/ui/templates/login.html | 9 ---- core/admin/mailu/ui/views/base.py | 3 +- core/admin/mailu/utils.py | 4 +- 14 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 core/admin/mailu/sso/__init__.py create mode 100644 core/admin/mailu/sso/forms.py create mode 100644 core/admin/mailu/sso/templates/base_sso.html create mode 100644 core/admin/mailu/sso/templates/form_sso.html create mode 100644 core/admin/mailu/sso/templates/login.html create mode 100644 core/admin/mailu/sso/views/__init__.py create mode 100644 core/admin/mailu/sso/views/base.py create mode 100644 core/admin/mailu/sso/views/hello.py delete mode 100644 core/admin/mailu/ui/templates/login.html diff --git a/core/admin/Dockerfile b/core/admin/Dockerfile index 97cf1736..2406bb5c 100644 --- a/core/admin/Dockerfile +++ b/core/admin/Dockerfile @@ -31,6 +31,7 @@ RUN apk add --no-cache openssl curl postgresql-libs mariadb-connector-c \ && apk del --no-cache build-dep COPY --from=assets static ./mailu/ui/static +COPY --from=assets static ./mailu/sso/static COPY mailu ./mailu COPY migrations ./migrations COPY start.py /start.py diff --git a/core/admin/mailu/__init__.py b/core/admin/mailu/__init__.py index 8ab8ed0e..f80533fe 100644 --- a/core/admin/mailu/__init__.py +++ b/core/admin/mailu/__init__.py @@ -48,10 +48,10 @@ def create_app_from_config(config): ) # Import views - from mailu import ui, internal + from mailu import ui, internal, sso app.register_blueprint(ui.ui, url_prefix='/ui') app.register_blueprint(internal.internal, url_prefix='/internal') - + app.register_blueprint(sso.sso, url_prefix='/sso') return app diff --git a/core/admin/mailu/sso/__init__.py b/core/admin/mailu/sso/__init__.py new file mode 100644 index 00000000..2d3c6d84 --- /dev/null +++ b/core/admin/mailu/sso/__init__.py @@ -0,0 +1,6 @@ +from flask import Blueprint + + +sso = Blueprint('sso', __name__, static_folder='static', template_folder='templates') + +from mailu.sso.views import * diff --git a/core/admin/mailu/sso/forms.py b/core/admin/mailu/sso/forms.py new file mode 100644 index 00000000..a81667a2 --- /dev/null +++ b/core/admin/mailu/sso/forms.py @@ -0,0 +1,14 @@ +from wtforms import validators, fields, widgets +from wtforms_components import fields as fields_ +from flask_babel import lazy_gettext as _ + +import flask_login +import flask_wtf +import re + +class LoginForm(flask_wtf.FlaskForm): + class Meta: + csrf = False + email = fields.StringField(_('E-mail'), [validators.Email()]) + pw = fields.PasswordField(_('Password'), [validators.DataRequired()]) + submit = fields.SubmitField(_('Sign in')) diff --git a/core/admin/mailu/sso/templates/base_sso.html b/core/admin/mailu/sso/templates/base_sso.html new file mode 100644 index 00000000..a95cb23b --- /dev/null +++ b/core/admin/mailu/sso/templates/base_sso.html @@ -0,0 +1,51 @@ +{% import "macros.html" as macros %} +{% import "bootstrap/utils.html" as utils %} + + + + + + + Mailu-login - {{ config["SITENAME"] }} + + +
+
+ +
+ +
+
+
+ {% block main_action %} + {% endblock %} +
+

+ {% block title %}{% endblock %} + {% block subtitle %}{% endblock %} +

+
+ +
+ {{ utils.flashed_messages(container=False) }} + {% block content %}{% endblock %} +
+
+ +
+ + + + diff --git a/core/admin/mailu/sso/templates/form_sso.html b/core/admin/mailu/sso/templates/form_sso.html new file mode 100644 index 00000000..fcabad41 --- /dev/null +++ b/core/admin/mailu/sso/templates/form_sso.html @@ -0,0 +1,7 @@ +{% extends "base_sso.html" %} + +{% block content %} +{% call macros.box() %} +{{ macros.form(form) }} +{% endcall %} +{% endblock %} diff --git a/core/admin/mailu/sso/templates/login.html b/core/admin/mailu/sso/templates/login.html new file mode 100644 index 00000000..851e6643 --- /dev/null +++ b/core/admin/mailu/sso/templates/login.html @@ -0,0 +1,9 @@ +{% extends "form_sso.html" %} + +{% block title %} +{% trans %}Sign in{% endtrans %} +{% endblock %} + +{% block subtitle %} +{% trans %}to access IF statement for switch text for loggin in what the administration tools{% endtrans %} +{% endblock %} diff --git a/core/admin/mailu/sso/views/__init__.py b/core/admin/mailu/sso/views/__init__.py new file mode 100644 index 00000000..38efde4c --- /dev/null +++ b/core/admin/mailu/sso/views/__init__.py @@ -0,0 +1,3 @@ +__all__ = [ + 'base', 'hello' +] diff --git a/core/admin/mailu/sso/views/base.py b/core/admin/mailu/sso/views/base.py new file mode 100644 index 00000000..dd6f60c9 --- /dev/null +++ b/core/admin/mailu/sso/views/base.py @@ -0,0 +1,30 @@ +from mailu import models +from mailu.sso import sso, forms + +from flask import current_app as app +import flask +import flask_login + +@sso.route('/login', methods=['GET', 'POST']) +def login(): + form = forms.LoginForm() + if form.validate_on_submit(): + user = models.User.login(form.email.data, form.pw.data) + if user: + flask.session.regenerate() + flask_login.login_user(user) + endpoint = flask.request.args.get('next', 'ui.index') + return flask.redirect(flask.url_for(endpoint) + or flask.url_for('ui.index')) + else: + flask.flash('Wrong e-mail or password', 'error') + return flask.render_template('login.html', form=form) + +""" +@ui.route('/logout', methods=['GET']) +@access.authenticated +def logout(): + flask_login.logout_user() + flask.session.destroy() + return flask.redirect(flask.url_for('.index')) +""" \ No newline at end of file diff --git a/core/admin/mailu/sso/views/hello.py b/core/admin/mailu/sso/views/hello.py new file mode 100644 index 00000000..2e9b5e35 --- /dev/null +++ b/core/admin/mailu/sso/views/hello.py @@ -0,0 +1,6 @@ +from mailu.sso import sso +from flask import current_app as app + +@sso.route("/") +def hello_world(): + return "

Hello, World!

" diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py index 32bb31ab..dff7008e 100644 --- a/core/admin/mailu/ui/forms.py +++ b/core/admin/mailu/ui/forms.py @@ -44,14 +44,14 @@ class MultipleEmailAddressesVerify(object): class ConfirmationForm(flask_wtf.FlaskForm): submit = fields.SubmitField(_('Confirm')) - +""" class LoginForm(flask_wtf.FlaskForm): class Meta: csrf = False email = fields.StringField(_('E-mail'), [validators.Email()]) pw = fields.PasswordField(_('Password'), [validators.DataRequired()]) submit = fields.SubmitField(_('Sign in')) - +""" class DomainForm(flask_wtf.FlaskForm): name = fields.StringField(_('Domain name'), [validators.DataRequired()]) diff --git a/core/admin/mailu/ui/templates/login.html b/core/admin/mailu/ui/templates/login.html deleted file mode 100644 index 26c47c08..00000000 --- a/core/admin/mailu/ui/templates/login.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "form.html" %} - -{% block title %} -{% trans %}Sign in{% endtrans %} -{% endblock %} - -{% block subtitle %} -{% trans %}to access the administration tools{% endtrans %} -{% endblock %} diff --git a/core/admin/mailu/ui/views/base.py b/core/admin/mailu/ui/views/base.py index eb5490bc..fc9daba6 100644 --- a/core/admin/mailu/ui/views/base.py +++ b/core/admin/mailu/ui/views/base.py @@ -12,6 +12,7 @@ def index(): return flask.redirect(flask.url_for('.user_settings')) +""" @ui.route('/login', methods=['GET', 'POST']) def login(): form = forms.LoginForm() @@ -26,7 +27,7 @@ def login(): else: flask.flash('Wrong e-mail or password', 'error') return flask.render_template('login.html', form=form) - +""" @ui.route('/logout', methods=['GET']) @access.authenticated diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index 02150754..c30f259e 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -28,13 +28,13 @@ from werkzeug.contrib import fixers # Login configuration login = flask_login.LoginManager() -login.login_view = "ui.login" +login.login_view = "sso.login" @login.unauthorized_handler def handle_needs_login(): """ redirect unauthorized requests to login page """ return flask.redirect( - flask.url_for('ui.login', next=flask.request.endpoint) + flask.url_for('sso.login', next=flask.request.endpoint) ) # Rate limiter From 8868aec0dcd02101ea09396c5d064b046b75ea44 Mon Sep 17 00:00:00 2001 From: Diman0 Date: Thu, 2 Sep 2021 17:08:50 +0200 Subject: [PATCH 02/48] Merge master. Make sso login working for admin. --- core/admin/mailu/configuration.py | 1 + core/admin/mailu/sso/forms.py | 2 + core/admin/mailu/sso/templates/base_sso.html | 61 +++++++++++-------- core/admin/mailu/sso/templates/form_sso.html | 2 +- core/admin/mailu/sso/templates/login.html | 6 +- .../mailu/sso/templates/sidebar_sso.html | 60 ++++++++++++++++++ core/admin/mailu/sso/views/base.py | 15 ++--- core/admin/mailu/ui/templates/sidebar.html | 2 +- core/nginx/conf/nginx.conf | 8 ++- 9 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 core/admin/mailu/sso/templates/sidebar_sso.html diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py index 7cd3a56b..025a173c 100644 --- a/core/admin/mailu/configuration.py +++ b/core/admin/mailu/configuration.py @@ -51,6 +51,7 @@ DEFAULT_CONFIG = { # Web settings 'SITENAME': 'Mailu', 'WEBSITE': 'https://mailu.io', + 'ADMIN' : 'none', 'WEB_ADMIN': '/admin', 'WEB_WEBMAIL': '/webmail', 'WEBMAIL': 'none', diff --git a/core/admin/mailu/sso/forms.py b/core/admin/mailu/sso/forms.py index a81667a2..bc2b5363 100644 --- a/core/admin/mailu/sso/forms.py +++ b/core/admin/mailu/sso/forms.py @@ -6,6 +6,8 @@ import flask_login import flask_wtf import re +LOCALPART_REGEX = "^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$" + class LoginForm(flask_wtf.FlaskForm): class Meta: csrf = False diff --git a/core/admin/mailu/sso/templates/base_sso.html b/core/admin/mailu/sso/templates/base_sso.html index a95cb23b..0edd692c 100644 --- a/core/admin/mailu/sso/templates/base_sso.html +++ b/core/admin/mailu/sso/templates/base_sso.html @@ -6,46 +6,53 @@ - Mailu-login - {{ config["SITENAME"] }} + Mailu - {{ config["SITENAME"] }} - +
-
- -
- + +
-
- {% block main_action %} - {% endblock %} +
+
+
+

{% block title %}{% endblock %}

+ {% block subtitle %}{% endblock %} +
+
+ {% block main_action %} + {% endblock %} +
+
-

- {% block title %}{% endblock %} - {% block subtitle %}{% endblock %} -

-
+
{{ utils.flashed_messages(container=False) }} {% block content %}{% endblock %} -
+
- - + + diff --git a/core/admin/mailu/sso/templates/form_sso.html b/core/admin/mailu/sso/templates/form_sso.html index fcabad41..d28b82bf 100644 --- a/core/admin/mailu/sso/templates/form_sso.html +++ b/core/admin/mailu/sso/templates/form_sso.html @@ -1,7 +1,7 @@ {% extends "base_sso.html" %} {% block content %} -{% call macros.box() %} +{% call macros.card() %} {{ macros.form(form) }} {% endcall %} {% endblock %} diff --git a/core/admin/mailu/sso/templates/login.html b/core/admin/mailu/sso/templates/login.html index 851e6643..81d83a46 100644 --- a/core/admin/mailu/sso/templates/login.html +++ b/core/admin/mailu/sso/templates/login.html @@ -5,5 +5,9 @@ {% endblock %} {% block subtitle %} -{% trans %}to access IF statement for switch text for loggin in what the administration tools{% endtrans %} +{% if endpoint == 'ui.index' %} +{% trans %}to access the configuration page{% endtrans %} +{% elif endpoint == 'ui.webmail' %} +{% trans %}to access the webmail page{% endtrans %} +{% endif %} {% endblock %} diff --git a/core/admin/mailu/sso/templates/sidebar_sso.html b/core/admin/mailu/sso/templates/sidebar_sso.html new file mode 100644 index 00000000..6e919731 --- /dev/null +++ b/core/admin/mailu/sso/templates/sidebar_sso.html @@ -0,0 +1,60 @@ + diff --git a/core/admin/mailu/sso/views/base.py b/core/admin/mailu/sso/views/base.py index dd6f60c9..702b1582 100644 --- a/core/admin/mailu/sso/views/base.py +++ b/core/admin/mailu/sso/views/base.py @@ -1,5 +1,6 @@ from mailu import models from mailu.sso import sso, forms +from mailu.ui import access from flask import current_app as app import flask @@ -8,23 +9,15 @@ import flask_login @sso.route('/login', methods=['GET', 'POST']) def login(): form = forms.LoginForm() + endpoint = flask.request.args.get('next', 'ui.index') if form.validate_on_submit(): user = models.User.login(form.email.data, form.pw.data) if user: flask.session.regenerate() flask_login.login_user(user) - endpoint = flask.request.args.get('next', 'ui.index') return flask.redirect(flask.url_for(endpoint) or flask.url_for('ui.index')) else: flask.flash('Wrong e-mail or password', 'error') - return flask.render_template('login.html', form=form) - -""" -@ui.route('/logout', methods=['GET']) -@access.authenticated -def logout(): - flask_login.logout_user() - flask.session.destroy() - return flask.redirect(flask.url_for('.index')) -""" \ No newline at end of file + return flask.render_template('login.html', form=form, endpoint=endpoint) + \ No newline at end of file diff --git a/core/admin/mailu/ui/templates/sidebar.html b/core/admin/mailu/ui/templates/sidebar.html index 0fdae9db..0938f8ac 100644 --- a/core/admin/mailu/ui/templates/sidebar.html +++ b/core/admin/mailu/ui/templates/sidebar.html @@ -125,7 +125,7 @@ {% else %} diff --git a/core/admin/mailu/ui/templates/base.html b/core/admin/mailu/ui/templates/base.html index 12f61149..e646e579 100644 --- a/core/admin/mailu/ui/templates/base.html +++ b/core/admin/mailu/ui/templates/base.html @@ -38,7 +38,7 @@ {{ session['language'] }} From 9bc685c30b339b70b106f8c5a1f74d20449d70c9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 29 Oct 2021 15:34:00 +0200 Subject: [PATCH 37/48] removed some more whitespace --- core/admin/mailu/internal/templates/default.sieve | 4 ++-- core/admin/mailu/manage.py | 2 +- core/admin/mailu/sso/__init__.py | 2 +- core/admin/mailu/sso/templates/form_sso.html | 6 +++--- core/admin/mailu/translations/pl/LC_MESSAGES/messages.po | 4 ++-- core/admin/mailu/ui/templates/macros.html | 2 +- core/admin/migrations/env.py | 2 +- core/dovecot/start.py | 2 +- core/postfix/start.py | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/admin/mailu/internal/templates/default.sieve b/core/admin/mailu/internal/templates/default.sieve index 5e995611..a29e7aba 100644 --- a/core/admin/mailu/internal/templates/default.sieve +++ b/core/admin/mailu/internal/templates/default.sieve @@ -19,7 +19,7 @@ if header :index 2 :matches "Received" "from * by * for <*>; *" } {% if user.spam_enabled %} -if spamtest :percent :value "gt" :comparator "i;ascii-numeric" "{{ user.spam_threshold }}" +if spamtest :percent :value "gt" :comparator "i;ascii-numeric" "{{ user.spam_threshold }}" { setflag "\\seen"; fileinto :create "Junk"; @@ -32,6 +32,6 @@ if exists "X-Virus" { stop; } -{% if user.reply_active %} +{% if user.reply_active %} vacation :days 1 {% if user.displayed_name != "" %}:from "{{ user.displayed_name }} <{{ user.email }}>"{% endif %} :subject "{{ user.reply_subject }}" "{{ user.reply_body }}"; {% endif %} diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py index 54f4b826..937c9f49 100644 --- a/core/admin/mailu/manage.py +++ b/core/admin/mailu/manage.py @@ -119,7 +119,7 @@ def password(localpart, domain_name, password): """ Change the password of an user """ email = f'{localpart}@{domain_name}' - user = models.User.query.get(email) + user = models.User.query.get(email) if user: user.set_password(password) else: diff --git a/core/admin/mailu/sso/__init__.py b/core/admin/mailu/sso/__init__.py index cc066234..98b5abd0 100644 --- a/core/admin/mailu/sso/__init__.py +++ b/core/admin/mailu/sso/__init__.py @@ -1,5 +1,5 @@ from flask import Blueprint -sso = Blueprint('sso', __name__, static_folder=None ,template_folder='templates') +sso = Blueprint('sso', __name__, static_folder=None, template_folder='templates') from mailu.sso.views import * diff --git a/core/admin/mailu/sso/templates/form_sso.html b/core/admin/mailu/sso/templates/form_sso.html index efcc95a9..b14e7600 100644 --- a/core/admin/mailu/sso/templates/form_sso.html +++ b/core/admin/mailu/sso/templates/form_sso.html @@ -3,9 +3,9 @@ {%- block content %} {%- call macros.card() %}
- {{ macros.form_field(form.email) }} - {{ macros.form_field(form.pw) }} - {{ macros.form_fields( fields, label=False, class="btn btn-default", spacing=False) }} + {{ macros.form_field(form.email) }} + {{ macros.form_field(form.pw) }} + {{ macros.form_fields(fields, label=False, class="btn btn-default", spacing=False) }}
{%- endcall %} {%- endblock %} diff --git a/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po b/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po index 664ff571..09130a7b 100644 --- a/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po +++ b/core/admin/mailu/translations/pl/LC_MESSAGES/messages.po @@ -551,11 +551,11 @@ msgid "" "cache\n" " expires." msgstr "" -"Jeśli nie wiesz, jak skonfigurować rekord MX dla swojej " +"Jeśli nie wiesz, jak skonfigurować rekord MX dla swojej " "strefy DNS,\n" "skontaktuj się z dostawcą DNS lub administratorem. Proszę również " "poczekać\n" -"kilka minut po ustawieniu MX , żeby pamięć podręczna " +"kilka minut po ustawieniu MX , żeby pamięć podręczna " "serwera lokalnego wygasła." #: mailu/ui/templates/fetch/create.html:4 diff --git a/core/admin/mailu/ui/templates/macros.html b/core/admin/mailu/ui/templates/macros.html index d18e6584..5143b697 100644 --- a/core/admin/mailu/ui/templates/macros.html +++ b/core/admin/mailu/ui/templates/macros.html @@ -58,7 +58,7 @@
{{ form.hidden_tag() }} {%- for field in form %} - {%- if bootstrap_is_hidden_field(field) %} + {%- if bootstrap_is_hidden_field(field) %} {{ field() }} {%- else %} {{ form_field(field) }} diff --git a/core/admin/migrations/env.py b/core/admin/migrations/env.py index cdfd2248..3e45bb18 100755 --- a/core/admin/migrations/env.py +++ b/core/admin/migrations/env.py @@ -37,7 +37,7 @@ def run_migrations_offline(): This configures the context with just a URL and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation + here as well. By skipping the Engine creation we don't even need a DBAPI to be available. Calls to context.execute() here emit the given string to the diff --git a/core/dovecot/start.py b/core/dovecot/start.py index 1845eb93..03bdfa80 100755 --- a/core/dovecot/start.py +++ b/core/dovecot/start.py @@ -6,7 +6,7 @@ import multiprocessing import logging as log import sys -from podop import run_server +from podop import run_server from socrate import system, conf log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) diff --git a/core/postfix/start.py b/core/postfix/start.py index c889dce1..06a097b8 100755 --- a/core/postfix/start.py +++ b/core/postfix/start.py @@ -7,7 +7,7 @@ import multiprocessing import logging as log import sys -from podop import run_server +from podop import run_server from pwd import getpwnam from socrate import system, conf From 80a85c27a98a3a2a7ea62cacaa2ad4f2f71f2c32 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 30 Oct 2021 15:30:59 +0200 Subject: [PATCH 38/48] Silent healthchecks in logs --- core/nginx/conf/nginx.conf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index 4db963d3..bc0a51ec 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -13,7 +13,6 @@ http { # Standard HTTP configuration with slight hardening include /etc/nginx/mime.types; default_type application/octet-stream; - access_log /dev/stdout; sendfile on; keepalive_timeout 65; server_tokens off; @@ -38,6 +37,13 @@ http { ~*\.(ico|css|js|gif|jpeg|jpg|png|woff2?|ttf|otf|svg|tiff|eot|webp)$ 97d; } + map $request_uri $loggable { + /health 0; + /auth/email 0; + default 1; + } + access_log /dev/stdout combined if=$loggable; + # compression gzip on; gzip_static on; From 53a0363b9e86a6a0219e712ace280004bcfee6c0 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 30 Oct 2021 15:39:13 +0200 Subject: [PATCH 39/48] Deal with the noisy keepalive messages We don't particularly care about HTTP... and that's what's noisy. --- core/nginx/conf/nginx.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/nginx/conf/nginx.conf b/core/nginx/conf/nginx.conf index bc0a51ec..71cbf9ee 100644 --- a/core/nginx/conf/nginx.conf +++ b/core/nginx/conf/nginx.conf @@ -1,7 +1,7 @@ # Basic configuration user nginx; worker_processes auto; -error_log /dev/stderr info; +error_log /dev/stderr notice; pid /var/run/nginx.pid; load_module "modules/ngx_mail_module.so"; @@ -252,6 +252,7 @@ mail { auth_http http://127.0.0.1:8000/auth/email; proxy_pass_error_message on; resolver {{ RESOLVER }} ipv6=off valid=30s; + error_log /dev/stderr info; {% if TLS and not TLS_ERROR %} include /etc/nginx/tls.conf; From f3c93212c6555432f4ae8e1f33489a41bb9f6e20 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 31 Oct 2021 19:41:12 +0100 Subject: [PATCH 40/48] The Rate-limiter should run after the deny --- core/admin/mailu/internal/nginx.py | 6 ------ core/admin/mailu/internal/views/auth.py | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index 04d6d94e..027db935 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -78,12 +78,6 @@ def handle_authentication(headers): # Authenticated user elif method == "plain": is_valid_user = False - if headers["Auth-Port"] == '25': - return { - "Auth-Status": "AUTH not supported", - "Auth-Error-Code": "502 5.5.1", - "Auth-Wait": 0 - } # According to RFC2616 section 3.7.1 and PEP 3333, HTTP headers should # be ASCII and are generally considered ISO8859-1. However when passing # the password, nginx does not transcode the input UTF string, thus diff --git a/core/admin/mailu/internal/views/auth.py b/core/admin/mailu/internal/views/auth.py index 1afb53b5..344be78b 100644 --- a/core/admin/mailu/internal/views/auth.py +++ b/core/admin/mailu/internal/views/auth.py @@ -11,6 +11,13 @@ def nginx_authentication(): """ Main authentication endpoint for Nginx email server """ client_ip = flask.request.headers["Client-Ip"] + headers = flask.request.headers + if headers["Auth-Port"] == '25' and headers['Auth-Method'] == 'plain': + response = flask.Response() + response.headers['Auth-Status'] = 'AUTH not supported' + response.headers['Auth-Error-Code'] = '502 5.5.1' + utils.limiter.rate_limit_ip(client_ip) + return response if utils.limiter.should_rate_limit_ip(client_ip): status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit') response = flask.Response() From 9d474f32a6f9eade5a312dea7e509e86ac0653d4 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 31 Oct 2021 19:47:16 +0100 Subject: [PATCH 41/48] RELAYNETS is comma separated! --- core/postfix/conf/main.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 3f478ed5..9a609ee3 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -17,7 +17,7 @@ queue_directory = /queue message_size_limit = {{ MESSAGE_SIZE_LIMIT }} # Relayed networks -mynetworks = 127.0.0.1/32 [::1]/128 {{ SUBNET }} {{ RELAYNETS }} +mynetworks = 127.0.0.1/32 [::1]/128 {{ SUBNET }} {{ RELAYNETS.split(",") }} # Empty alias list to override the configuration variable and disable NIS alias_maps = From 2170e07731a423cf30cb8f70b0a171c7bfcfa606 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sun, 31 Oct 2021 19:57:51 +0100 Subject: [PATCH 42/48] Tell rspamd about RELAYNETS --- core/rspamd/conf/options.inc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/rspamd/conf/options.inc diff --git a/core/rspamd/conf/options.inc b/core/rspamd/conf/options.inc new file mode 100644 index 00000000..22bae565 --- /dev/null +++ b/core/rspamd/conf/options.inc @@ -0,0 +1,3 @@ +{% if RELAYNETS %} +local_networks = [{{ RELAYNETS }}]; +{% endif %} From 70b374c46f1a1c2ae673d26a13c9cf894cc8e0c0 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 1 Nov 2021 09:24:26 +0100 Subject: [PATCH 43/48] Document that RELAYNETS is comma separated --- docs/configuration.rst | 13 ++++++------- towncrier/newsfragments/360.bugfix | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 towncrier/newsfragments/360.bugfix diff --git a/docs/configuration.rst b/docs/configuration.rst index f5bd9582..0e2a275a 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -73,14 +73,13 @@ The ``MESSAGE_RATELIMIT`` is the limit of messages a single user can send. This meant to fight outbound spam in case of compromised or malicious account on the server. -The ``RELAYNETS`` are network addresses for which mail is relayed for free with -no authentication required. This should be used with great care. If you want other -Docker services' outbound mail to be relayed, you can set this to ``172.16.0.0/12`` -to include **all** Docker networks. The default is to leave this empty. +The ``RELAYNETS`` (default: unset) is a comma delimited list of network addresses +for which mail is relayed for, with no authentication required. This should be +used with great care. -The ``RELAYHOST`` is an optional address of a mail server relaying all outgoing -mail in following format: ``[HOST]:PORT``. -``RELAYUSER`` and ``RELAYPASSWORD`` can be used when authentication is needed. +The ``RELAYHOST`` is an optional address of a mail server to use as a smarthost for +all outgoing mail in following format: ``[HOST]:PORT``. +``RELAYUSER`` and ``RELAYPASSWORD`` can be used when authentication is required. By default postfix uses "opportunistic TLS" for outbound mail. This can be changed by setting ``OUTBOUND_TLS_LEVEL`` to ``encrypt`` or ``secure``. This setting is diff --git a/towncrier/newsfragments/360.bugfix b/towncrier/newsfragments/360.bugfix new file mode 100644 index 00000000..d433e0e3 --- /dev/null +++ b/towncrier/newsfragments/360.bugfix @@ -0,0 +1 @@ +RELAYNETS should be a comma separated list of networks From c8316cead101c6fd53bbf8ea770879d775068500 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 1 Nov 2021 09:26:54 +0100 Subject: [PATCH 44/48] Improve wording --- docs/configuration.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 0e2a275a..fa574415 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -74,12 +74,13 @@ meant to fight outbound spam in case of compromised or malicious account on the server. The ``RELAYNETS`` (default: unset) is a comma delimited list of network addresses -for which mail is relayed for, with no authentication required. This should be -used with great care. +for which mail is relayed for with no authentication required. This should be +used with great care as misconfigurations may turn your Mailu instance into an +open-relay! -The ``RELAYHOST`` is an optional address of a mail server to use as a smarthost for -all outgoing mail in following format: ``[HOST]:PORT``. -``RELAYUSER`` and ``RELAYPASSWORD`` can be used when authentication is required. +The ``RELAYHOST`` is an optional address to use as a smarthost for all outgoing +mail in following format: ``[HOST]:PORT``. ``RELAYUSER`` and ``RELAYPASSWORD`` +can be used when authentication is required. By default postfix uses "opportunistic TLS" for outbound mail. This can be changed by setting ``OUTBOUND_TLS_LEVEL`` to ``encrypt`` or ``secure``. This setting is From 8dad40f67c8f98a643c867e2580c55d8730271ab Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 1 Nov 2021 12:48:48 +0100 Subject: [PATCH 45/48] doh --- core/postfix/conf/main.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/postfix/conf/main.cf b/core/postfix/conf/main.cf index 9a609ee3..5fffc330 100644 --- a/core/postfix/conf/main.cf +++ b/core/postfix/conf/main.cf @@ -17,7 +17,7 @@ queue_directory = /queue message_size_limit = {{ MESSAGE_SIZE_LIMIT }} # Relayed networks -mynetworks = 127.0.0.1/32 [::1]/128 {{ SUBNET }} {{ RELAYNETS.split(",") }} +mynetworks = 127.0.0.1/32 [::1]/128 {{ SUBNET }} {{ RELAYNETS.split(",") | join(' ') }} # Empty alias list to override the configuration variable and disable NIS alias_maps = From fe58316776138cb5dee9c351c8dfe3f1d7639ad5 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 1 Nov 2021 14:13:58 +0100 Subject: [PATCH 46/48] The DKIM folder isn't required for rspamd --- setup/flavors/compose/docker-compose.yml | 1 - setup/flavors/stack/docker-compose.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml index 08bba13b..2675a2ab 100644 --- a/setup/flavors/compose/docker-compose.yml +++ b/setup/flavors/compose/docker-compose.yml @@ -90,7 +90,6 @@ services: env_file: {{ env }} volumes: - "{{ root }}/filter:/var/lib/rspamd" - - "{{ root }}/dkim:/dkim:ro" - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d:ro" depends_on: - front diff --git a/setup/flavors/stack/docker-compose.yml b/setup/flavors/stack/docker-compose.yml index df1fe7b4..24afa9f3 100644 --- a/setup/flavors/stack/docker-compose.yml +++ b/setup/flavors/stack/docker-compose.yml @@ -74,7 +74,6 @@ services: env_file: {{ env }} volumes: - "{{ root }}/filter:/var/lib/rspamd" - - "{{ root }}/dkim:/dkim:ro" - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d:ro" deploy: replicas: 1 From 74b31dc407432736ddb1d858160b00e401f02198 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Mon, 1 Nov 2021 17:52:12 +0100 Subject: [PATCH 47/48] Ensure that RCVD_NO_TLS_LAST doesn't add spam points --- core/rspamd/conf/settings.conf | 4 ++++ towncrier/newsfragments/1705.enhancement | 1 + 2 files changed, 5 insertions(+) create mode 100644 core/rspamd/conf/settings.conf create mode 100644 towncrier/newsfragments/1705.enhancement diff --git a/core/rspamd/conf/settings.conf b/core/rspamd/conf/settings.conf new file mode 100644 index 00000000..01e9780f --- /dev/null +++ b/core/rspamd/conf/settings.conf @@ -0,0 +1,4 @@ +apply { + # see https://github.com/Mailu/Mailu/issues/1705 + RCVD_NO_TLS_LAST = 0; +} diff --git a/towncrier/newsfragments/1705.enhancement b/towncrier/newsfragments/1705.enhancement new file mode 100644 index 00000000..9a6cb11a --- /dev/null +++ b/towncrier/newsfragments/1705.enhancement @@ -0,0 +1 @@ +Ensure that RCVD_NO_TLS_LAST doesn't add to the spam score (as TLS usage can't be determined) From e8b5f1a185e7c4f58a73a4f2e546692d27fec863 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 2 Nov 2021 12:59:59 +0100 Subject: [PATCH 48/48] round display of range inputs to 2 decimals --- core/admin/assets/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/assets/app.js b/core/admin/assets/app.js index 5df8052c..54602d1f 100644 --- a/core/admin/assets/app.js +++ b/core/admin/assets/app.js @@ -43,7 +43,7 @@ $('document').ready(function() { var infinity = $(this).data('infinity'); var step = $(this).attr('step'); $(this).on('input', function() { - value_element.text((infinity && this.value == 0) ? '∞' : this.value/step); + value_element.text((infinity && this.value == 0) ? '∞' : (this.value/step).toFixed(2)); }).trigger('input'); } });