From a071181c610b19420845cec7e7b8d99c66cf918a Mon Sep 17 00:00:00 2001 From: cristi Date: Fri, 25 Jan 2019 16:04:20 +0200 Subject: [PATCH 1/7] Allow to automatically create admin account during startup. --- core/admin/mailu/manage.py | 40 +++++++++++++++++++++++++++++--------- core/admin/start.py | 8 ++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py index e11644e7..095b9290 100644 --- a/core/admin/mailu/manage.py +++ b/core/admin/mailu/manage.py @@ -42,22 +42,44 @@ def advertise(): @click.argument('localpart') @click.argument('domain_name') @click.argument('password') +@click.option('-m', '--mode') @flask_cli.with_appcontext -def admin(localpart, domain_name, password): +def admin(localpart, domain_name, password, mode = 'create'): """ Create an admin user + 'mode' can be: + - 'create' (default) Will try to create user and will raise an exception if present + - 'ifmissing': if user exists, nothing happens, else it will be created + - 'update': user is created or, if it exists, its password gets updated """ domain = models.Domain.query.get(domain_name) if not domain: domain = models.Domain(name=domain_name) db.session.add(domain) - user = models.User( - localpart=localpart, - domain=domain, - global_admin=True - ) - user.set_password(password) - db.session.add(user) - db.session.commit() + + user = None + if mode == 'ifmissing' or mode == 'update': + email = '{}@{}'.format(localpart, domain_name) + user = models.User.query.get(email) + + if user and mode == 'ifmissing': + print('user %s exists, not updating' % email) + return + + if not user: + user = models.User( + localpart=localpart, + domain=domain, + global_admin=True + ) + user.set_password(password) + db.session.add(user) + db.session.commit() + print("created admin user") + elif mode == 'update': + user.set_password(password) + db.session.commit() + print("updated admin password") + @mailu.command() diff --git a/core/admin/start.py b/core/admin/start.py index bf0dc38f..e8de9f77 100755 --- a/core/admin/start.py +++ b/core/admin/start.py @@ -4,4 +4,12 @@ import os os.system("flask mailu advertise") os.system("flask db upgrade") + +if 'INITIAL_ADMIN_ACCOUNT' in os.environ and 'INITIAL_ADMIN_DOMAIN' in os.environ and 'INITIAL_ADMIN_PW' in os.environ: + mode = 'ifmissing' + if 'INITIAL_ADMIN_MODE' in os.environ: + mode = os.environ['INITIAL_ADMIN_MODE'] + os.system("flask mailu admin %s %s '%s' --mode %s" % ( + os.environ['INITIAL_ADMIN_ACCOUNT'], os.environ['INITIAL_ADMIN_DOMAIN'], os.environ['INITIAL_ADMIN_PW'], mode)) + os.system("gunicorn -w 4 -b :80 --access-logfile - --error-logfile - --preload 'mailu:create_app()'") From cfbc38b956c0a35304b6e8760c80f8f792716799 Mon Sep 17 00:00:00 2001 From: cristi Date: Fri, 25 Jan 2019 16:22:27 +0200 Subject: [PATCH 2/7] Updated doc --- docs/kubernetes/mailu/index.rst | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/kubernetes/mailu/index.rst b/docs/kubernetes/mailu/index.rst index 0f2451fa..425f4858 100644 --- a/docs/kubernetes/mailu/index.rst +++ b/docs/kubernetes/mailu/index.rst @@ -131,11 +131,31 @@ And in the pod run the following command. The command uses following entries: python manage.py admin root example.com password - ``admin`` Make it an admin user -- ``root`` The first part of the e-mail adres (ROOT@example.com) +- ``root`` The first part of the e-mail address (ROOT@example.com) - ``example.com`` the domain appendix - ``password`` the chosen password for the user +Now you should be able to login on the mail account: https://mail.example.com/admin + + +Create the first admin account - automatically +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to have your user created automatically, you need to edit the Deployment descriptor and add +a few environment variables: + +.. code-block:: bash + +- INITIAL_ADMIN_ACCOUNT = ``root`` The first part of the e-mail address (ROOT@example.com) +- INITIAL_ADMIN_DOMAIN = ``example.com`` the domain appendix +- INITIAL_ADMIN_PW = ``password`` the chosen password for the user + + +Optionally, you can add the environment ``INITIAL_ADMIN_MODE`` with the value ``update`` if you want to +code to *always* update the password whenever container is started. Which could mean anytime, +so you probably do not want this :-) + Now you should be able to login on the mail account: https://mail.example.com/admin Adaptations From 244c78a17b6983c263923d94d08a5c8586bf28f4 Mon Sep 17 00:00:00 2001 From: cristi Date: Fri, 25 Jan 2019 16:24:49 +0200 Subject: [PATCH 3/7] Adding entry in Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd22a47..b52ee6cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ v1.6.1 - unreleased ------------------- - Enhancement: Make Unbound drop privileges after binding to port - Enhancement: Create an Authentication Token with IPv6 address restriction ([#829](https://github.com/Mailu/Mailu/issues/829)) +- Enhancement: Automatically create admin user on container startup if given appropriate environment variables v1.6.0 - 2019-01-18 ------------------- From 078082fac9211f867a99c2f8429f87388b90fd0f Mon Sep 17 00:00:00 2001 From: cristi Date: Tue, 21 May 2019 21:06:25 +0300 Subject: [PATCH 4/7] Hopefully improved documentation around initial admin account creation. --- docs/compose/setup.rst | 8 +++++++- docs/configuration.rst | 25 +++++++++++++++++++++++++ docs/kubernetes/mailu/index.rst | 31 +++++++++++-------------------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/docs/compose/setup.rst b/docs/compose/setup.rst index c1a620e6..2352f16e 100644 --- a/docs/compose/setup.rst +++ b/docs/compose/setup.rst @@ -96,7 +96,13 @@ You may now start Mailu. Move the to the Mailu directory and run: docker-compose up -d -Finally, you must create the initial admin user account: +Finally, you need an admin user account. + +You can have the system create it automatically: +use the environment variables ``INITIAL_ACCOUNT*`` as described in :ref:`admin_account` +You should set ``INITIAL_ADMIN_MODE`` also to either ``update`` or ``ifmissing``. Leaving it with the default value could cause errors when restarting the system. + +Else, if you don't go with the automatic way, you need to manually create the admin account now: .. code-block:: bash diff --git a/docs/configuration.rst b/docs/configuration.rst index e7dfa2af..4e38f109 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -91,6 +91,31 @@ Both ``SITENAME`` and ``WEBSITE`` are customization options for the panel menu in the admin interface, while ``SITENAME`` is a customization option for every Web interface. +.. _admin_account: + +Admin account - automatic creation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For administrative tasks, an admin user account will be needed. You can create it manually, +after deploying the system, or automatically. +To create it manually, follow the specific deployment method documentation. + +To have the account created automatically, you just need to define a few environment variables: + +.. code-block:: bash + + INITIAL_ADMIN_ACCOUNT = ``root`` The first part of the e-mail address (ROOT@example.com) + INITIAL_ADMIN_DOMAIN = ``example.com`` the domain appendix. Most probably identical to the DOMAIN variable + INITIAL_ADMIN_PW = ``password`` the chosen password for the user + +Also, environment variable ``INITIAL_ADMIN_MODE`` defines how the code should behave when it will +try to create the admin user: + +- ``create`` (default) Will try to create user and will raise an exception if present +- ``ifmissing``: if user exists, nothing happens, else it will be created +- ``update``: user is created or, if it exists, its password gets updated + +Depending on your particular deployment you most probably will want to change the default. + Advanced settings ----------------- diff --git a/docs/kubernetes/mailu/index.rst b/docs/kubernetes/mailu/index.rst index 13943606..ab5f3ca4 100644 --- a/docs/kubernetes/mailu/index.rst +++ b/docs/kubernetes/mailu/index.rst @@ -117,7 +117,17 @@ Create the first admin account ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When the cluster is online you need to create you master user to access https://mail.example.com/admin -Enter the main ``admin`` pod to create the root account: + +You can create it now manually, or have the system create it automatically. + +If you want the system to create the admin user account automatically, see :ref:`admin_account` +about the environment variables needed (``INITIAL_ADMIN_*``). +Also, important, taking into consideration that a pod in Kubernetes can be stopped/rescheduled at +any time, you should set ``INITIAL_ADMIN_MODE`` to either ``update`` or ``ifmissing`` - depending on what you +want to happen to its password. + + +To create the admin user account manually, enter the main ``admin`` pod: .. code-block:: bash @@ -139,25 +149,6 @@ And in the pod run the following command. The command uses following entries: Now you should be able to login on the mail account: https://mail.example.com/admin -Create the first admin account - automatically -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want to have your user created automatically, you need to edit the Deployment descriptor and add -a few environment variables: - -.. code-block:: bash - -- INITIAL_ADMIN_ACCOUNT = ``root`` The first part of the e-mail address (ROOT@example.com) -- INITIAL_ADMIN_DOMAIN = ``example.com`` the domain appendix -- INITIAL_ADMIN_PW = ``password`` the chosen password for the user - - -Optionally, you can add the environment ``INITIAL_ADMIN_MODE`` with the value ``update`` if you want to -code to *always* update the password whenever container is started. Which could mean anytime, -so you probably do not want this :-) - -Now you should be able to login on the mail account: https://mail.example.com/admin - Adaptations ----------- From 52971ed42eefc77d5e3b9c028d6601985e25b180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Wed, 21 Aug 2019 16:34:25 +0300 Subject: [PATCH 5/7] Use os.environment.get() --- core/admin/start.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/admin/start.py b/core/admin/start.py index e8de9f77..d8165ff3 100755 --- a/core/admin/start.py +++ b/core/admin/start.py @@ -5,11 +5,12 @@ import os os.system("flask mailu advertise") os.system("flask db upgrade") -if 'INITIAL_ADMIN_ACCOUNT' in os.environ and 'INITIAL_ADMIN_DOMAIN' in os.environ and 'INITIAL_ADMIN_PW' in os.environ: - mode = 'ifmissing' - if 'INITIAL_ADMIN_MODE' in os.environ: - mode = os.environ['INITIAL_ADMIN_MODE'] - os.system("flask mailu admin %s %s '%s' --mode %s" % ( - os.environ['INITIAL_ADMIN_ACCOUNT'], os.environ['INITIAL_ADMIN_DOMAIN'], os.environ['INITIAL_ADMIN_PW'], mode)) +account = os.environ.get("INITIAL_ADMIN_ACCOUNT") +domain = os.environ.get("INITIAL_ADMIN_DOMAIN") +password = os.environ.get("INITIAL_ADMIN_PW") + +if account is not None and domain is not None and password is not None: + mode = os.environ.get("INITIAL_ADMIN_MODE", default="ifmissing") + os.system("flask mailu admin %s %s '%s' --mode %s" % (account, domain, password, mode)) os.system("gunicorn -w 4 -b :80 --access-logfile - --error-logfile - --preload 'mailu:create_app()'") From 7d964874e8c9546c557280d6ad2cfbe3713f9382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Wed, 21 Aug 2019 16:35:41 +0300 Subject: [PATCH 6/7] Cleanup spaces around '=' --- core/admin/mailu/manage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/mailu/manage.py b/core/admin/mailu/manage.py index c22f9264..eeda30f3 100644 --- a/core/admin/mailu/manage.py +++ b/core/admin/mailu/manage.py @@ -44,7 +44,7 @@ def advertise(): @click.argument('password') @click.option('-m', '--mode') @flask_cli.with_appcontext -def admin(localpart, domain_name, password, mode = 'create'): +def admin(localpart, domain_name, password, mode='create'): """ Create an admin user 'mode' can be: - 'create' (default) Will try to create user and will raise an exception if present From 2a20c1e8eeb6b93180314bf310d0c02df9a0a7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Wed, 21 Aug 2019 17:50:14 +0300 Subject: [PATCH 7/7] Tests for initial admin create --- tests/compose/core/00_create_users.sh | 12 +++++++++--- tests/compose/core/mailu.env | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/compose/core/00_create_users.sh b/tests/compose/core/00_create_users.sh index eb214f64..d2ee5c20 100755 --- a/tests/compose/core/00_create_users.sh +++ b/tests/compose/core/00_create_users.sh @@ -1,5 +1,11 @@ -echo "Creating users ..." -docker-compose -f tests/compose/core/docker-compose.yml exec admin flask mailu admin admin mailu.io password || exit 1 +echo "Users tests ..." +# Should fail, admin is already auto-created +docker-compose -f tests/compose/core/docker-compose.yml exec admin flask mailu admin admin mailu.io 'FooBar' && exit 1 +echo "The above error was intended!" +# Should not fail, but does nothing; ifmissing mode +docker-compose -f tests/compose/core/docker-compose.yml exec admin flask mailu admin admin mailu.io 'FooBar' --mode=ifmissing || exit 1 +# Should not fail and update the password; update mode +docker-compose -f tests/compose/core/docker-compose.yml exec admin flask mailu admin admin mailu.io 'password' --mode=update || exit 1 docker-compose -f tests/compose/core/docker-compose.yml exec admin flask mailu user user mailu.io 'password' 'SHA512-CRYPT' || exit 1 docker-compose -f tests/compose/core/docker-compose.yml exec admin flask mailu user 'user/with/slash' mailu.io 'password' 'SHA512-CRYPT' || exit 1 -echo "Admin and user successfully created!" +echo "User testing succesfull!" diff --git a/tests/compose/core/mailu.env b/tests/compose/core/mailu.env index d77f3a2d..dd7bd25f 100644 --- a/tests/compose/core/mailu.env +++ b/tests/compose/core/mailu.env @@ -140,3 +140,8 @@ REAL_IP_FROM= # choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no) REJECT_UNLISTED_RECIPIENT= + +# Test for initial admin create +INITIAL_ADMIN_ACCOUNT=admin +INITIAL_ADMIN_DOMAIN=mailu.io +INITIAL_ADMIN_PW=FooBar \ No newline at end of file