diff --git a/management/auth.py b/management/auth.py index ca25cf54..c576d01c 100644 --- a/management/auth.py +++ b/management/auth.py @@ -1,4 +1,7 @@ import base64, os, os.path, hmac, json, secrets +from datetime import timedelta + +from expiringdict import ExpiringDict import utils from mailconfig import get_mail_password, get_mail_user_privileges @@ -8,12 +11,13 @@ DEFAULT_KEY_PATH = '/var/lib/mailinabox/api.key' DEFAULT_AUTH_REALM = 'Mail-in-a-Box Management Server' class AuthService: - def __init__(self, session): + def __init__(self): self.auth_realm = DEFAULT_AUTH_REALM self.key_path = DEFAULT_KEY_PATH + self.max_session_duration = timedelta(days=2) self.init_system_api_key() - self.sessions = session + self.sessions = ExpiringDict(max_len=64, max_age_seconds=self.max_session_duration.total_seconds()) def init_system_api_key(self): """Write an API key to a local file so local processes can use the API""" diff --git a/management/daemon.py b/management/daemon.py index 5773f4ed..98c6689c 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -15,19 +15,18 @@ import multiprocessing.pool, subprocess from functools import wraps -from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response, session +from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response import auth, utils from mailconfig import get_mail_users, get_mail_users_ex, get_admins, add_mail_user, set_mail_password, remove_mail_user from mailconfig import get_mail_user_privileges, add_remove_mail_user_privilege from mailconfig import get_mail_aliases, get_mail_aliases_ex, get_mail_domains, add_mail_alias, remove_mail_alias from mfa import get_public_mfa_state, provision_totp, validate_totp_secret, enable_mfa, disable_mfa -from datetime import timedelta - -DEFAULT_SESSION_SECRET_PATH = '/var/lib/mailinabox/session.key' env = utils.load_environment() +auth_service = auth.AuthService() + # We may deploy via a symbolic link, which confuses flask's template finding. me = __file__ try: @@ -45,16 +44,6 @@ with open(os.path.join(os.path.dirname(me), "csr_country_codes.tsv")) as f: app = Flask(__name__, template_folder=os.path.abspath(os.path.join(os.path.dirname(me), "templates"))) -# sets up Flask session to be permanent and lasting 2 days. -with open(DEFAULT_SESSION_SECRET_PATH, 'r') as file: - app.secret_key = file.read() -app.config['SESSION_PERMANENT'] = True -app.config['SESSION_TYPE'] = 'filesystem' -app.config['PERMANENT_SESSION_LIFETIME']=timedelta(days=2) - -# AuthService uses the Flask session -auth_service = auth.AuthService(session) - # Decorator to protect views that require a user with 'admin' privileges. def authorized_personnel_only(viewfunc): @wraps(viewfunc) @@ -173,7 +162,7 @@ def login(): "privileges": privs, "api_key": auth_service.create_session_key(email, env, type='login'), } - session.permanent = True + app.logger.info("New login session created for {}".format(email)) # Return. diff --git a/setup/management.sh b/setup/management.sh index 90f93eda..cebed8d5 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -101,12 +101,11 @@ export LC_TYPE=en_US.UTF-8 mkdir -p /var/lib/mailinabox tr -cd '[:xdigit:]' < /dev/urandom | head -c 32 > /var/lib/mailinabox/api.key -tr -cd '[:alnum:]' < /dev/urandom | head -c 64 > /var/lib/mailinabox/session.key -chmod 640 /var/lib/mailinabox/{api,session}.key +chmod 640 /var/lib/mailinabox/api.key source $venv/bin/activate export PYTHONPATH=$(pwd)/management -exec gunicorn -b localhost:10222 -w 2 wsgi:app +exec gunicorn -b localhost:10222 -w 1 wsgi:app EOF chmod +x $inst_dir/start cp --remove-destination conf/mailinabox.service /lib/systemd/system/mailinabox.service # target was previously a symlink so remove it first