mirror of
https://github.com/Mailu/Mailu.git
synced 2025-02-21 19:19:39 +02:00
Merge remote-tracking branch 'upstream/master' into import-export
This commit is contained in:
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.
|
||||
|
1
towncrier/newsfragments/1610.feature
Normal file
1
towncrier/newsfragments/1610.feature
Normal file
@ -0,0 +1 @@
|
||||
Add possibility to enforce inbound STARTTLS via INBOUND_TLS_LEVEL=true
|
1
towncrier/newsfragments/1638.fix
Normal file
1
towncrier/newsfragments/1638.fix
Normal file
@ -0,0 +1 @@
|
||||
Hide the login of the user in sent emails
|
1
towncrier/newsfragments/1660.bugfix
Normal file
1
towncrier/newsfragments/1660.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Don't replace nested headers (typically in attached emails)
|
1
towncrier/newsfragments/1783.misc
Normal file
1
towncrier/newsfragments/1783.misc
Normal file
@ -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…
x
Reference in New Issue
Block a user