Merge pull request #504 from modoboa/custom-tls-fix

Added custom tls cert support
This commit is contained in:
Antoine Nguyen
2024-04-24 08:30:43 +02:00
committed by GitHub
5 changed files with 109 additions and 18 deletions

View File

@@ -1,5 +1,5 @@
modoboa-installer
=================
**modoboa-installer**
=====================
|workflow| |codecov|
@@ -76,7 +76,7 @@ If you want more information about the installation process, add the
``--debug`` option to your command line.
Upgrade mode
------------
============
An experimental upgrade mode is available.
@@ -92,7 +92,7 @@ You can activate it as follows::
It will automatically install latest versions of modoboa and its plugins.
Backup mode
-----------
===========
An experimental backup mode is available.
@@ -129,7 +129,7 @@ configuration file (set enabled to False).
This can be useful for larger instance.
Restore mode
------------
============
An experimental restore mode is available.
@@ -140,7 +140,7 @@ You can start the process as follows::
Then wait for the process to finish.
Change the generated hostname
-----------------------------
=============================
By default, the installer will setup your email server using the
following hostname: ``mail.<your domain>``. If you want a different
@@ -159,12 +159,24 @@ modifications.
Finally, run the installer without the
``--stop-after-configfile-check`` option.
Let's Encrypt certificate
-------------------------
Certificate
===========
Self-signed
-----------
It is the default type of certificate the installer will generate, it
is however not recommended for production use.
Letsencrypt
-----------
.. warning::
Please note this option requires the hostname you're using to be
Please note that by using this option, you agree to the `ToS
<https://community.letsencrypt.org/tos>`_ of
letsencrypt and that your IP will be logged (see ToS).
Please also note this option requires the hostname you're using to be
valid (ie. it can be resolved with a DNS query) and to match the
server you're installing Modoboa on.
@@ -175,6 +187,8 @@ modify the following settings::
[certificate]
generate = true
type = letsencrypt
tls_cert_file_path =
tls_key_file_path =
[letsencrypt]
email = admin@example.com
@@ -182,6 +196,27 @@ modify the following settings::
Change the ``email`` setting to a valid value since it will be used
for account recovery.
Manual
------
.. warning::
It is not possible to configure manual certs interactively, so
you'll have to do it in 2 steps. Please run ``run.py`` with
`--stop-after-configfile-check` first, configure your file as
desired and apply the configuration as written bellow. Then run
``run.py`` again but without `--stop-after-configfile-check` or
`--interactive`.
If you want to use already generated certs, simply edit the
``installer.cfg`` file and modify the following settings::
[certificate]
generate = true
type = manual
tls_cert_file_path = *path to tls key file*
tls_key_file_path = * path to tls fullchain file*
.. |workflow| image:: https://github.com/modoboa/modoboa-installer/workflows/Modoboa%20installer/badge.svg
.. |codecov| image:: http://codecov.io/github/modoboa/modoboa-installer/coverage.svg?branch=master
:target: http://codecov.io/github/modoboa/modoboa-installer?branch=master

View File

@@ -39,7 +39,16 @@ ConfigDictTemplate = [
"default": "self-signed",
"customizable": True,
"question": "Please choose your certificate type",
"values": ["self-signed", "letsencrypt"],
"values": ["self-signed", "letsencrypt", "manual"],
"non_interactive_values": ["manual"],
},
{
"option": "tls_cert_file_path",
"default": ""
},
{
"option": "tls_key_file_path",
"default": ""
}
],
},

View File

@@ -7,7 +7,7 @@ from . import package
from . import utils
class CertificateBackend(object):
class CertificateBackend:
"""Base class."""
def __init__(self, config):
@@ -24,13 +24,44 @@ class CertificateBackend(object):
return False
return True
def generate_cert(self):
"""Create a certificate."""
pass
class ManualCertificate(CertificateBackend):
"""Use certificate provided."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
path_correct = True
self.tls_cert_file_path = self.config.get("certificate",
"tls_key_file_path")
self.tls_key_file_path = self.config.get("certificate",
"tls_cert_file_path")
if not os.path.exists(self.tls_key_file_path):
utils.error("'tls_key_file_path' path is not accessible")
path_correct = False
if not os.path.exists(self.tls_cert_file_path):
utils.error("'tls_cert_file_path' path is not accessible")
path_correct = False
if not path_correct:
sys.exit(1)
self.config.set("general", "tls_key_file",
self.tls_key_file_path)
self.config.set("general", "tls_cert_file",
self.tls_cert_file_path)
class SelfSignedCertificate(CertificateBackend):
"""Create a self signed certificate."""
def __init__(self, *args, **kwargs):
"""Sanity checks."""
super(SelfSignedCertificate, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.config.has_option("general", "tls_key_file"):
# Compatibility
return
@@ -65,7 +96,7 @@ class LetsEncryptCertificate(CertificateBackend):
def __init__(self, *args, **kwargs):
"""Update config."""
super(LetsEncryptCertificate, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.hostname = self.config.get("general", "hostname")
self.config.set("general", "tls_cert_file", (
"/etc/letsencrypt/live/{}/fullchain.pem".format(self.hostname)))
@@ -119,8 +150,13 @@ class LetsEncryptCertificate(CertificateBackend):
def get_backend(config):
"""Return the appropriate backend."""
if not config.getboolean("certificate", "generate"):
cert_type = config.get("certificate", "type")
condition = (not config.getboolean("certificate", "generate") and
cert_type != "manual")
if condition:
return None
if config.get("certificate", "type") == "letsencrypt":
if cert_type == "letsencrypt":
return LetsEncryptCertificate(config)
if cert_type == "manual":
return ManualCertificate(config)
return SelfSignedCertificate(config)

View File

@@ -316,6 +316,17 @@ def get_entry_value(entry, interactive):
if entry.get("values") and user_value != "":
user_value = values[int(user_value)]
non_interactive_values = entry.get("non_interactive_values", [])
if user_value in non_interactive_values:
error(
f"{user_value} cannot be set interactively. "
"Please configure installer.cfg manually by running "
"'python3 run.py --stop-after-configfile-check domain'. "
"Check modoboa-installer README for more information."
)
sys.exit(1)
return user_value if user_value else default_value

2
run.py
View File

@@ -203,7 +203,7 @@ def main(input_args):
if not args.skip_checks:
utils.printcolor("Checking the installer...", utils.BLUE)
checks.handle()
utils.success("Checks complete")
utils.success("Checks complete\n")
is_config_file_available, outdate_config = utils.check_config_file(
args.configfile, args.interactive, args.upgrade, args.backup, is_restoring)