Merge remote-tracking branch 'upstream/master' into import-export

master
Alexander Graf 3 years ago
commit dc5464f254

@ -1,5 +1,8 @@
import flask
import flask_bootstrap
import redis
from flask_kvsession import KVSessionExtension
from simplekv.memory.redisstore import RedisStore
from mailu import utils, debug, models, manage, configuration
@ -17,6 +20,7 @@ def create_app_from_config(config):
# Initialize application extensions
config.init_app(app)
models.db.init_app(app)
KVSessionExtension(RedisStore(redis.StrictRedis().from_url('redis://{0}/3'.format(config['REDIS_ADDRESS']))), app).cleanup_sessions(app)
utils.limiter.init_app(app)
utils.babel.init_app(app)
utils.login.init_app(app)

@ -1,5 +1,6 @@
import os
from datetime import timedelta
from socrate import system
DEFAULT_CONFIG = {
@ -31,6 +32,7 @@ DEFAULT_CONFIG = {
'HOSTNAMES': 'mail.mailu.io,alternative.mailu.io,yetanother.mailu.io',
'POSTMASTER': 'postmaster',
'TLS_FLAVOR': 'cert',
'INBOUND_TLS_ENFORCE': False,
'AUTH_RATELIMIT': '10/minute;1000/hour',
'AUTH_RATELIMIT_SUBNET': True,
'DISABLE_STATISTICS': False,
@ -53,6 +55,7 @@ DEFAULT_CONFIG = {
'RECAPTCHA_PRIVATE_KEY': '',
# Advanced settings
'LOG_LEVEL': 'WARNING',
'SESSION_LIFETIME': 24,
'SESSION_COOKIE_SECURE': True,
'CREDENTIAL_ROUNDS': 12,
# Host settings
@ -135,6 +138,8 @@ class ConfigManager(dict):
self.config['QUOTA_STORAGE_URL'] = 'redis://{0}/1'.format(self.config['REDIS_ADDRESS'])
self.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
self.config['SESSION_COOKIE_HTTPONLY'] = True
self.config['SESSION_KEY_BITS'] = 128
self.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=int(self.config['SESSION_LIFETIME']))
# update the app config itself
app.config = self

@ -17,6 +17,9 @@ STATUSES = {
"smtp": "535 5.7.8",
"pop3": "-ERR Authentication failed"
}),
"encryption": ("Must issue a STARTTLS command first", {
"smtp": "530 5.7.0"
}),
}
def check_credentials(user, password, ip, protocol=None):
@ -42,12 +45,27 @@ def handle_authentication(headers):
protocol = headers["Auth-Protocol"]
# Incoming mail, no authentication
if method == "none" and protocol == "smtp":
server, port = get_server(headers["Auth-Protocol"], False)
return {
"Auth-Status": "OK",
"Auth-Server": server,
"Auth-Port": port
}
server, port = get_server(protocol, False)
if app.config["INBOUND_TLS_ENFORCE"]:
if "Auth-SSL" in headers and headers["Auth-SSL"] == "on":
return {
"Auth-Status": "OK",
"Auth-Server": server,
"Auth-Port": port
}
else:
status, code = get_status(protocol, "encryption")
return {
"Auth-Status": status,
"Auth-Error-Code" : code,
"Auth-Wait": 0
}
else:
return {
"Auth-Status": "OK",
"Auth-Server": server,
"Auth-Port": port
}
# Authenticated user
elif method == "plain":
server, port = get_server(headers["Auth-Protocol"], True)

@ -46,6 +46,8 @@ class ConfirmationForm(flask_wtf.FlaskForm):
class LoginForm(flask_wtf.FlaskForm):
class Meta:
csrf = False
email = fields.StringField(_('E-mail'), [validators.Email()])
pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
submit = fields.SubmitField(_('Sign in'))

