Merge branch 'refactor-nginx'

master
kaiyou 7 years ago
commit 9dd4150e7c

@ -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)
@ -38,8 +36,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
@ -76,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
###################################
@ -87,6 +95,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

@ -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

@ -9,18 +9,15 @@ import flask_babel
import os
import docker
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',
'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,
@ -50,14 +47,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()))
@ -82,7 +71,9 @@ def inject_user():
return dict(current_user=flask_login.current_user)
# Import views
from mailu.views import *
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):

@ -0,0 +1,6 @@
from flask import Blueprint
internal = Blueprint('internal', __name__)
from mailu.internal import views

@ -0,0 +1,70 @@
from mailu import db, models
import socket
SUPPORTED_AUTH_METHODS = ["none", "plain"]
STATUSES = {
"authentication": ("Authentication credentials invalid", {
"imap": "AUTHENTICATIONFAILED",
"smtp": "535 5.7.8",
"pop3": ""
}),
}
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
"""
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):
hostname, port = SERVER_MAP[protocol]
address = socket.gethostbyname(hostname)
return address, port

@ -0,0 +1,13 @@
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

@ -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)

@ -0,0 +1,6 @@
from flask import Blueprint
ui = Blueprint('ui', __name__, static_folder='static', template_folder='templates')
from mailu.ui.views import *

@ -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

@ -15,6 +15,7 @@
{% endblock %}
{% block box %}
{% let hostname = config["HOSTNAMES"].split(",")[0] %}
<table class="table table-bordered">
<tbody>
<tr>
@ -28,8 +29,8 @@
<tr>
<th>{% trans %}DNS SPF entries{% endtrans %}</th>
<td><pre>
{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"
{{ domain.name }}. 600 IN SPF "v=spf1 mx a:{{ config["HOSTNAME"] }} -all"</pre></td>
{{ domain.name }}. 600 IN TXT "v=spf1 mx a:{{ hostname }} -all"
{{ domain.name }}. 600 IN SPF "v=spf1 mx a:{{ hostname }} -all"</pre></td>
</tr>
{% if domain.dkim_publickey %}
<tr>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save