From 974c351802598a2f1f43fe8c07f169be6dd76d5e Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 12:00:39 +0200 Subject: [PATCH 01/27] Move the admin interface back to a blueprint --- admin/mailu/__init__.py | 5 ++-- admin/mailu/ui/__init__.py | 6 ++++ admin/mailu/{ => ui}/access.py | 3 +- admin/mailu/{ => ui}/forms.py | 0 .../static/adminlte/css/AdminLTE.min.css | 0 .../static/adminlte/css/skin-blue.min.css | 0 .../{ => ui}/static/adminlte/js/app.min.js | 0 admin/mailu/{ => ui}/static/app.css | 0 .../static/bootstrap/css/bootstrap.css.map | 0 .../static/bootstrap/css/bootstrap.min.css | 0 .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../fonts/glyphicons-halflings-regular.woff2 | Bin .../static/bootstrap/js/bootstrap.min.js | 0 .../static/jquery/js/jquery-2.2.2.min.js | 0 .../static/select2/css/select2.min.css | 0 .../{ => ui}/static/select2/js/i18n/ar.js | 0 .../{ => ui}/static/select2/js/i18n/az.js | 0 .../{ => ui}/static/select2/js/i18n/bg.js | 0 .../{ => ui}/static/select2/js/i18n/ca.js | 0 .../{ => ui}/static/select2/js/i18n/cs.js | 0 .../{ => ui}/static/select2/js/i18n/da.js | 0 .../{ => ui}/static/select2/js/i18n/de.js | 0 .../{ => ui}/static/select2/js/i18n/el.js | 0 .../{ => ui}/static/select2/js/i18n/en.js | 0 .../{ => ui}/static/select2/js/i18n/es.js | 0 .../{ => ui}/static/select2/js/i18n/et.js | 0 .../{ => ui}/static/select2/js/i18n/eu.js | 0 .../{ => ui}/static/select2/js/i18n/fa.js | 0 .../{ => ui}/static/select2/js/i18n/fi.js | 0 .../{ => ui}/static/select2/js/i18n/fr.js | 0 .../{ => ui}/static/select2/js/i18n/gl.js | 0 .../{ => ui}/static/select2/js/i18n/he.js | 0 .../{ => ui}/static/select2/js/i18n/hi.js | 0 .../{ => ui}/static/select2/js/i18n/hr.js | 0 .../{ => ui}/static/select2/js/i18n/hu.js | 0 .../{ => ui}/static/select2/js/i18n/id.js | 0 .../{ => ui}/static/select2/js/i18n/is.js | 0 .../{ => ui}/static/select2/js/i18n/it.js | 0 .../{ => ui}/static/select2/js/i18n/ja.js | 0 .../{ => ui}/static/select2/js/i18n/km.js | 0 .../{ => ui}/static/select2/js/i18n/ko.js | 0 .../{ => ui}/static/select2/js/i18n/lt.js | 0 .../{ => ui}/static/select2/js/i18n/lv.js | 0 .../{ => ui}/static/select2/js/i18n/mk.js | 0 .../{ => ui}/static/select2/js/i18n/ms.js | 0 .../{ => ui}/static/select2/js/i18n/nb.js | 0 .../{ => ui}/static/select2/js/i18n/nl.js | 0 .../{ => ui}/static/select2/js/i18n/pl.js | 0 .../{ => ui}/static/select2/js/i18n/pt-BR.js | 0 .../{ => ui}/static/select2/js/i18n/pt.js | 0 .../{ => ui}/static/select2/js/i18n/ro.js | 0 .../{ => ui}/static/select2/js/i18n/ru.js | 0 .../{ => ui}/static/select2/js/i18n/sk.js | 0 .../static/select2/js/i18n/sr-Cyrl.js | 0 .../{ => ui}/static/select2/js/i18n/sr.js | 0 .../{ => ui}/static/select2/js/i18n/sv.js | 0 .../{ => ui}/static/select2/js/i18n/th.js | 0 .../{ => ui}/static/select2/js/i18n/tr.js | 0 .../{ => ui}/static/select2/js/i18n/uk.js | 0 .../{ => ui}/static/select2/js/i18n/vi.js | 0 .../{ => ui}/static/select2/js/i18n/zh-CN.js | 0 .../{ => ui}/static/select2/js/i18n/zh-TW.js | 0 .../static/select2/js/select2.full.min.js | 0 .../{ => ui}/static/select2/js/select2.min.js | 0 .../{ => ui}/templates/admin/create.html | 0 .../mailu/{ => ui}/templates/admin/list.html | 0 .../{ => ui}/templates/alias/create.html | 0 .../mailu/{ => ui}/templates/alias/edit.html | 0 .../mailu/{ => ui}/templates/alias/list.html | 0 .../templates/alternative/create.html | 0 .../{ => ui}/templates/alternative/list.html | 0 .../{ => ui}/templates/announcement.html | 0 admin/mailu/{ => ui}/templates/base.html | 0 admin/mailu/{ => ui}/templates/confirm.html | 0 .../{ => ui}/templates/docker-error.html | 0 .../{ => ui}/templates/domain/create.html | 0 .../{ => ui}/templates/domain/details.html | 0 .../mailu/{ => ui}/templates/domain/edit.html | 0 .../mailu/{ => ui}/templates/domain/list.html | 0 .../{ => ui}/templates/fetch/create.html | 0 .../mailu/{ => ui}/templates/fetch/edit.html | 0 .../mailu/{ => ui}/templates/fetch/list.html | 0 admin/mailu/{ => ui}/templates/form.html | 0 admin/mailu/{ => ui}/templates/helpers.html | 0 admin/mailu/{ => ui}/templates/index.html | 0 admin/mailu/{ => ui}/templates/login.html | 0 admin/mailu/{ => ui}/templates/macros.html | 0 .../{ => ui}/templates/manager/create.html | 0 .../{ => ui}/templates/manager/list.html | 0 .../{ => ui}/templates/relay/create.html | 0 .../mailu/{ => ui}/templates/relay/edit.html | 0 .../mailu/{ => ui}/templates/relay/list.html | 0 admin/mailu/{ => ui}/templates/services.html | 0 admin/mailu/{ => ui}/templates/sidebar.html | 0 .../mailu/{ => ui}/templates/user/create.html | 0 admin/mailu/{ => ui}/templates/user/edit.html | 0 .../{ => ui}/templates/user/forward.html | 0 admin/mailu/{ => ui}/templates/user/list.html | 0 .../{ => ui}/templates/user/password.html | 0 .../mailu/{ => ui}/templates/user/reply.html | 0 .../{ => ui}/templates/user/settings.html | 0 admin/mailu/{ => ui}/templates/working.html | 0 admin/mailu/{ => ui}/views/__init__.py | 0 admin/mailu/{ => ui}/views/admins.py | 9 +++--- admin/mailu/{ => ui}/views/aliases.py | 11 +++---- admin/mailu/{ => ui}/views/alternatives.py | 9 +++--- admin/mailu/{ => ui}/views/base.py | 18 +++++------- admin/mailu/{ => ui}/views/domains.py | 15 +++++----- admin/mailu/{ => ui}/views/fetches.py | 15 +++++----- admin/mailu/{ => ui}/views/managers.py | 9 +++--- admin/mailu/{ => ui}/views/relays.py | 11 +++---- admin/mailu/{ => ui}/views/users.py | 27 +++++++++--------- 115 files changed, 75 insertions(+), 63 deletions(-) create mode 100644 admin/mailu/ui/__init__.py rename admin/mailu/{ => ui}/access.py (98%) rename admin/mailu/{ => ui}/forms.py (100%) rename admin/mailu/{ => ui}/static/adminlte/css/AdminLTE.min.css (100%) rename admin/mailu/{ => ui}/static/adminlte/css/skin-blue.min.css (100%) rename admin/mailu/{ => ui}/static/adminlte/js/app.min.js (100%) rename admin/mailu/{ => ui}/static/app.css (100%) rename admin/mailu/{ => ui}/static/bootstrap/css/bootstrap.css.map (100%) rename admin/mailu/{ => ui}/static/bootstrap/css/bootstrap.min.css (100%) rename admin/mailu/{ => ui}/static/bootstrap/fonts/glyphicons-halflings-regular.eot (100%) rename admin/mailu/{ => ui}/static/bootstrap/fonts/glyphicons-halflings-regular.svg (100%) rename admin/mailu/{ => ui}/static/bootstrap/fonts/glyphicons-halflings-regular.ttf (100%) rename admin/mailu/{ => ui}/static/bootstrap/fonts/glyphicons-halflings-regular.woff (100%) rename admin/mailu/{ => ui}/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 (100%) rename admin/mailu/{ => ui}/static/bootstrap/js/bootstrap.min.js (100%) rename admin/mailu/{ => ui}/static/jquery/js/jquery-2.2.2.min.js (100%) rename admin/mailu/{ => ui}/static/select2/css/select2.min.css (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ar.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/az.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/bg.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ca.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/cs.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/da.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/de.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/el.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/en.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/es.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/et.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/eu.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/fa.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/fi.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/fr.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/gl.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/he.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/hi.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/hr.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/hu.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/id.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/is.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/it.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ja.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/km.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ko.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/lt.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/lv.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/mk.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ms.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/nb.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/nl.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/pl.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/pt-BR.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/pt.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ro.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/ru.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/sk.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/sr-Cyrl.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/sr.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/sv.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/th.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/tr.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/uk.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/vi.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/zh-CN.js (100%) rename admin/mailu/{ => ui}/static/select2/js/i18n/zh-TW.js (100%) rename admin/mailu/{ => ui}/static/select2/js/select2.full.min.js (100%) rename admin/mailu/{ => ui}/static/select2/js/select2.min.js (100%) rename admin/mailu/{ => ui}/templates/admin/create.html (100%) rename admin/mailu/{ => ui}/templates/admin/list.html (100%) rename admin/mailu/{ => ui}/templates/alias/create.html (100%) rename admin/mailu/{ => ui}/templates/alias/edit.html (100%) rename admin/mailu/{ => ui}/templates/alias/list.html (100%) rename admin/mailu/{ => ui}/templates/alternative/create.html (100%) rename admin/mailu/{ => ui}/templates/alternative/list.html (100%) rename admin/mailu/{ => ui}/templates/announcement.html (100%) rename admin/mailu/{ => ui}/templates/base.html (100%) rename admin/mailu/{ => ui}/templates/confirm.html (100%) rename admin/mailu/{ => ui}/templates/docker-error.html (100%) rename admin/mailu/{ => ui}/templates/domain/create.html (100%) rename admin/mailu/{ => ui}/templates/domain/details.html (100%) rename admin/mailu/{ => ui}/templates/domain/edit.html (100%) rename admin/mailu/{ => ui}/templates/domain/list.html (100%) rename admin/mailu/{ => ui}/templates/fetch/create.html (100%) rename admin/mailu/{ => ui}/templates/fetch/edit.html (100%) rename admin/mailu/{ => ui}/templates/fetch/list.html (100%) rename admin/mailu/{ => ui}/templates/form.html (100%) rename admin/mailu/{ => ui}/templates/helpers.html (100%) rename admin/mailu/{ => ui}/templates/index.html (100%) rename admin/mailu/{ => ui}/templates/login.html (100%) rename admin/mailu/{ => ui}/templates/macros.html (100%) rename admin/mailu/{ => ui}/templates/manager/create.html (100%) rename admin/mailu/{ => ui}/templates/manager/list.html (100%) rename admin/mailu/{ => ui}/templates/relay/create.html (100%) rename admin/mailu/{ => ui}/templates/relay/edit.html (100%) rename admin/mailu/{ => ui}/templates/relay/list.html (100%) rename admin/mailu/{ => ui}/templates/services.html (100%) rename admin/mailu/{ => ui}/templates/sidebar.html (100%) rename admin/mailu/{ => ui}/templates/user/create.html (100%) rename admin/mailu/{ => ui}/templates/user/edit.html (100%) rename admin/mailu/{ => ui}/templates/user/forward.html (100%) rename admin/mailu/{ => ui}/templates/user/list.html (100%) rename admin/mailu/{ => ui}/templates/user/password.html (100%) rename admin/mailu/{ => ui}/templates/user/reply.html (100%) rename admin/mailu/{ => ui}/templates/user/settings.html (100%) rename admin/mailu/{ => ui}/templates/working.html (100%) rename admin/mailu/{ => ui}/views/__init__.py (100%) rename admin/mailu/{ => ui}/views/admins.py (86%) rename admin/mailu/{ => ui}/views/aliases.py (87%) rename admin/mailu/{ => ui}/views/alternatives.py (86%) rename admin/mailu/{ => ui}/views/base.py (84%) rename admin/mailu/{ => ui}/views/domains.py (85%) rename admin/mailu/{ => ui}/views/fetches.py (79%) rename admin/mailu/{ => ui}/views/managers.py (87%) rename admin/mailu/{ => ui}/views/relays.py (86%) rename admin/mailu/{ => ui}/views/users.py (86%) diff --git a/admin/mailu/__init__.py b/admin/mailu/__init__.py index aae26a4f..33c0d5e4 100644 --- a/admin/mailu/__init__.py +++ b/admin/mailu/__init__.py @@ -13,7 +13,7 @@ from apscheduler.schedulers import background # Create application -app = flask.Flask(__name__, static_url_path='/admin/app_static') +app = flask.Flask(__name__) default_config = { 'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db', @@ -82,7 +82,8 @@ def inject_user(): return dict(current_user=flask_login.current_user) # Import views -from mailu.views import * +from mailu import ui +app.register_blueprint(ui.ui, url_prefix='/ui') # Create the prefix middleware class PrefixMiddleware(object): diff --git a/admin/mailu/ui/__init__.py b/admin/mailu/ui/__init__.py new file mode 100644 index 00000000..ec3601a1 --- /dev/null +++ b/admin/mailu/ui/__init__.py @@ -0,0 +1,6 @@ +from flask import Blueprint + + +ui = Blueprint('ui', __name__, static_folder='static', template_folder='templates') + +from mailu.ui.views import * diff --git a/admin/mailu/access.py b/admin/mailu/ui/access.py similarity index 98% rename from admin/mailu/access.py rename to admin/mailu/ui/access.py index b4179a53..2b6a3767 100644 --- a/admin/mailu/access.py +++ b/admin/mailu/ui/access.py @@ -1,4 +1,5 @@ -from mailu import db, models, forms +from mailu import db, models +from mailu.ui import forms import flask import flask_login diff --git a/admin/mailu/forms.py b/admin/mailu/ui/forms.py similarity index 100% rename from admin/mailu/forms.py rename to admin/mailu/ui/forms.py diff --git a/admin/mailu/static/adminlte/css/AdminLTE.min.css b/admin/mailu/ui/static/adminlte/css/AdminLTE.min.css similarity index 100% rename from admin/mailu/static/adminlte/css/AdminLTE.min.css rename to admin/mailu/ui/static/adminlte/css/AdminLTE.min.css diff --git a/admin/mailu/static/adminlte/css/skin-blue.min.css b/admin/mailu/ui/static/adminlte/css/skin-blue.min.css similarity index 100% rename from admin/mailu/static/adminlte/css/skin-blue.min.css rename to admin/mailu/ui/static/adminlte/css/skin-blue.min.css diff --git a/admin/mailu/static/adminlte/js/app.min.js b/admin/mailu/ui/static/adminlte/js/app.min.js similarity index 100% rename from admin/mailu/static/adminlte/js/app.min.js rename to admin/mailu/ui/static/adminlte/js/app.min.js diff --git a/admin/mailu/static/app.css b/admin/mailu/ui/static/app.css similarity index 100% rename from admin/mailu/static/app.css rename to admin/mailu/ui/static/app.css diff --git a/admin/mailu/static/bootstrap/css/bootstrap.css.map b/admin/mailu/ui/static/bootstrap/css/bootstrap.css.map similarity index 100% rename from admin/mailu/static/bootstrap/css/bootstrap.css.map rename to admin/mailu/ui/static/bootstrap/css/bootstrap.css.map diff --git a/admin/mailu/static/bootstrap/css/bootstrap.min.css b/admin/mailu/ui/static/bootstrap/css/bootstrap.min.css similarity index 100% rename from admin/mailu/static/bootstrap/css/bootstrap.min.css rename to admin/mailu/ui/static/bootstrap/css/bootstrap.min.css diff --git a/admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.eot b/admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.eot rename to admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.svg b/admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.svg rename to admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.svg diff --git a/admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.ttf b/admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.ttf rename to admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.woff b/admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.woff rename to admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from admin/mailu/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 rename to admin/mailu/ui/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 diff --git a/admin/mailu/static/bootstrap/js/bootstrap.min.js b/admin/mailu/ui/static/bootstrap/js/bootstrap.min.js similarity index 100% rename from admin/mailu/static/bootstrap/js/bootstrap.min.js rename to admin/mailu/ui/static/bootstrap/js/bootstrap.min.js diff --git a/admin/mailu/static/jquery/js/jquery-2.2.2.min.js b/admin/mailu/ui/static/jquery/js/jquery-2.2.2.min.js similarity index 100% rename from admin/mailu/static/jquery/js/jquery-2.2.2.min.js rename to admin/mailu/ui/static/jquery/js/jquery-2.2.2.min.js diff --git a/admin/mailu/static/select2/css/select2.min.css b/admin/mailu/ui/static/select2/css/select2.min.css similarity index 100% rename from admin/mailu/static/select2/css/select2.min.css rename to admin/mailu/ui/static/select2/css/select2.min.css diff --git a/admin/mailu/static/select2/js/i18n/ar.js b/admin/mailu/ui/static/select2/js/i18n/ar.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ar.js rename to admin/mailu/ui/static/select2/js/i18n/ar.js diff --git a/admin/mailu/static/select2/js/i18n/az.js b/admin/mailu/ui/static/select2/js/i18n/az.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/az.js rename to admin/mailu/ui/static/select2/js/i18n/az.js diff --git a/admin/mailu/static/select2/js/i18n/bg.js b/admin/mailu/ui/static/select2/js/i18n/bg.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/bg.js rename to admin/mailu/ui/static/select2/js/i18n/bg.js diff --git a/admin/mailu/static/select2/js/i18n/ca.js b/admin/mailu/ui/static/select2/js/i18n/ca.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ca.js rename to admin/mailu/ui/static/select2/js/i18n/ca.js diff --git a/admin/mailu/static/select2/js/i18n/cs.js b/admin/mailu/ui/static/select2/js/i18n/cs.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/cs.js rename to admin/mailu/ui/static/select2/js/i18n/cs.js diff --git a/admin/mailu/static/select2/js/i18n/da.js b/admin/mailu/ui/static/select2/js/i18n/da.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/da.js rename to admin/mailu/ui/static/select2/js/i18n/da.js diff --git a/admin/mailu/static/select2/js/i18n/de.js b/admin/mailu/ui/static/select2/js/i18n/de.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/de.js rename to admin/mailu/ui/static/select2/js/i18n/de.js diff --git a/admin/mailu/static/select2/js/i18n/el.js b/admin/mailu/ui/static/select2/js/i18n/el.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/el.js rename to admin/mailu/ui/static/select2/js/i18n/el.js diff --git a/admin/mailu/static/select2/js/i18n/en.js b/admin/mailu/ui/static/select2/js/i18n/en.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/en.js rename to admin/mailu/ui/static/select2/js/i18n/en.js diff --git a/admin/mailu/static/select2/js/i18n/es.js b/admin/mailu/ui/static/select2/js/i18n/es.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/es.js rename to admin/mailu/ui/static/select2/js/i18n/es.js diff --git a/admin/mailu/static/select2/js/i18n/et.js b/admin/mailu/ui/static/select2/js/i18n/et.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/et.js rename to admin/mailu/ui/static/select2/js/i18n/et.js diff --git a/admin/mailu/static/select2/js/i18n/eu.js b/admin/mailu/ui/static/select2/js/i18n/eu.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/eu.js rename to admin/mailu/ui/static/select2/js/i18n/eu.js diff --git a/admin/mailu/static/select2/js/i18n/fa.js b/admin/mailu/ui/static/select2/js/i18n/fa.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/fa.js rename to admin/mailu/ui/static/select2/js/i18n/fa.js diff --git a/admin/mailu/static/select2/js/i18n/fi.js b/admin/mailu/ui/static/select2/js/i18n/fi.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/fi.js rename to admin/mailu/ui/static/select2/js/i18n/fi.js diff --git a/admin/mailu/static/select2/js/i18n/fr.js b/admin/mailu/ui/static/select2/js/i18n/fr.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/fr.js rename to admin/mailu/ui/static/select2/js/i18n/fr.js diff --git a/admin/mailu/static/select2/js/i18n/gl.js b/admin/mailu/ui/static/select2/js/i18n/gl.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/gl.js rename to admin/mailu/ui/static/select2/js/i18n/gl.js diff --git a/admin/mailu/static/select2/js/i18n/he.js b/admin/mailu/ui/static/select2/js/i18n/he.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/he.js rename to admin/mailu/ui/static/select2/js/i18n/he.js diff --git a/admin/mailu/static/select2/js/i18n/hi.js b/admin/mailu/ui/static/select2/js/i18n/hi.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/hi.js rename to admin/mailu/ui/static/select2/js/i18n/hi.js diff --git a/admin/mailu/static/select2/js/i18n/hr.js b/admin/mailu/ui/static/select2/js/i18n/hr.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/hr.js rename to admin/mailu/ui/static/select2/js/i18n/hr.js diff --git a/admin/mailu/static/select2/js/i18n/hu.js b/admin/mailu/ui/static/select2/js/i18n/hu.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/hu.js rename to admin/mailu/ui/static/select2/js/i18n/hu.js diff --git a/admin/mailu/static/select2/js/i18n/id.js b/admin/mailu/ui/static/select2/js/i18n/id.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/id.js rename to admin/mailu/ui/static/select2/js/i18n/id.js diff --git a/admin/mailu/static/select2/js/i18n/is.js b/admin/mailu/ui/static/select2/js/i18n/is.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/is.js rename to admin/mailu/ui/static/select2/js/i18n/is.js diff --git a/admin/mailu/static/select2/js/i18n/it.js b/admin/mailu/ui/static/select2/js/i18n/it.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/it.js rename to admin/mailu/ui/static/select2/js/i18n/it.js diff --git a/admin/mailu/static/select2/js/i18n/ja.js b/admin/mailu/ui/static/select2/js/i18n/ja.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ja.js rename to admin/mailu/ui/static/select2/js/i18n/ja.js diff --git a/admin/mailu/static/select2/js/i18n/km.js b/admin/mailu/ui/static/select2/js/i18n/km.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/km.js rename to admin/mailu/ui/static/select2/js/i18n/km.js diff --git a/admin/mailu/static/select2/js/i18n/ko.js b/admin/mailu/ui/static/select2/js/i18n/ko.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ko.js rename to admin/mailu/ui/static/select2/js/i18n/ko.js diff --git a/admin/mailu/static/select2/js/i18n/lt.js b/admin/mailu/ui/static/select2/js/i18n/lt.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/lt.js rename to admin/mailu/ui/static/select2/js/i18n/lt.js diff --git a/admin/mailu/static/select2/js/i18n/lv.js b/admin/mailu/ui/static/select2/js/i18n/lv.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/lv.js rename to admin/mailu/ui/static/select2/js/i18n/lv.js diff --git a/admin/mailu/static/select2/js/i18n/mk.js b/admin/mailu/ui/static/select2/js/i18n/mk.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/mk.js rename to admin/mailu/ui/static/select2/js/i18n/mk.js diff --git a/admin/mailu/static/select2/js/i18n/ms.js b/admin/mailu/ui/static/select2/js/i18n/ms.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ms.js rename to admin/mailu/ui/static/select2/js/i18n/ms.js diff --git a/admin/mailu/static/select2/js/i18n/nb.js b/admin/mailu/ui/static/select2/js/i18n/nb.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/nb.js rename to admin/mailu/ui/static/select2/js/i18n/nb.js diff --git a/admin/mailu/static/select2/js/i18n/nl.js b/admin/mailu/ui/static/select2/js/i18n/nl.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/nl.js rename to admin/mailu/ui/static/select2/js/i18n/nl.js diff --git a/admin/mailu/static/select2/js/i18n/pl.js b/admin/mailu/ui/static/select2/js/i18n/pl.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/pl.js rename to admin/mailu/ui/static/select2/js/i18n/pl.js diff --git a/admin/mailu/static/select2/js/i18n/pt-BR.js b/admin/mailu/ui/static/select2/js/i18n/pt-BR.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/pt-BR.js rename to admin/mailu/ui/static/select2/js/i18n/pt-BR.js diff --git a/admin/mailu/static/select2/js/i18n/pt.js b/admin/mailu/ui/static/select2/js/i18n/pt.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/pt.js rename to admin/mailu/ui/static/select2/js/i18n/pt.js diff --git a/admin/mailu/static/select2/js/i18n/ro.js b/admin/mailu/ui/static/select2/js/i18n/ro.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ro.js rename to admin/mailu/ui/static/select2/js/i18n/ro.js diff --git a/admin/mailu/static/select2/js/i18n/ru.js b/admin/mailu/ui/static/select2/js/i18n/ru.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/ru.js rename to admin/mailu/ui/static/select2/js/i18n/ru.js diff --git a/admin/mailu/static/select2/js/i18n/sk.js b/admin/mailu/ui/static/select2/js/i18n/sk.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/sk.js rename to admin/mailu/ui/static/select2/js/i18n/sk.js diff --git a/admin/mailu/static/select2/js/i18n/sr-Cyrl.js b/admin/mailu/ui/static/select2/js/i18n/sr-Cyrl.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/sr-Cyrl.js rename to admin/mailu/ui/static/select2/js/i18n/sr-Cyrl.js diff --git a/admin/mailu/static/select2/js/i18n/sr.js b/admin/mailu/ui/static/select2/js/i18n/sr.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/sr.js rename to admin/mailu/ui/static/select2/js/i18n/sr.js diff --git a/admin/mailu/static/select2/js/i18n/sv.js b/admin/mailu/ui/static/select2/js/i18n/sv.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/sv.js rename to admin/mailu/ui/static/select2/js/i18n/sv.js diff --git a/admin/mailu/static/select2/js/i18n/th.js b/admin/mailu/ui/static/select2/js/i18n/th.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/th.js rename to admin/mailu/ui/static/select2/js/i18n/th.js diff --git a/admin/mailu/static/select2/js/i18n/tr.js b/admin/mailu/ui/static/select2/js/i18n/tr.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/tr.js rename to admin/mailu/ui/static/select2/js/i18n/tr.js diff --git a/admin/mailu/static/select2/js/i18n/uk.js b/admin/mailu/ui/static/select2/js/i18n/uk.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/uk.js rename to admin/mailu/ui/static/select2/js/i18n/uk.js diff --git a/admin/mailu/static/select2/js/i18n/vi.js b/admin/mailu/ui/static/select2/js/i18n/vi.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/vi.js rename to admin/mailu/ui/static/select2/js/i18n/vi.js diff --git a/admin/mailu/static/select2/js/i18n/zh-CN.js b/admin/mailu/ui/static/select2/js/i18n/zh-CN.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/zh-CN.js rename to admin/mailu/ui/static/select2/js/i18n/zh-CN.js diff --git a/admin/mailu/static/select2/js/i18n/zh-TW.js b/admin/mailu/ui/static/select2/js/i18n/zh-TW.js similarity index 100% rename from admin/mailu/static/select2/js/i18n/zh-TW.js rename to admin/mailu/ui/static/select2/js/i18n/zh-TW.js diff --git a/admin/mailu/static/select2/js/select2.full.min.js b/admin/mailu/ui/static/select2/js/select2.full.min.js similarity index 100% rename from admin/mailu/static/select2/js/select2.full.min.js rename to admin/mailu/ui/static/select2/js/select2.full.min.js diff --git a/admin/mailu/static/select2/js/select2.min.js b/admin/mailu/ui/static/select2/js/select2.min.js similarity index 100% rename from admin/mailu/static/select2/js/select2.min.js rename to admin/mailu/ui/static/select2/js/select2.min.js diff --git a/admin/mailu/templates/admin/create.html b/admin/mailu/ui/templates/admin/create.html similarity index 100% rename from admin/mailu/templates/admin/create.html rename to admin/mailu/ui/templates/admin/create.html diff --git a/admin/mailu/templates/admin/list.html b/admin/mailu/ui/templates/admin/list.html similarity index 100% rename from admin/mailu/templates/admin/list.html rename to admin/mailu/ui/templates/admin/list.html diff --git a/admin/mailu/templates/alias/create.html b/admin/mailu/ui/templates/alias/create.html similarity index 100% rename from admin/mailu/templates/alias/create.html rename to admin/mailu/ui/templates/alias/create.html diff --git a/admin/mailu/templates/alias/edit.html b/admin/mailu/ui/templates/alias/edit.html similarity index 100% rename from admin/mailu/templates/alias/edit.html rename to admin/mailu/ui/templates/alias/edit.html diff --git a/admin/mailu/templates/alias/list.html b/admin/mailu/ui/templates/alias/list.html similarity index 100% rename from admin/mailu/templates/alias/list.html rename to admin/mailu/ui/templates/alias/list.html diff --git a/admin/mailu/templates/alternative/create.html b/admin/mailu/ui/templates/alternative/create.html similarity index 100% rename from admin/mailu/templates/alternative/create.html rename to admin/mailu/ui/templates/alternative/create.html diff --git a/admin/mailu/templates/alternative/list.html b/admin/mailu/ui/templates/alternative/list.html similarity index 100% rename from admin/mailu/templates/alternative/list.html rename to admin/mailu/ui/templates/alternative/list.html diff --git a/admin/mailu/templates/announcement.html b/admin/mailu/ui/templates/announcement.html similarity index 100% rename from admin/mailu/templates/announcement.html rename to admin/mailu/ui/templates/announcement.html diff --git a/admin/mailu/templates/base.html b/admin/mailu/ui/templates/base.html similarity index 100% rename from admin/mailu/templates/base.html rename to admin/mailu/ui/templates/base.html diff --git a/admin/mailu/templates/confirm.html b/admin/mailu/ui/templates/confirm.html similarity index 100% rename from admin/mailu/templates/confirm.html rename to admin/mailu/ui/templates/confirm.html diff --git a/admin/mailu/templates/docker-error.html b/admin/mailu/ui/templates/docker-error.html similarity index 100% rename from admin/mailu/templates/docker-error.html rename to admin/mailu/ui/templates/docker-error.html diff --git a/admin/mailu/templates/domain/create.html b/admin/mailu/ui/templates/domain/create.html similarity index 100% rename from admin/mailu/templates/domain/create.html rename to admin/mailu/ui/templates/domain/create.html diff --git a/admin/mailu/templates/domain/details.html b/admin/mailu/ui/templates/domain/details.html similarity index 100% rename from admin/mailu/templates/domain/details.html rename to admin/mailu/ui/templates/domain/details.html diff --git a/admin/mailu/templates/domain/edit.html b/admin/mailu/ui/templates/domain/edit.html similarity index 100% rename from admin/mailu/templates/domain/edit.html rename to admin/mailu/ui/templates/domain/edit.html diff --git a/admin/mailu/templates/domain/list.html b/admin/mailu/ui/templates/domain/list.html similarity index 100% rename from admin/mailu/templates/domain/list.html rename to admin/mailu/ui/templates/domain/list.html diff --git a/admin/mailu/templates/fetch/create.html b/admin/mailu/ui/templates/fetch/create.html similarity index 100% rename from admin/mailu/templates/fetch/create.html rename to admin/mailu/ui/templates/fetch/create.html diff --git a/admin/mailu/templates/fetch/edit.html b/admin/mailu/ui/templates/fetch/edit.html similarity index 100% rename from admin/mailu/templates/fetch/edit.html rename to admin/mailu/ui/templates/fetch/edit.html diff --git a/admin/mailu/templates/fetch/list.html b/admin/mailu/ui/templates/fetch/list.html similarity index 100% rename from admin/mailu/templates/fetch/list.html rename to admin/mailu/ui/templates/fetch/list.html diff --git a/admin/mailu/templates/form.html b/admin/mailu/ui/templates/form.html similarity index 100% rename from admin/mailu/templates/form.html rename to admin/mailu/ui/templates/form.html diff --git a/admin/mailu/templates/helpers.html b/admin/mailu/ui/templates/helpers.html similarity index 100% rename from admin/mailu/templates/helpers.html rename to admin/mailu/ui/templates/helpers.html diff --git a/admin/mailu/templates/index.html b/admin/mailu/ui/templates/index.html similarity index 100% rename from admin/mailu/templates/index.html rename to admin/mailu/ui/templates/index.html diff --git a/admin/mailu/templates/login.html b/admin/mailu/ui/templates/login.html similarity index 100% rename from admin/mailu/templates/login.html rename to admin/mailu/ui/templates/login.html diff --git a/admin/mailu/templates/macros.html b/admin/mailu/ui/templates/macros.html similarity index 100% rename from admin/mailu/templates/macros.html rename to admin/mailu/ui/templates/macros.html diff --git a/admin/mailu/templates/manager/create.html b/admin/mailu/ui/templates/manager/create.html similarity index 100% rename from admin/mailu/templates/manager/create.html rename to admin/mailu/ui/templates/manager/create.html diff --git a/admin/mailu/templates/manager/list.html b/admin/mailu/ui/templates/manager/list.html similarity index 100% rename from admin/mailu/templates/manager/list.html rename to admin/mailu/ui/templates/manager/list.html diff --git a/admin/mailu/templates/relay/create.html b/admin/mailu/ui/templates/relay/create.html similarity index 100% rename from admin/mailu/templates/relay/create.html rename to admin/mailu/ui/templates/relay/create.html diff --git a/admin/mailu/templates/relay/edit.html b/admin/mailu/ui/templates/relay/edit.html similarity index 100% rename from admin/mailu/templates/relay/edit.html rename to admin/mailu/ui/templates/relay/edit.html diff --git a/admin/mailu/templates/relay/list.html b/admin/mailu/ui/templates/relay/list.html similarity index 100% rename from admin/mailu/templates/relay/list.html rename to admin/mailu/ui/templates/relay/list.html diff --git a/admin/mailu/templates/services.html b/admin/mailu/ui/templates/services.html similarity index 100% rename from admin/mailu/templates/services.html rename to admin/mailu/ui/templates/services.html diff --git a/admin/mailu/templates/sidebar.html b/admin/mailu/ui/templates/sidebar.html similarity index 100% rename from admin/mailu/templates/sidebar.html rename to admin/mailu/ui/templates/sidebar.html diff --git a/admin/mailu/templates/user/create.html b/admin/mailu/ui/templates/user/create.html similarity index 100% rename from admin/mailu/templates/user/create.html rename to admin/mailu/ui/templates/user/create.html diff --git a/admin/mailu/templates/user/edit.html b/admin/mailu/ui/templates/user/edit.html similarity index 100% rename from admin/mailu/templates/user/edit.html rename to admin/mailu/ui/templates/user/edit.html diff --git a/admin/mailu/templates/user/forward.html b/admin/mailu/ui/templates/user/forward.html similarity index 100% rename from admin/mailu/templates/user/forward.html rename to admin/mailu/ui/templates/user/forward.html diff --git a/admin/mailu/templates/user/list.html b/admin/mailu/ui/templates/user/list.html similarity index 100% rename from admin/mailu/templates/user/list.html rename to admin/mailu/ui/templates/user/list.html diff --git a/admin/mailu/templates/user/password.html b/admin/mailu/ui/templates/user/password.html similarity index 100% rename from admin/mailu/templates/user/password.html rename to admin/mailu/ui/templates/user/password.html diff --git a/admin/mailu/templates/user/reply.html b/admin/mailu/ui/templates/user/reply.html similarity index 100% rename from admin/mailu/templates/user/reply.html rename to admin/mailu/ui/templates/user/reply.html diff --git a/admin/mailu/templates/user/settings.html b/admin/mailu/ui/templates/user/settings.html similarity index 100% rename from admin/mailu/templates/user/settings.html rename to admin/mailu/ui/templates/user/settings.html diff --git a/admin/mailu/templates/working.html b/admin/mailu/ui/templates/working.html similarity index 100% rename from admin/mailu/templates/working.html rename to admin/mailu/ui/templates/working.html diff --git a/admin/mailu/views/__init__.py b/admin/mailu/ui/views/__init__.py similarity index 100% rename from admin/mailu/views/__init__.py rename to admin/mailu/ui/views/__init__.py diff --git a/admin/mailu/views/admins.py b/admin/mailu/ui/views/admins.py similarity index 86% rename from admin/mailu/views/admins.py rename to admin/mailu/ui/views/admins.py index bbefaa1a..e9771ec6 100644 --- a/admin/mailu/views/admins.py +++ b/admin/mailu/ui/views/admins.py @@ -1,17 +1,18 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, forms, access import flask import flask_login -@app.route('/admin/list', methods=['GET']) +@ui.route('/admin/list', methods=['GET']) @access.global_admin def admin_list(): admins = models.User.query.filter_by(global_admin=True) return flask.render_template('admin/list.html', admins=admins) -@app.route('/admin/create', methods=['GET', 'POST']) +@ui.route('/admin/create', methods=['GET', 'POST']) @access.global_admin def admin_create(): form = forms.AdminForm() @@ -32,7 +33,7 @@ def admin_create(): return flask.render_template('admin/create.html', form=form) -@app.route('/admin/delete/', methods=['GET', 'POST']) +@ui.route('/admin/delete/', methods=['GET', 'POST']) @access.global_admin @access.confirmation_required("delete admin {admin}") def admin_delete(admin): diff --git a/admin/mailu/views/aliases.py b/admin/mailu/ui/views/aliases.py similarity index 87% rename from admin/mailu/views/aliases.py rename to admin/mailu/ui/views/aliases.py index c61e5715..7fc03266 100644 --- a/admin/mailu/views/aliases.py +++ b/admin/mailu/ui/views/aliases.py @@ -1,17 +1,18 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, forms, access import flask import wtforms_components -@app.route('/alias/list/', methods=['GET']) +@ui.route('/alias/list/', methods=['GET']) @access.domain_admin(models.Domain, 'domain_name') def alias_list(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) return flask.render_template('alias/list.html', domain=domain) -@app.route('/alias/create/', methods=['GET', 'POST']) +@ui.route('/alias/create/', methods=['GET', 'POST']) @access.domain_admin(models.Domain, 'domain_name') def alias_create(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) @@ -35,7 +36,7 @@ def alias_create(domain_name): domain=domain, form=form) -@app.route('/alias/edit/', methods=['GET', 'POST']) +@ui.route('/alias/edit/', methods=['GET', 'POST']) @access.domain_admin(models.Alias, 'alias') def alias_edit(alias): alias = models.Alias.query.get(alias) or flask.abort(404) @@ -52,7 +53,7 @@ def alias_edit(alias): form=form, alias=alias, domain=alias.domain) -@app.route('/alias/delete/', methods=['GET', 'POST']) +@ui.route('/alias/delete/', methods=['GET', 'POST']) @access.domain_admin(models.Alias, 'alias') @access.confirmation_required("delete {alias}") def alias_delete(alias): diff --git a/admin/mailu/views/alternatives.py b/admin/mailu/ui/views/alternatives.py similarity index 86% rename from admin/mailu/views/alternatives.py rename to admin/mailu/ui/views/alternatives.py index aa13bd3a..79acb074 100644 --- a/admin/mailu/views/alternatives.py +++ b/admin/mailu/ui/views/alternatives.py @@ -1,17 +1,18 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, forms, access import flask import wtforms_components -@app.route('/alternative/list/', methods=['GET']) +@ui.route('/alternative/list/', methods=['GET']) @access.global_admin def alternative_list(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) return flask.render_template('alternative/list.html', domain=domain) -@app.route('/alternative/create/', methods=['GET', 'POST']) +@ui.route('/alternative/create/', methods=['GET', 'POST']) @access.global_admin def alternative_create(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) @@ -34,7 +35,7 @@ def alternative_create(domain_name): domain=domain, form=form) -@app.route('/alternative/delete/', methods=['GET', 'POST']) +@ui.route('/alternative/delete/', methods=['GET', 'POST']) @access.global_admin @access.confirmation_required("delete {alternative}") def alternative_delete(alternative): diff --git a/admin/mailu/views/base.py b/admin/mailu/ui/views/base.py similarity index 84% rename from admin/mailu/views/base.py rename to admin/mailu/ui/views/base.py index 850c06ff..eae240e1 100644 --- a/admin/mailu/views/base.py +++ b/admin/mailu/ui/views/base.py @@ -1,4 +1,5 @@ -from mailu import dockercli, app, db, models, forms, access +from mailu import dockercli, app, db, models +from mailu.ui import ui, forms, access import flask import flask_login @@ -8,18 +9,13 @@ from email.mime import text from urllib import parse -@app.route('/home', methods=["GET"]) -def home(): - return flask.redirect('/webmail/') - - -@app.route('/', methods=["GET"]) +@ui.route('/', methods=["GET"]) @access.authenticated def index(): return flask.redirect(flask.url_for('.user_settings')) -@app.route('/login', methods=['GET', 'POST']) +@ui.route('/login', methods=['GET', 'POST']) def login(): form = forms.LoginForm() if form.validate_on_submit(): @@ -34,14 +30,14 @@ def login(): return flask.render_template('login.html', form=form) -@app.route('/logout', methods=['GET']) +@ui.route('/logout', methods=['GET']) @access.authenticated def logout(): flask_login.logout_user() return flask.redirect(flask.url_for('.index')) -@app.route('/services', methods=['GET']) +@ui.route('/services', methods=['GET']) @access.global_admin def services(): try: @@ -51,7 +47,7 @@ def services(): return flask.render_template('services.html', containers=containers) -@app.route('/announcement', methods=['GET', 'POST']) +@ui.route('/announcement', methods=['GET', 'POST']) @access.global_admin def announcement(): from_address = '{}@{}'.format( diff --git a/admin/mailu/views/domains.py b/admin/mailu/ui/views/domains.py similarity index 85% rename from admin/mailu/views/domains.py rename to admin/mailu/ui/views/domains.py index f2542d4d..f1145e83 100644 --- a/admin/mailu/views/domains.py +++ b/admin/mailu/ui/views/domains.py @@ -1,16 +1,17 @@ -from mailu import app, db, models, forms, access +from mailu import app, db, models +from mailu.ui import ui, forms, access import flask import wtforms_components -@app.route('/domain', methods=['GET']) +@ui.route('/domain', methods=['GET']) @access.authenticated def domain_list(): return flask.render_template('domain/list.html') -@app.route('/domain/create', methods=['GET', 'POST']) +@ui.route('/domain/create', methods=['GET', 'POST']) @access.global_admin def domain_create(): form = forms.DomainForm() @@ -30,7 +31,7 @@ def domain_create(): return flask.render_template('domain/create.html', form=form) -@app.route('/domain/edit/', methods=['GET', 'POST']) +@ui.route('/domain/edit/', methods=['GET', 'POST']) @access.global_admin def domain_edit(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) @@ -46,7 +47,7 @@ def domain_edit(domain_name): domain=domain) -@app.route('/domain/delete/', methods=['GET', 'POST']) +@ui.route('/domain/delete/', methods=['GET', 'POST']) @access.global_admin @access.confirmation_required("delete {domain_name}") def domain_delete(domain_name): @@ -57,7 +58,7 @@ def domain_delete(domain_name): return flask.redirect(flask.url_for('.domain_list')) -@app.route('/domain/details/', methods=['GET']) +@ui.route('/domain/details/', methods=['GET']) @access.domain_admin(models.Domain, 'domain_name') def domain_details(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) @@ -65,7 +66,7 @@ def domain_details(domain_name): config=app.config) -@app.route('/domain/genkeys/', methods=['GET', 'POST']) +@ui.route('/domain/genkeys/', methods=['GET', 'POST']) @access.domain_admin(models.Domain, 'domain_name') @access.confirmation_required("regenerate keys for {domain_name}") def domain_genkeys(domain_name): diff --git a/admin/mailu/views/fetches.py b/admin/mailu/ui/views/fetches.py similarity index 79% rename from admin/mailu/views/fetches.py rename to admin/mailu/ui/views/fetches.py index e3be2bdd..b4ddb824 100644 --- a/admin/mailu/views/fetches.py +++ b/admin/mailu/ui/views/fetches.py @@ -1,11 +1,12 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, forms, access import flask import flask_login -@app.route('/fetch/list', methods=['GET', 'POST'], defaults={'user_email': None}) -@app.route('/fetch/list/', methods=['GET']) +@ui.route('/fetch/list', methods=['GET', 'POST'], defaults={'user_email': None}) +@ui.route('/fetch/list/', methods=['GET']) @access.owner(models.User, 'user_email') def fetch_list(user_email): user_email = user_email or flask_login.current_user.email @@ -13,8 +14,8 @@ def fetch_list(user_email): return flask.render_template('fetch/list.html', user=user) -@app.route('/fetch/create', methods=['GET', 'POST'], defaults={'user_email': None}) -@app.route('/fetch/create/', methods=['GET', 'POST']) +@ui.route('/fetch/create', methods=['GET', 'POST'], defaults={'user_email': None}) +@ui.route('/fetch/create/', methods=['GET', 'POST']) @access.owner(models.User, 'user_email') def fetch_create(user_email): user_email = user_email or flask_login.current_user.email @@ -31,7 +32,7 @@ def fetch_create(user_email): return flask.render_template('fetch/create.html', form=form) -@app.route('/fetch/edit/', methods=['GET', 'POST']) +@ui.route('/fetch/edit/', methods=['GET', 'POST']) @access.owner(models.Fetch, 'fetch_id') def fetch_edit(fetch_id): fetch = models.Fetch.query.get(fetch_id) or flask.abort(404) @@ -46,7 +47,7 @@ def fetch_edit(fetch_id): form=form, fetch=fetch) -@app.route('/fetch/delete/', methods=['GET', 'POST']) +@ui.route('/fetch/delete/', methods=['GET', 'POST']) @access.confirmation_required("delete a fetched account") @access.owner(models.Fetch, 'fetch_id') def fetch_delete(fetch_id): diff --git a/admin/mailu/views/managers.py b/admin/mailu/ui/views/managers.py similarity index 87% rename from admin/mailu/views/managers.py rename to admin/mailu/ui/views/managers.py index 731ee1c7..15ebcd50 100644 --- a/admin/mailu/views/managers.py +++ b/admin/mailu/ui/views/managers.py @@ -1,17 +1,18 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, forms, access import flask import flask_login -@app.route('/manager/list/', methods=['GET']) +@ui.route('/manager/list/', methods=['GET']) @access.domain_admin(models.Domain, 'domain_name') def manager_list(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) return flask.render_template('manager/list.html', domain=domain) -@app.route('/manager/create/', methods=['GET', 'POST']) +@ui.route('/manager/create/', methods=['GET', 'POST']) @access.domain_admin(models.Domain, 'domain_name') def manager_create(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) @@ -37,7 +38,7 @@ def manager_create(domain_name): domain=domain, form=form) -@app.route('/manager/delete//', methods=['GET', 'POST']) +@ui.route('/manager/delete//', methods=['GET', 'POST']) @access.confirmation_required("remove manager {user_email}") @access.domain_admin(models.Domain, 'domain_name') def manager_delete(domain_name, user_email): diff --git a/admin/mailu/views/relays.py b/admin/mailu/ui/views/relays.py similarity index 86% rename from admin/mailu/views/relays.py rename to admin/mailu/ui/views/relays.py index 599f2514..a0ddc614 100644 --- a/admin/mailu/views/relays.py +++ b/admin/mailu/ui/views/relays.py @@ -1,17 +1,18 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, forms, access import flask import wtforms_components -@app.route('/relay', methods=['GET']) +@ui.route('/relay', methods=['GET']) @access.global_admin def relay_list(): relays = models.Relay.query.all() return flask.render_template('relay/list.html', relays=relays) -@app.route('/relay/create', methods=['GET', 'POST']) +@ui.route('/relay/create', methods=['GET', 'POST']) @access.global_admin def relay_create(): form = forms.RelayForm() @@ -31,7 +32,7 @@ def relay_create(): return flask.render_template('relay/create.html', form=form) -@app.route('/relay/edit/', methods=['GET', 'POST']) +@ui.route('/relay/edit/', methods=['GET', 'POST']) @access.global_admin def relay_edit(relay_name): relay = models.Relay.query.get(relay_name) or flask.abort(404) @@ -47,7 +48,7 @@ def relay_edit(relay_name): relay=relay) -@app.route('/relay/delete/', methods=['GET', 'POST']) +@ui.route('/relay/delete/', methods=['GET', 'POST']) @access.global_admin @access.confirmation_required("delete {relay_name}") def relay_delete(relay_name): diff --git a/admin/mailu/views/users.py b/admin/mailu/ui/views/users.py similarity index 86% rename from admin/mailu/views/users.py rename to admin/mailu/ui/views/users.py index 1e6c533a..dd787bc9 100644 --- a/admin/mailu/views/users.py +++ b/admin/mailu/ui/views/users.py @@ -1,4 +1,5 @@ -from mailu import app, db, models, forms, access +from mailu import db, models +from mailu.ui import ui, access, forms import flask import flask_login @@ -6,14 +7,14 @@ import wtforms import wtforms_components -@app.route('/user/list/', methods=['GET']) +@ui.route('/user/list/', methods=['GET']) @access.domain_admin(models.Domain, 'domain_name') def user_list(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) return flask.render_template('user/list.html', domain=domain) -@app.route('/user/create/', methods=['GET', 'POST']) +@ui.route('/user/create/', methods=['GET', 'POST']) @access.domain_admin(models.Domain, 'domain_name') def user_create(domain_name): domain = models.Domain.query.get(domain_name) or flask.abort(404) @@ -41,7 +42,7 @@ def user_create(domain_name): domain=domain, form=form) -@app.route('/user/edit/', methods=['GET', 'POST']) +@ui.route('/user/edit/', methods=['GET', 'POST']) @access.domain_admin(models.User, 'user_email') def user_edit(user_email): user = models.User.query.get(user_email) or flask.abort(404) @@ -69,7 +70,7 @@ def user_edit(user_email): domain=user.domain, max_quota_bytes=max_quota_bytes) -@app.route('/user/delete/', methods=['GET', 'POST']) +@ui.route('/user/delete/', methods=['GET', 'POST']) @access.domain_admin(models.User, 'user_email') @access.confirmation_required("delete {user_email}") def user_delete(user_email): @@ -82,8 +83,8 @@ def user_delete(user_email): flask.url_for('.user_list', domain_name=domain.name)) -@app.route('/user/settings', methods=['GET', 'POST'], defaults={'user_email': None}) -@app.route('/user/usersettings/', methods=['GET', 'POST']) +@ui.route('/user/settings', methods=['GET', 'POST'], defaults={'user_email': None}) +@ui.route('/user/usersettings/', methods=['GET', 'POST']) @access.owner(models.User, 'user_email') def user_settings(user_email): user_email_or_current = user_email or flask_login.current_user.email @@ -99,8 +100,8 @@ def user_settings(user_email): return flask.render_template('user/settings.html', form=form, user=user) -@app.route('/user/password', methods=['GET', 'POST'], defaults={'user_email': None}) -@app.route('/user/password/', methods=['GET', 'POST']) +@ui.route('/user/password', methods=['GET', 'POST'], defaults={'user_email': None}) +@ui.route('/user/password/', methods=['GET', 'POST']) @access.owner(models.User, 'user_email') def user_password(user_email): user_email_or_current = user_email or flask_login.current_user.email @@ -119,8 +120,8 @@ def user_password(user_email): return flask.render_template('user/password.html', form=form, user=user) -@app.route('/user/forward', methods=['GET', 'POST'], defaults={'user_email': None}) -@app.route('/user/forward/', methods=['GET', 'POST']) +@ui.route('/user/forward', methods=['GET', 'POST'], defaults={'user_email': None}) +@ui.route('/user/forward/', methods=['GET', 'POST']) @access.owner(models.User, 'user_email') def user_forward(user_email): user_email_or_current = user_email or flask_login.current_user.email @@ -136,8 +137,8 @@ def user_forward(user_email): return flask.render_template('user/forward.html', form=form, user=user) -@app.route('/user/reply', methods=['GET', 'POST'], defaults={'user_email': None}) -@app.route('/user/reply/', methods=['GET', 'POST']) +@ui.route('/user/reply', methods=['GET', 'POST'], defaults={'user_email': None}) +@ui.route('/user/reply/', methods=['GET', 'POST']) @access.owner(models.User, 'user_email') def user_reply(user_email): user_email_or_current = user_email or flask_login.current_user.email From 97505d13672fb2bf90b03ae81ccb90d2033dab50 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 12:02:26 +0200 Subject: [PATCH 02/27] Remove TLS tasks from the admin container --- admin/mailu/__init__.py | 8 ----- admin/mailu/tlstasks.py | 68 ----------------------------------------- 2 files changed, 76 deletions(-) delete mode 100644 admin/mailu/tlstasks.py diff --git a/admin/mailu/__init__.py b/admin/mailu/__init__.py index 33c0d5e4..947a72f9 100644 --- a/admin/mailu/__init__.py +++ b/admin/mailu/__init__.py @@ -50,14 +50,6 @@ migrate = flask_migrate.Migrate(app, db) manager = flask_script.Manager(app) manager.add_command('db', flask_migrate.MigrateCommand) -# Task scheduling -scheduler = background.BackgroundScheduler({ - 'apscheduler.timezone': 'UTC' -}) -if not app.debug or os.environ.get('WERKZEUG_RUN_MAIN') == 'true': - scheduler.start() - from mailu import tlstasks - # Babel configuration babel = flask_babel.Babel(app) translations = list(map(str, babel.list_translations())) diff --git a/admin/mailu/tlstasks.py b/admin/mailu/tlstasks.py deleted file mode 100644 index 0a0c9f2a..00000000 --- a/admin/mailu/tlstasks.py +++ /dev/null @@ -1,68 +0,0 @@ -from mailu import app, scheduler, dockercli - -import urllib3 -import json -import os -import base64 -import subprocess - - -def install_certs(domain): - """ Extract certificates from the given domain and install them - to the certificate path. - """ - path = app.config["CERTS_PATH"] - acme_path = os.path.join(path, "acme.json") - key_path = os.path.join(path, "key.pem") - cert_path = os.path.join(path, "cert.pem") - if not os.path.exists(acme_path): - print("Could not find traefik acme configuration") - return - with open(acme_path, "r") as handler: - data = json.loads(handler.read()) - for item in data["DomainsCertificate"]["Certs"]: - if domain == item["Domains"]["Main"]: - cert = base64.b64decode(item["Certificate"]["Certificate"]) - key = base64.b64decode(item["Certificate"]["PrivateKey"]) - break - else: - print("Could not find the proper certificate from traefik") - return - if os.path.exists(cert_path): - with open(cert_path, "rb") as handler: - if handler.read() == cert: - return - print("Installing the new certificate from traefik") - with open(cert_path, "wb") as handler: - handler.write(cert) - with open(key_path, "wb") as handler: - handler.write(key) - - -def restart_services(): - print("Reloading services using TLS") - dockercli.reload("http", "smtp", "imap") - - -@scheduler.scheduled_job('date') -def create_dhparam(): - path = app.config["CERTS_PATH"] - dhparam_path = os.path.join(path, "dhparam.pem") - if not os.path.exists(dhparam_path): - print("Creating DH params") - subprocess.call(["openssl", "dhparam", "-out", dhparam_path, "2048"]) - restart_services() - - -@scheduler.scheduled_job('date') -@scheduler.scheduled_job('cron', day='*/4', hour=0, minute=0) -def refresh_certs(): - if not app.config["TLS_FLAVOR"] == "letsencrypt": - return - if not app.config["FRONTEND"] == "traefik": - print("Letsencrypt certificates are compatible with traefik only") - return - print("Requesting traefik to make sure the certificate is fresh") - hostname = app.config["HOSTNAME"] - urllib3.PoolManager().request("GET", "https://{}".format(hostname)) - install_certs(hostname) From 870c7dbe5c3b4e34864816cf54d6e7692bda9a59 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 12:04:52 +0200 Subject: [PATCH 03/27] Remove unused requirements from the admin app --- admin/requirements-prod.txt | 1 - admin/requirements.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/admin/requirements-prod.txt b/admin/requirements-prod.txt index 43c10f0b..d5ec8472 100644 --- a/admin/requirements-prod.txt +++ b/admin/requirements-prod.txt @@ -1,5 +1,4 @@ alembic==0.9.5 -APScheduler==3.3.1 asn1crypto==0.22.0 Babel==2.5.1 certifi==2017.7.27.1 diff --git a/admin/requirements.txt b/admin/requirements.txt index 4c069a89..47d3147b 100644 --- a/admin/requirements.txt +++ b/admin/requirements.txt @@ -11,6 +11,5 @@ passlib gunicorn docker-py tabulate -apscheduler PyYAML PyOpenSSL From 87cbeacecd718acc9532bf2f7e8826fd6d084096 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 12:05:38 +0200 Subject: [PATCH 04/27] Do not install openssl in the admin container anymore --- admin/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/admin/Dockerfile b/admin/Dockerfile index 971b407b..78fa753e 100644 --- a/admin/Dockerfile +++ b/admin/Dockerfile @@ -5,7 +5,6 @@ WORKDIR /app COPY requirements-prod.txt requirements.txt RUN apk --update add --virtual build-dep openssl-dev libffi-dev python-dev build-base \ - && apk add openssl \ && pip install -r requirements.txt \ && apk del build-dep From 1854349ecc2d808a32d8cdd16c6db3b15eeeb3e6 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 13:09:12 +0200 Subject: [PATCH 05/27] Use jinja2 for building nginx config --- nginx/Dockerfile | 9 +++++++ nginx/nginx.conf | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ nginx/start.sh | 5 ++++ 3 files changed, 75 insertions(+) create mode 100644 nginx/Dockerfile create mode 100644 nginx/nginx.conf create mode 100755 nginx/start.sh diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 00000000..a3932ab4 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine:edge + +RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ + && apk add --no-cache nginx nginx-mod-mail py-setuptools jinja2-cli@testing + +COPY conf /conf +COPY start.sh /start.sh + +CMD /start.sh diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 00000000..5e2a0e27 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,61 @@ +# Basic configuration +user nginx; +worker_processes 1; +error_log /dev/stderr info; +pid /var/run/nginx.pid; +load_module "modules/ngx_mail_module.so"; + +events { + worker_connections 1024; +} + +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; + + server { + listen 80; + + # Actual logic + location / { + return 301 $scheme://$host/webmail/; + } + + location /webmail { + proxy_pass http://webmail; + } + + location /admin { + proxy_pass http://admin; + } + + location /webdav { + proxy_pass http://webdav:5232; + } + } +} + +mail { + server_name test.mailu.io; + auth_http http://172.18.0.1:5000/nginx; + proxy_pass_error_message on; + + server { + listen 25; + protocol smtp; + smtp_auth plain; + } + + server { + listen 143; + protocol imap; + imap_auth plain; + } + + +} diff --git a/nginx/start.sh b/nginx/start.sh new file mode 100755 index 00000000..9f09a488 --- /dev/null +++ b/nginx/start.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +jinja2 /conf/nginx.conf > /etc/nginx/nginx.conf + +exec nginx -g 'daemon off;' From 995147f44404c7ae279f0eaf88f732f96c9e756f Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 13:09:47 +0200 Subject: [PATCH 06/27] Create the conf/ directory for nginx --- nginx/{ => conf}/nginx.conf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename nginx/{ => conf}/nginx.conf (100%) diff --git a/nginx/nginx.conf b/nginx/conf/nginx.conf similarity index 100% rename from nginx/nginx.conf rename to nginx/conf/nginx.conf From 755d9f052009292783fee094f8bff31f954c3849 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 14:01:03 +0200 Subject: [PATCH 07/27] Prepare nginx as a unique frontend --- nginx/Dockerfile | 7 +++---- nginx/conf/nginx.conf | 32 +++++++++++++++++++++++++++----- nginx/start.py | 12 ++++++++++++ nginx/start.sh | 5 ----- 4 files changed, 42 insertions(+), 14 deletions(-) create mode 100755 nginx/start.py delete mode 100755 nginx/start.sh diff --git a/nginx/Dockerfile b/nginx/Dockerfile index a3932ab4..64045e2c 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,9 +1,8 @@ FROM alpine:edge -RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ - && apk add --no-cache nginx nginx-mod-mail py-setuptools jinja2-cli@testing +RUN apk add --no-cache nginx nginx-mod-mail python py-jinja2 COPY conf /conf -COPY start.sh /start.sh +COPY start.py /start.py -CMD /start.sh +CMD /start.py diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index 5e2a0e27..895ae023 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -1,6 +1,6 @@ # Basic configuration user nginx; -worker_processes 1; +worker_processes 4; error_log /dev/stderr info; pid /var/run/nginx.pid; load_module "modules/ngx_mail_module.so"; @@ -21,7 +21,26 @@ http { server { listen 80; + {% if TLS_FLAVOR != 'notls' %} + listen 443 ssl; + + ssl_protocols TLSv1.1 TLSv1.2; + ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA'; + ssl_prefer_server_ciphers on; + ssl_session_timeout 5m; + ssl_session_cache shared:SSL:50m; + ssl_certificate /certs/cert.pem; + ssl_certificate_key /certs/key.pem; + + add_header Strict-Transport-Security max-age=15768000; + + if ($scheme = http) { + return 301 https://$host$request_uri; + } + {% endif %} + # Actual logic + {% if WEBMAIL != 'none' %} location / { return 301 $scheme://$host/webmail/; } @@ -29,20 +48,25 @@ http { location /webmail { proxy_pass http://webmail; } + {% endif %} + {% if ADMIN == 'true' %} location /admin { proxy_pass http://admin; } + {% endif %} + {% if WEBDAV != 'none' %} location /webdav { proxy_pass http://webdav:5232; } + {% endif %} } } mail { - server_name test.mailu.io; - auth_http http://172.18.0.1:5000/nginx; + server_name {{ HOSTNAME }}; + auth_http http://{{ ADMIN_ADDRESS }}/nginx; proxy_pass_error_message on; server { @@ -56,6 +80,4 @@ mail { protocol imap; imap_auth plain; } - - } diff --git a/nginx/start.py b/nginx/start.py new file mode 100755 index 00000000..15e4fee5 --- /dev/null +++ b/nginx/start.py @@ -0,0 +1,12 @@ +#!/usr/bin/python + +import jinja2 +import os +import socket + +convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ)) + +# Actual startup script +os.environ["ADMIN_ADDRESS"] = socket.gethostbyname("admin") +convert("/conf/nginx.conf", "/etc/nginx/nginx.conf") +os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"]) diff --git a/nginx/start.sh b/nginx/start.sh deleted file mode 100755 index 9f09a488..00000000 --- a/nginx/start.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -jinja2 /conf/nginx.conf > /etc/nginx/nginx.conf - -exec nginx -g 'daemon off;' From 67423b057d02b806180b5b8242ba0aaf3d8f06d1 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 14:01:26 +0200 Subject: [PATCH 08/27] Prepare the docker compose configuration for nginx as a uninque frontend --- .env.dist | 7 ++----- docker-compose.yml.dist | 39 ++++++++++----------------------------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/.env.dist b/.env.dist index 49365bc2..1c46082a 100644 --- a/.env.dist +++ b/.env.dist @@ -38,8 +38,8 @@ TLS_FLAVOR=cert # Optional features ################################### -# Choose which frontend Web server to run if any (value: traefik, none) -FRONTEND=none +# Expose the admin interface (value: true, false) +ADMIN=false # Choose which webmail to run if any (values: roundcube, rainloop, none) WEBMAIL=none @@ -87,6 +87,3 @@ COMPOSE_PROJECT_NAME=mailu # (value: SHA512-CRYPT, SHA256-CRYPT, MD5-CRYPT, CRYPT) PASSWORD_SCHEME=SHA512-CRYPT -# SSL DHPARAM Bits -NGINX_SSL_DHPARAM_BITS=2048 - diff --git a/docker-compose.yml.dist b/docker-compose.yml.dist index 11b5079f..35f6ac5f 100644 --- a/docker-compose.yml.dist +++ b/docker-compose.yml.dist @@ -2,17 +2,23 @@ version: '2' services: - http: - # build: $FRONTEND - image: mailu/$FRONTEND:$VERSION + front: + # build: nginx + image: mailu/nginx:$VERSION restart: always env_file: .env ports: - "$BIND_ADDRESS:80:80" - "$BIND_ADDRESS:443:443" + - "$BIND_ADDRESS:110:110" + - "$BIND_ADDRESS:143:143" + - "$BIND_ADDRESS:993:993" + - "$BIND_ADDRESS:995:995" + - "$BIND_ADDRESS:25:25" + - "$BIND_ADDRESS:465:465" + - "$BIND_ADDRESS:587:587" volumes: - "$ROOT/certs:/certs" - - /var/run/docker.sock:/docker.sock:ro redis: image: redis:latest @@ -25,16 +31,9 @@ services: image: mailu/dovecot:$VERSION restart: always env_file: .env - ports: - - "$BIND_ADDRESS:110:110" - - "$BIND_ADDRESS:143:143" - - "$BIND_ADDRESS:993:993" - - "$BIND_ADDRESS:995:995" - - "$BIND_ADDRESS:4190:4190" volumes: - "$ROOT/data:/data" - "$ROOT/mail:/mail" - - "$ROOT/certs:/certs" - "$ROOT/overrides:/overrides" smtp: @@ -42,13 +41,8 @@ services: image: mailu/postfix:$VERSION restart: always env_file: .env - ports: - - "$BIND_ADDRESS:25:25" - - "$BIND_ADDRESS:465:465" - - "$BIND_ADDRESS:587:587" volumes: - "$ROOT/data:/data" - - "$ROOT/certs:/certs" - "$ROOT/overrides:/overrides" milter: @@ -88,29 +82,16 @@ services: admin: # build: admin image: mailu/admin:$VERSION - labels: - - traefik.enable=true - - traefik.admin.frontend.rule=Host:$HOSTNAME;PathPrefixStrip:/admin/ - - traefik.admin.port=80 - - traefik.home.frontend.rule=Host:$HOSTNAME;Path:/ - - traefik.home.port=80 restart: always env_file: .env - ports: - - "127.0.0.1:8000:80" volumes: - "$ROOT/data:/data" - "$ROOT/dkim:/dkim" - - "$ROOT/certs:/certs" - /var/run/docker.sock:/var/run/docker.sock:ro webmail: # build: "$WEBMAIL" image: "mailu/$WEBMAIL:$VERSION" - labels: - - traefik.enable=true - - traefik.frontend.rule=Host:$HOSTNAME;PathPrefixStrip:/webmail/ - - traefik.port=80 restart: always env_file: .env volumes: From a11eb4ba35530595b379ef95b239d4c04ee3e227 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 14:34:46 +0200 Subject: [PATCH 09/27] Switch to python and Jinja2 for the Postfix container --- postfix/Dockerfile | 10 +++----- postfix/conf/main.cf | 39 ++-------------------------- postfix/conf/master.cf | 11 -------- postfix/{ => conf}/rsyslog.conf | 0 postfix/start.py | 38 ++++++++++++++++++++++++++++ postfix/start.sh | 45 --------------------------------- 6 files changed, 44 insertions(+), 99 deletions(-) rename postfix/{ => conf}/rsyslog.conf (100%) create mode 100755 postfix/start.py delete mode 100755 postfix/start.sh diff --git a/postfix/Dockerfile b/postfix/Dockerfile index 2408ef3e..4dc1c893 100644 --- a/postfix/Dockerfile +++ b/postfix/Dockerfile @@ -1,10 +1,8 @@ FROM alpine -RUN apk add --no-cache bash postfix postfix-sqlite postfix-pcre rsyslog +RUN apk add --no-cache postfix postfix-sqlite postfix-pcre rsyslog python py-jinja2 -COPY conf /etc/postfix -COPY rsyslog.conf /etc/rsyslog.conf +COPY conf /conf +COPY start.py /start.py -COPY start.sh /start.sh - -CMD ["/start.sh"] +CMD /start.py diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 5e37543f..e1ba19fc 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -38,24 +38,7 @@ recipient_delimiter = {{ RECIPIENT_DELIMITER }} # General TLS configuration tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA tls_preempt_cipherlist = yes - -# Only one key/certificate pair is used, SNI not being supported by all -# services and not a strong requirement. Also, TLS is enforced for submission -# and smtps in master.cf. -smtpd_tls_security_level = may -smtpd_tls_cert_file=/certs/cert.pem -smtpd_tls_key_file=/certs/key.pem -smtpd_tls_dh1024_param_file=/certs/dhparam.pem -smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache - -# Server-side TLS is hardened, it should be up to the client to update his or -# her TLS stack in order to connect to the mail server. Hardening is based on -# https://bettercrypto.org/static/applied-crypto-hardening.pdf -smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3 -smtpd_tls_protocols = !SSLv2, !SSLv3 -smtpd_tls_ciphers = high -smtpd_tls_mandatory_ciphers = high - +tls_ssl_options = NO_COMPRESSION # Outgoing TLS is more flexible because 1. not all receiving servers will # support TLS, 2. not all will have and up-to-date TLS stack. @@ -64,23 +47,6 @@ smtp_tls_mandatory_protocols = !SSLv2, !SSLv3 smtp_tls_protocols =!SSLv2,!SSLv3 smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache -# General TLS hardening -tls_ssl_options = NO_COMPRESSION -tls_preempt_cipherlist = yes - -############### -# SASL -############### - -smtpd_sasl_local_domain = $myhostname - -# Authentication is done against dovecot, which acts as the main authention -# source -smtpd_sasl_type = dovecot -smtpd_sasl_path = inet:imap:2102 -smtpd_sasl_auth_enable = yes -smtpd_sasl_security_options = noanonymous - ############### # Virtual ############### @@ -118,15 +84,14 @@ smtpd_sender_restrictions = reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unlisted_sender, - reject_sender_login_mismatch, permit # Recipient restrictions: smtpd_recipient_restrictions = + permit_mynetworks, reject_unauth_pipelining, reject_non_fqdn_recipient, reject_unknown_recipient_domain, - permit_mynetworks, permit ############### diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index a196f496..47cb4af6 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -3,17 +3,6 @@ # Exposed SMTP services smtp inet n - n - - smtpd - -o smtpd_helo_restrictions=permit_mynetworks,permit -submission inet n - n - - smtpd - -o smtpd_tls_security_level=encrypt - -o smtpd_sasl_auth_enable=yes - -o smtpd_client_restrictions=permit_sasl_authenticated,reject - -o cleanup_service_name=outclean -smtps inet n - n - - smtpd - -o smtpd_tls_security_level=encrypt - -o smtpd_sasl_auth_enable=yes - -o smtpd_tls_wrappermode=yes - -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o cleanup_service_name=outclean # Additional services diff --git a/postfix/rsyslog.conf b/postfix/conf/rsyslog.conf similarity index 100% rename from postfix/rsyslog.conf rename to postfix/conf/rsyslog.conf diff --git a/postfix/start.py b/postfix/start.py new file mode 100755 index 00000000..994c4431 --- /dev/null +++ b/postfix/start.py @@ -0,0 +1,38 @@ +#!/usr/bin/python + +import jinja2 +import os +import socket +import glob +import shutil + +convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ)) + +# Actual startup script +os.environ["FRONT_ADDRESS"] = socket.gethostbyname("front") + +for postfix_file in glob.glob("/conf/*.cf"): + convert(postfix_file, os.path.join("/etc/postfix", os.path.basename(postfix_file))) + +if os.path.exists("/overrides/postfix.cf"): + for line in open("/overrides/postfix.cf").read().strip().split("\n"): + os.system('postconf -e "{}"'.format(line)) + +if os.path.exists("/overrides/postfix.master"): + for line in open("/overrides/postfix.master").read().strip().split("\n"): + os.system('postconf -Me "{}"'.format(line)) + +for map_file in glob.glob("/overrides/*.map"): + destination = os.path.join("/etc/postfix", os.path.basename(map_file)) + shutil.copyfile(map_file, destination) + os.system("postmap {}".format(destination)) + os.remove(destination) + +convert("/conf/rsyslog.conf", "/etc/rsyslog.conf") + +# Run postfix +if os.path.exists("/var/run/rsyslogd.pid"): + os.remove("/var/run/rsyslogd.pid") +os.system("/usr/lib/postfix/post-install meta_directory=/etc/postfix create-missing") +os.system("/usr/lib/postfix/master &") +os.execv("/usr/sbin/rsyslogd", ["rsyslogd", "-n"]) diff --git a/postfix/start.sh b/postfix/start.sh deleted file mode 100755 index e34543a5..00000000 --- a/postfix/start.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# Substitute configuration -for VARIABLE in `env | cut -f1 -d=`; do - sed -i "s={{ $VARIABLE }}=${!VARIABLE}=g" /etc/postfix/*.cf -done - -# Override Postfix main configuration -if [ -f /overrides/postfix.cf ]; then - while read line; do - postconf -e "$line" - done < /overrides/postfix.cf - echo "Loaded '/overrides/postfix.cf'" -else - echo "No extra postfix settings loaded because optional '/overrides/postfix.cf' not provided." -fi - -# Override Postfix master configuration -if [ -f /overrides/postfix.master ]; then - while read line; do - postconf -Me "$line" - done < /overrides/postfix.master - echo "Loaded '/overrides/postfix.master'" -else - echo "No extra postfix settings loaded because optional '/overrides/postfix.master' not provided." -fi - -# Include table-map files -if ls -A /overrides/*.map 1> /dev/null 2>&1; then - cp /overrides/*.map /etc/postfix/ - postmap /etc/postfix/*.map - rm /etc/postfix/*.map - chown root:root /etc/postfix/*.db - chmod 0600 /etc/postfix/*.db - echo "Loaded 'map files'" -else - echo "No extra map files loaded because optional '/overrides/*.map' not provided." -fi - -# Actually run Postfix -rm -f /var/run/rsyslogd.pid -chown -R postfix: /queue -/usr/lib/postfix/post-install meta_directory=/etc/postfix create-missing -/usr/lib/postfix/master & -exec rsyslogd -n From 13b9a9207d2453ca698ec3e7bb7252803df5e350 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 14:42:40 +0200 Subject: [PATCH 10/27] Switch to python and Jinja2 for the dovecot container --- dovecot/Dockerfile | 10 ++++----- dovecot/conf/dovecot-sql.conf.ext | 2 +- dovecot/conf/dovecot.conf | 35 ++++++------------------------- dovecot/start.py | 20 ++++++++++++++++++ dovecot/start.sh | 13 ------------ 5 files changed, 31 insertions(+), 49 deletions(-) create mode 100755 dovecot/start.py delete mode 100755 dovecot/start.sh diff --git a/dovecot/Dockerfile b/dovecot/Dockerfile index ee80fe42..9e87f12f 100644 --- a/dovecot/Dockerfile +++ b/dovecot/Dockerfile @@ -3,12 +3,10 @@ FROM alpine:edge RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ && apk add --no-cache \ dovecot dovecot-sqlite dovecot-pigeonhole-plugin dovecot-pigeonhole-plugin-extdata \ - rspamd-client@testing \ - bash + rspamd-client@testing python py-jinja2 -COPY conf /etc/dovecot +COPY conf /conf COPY sieve /var/lib/dovecot +COPY start.py /start.py -COPY start.sh /start.sh - -CMD ["/start.sh"] +CMD /start.py diff --git a/dovecot/conf/dovecot-sql.conf.ext b/dovecot/conf/dovecot-sql.conf.ext index e42abb84..d2e31016 100644 --- a/dovecot/conf/dovecot-sql.conf.ext +++ b/dovecot/conf/dovecot-sql.conf.ext @@ -3,7 +3,7 @@ connect = /data/main.db # Return the user hashed password password_query = \ - SELECT password \ + SELECT NULL as password, 'Y' as nopassword, '{{ FRONT_ADDRESS }}' as allow_nets \ FROM user \ WHERE user.email = '%u' diff --git a/dovecot/conf/dovecot.conf b/dovecot/conf/dovecot.conf index faa41bf7..a1ec72fd 100644 --- a/dovecot/conf/dovecot.conf +++ b/dovecot/conf/dovecot.conf @@ -52,27 +52,11 @@ namespace inbox { } } -############### -# TLS -############### -ssl = yes -ssl_cert = Date: Sun, 24 Sep 2017 15:42:51 +0200 Subject: [PATCH 11/27] Do not disable login mismatch check in Postfix --- postfix/conf/main.cf | 1 + 1 file changed, 1 insertion(+) diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index e1ba19fc..04b67553 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -84,6 +84,7 @@ smtpd_sender_restrictions = reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unlisted_sender, + reject_sender_login_mismatch, permit # Recipient restrictions: From 94a13aabf0023fd8c3224e08669833ff8e1c7056 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 15:43:23 +0200 Subject: [PATCH 12/27] Handle nginx authentication requests in the admin container --- admin/mailu/__init__.py | 3 +- admin/mailu/internal/__init__.py | 6 +++ admin/mailu/internal/nginx.py | 64 ++++++++++++++++++++++++++++++++ admin/mailu/internal/views.py | 14 +++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 admin/mailu/internal/__init__.py create mode 100644 admin/mailu/internal/nginx.py create mode 100644 admin/mailu/internal/views.py diff --git a/admin/mailu/__init__.py b/admin/mailu/__init__.py index 947a72f9..e62ea738 100644 --- a/admin/mailu/__init__.py +++ b/admin/mailu/__init__.py @@ -74,8 +74,9 @@ def inject_user(): return dict(current_user=flask_login.current_user) # Import views -from mailu import ui +from mailu import ui, internal app.register_blueprint(ui.ui, url_prefix='/ui') +app.register_blueprint(internal.internal, url_prefix='/internal') # Create the prefix middleware class PrefixMiddleware(object): diff --git a/admin/mailu/internal/__init__.py b/admin/mailu/internal/__init__.py new file mode 100644 index 00000000..9e8c3690 --- /dev/null +++ b/admin/mailu/internal/__init__.py @@ -0,0 +1,6 @@ +from flask import Blueprint + + +internal = Blueprint('internal', __name__) + +from mailu.internal import views diff --git a/admin/mailu/internal/nginx.py b/admin/mailu/internal/nginx.py new file mode 100644 index 00000000..6fcb3b2b --- /dev/null +++ b/admin/mailu/internal/nginx.py @@ -0,0 +1,64 @@ +from mailu import db, models + + +SUPPORTED_AUTH_METHODS = ["none", "plain"] + +STATUSES = { + "authentication": ("Authentication credentials invalid", { + "imap": "AUTHENTICATIONFAILED", + "smtp": "535 5.7.8", + "pop3": "" + }), +} + + +def handle_authentication(headers): + """ Handle an HTTP nginx authentication request + See: http://nginx.org/en/docs/mail/ngx_mail_auth_http_module.html#protocol + """ + method = headers["Auth-Method"] + protocol = headers["Auth-Protocol"] + server, port = get_server(headers["Auth-Protocol"]) + # Incoming mail, no authentication + if method == "none" and protocol == "smtp": + return { + "Auth-Status": "OK", + "Auth-Server": server, + "Auth-Port": port + } + # Authenticated user + elif method == "plain": + user_email = headers["Auth-User"] + password = headers["Auth-Pass"] + user = models.User.query.get(user_email) + if user and user.check_password(password): + return { + "Auth-Status": "OK", + "Auth-Server": server, + "Auth-Port": port + } + else: + status, code = get_status(protocol, "authentication") + return { + "Auth-Status": status, + "Auth-Error-Code": code, + "Auth-Wait": 0 + } + # Unexpected + else: + return {} + + +def get_status(protocol, status): + """ Return the proper error code depending on the protocol + """ + status, codes = STATUSES[status] + return status, codes[protocol] + + +def get_server(protocol): + servers = { + "imap": ("172.18.0.12", 143), + "smtp": ("172.18.0.9", 25) + } + return servers[protocol] diff --git a/admin/mailu/internal/views.py b/admin/mailu/internal/views.py new file mode 100644 index 00000000..f1dd9d19 --- /dev/null +++ b/admin/mailu/internal/views.py @@ -0,0 +1,14 @@ +from mailu import db, models +from mailu.internal import internal, nginx + +import flask + + +@internal.route("/nginx") +def nginx_authentication(): + headers = nginx.handle_authentication(flask.request.headers) + response = flask.Response() + for key, value in headers.items(): + response.headers[key] = str(value) + return response + From 5097f578551ce7f1c5dfccc4b4fc63621ff9f990 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 15:43:46 +0200 Subject: [PATCH 13/27] Redirect nginx authentication requests to the admin container --- nginx/conf/nginx.conf | 7 +++++-- nginx/start.py | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index 895ae023..46b36e1f 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -46,18 +46,21 @@ http { } location /webmail { + rewrite ^/webmail/(.*) /$1 break; proxy_pass http://webmail; } {% endif %} {% if ADMIN == 'true' %} - location /admin { + location /admin/ui { + rewrite ^/admin/(.*) /$1 break; proxy_pass http://admin; } {% endif %} {% if WEBDAV != 'none' %} location /webdav { + rewrite ^/webdav/(.*) /$1 break; proxy_pass http://webdav:5232; } {% endif %} @@ -66,7 +69,7 @@ http { mail { server_name {{ HOSTNAME }}; - auth_http http://{{ ADMIN_ADDRESS }}/nginx; + auth_http http://{{ ADMIN_ADDRESS }}/internal/nginx; proxy_pass_error_message on; server { diff --git a/nginx/start.py b/nginx/start.py index 15e4fee5..e5e13328 100755 --- a/nginx/start.py +++ b/nginx/start.py @@ -7,6 +7,7 @@ import socket convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ)) # Actual startup script -os.environ["ADMIN_ADDRESS"] = socket.gethostbyname("admin") +if "ADMIN_ADDRESS" not in os.environ: + os.environ["ADMIN_ADDRESS"] = socket.gethostbyname("admin") convert("/conf/nginx.conf", "/etc/nginx/nginx.conf") os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"]) From 3c44006ccb64ecb58ce56422596dffa3b42a09fa Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 15:44:01 +0200 Subject: [PATCH 14/27] Point rainloop to the front container --- rainloop/default.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rainloop/default.ini b/rainloop/default.ini index 5c6f79bc..53545bef 100644 --- a/rainloop/default.ini +++ b/rainloop/default.ini @@ -1,15 +1,15 @@ -imap_host = "imap" +imap_host = "front" imap_port = 143 -imap_secure = "TLS" +imap_secure = "None" imap_short_login = Off sieve_use = On sieve_allow_raw = Off sieve_host = "imap" sieve_port = 4190 sieve_secure = "TLS" -smtp_host = "smtp" -smtp_port = 587 -smtp_secure = "TLS" +smtp_host = "front" +smtp_port = 25 +smtp_secure = "None" smtp_short_login = Off smtp_auth = On smtp_php_mail = Off From 3dac7fc597a40bf2a9600f38965ba18a49ce2611 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 15:44:23 +0200 Subject: [PATCH 15/27] Point roundcube to the front container --- roundcube/config.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roundcube/config.inc.php b/roundcube/config.inc.php index 65de8617..a867a169 100644 --- a/roundcube/config.inc.php +++ b/roundcube/config.inc.php @@ -17,10 +17,10 @@ $config['plugins'] = array( ); // Mail servers -$config['default_host'] = 'tls://imap'; +$config['default_host'] = 'front'; $config['default_port'] = 143; -$config['smtp_server'] = 'tls://smtp'; -$config['smtp_port'] = 587; +$config['smtp_server'] = 'front'; +$config['smtp_port'] = 25; $config['smtp_user'] = '%u'; $config['smtp_pass'] = '%p'; From 1b13728df3eff679fc8c55f16db34493b054d0a1 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 15:44:44 +0200 Subject: [PATCH 16/27] Fix the dovecot startup script --- dovecot/start.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dovecot/start.py b/dovecot/start.py index ce6390cd..f95762e3 100755 --- a/dovecot/start.py +++ b/dovecot/start.py @@ -10,11 +10,9 @@ convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read() # Actual startup script os.environ["FRONT_ADDRESS"] = socket.gethostbyname("front") -for postfix_file in glob.glob("/conf/*.cf"): - convert(postfix_file, os.path.join("/etc/postfix", os.path.basename(postfix_file))) - -convert("/conf/rsyslog.conf", "/etc/rsyslog.conf") +for dovecot_file in glob.glob("/conf/*"): + convert(dovecot_file, os.path.join("/etc/dovecot", os.path.basename(dovecot_file))) # Run postfix os.system("chown -R mail:mail /mail /var/lib/dovecot") -os.execv("/usr/sbin/dovecot" ["dovecot", "-c", "/etc/dovecot/dovecot.conf", "-F"]) +os.execv("/usr/sbin/dovecot", ["dovecot", "-c", "/etc/dovecot/dovecot.conf", "-F"]) From 3b01f968b38dd09f3ae42d2785519768a1e01ba6 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 15:45:10 +0200 Subject: [PATCH 17/27] Remove traefik which has become incompatible --- traefik/Dockerfile | 8 -------- traefik/conf/cert.toml | 24 ------------------------ traefik/conf/letsencrypt.toml | 29 ----------------------------- traefik/conf/notls.toml | 14 -------------- traefik/start.sh | 12 ------------ 5 files changed, 87 deletions(-) delete mode 100644 traefik/Dockerfile delete mode 100644 traefik/conf/cert.toml delete mode 100644 traefik/conf/letsencrypt.toml delete mode 100644 traefik/conf/notls.toml delete mode 100755 traefik/start.sh diff --git a/traefik/Dockerfile b/traefik/Dockerfile deleted file mode 100644 index efb2b364..00000000 --- a/traefik/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM traefik:alpine - -RUN apk add --no-cache bash - -COPY conf /conf -COPY start.sh /start.sh - -CMD /start.sh diff --git a/traefik/conf/cert.toml b/traefik/conf/cert.toml deleted file mode 100644 index dab3cccb..00000000 --- a/traefik/conf/cert.toml +++ /dev/null @@ -1,24 +0,0 @@ -defaultEntryPoints = ["http", "https"] -logLevel = "ERROR" -accessLogsFile = "/dev/stdout" - -[entryPoints] - [entryPoints.http] - address = ":80" - [entryPoints.http.redirect] - entryPoint = "https" - [entryPoints.https] - address = ":443" - [entryPoints.https.tls] - MinVersion = "VersionTLS11" - CipherSuites = ["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"] - [[entryPoints.https.tls.certificates]] - CertFile = "/certs/cert.pem" - KeyFile = "/certs/key.pem" - -[docker] -endpoint = "unix:///docker.sock" -domain = "{{ DOMAIN }}" -watch = true -exposedbydefault = false - diff --git a/traefik/conf/letsencrypt.toml b/traefik/conf/letsencrypt.toml deleted file mode 100644 index ddc2a7b4..00000000 --- a/traefik/conf/letsencrypt.toml +++ /dev/null @@ -1,29 +0,0 @@ -defaultEntryPoints = ["http", "https"] -logLevel = "ERROR" -accessLogsFile = "/dev/stdout" - -[entryPoints] - [entryPoints.http] - address = ":80" - [entryPoints.http.redirect] - entryPoint = "https" - [entryPoints.https] - address = ":443" - [entryPoints.https.tls] - MinVersion = "VersionTLS11" - CipherSuites = ["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"] - -[docker] -endpoint = "unix:///docker.sock" -domain = "{{ DOMAIN }}" -watch = true -exposedbydefault = false - -[acme] -email = "{{ POSTMASTER }}@{{ DOMAIN }}" -storageFile = "/certs/acme.json" -onDemand = false -OnHostRule = true -entryPoint = "https" - - diff --git a/traefik/conf/notls.toml b/traefik/conf/notls.toml deleted file mode 100644 index 3226aa3f..00000000 --- a/traefik/conf/notls.toml +++ /dev/null @@ -1,14 +0,0 @@ -defaultEntryPoints = ["http"] -logLevel = "ERROR" -accessLogsFile = "/dev/stdout" - -[entryPoints] - [entryPoints.http] - address = ":80" - -[docker] -endpoint = "unix:///docker.sock" -domain = "{{ DOMAIN }}" -watch = true -exposedbydefault = false - diff --git a/traefik/start.sh b/traefik/start.sh deleted file mode 100755 index ee148a6d..00000000 --- a/traefik/start.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# Substitute configuration -for VARIABLE in `env | cut -f1 -d=`; do - sed -i "s={{ $VARIABLE }}=${!VARIABLE}=g" /conf/*.toml -done - -# Select the proper configuration -cp /conf/$TLS_FLAVOR.toml /conf/traefik.toml - -exec traefik -c /conf/traefik.toml - From 7e9c04cd6505c1dea01af3832c7545bd5236e67c Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 17:48:27 +0200 Subject: [PATCH 18/27] Do not include appscheduler --- admin/mailu/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/admin/mailu/__init__.py b/admin/mailu/__init__.py index e62ea738..6d69621e 100644 --- a/admin/mailu/__init__.py +++ b/admin/mailu/__init__.py @@ -9,9 +9,6 @@ import flask_babel import os import docker -from apscheduler.schedulers import background - - # Create application app = flask.Flask(__name__) @@ -20,7 +17,7 @@ default_config = { 'SQLALCHEMY_TRACK_MODIFICATIONS': False, 'SECRET_KEY': 'changeMe', 'DOCKER_SOCKET': 'unix:///var/run/docker.sock', - 'HOSTNAME': 'mail.mailu.io', + 'HOSTNAMES': 'mail.mailu.io', 'DOMAIN': 'mailu.io', 'POSTMASTER': 'postmaster', 'DEBUG': False, From a57096e6131e281a01f40f036c83f40f32438d1e Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 17:49:39 +0200 Subject: [PATCH 19/27] Support specifyin multiple hostnames --- .env.dist | 4 +--- admin/mailu/ui/templates/domain/details.html | 5 +++-- dovecot/conf/dovecot.conf | 2 +- postfix/conf/main.cf | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.env.dist b/.env.dist index 1c46082a..17bfaaad 100644 --- a/.env.dist +++ b/.env.dist @@ -23,9 +23,7 @@ BIND_ADDRESS=127.0.0.1 # Main mail domain DOMAIN=mailu.io -# Main hostname for announces, and list of all available hostnames, separated -# by comas -HOSTNAME=mail.mailu.io +# Hostnames for this server, separated with comas HOSTNAMES=mail.mailu.io,alternative.mailu.io,yetanother.mailu.io # Postmaster local part (will append the main mail domain) diff --git a/admin/mailu/ui/templates/domain/details.html b/admin/mailu/ui/templates/domain/details.html index 1ae45e16..5b3655de 100644 --- a/admin/mailu/ui/templates/domain/details.html +++ b/admin/mailu/ui/templates/domain/details.html @@ -15,6 +15,7 @@ {% endblock %} {% block box %} +{% let hostname = config["HOSTNAMES"].split(",")[0] %} @@ -28,8 +29,8 @@ +{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ hostname }} -all" +{{ domain.name }}. 600 IN SPF "v=spf1 mx a:{{ hostname }} -all" {% if domain.dkim_publickey %} diff --git a/dovecot/conf/dovecot.conf b/dovecot/conf/dovecot.conf index a1ec72fd..98cff4bc 100644 --- a/dovecot/conf/dovecot.conf +++ b/dovecot/conf/dovecot.conf @@ -4,7 +4,7 @@ log_path = /dev/stderr protocols = imap pop3 lmtp sieve postmaster_address = {{ POSTMASTER }}@{{ DOMAIN }} -hostname = {{ HOSTNAME }} +hostname = {{ HOSTNAMES.split(",")[0] }} mail_plugins = $mail_plugins quota submission_host = smtp diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 04b67553..afe48813 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -4,7 +4,7 @@ # Main domain and hostname mydomain = {{ DOMAIN }} -myhostname = {{ HOSTNAME }} +myhostname = {{ HOSTNAMES.split(",")[0] }} myorigin = $mydomain # Queue location From 808809b37ae7baae59cc893bad29fea1a1afbd06 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 17:50:10 +0200 Subject: [PATCH 20/27] Add letsencrypt support in the nginx container --- nginx/Dockerfile | 4 ++-- nginx/conf/nginx.conf | 24 ++++++++++++++++++++---- nginx/config.py | 27 +++++++++++++++++++++++++++ nginx/letsencrypt.py | 29 +++++++++++++++++++++++++++++ nginx/start.py | 16 +++++++++------- 5 files changed, 87 insertions(+), 13 deletions(-) create mode 100755 nginx/config.py create mode 100755 nginx/letsencrypt.py diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 64045e2c..ad6752ac 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,8 +1,8 @@ FROM alpine:edge -RUN apk add --no-cache nginx nginx-mod-mail python py-jinja2 +RUN apk add --no-cache nginx nginx-mod-mail python py-jinja2 certbot openssl COPY conf /conf -COPY start.py /start.py +COPY *.py / CMD /start.py diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index 46b36e1f..f1926000 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -21,7 +21,8 @@ http { server { listen 80; - {% if TLS_FLAVOR != 'notls' %} + # TLS configuration + {% if TLS and not TLS_ERROR %} listen 443 ssl; ssl_protocols TLSv1.1 TLSv1.2; @@ -29,8 +30,8 @@ http { ssl_prefer_server_ciphers on; ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; - ssl_certificate /certs/cert.pem; - ssl_certificate_key /certs/key.pem; + ssl_certificate {{ TLS[0] }}; + ssl_certificate_key {{ TLS[1] }}; add_header Strict-Transport-Security max-age=15768000; @@ -39,7 +40,18 @@ http { } {% endif %} + {% if TLS_FLAVOR == 'letsencrypt' %} + location ^~ /.well-known/acme-challenge/ { + proxy_pass http://localhost:8000; + } + {% endif %} + # Actual logic + {% if TLS_ERROR %} + location / { + return 403 + } + {% else %} {% if WEBMAIL != 'none' %} location / { return 301 $scheme://$host/webmail/; @@ -52,6 +64,9 @@ http { {% endif %} {% if ADMIN == 'true' %} + location /admin { + return 301 $scheme://$host/admin/ui; + } location /admin/ui { rewrite ^/admin/(.*) /$1 break; proxy_pass http://admin; @@ -64,11 +79,12 @@ http { proxy_pass http://webdav:5232; } {% endif %} + {% endif %} } } mail { - server_name {{ HOSTNAME }}; + server_name {{ HOSTNAMES.split(",")[0] }}; auth_http http://{{ ADMIN_ADDRESS }}/internal/nginx; proxy_pass_error_message on; diff --git a/nginx/config.py b/nginx/config.py new file mode 100755 index 00000000..7408a5c6 --- /dev/null +++ b/nginx/config.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +import jinja2 +import os +import socket + +convert = lambda src, dst, args: open(dst, "w").write(jinja2.Template(open(src).read()).render(**args)) + +args = os.environ.copy() + +if "ADMIN_ADDRESS" not in os.environ: + args["ADMIN_ADDRESS"] = socket.gethostbyname("admin") + +args["TLS"] = { + "cert": ("/certs/cert.pem", "/certs/key.pem"), + "letsencrypt": ("/certs/letsencrypt/live/mailu/fullchain.pem", + "/certs/letsencrypt/live/mailu/privkey.pem"), + "notls": None +}[args["TLS_FLAVOR"]] + +if args["TLS"] and not all(os.path.exists(file_path) for file_path in args["TLS"]): + print("Missing cert or key file, disabling TLS") + args["TLS_ERROR"] = "yes" + + +convert("/conf/nginx.conf", "/etc/nginx/nginx.conf", args) +os.system("nginx -s reload") diff --git a/nginx/letsencrypt.py b/nginx/letsencrypt.py new file mode 100755 index 00000000..18aea292 --- /dev/null +++ b/nginx/letsencrypt.py @@ -0,0 +1,29 @@ +#!/usr/bin/python + +import os +import time +import subprocess + + +command = [ + "certbot", + "-n", "--agree-tos", # non-interactive + "-d", os.environ["HOSTNAMES"], + "-m", "{}@{}".format(os.environ["POSTMASTER"], os.environ["DOMAIN"]), + "certonly", "--standalone", + "--cert-name", "mailu", + "--preferred-challenges", "http", "--http-01-port", "8000", + "--keep-until-expiring", + "--rsa-key-size", "4096", + "--config-dir", "/certs/letsencrypt", + "--post-hook", "/config.py" +] + +# Wait for nginx to start +time.sleep(5) + +# Run certbot every hour +while True: + subprocess.call(command) + time.sleep(3600) + diff --git a/nginx/start.py b/nginx/start.py index e5e13328..daf05e1a 100755 --- a/nginx/start.py +++ b/nginx/start.py @@ -1,13 +1,15 @@ #!/usr/bin/python -import jinja2 import os -import socket - -convert = lambda src, dst: open(dst, "w").write(jinja2.Template(open(src).read()).render(**os.environ)) +import subprocess + # Actual startup script -if "ADMIN_ADDRESS" not in os.environ: - os.environ["ADMIN_ADDRESS"] = socket.gethostbyname("admin") -convert("/conf/nginx.conf", "/etc/nginx/nginx.conf") +if not os.path.exists("/certs/dhparam.pem") and os.environ["TLS_FLAVOR"] != "notls": + os.system("openssl dhparam -out /certs/dhparam.pem 4096") + +if os.environ["TLS_FLAVOR"] == "letsencrypt": + subprocess.Popen(["/letsencrypt.py"]) + +subprocess.call(["/config.py"]) os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"]) From 648d092e881bb5f57b67d5066b642e4dbf1928f0 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 17:59:58 +0200 Subject: [PATCH 21/27] Fix proxifying to static assets --- nginx/conf/nginx.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index f1926000..8b4266d6 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -67,8 +67,9 @@ http { location /admin { return 301 $scheme://$host/admin/ui; } - location /admin/ui { + location ~ /admin/(ui|static) { rewrite ^/admin/(.*) /$1 break; + proxy_set_header X-Forwarded-Prefix /admin; proxy_pass http://admin; } {% endif %} From 4521fa9e29ddc1ceface270bfde253dcf120cff4 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 18:16:36 +0200 Subject: [PATCH 22/27] Make path to admin and webmail configurable, related to #236 --- .env.dist | 10 ++++++++++ nginx/conf/nginx.conf | 14 +++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.env.dist b/.env.dist index 17bfaaad..b1fba622 100644 --- a/.env.dist +++ b/.env.dist @@ -74,6 +74,16 @@ RECIPIENT_DELIMITER=+ DMARC_RUA=admin DMARC_RUF=admin +################################### +# Web settings +################################### + +# Path to the admin interface if enabled +WEB_ADMIN = /admin + +# Path to the webmail if enabled +WEB_WEBMAIL = /webmail + ################################### # Advanced settings ################################### diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index 8b4266d6..a7e07a13 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -57,19 +57,19 @@ http { return 301 $scheme://$host/webmail/; } - location /webmail { - rewrite ^/webmail/(.*) /$1 break; + location {{ WEB_WEBMAIL }} { + rewrite ^{{ WEB_WEBMAIL }}/(.*) /$1 break; proxy_pass http://webmail; } {% endif %} {% if ADMIN == 'true' %} - location /admin { - return 301 $scheme://$host/admin/ui; + location {{ WEB_ADMIN }} { + return 301 $scheme://$host{{ WEB_ADMIN }}/ui; } - location ~ /admin/(ui|static) { - rewrite ^/admin/(.*) /$1 break; - proxy_set_header X-Forwarded-Prefix /admin; + location ~ {{ WEB_ADMIN }}/(ui|static) { + rewrite ^{{ WEB_ADMIN }}/(.*) /$1 break; + proxy_set_header X-Forwarded-Prefix {{ WEB_ADMIN }}; proxy_pass http://admin; } {% endif %} From 4892fe47e623af869d991fb34003896f048282d3 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 18:33:13 +0200 Subject: [PATCH 23/27] Limit xclient to connections from the front container --- postfix/conf/main.cf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index afe48813..e23dcba7 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -31,6 +31,9 @@ relayhost = {{ RELAYHOST }} # Recipient delimiter for extended addresses recipient_delimiter = {{ RECIPIENT_DELIMITER }} +# XClient for connection from the frontend +smtpd_authorized_xclient_hosts = {{ FRONT_ADDRESS }} + ############### # TLS ############### From ba4ed579df94743460706f99017716a81ce23fe9 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 18:43:14 +0200 Subject: [PATCH 24/27] Support TLS and STARTTLS for mail --- nginx/conf/nginx.conf | 47 ++++++++++++++++++++++++++++++++++--------- nginx/conf/tls.conf | 7 +++++++ nginx/config.py | 1 + 3 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 nginx/conf/tls.conf diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index a7e07a13..23f81563 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -24,15 +24,8 @@ http { # TLS configuration {% if TLS and not TLS_ERROR %} listen 443 ssl; - - ssl_protocols TLSv1.1 TLSv1.2; - ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA'; - ssl_prefer_server_ciphers on; - ssl_session_timeout 5m; - ssl_session_cache shared:SSL:50m; - ssl_certificate {{ TLS[0] }}; - ssl_certificate_key {{ TLS[1] }}; - + include /etc/nginx/tls.conf; + ssl_session_cache shared:SSLHTTP:50m; add_header Strict-Transport-Security max-age=15768000; if ($scheme = http) { @@ -89,15 +82,49 @@ mail { auth_http http://{{ ADMIN_ADDRESS }}/internal/nginx; proxy_pass_error_message on; + {% if TLS and not TLS_ERROR %} + include /etc/nginx/tls.conf; + ssl_session_cache shared:SSLMAIL:50m; + {% endif %} + server { listen 25; + {% if TLS_FLAVOR != 'notls' %} + starttls on; + {% endif %} + protocol smtp; + smtp_auth none; + } + + {% if not TLS_ERROR %} + server { + listen 143; + {% if TLS %} + starttls only; + {% endif %} + protocol imap; + imap_auth plain; + } + + {% if TLS %} + server { + listen 465 ssl; protocol smtp; smtp_auth plain; } server { - listen 143; + listen 597; + starttls only; + protocol smtp; + smtp_auth plain; + } + + server { + listen 993 ssl; protocol imap; imap_auth plain; } + {% endif %} + {% endif %} } diff --git a/nginx/conf/tls.conf b/nginx/conf/tls.conf new file mode 100644 index 00000000..e362bab4 --- /dev/null +++ b/nginx/conf/tls.conf @@ -0,0 +1,7 @@ +ssl_protocols TLSv1.1 TLSv1.2; +ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA'; +ssl_prefer_server_ciphers on; +ssl_session_timeout 5m; +ssl_certificate {{ TLS[0] }}; +ssl_certificate_key {{ TLS[1] }}; + diff --git a/nginx/config.py b/nginx/config.py index 7408a5c6..5f1e0355 100755 --- a/nginx/config.py +++ b/nginx/config.py @@ -23,5 +23,6 @@ if args["TLS"] and not all(os.path.exists(file_path) for file_path in args["TLS" args["TLS_ERROR"] = "yes" +convert("/conf/tls.conf", "/etc/nginx/tls.conf", args) convert("/conf/nginx.conf", "/etc/nginx/nginx.conf", args) os.system("nginx -s reload") From effb311742aad2b1b3fe6a1e970c0c3ba16b79c1 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sun, 24 Sep 2017 22:43:16 +0200 Subject: [PATCH 25/27] Use relative redirects, as suggsted in #272 --- nginx/conf/nginx.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nginx/conf/nginx.conf b/nginx/conf/nginx.conf index 23f81563..446c039f 100644 --- a/nginx/conf/nginx.conf +++ b/nginx/conf/nginx.conf @@ -17,6 +17,7 @@ http { sendfile on; keepalive_timeout 65; server_tokens off; + absolute_redirect off; server { listen 80; @@ -58,7 +59,7 @@ http { {% if ADMIN == 'true' %} location {{ WEB_ADMIN }} { - return 301 $scheme://$host{{ WEB_ADMIN }}/ui; + return 301 {{ WEB_ADMIN }}/ui; } location ~ {{ WEB_ADMIN }}/(ui|static) { rewrite ^{{ WEB_ADMIN }}/(.*) /$1 break; From 9e3e86f0d04850db553b0daf233acae36a0f82eb Mon Sep 17 00:00:00 2001 From: kaiyou Date: Thu, 28 Sep 2017 21:47:01 +0200 Subject: [PATCH 26/27] Remove spaces around = sign in .env --- .env.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.dist b/.env.dist index b1fba622..cb3a97d0 100644 --- a/.env.dist +++ b/.env.dist @@ -79,10 +79,10 @@ DMARC_RUF=admin ################################### # Path to the admin interface if enabled -WEB_ADMIN = /admin +WEB_ADMIN=/admin # Path to the webmail if enabled -WEB_WEBMAIL = /webmail +WEB_WEBMAIL=/webmail ################################### # Advanced settings From 5786bb3e1bed73266a44b41290dcaa5186519713 Mon Sep 17 00:00:00 2001 From: kaiyou Date: Sat, 21 Oct 2017 15:22:40 +0200 Subject: [PATCH 27/27] Return the server address dynamically --- admin/mailu/internal/nginx.py | 16 +++++++++++----- admin/mailu/internal/views.py | 1 - 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/admin/mailu/internal/nginx.py b/admin/mailu/internal/nginx.py index 6fcb3b2b..a7a8c5b4 100644 --- a/admin/mailu/internal/nginx.py +++ b/admin/mailu/internal/nginx.py @@ -1,5 +1,7 @@ from mailu import db, models +import socket + SUPPORTED_AUTH_METHODS = ["none", "plain"] @@ -12,6 +14,12 @@ STATUSES = { } +SERVER_MAP = { + "imap": ("imap", 143), + "smtp": ("smtp", 25) +} + + def handle_authentication(headers): """ Handle an HTTP nginx authentication request See: http://nginx.org/en/docs/mail/ngx_mail_auth_http_module.html#protocol @@ -57,8 +65,6 @@ def get_status(protocol, status): def get_server(protocol): - servers = { - "imap": ("172.18.0.12", 143), - "smtp": ("172.18.0.9", 25) - } - return servers[protocol] + hostname, port = SERVER_MAP[protocol] + address = socket.gethostbyname(hostname) + return address, port diff --git a/admin/mailu/internal/views.py b/admin/mailu/internal/views.py index f1dd9d19..04d3268a 100644 --- a/admin/mailu/internal/views.py +++ b/admin/mailu/internal/views.py @@ -11,4 +11,3 @@ def nginx_authentication(): for key, value in headers.items(): response.headers[key] = str(value) return response -
{% trans %}DNS SPF entries{% endtrans %}
-{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"
-{{ domain.name }}. 600 IN SPF "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"