@ -17,6 +17,7 @@ def login():
if form.validate_on_submit():
user = models.User.login(form.email.data, form.pw.data)
if user:
flask.session.regenerate()
flask_login.login_user(user)
endpoint = flask.request.args.get('next', '.index')
return flask.redirect(flask.url_for(endpoint)
@ -30,6 +31,7 @@ def login():
@access.authenticated
def logout():
flask_login.logout_user()
flask.session.destroy()
return flask.redirect(flask.url_for('.index'))

@ -119,6 +119,7 @@ def user_password(user_email):
if form.pw.data != form.pw2.data:
flask.flash('Passwords do not match', 'error')
else:
flask.session.regenerate()
user.set_password(form.pw.data)
models.db.session.commit()
flask.flash('Password updated for %s' % user)
@ -186,6 +187,7 @@ def user_signup(domain_name=None):
if domain.has_email(form.localpart.data):
flask.flash('Email is already used', 'error')
else:
flask.session.regenerate()
user = models.User(domain=domain)
form.populate_obj(user)
user.set_password(form.pw.data)

@ -13,6 +13,7 @@ Flask==1.0.2
Flask-Babel==0.12.2
Flask-Bootstrap==3.3.7.1
Flask-DebugToolbar==0.10.1
Flask-KVSession==0.6.2
Flask-Limiter==1.0.1
Flask-Login==0.4.1
flask-marshmallow==0.14.0

@ -3,6 +3,7 @@ Flask-Login
Flask-SQLAlchemy
Flask-bootstrap
Flask-Babel
Flask-KVSession
Flask-migrate
Flask-script
Flask-wtf

@ -12,6 +12,7 @@ smtp inet n - n - - smtpd
-o cleanup_service_name=outclean
outclean unix n - n - 0 cleanup
-o header_checks=pcre:/etc/postfix/outclean_header_filter.cf
-o nested_header_checks=
# Internal postfix services
pickup unix n - n 60 1 pickup

@ -5,6 +5,9 @@ skip_authenticated = false;
use = ["x-spamd-bar", "x-spam-level", "x-virus", "authentication-results"];
routines {
authentication-results {
add_smtp_user = false;
}
x-virus {
symbols = ["CLAM_VIRUS", "FPROT_VIRUS", "JUST_EICAR"];
}

@ -73,6 +73,13 @@ By default postfix uses "opportunistic TLS" for outbound mail. This can be chang
by setting ``OUTBOUND_TLS_LEVEL`` to ``encrypt``. This setting is highly recommended
if you are a relayhost that supports TLS.
Similarily by default nginx uses "opportunistic TLS" for inbound mail. This can be changed
by setting ``INBOUND_TLS_ENFORCE`` to ``True``. Please note that this is forbidden for
internet facing hosts according to e.g. `RFC 3207`_ , because this prevents MTAs without STARTTLS
support or e.g. mismatching TLS versions to deliver emails to Mailu.
.. _`RFC 3207`: https://tools.ietf.org/html/rfc3207
.. _fetchmail:
The ``FETCHMAIL_DELAY`` is a delay (in seconds) for the fetchmail service to
@ -142,6 +149,8 @@ The ``CREDENTIAL_ROUNDS`` (default: 12) setting is the number of rounds used by
The ``SESSION_COOKIE_SECURE`` (default: True) setting controls the secure flag on the cookies of the administrative interface. It should only be turned off if you intend to access it over plain HTTP.
``SESSION_LIFETIME`` (default: 24) is the length in hours a session is valid for on the administrative interface.
The ``LOG_LEVEL`` setting is used by the python start-up scripts as a logging threshold.
Log messages equal or higher than this priority will be printed.
Can be one of: CRITICAL, ERROR, WARNING, INFO, DEBUG or NOTSET.

@ -0,0 +1 @@
Add possibility to enforce inbound STARTTLS via INBOUND_TLS_LEVEL=true

@ -0,0 +1 @@
Hide the login of the user in sent emails

@ -0,0 +1 @@
Don't replace nested headers (typically in attached emails)

@ -0,0 +1 @@
Switch from client side sessions (cookies) to server-side sessions (Redis). This simplies the security model a lot and allows for an easier recovery should a cookie ever land in the hands of an attacker.
Loading…
Cancel
Save