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| |workflow| |codecov|
@@ -76,7 +76,7 @@ If you want more information about the installation process, add the
``--debug`` option to your command line. ``--debug`` option to your command line.
Upgrade mode Upgrade mode
------------ ============
An experimental upgrade mode is available. 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. It will automatically install latest versions of modoboa and its plugins.
Backup mode Backup mode
----------- ===========
An experimental backup mode is available. An experimental backup mode is available.
@@ -129,7 +129,7 @@ configuration file (set enabled to False).
This can be useful for larger instance. This can be useful for larger instance.
Restore mode Restore mode
------------ ============
An experimental restore mode is available. An experimental restore mode is available.
@@ -140,7 +140,7 @@ You can start the process as follows::
Then wait for the process to finish. Then wait for the process to finish.
Change the generated hostname Change the generated hostname
----------------------------- =============================
By default, the installer will setup your email server using the By default, the installer will setup your email server using the
following hostname: ``mail.<your domain>``. If you want a different following hostname: ``mail.<your domain>``. If you want a different
@@ -159,14 +159,26 @@ modifications.
Finally, run the installer without the Finally, run the installer without the
``--stop-after-configfile-check`` option. ``--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:: .. 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
valid (ie. it can be resolved with a DNS query) and to match the <https://community.letsencrypt.org/tos>`_ of
server you're installing Modoboa on. 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.
If you want to generate a valid certificate using `Let's Encrypt If you want to generate a valid certificate using `Let's Encrypt
<https://letsencrypt.org/>`_, edit the ``installer.cfg`` file and <https://letsencrypt.org/>`_, edit the ``installer.cfg`` file and
@@ -175,6 +187,8 @@ modify the following settings::
[certificate] [certificate]
generate = true generate = true
type = letsencrypt type = letsencrypt
tls_cert_file_path =
tls_key_file_path =
[letsencrypt] [letsencrypt]
email = admin@example.com 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 Change the ``email`` setting to a valid value since it will be used
for account recovery. 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 .. |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 .. |codecov| image:: http://codecov.io/github/modoboa/modoboa-installer/coverage.svg?branch=master
:target: http://codecov.io/github/modoboa/modoboa-installer?branch=master :target: http://codecov.io/github/modoboa/modoboa-installer?branch=master

View File

@@ -39,7 +39,16 @@ ConfigDictTemplate = [
"default": "self-signed", "default": "self-signed",
"customizable": True, "customizable": True,
"question": "Please choose your certificate type", "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 from . import utils
class CertificateBackend(object): class CertificateBackend:
"""Base class.""" """Base class."""
def __init__(self, config): def __init__(self, config):
@@ -24,13 +24,44 @@ class CertificateBackend(object):
return False return False
return True 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): class SelfSignedCertificate(CertificateBackend):
"""Create a self signed certificate.""" """Create a self signed certificate."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Sanity checks.""" """Sanity checks."""
super(SelfSignedCertificate, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if self.config.has_option("general", "tls_key_file"): if self.config.has_option("general", "tls_key_file"):
# Compatibility # Compatibility
return return
@@ -65,7 +96,7 @@ class LetsEncryptCertificate(CertificateBackend):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Update config.""" """Update config."""
super(LetsEncryptCertificate, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.hostname = self.config.get("general", "hostname") self.hostname = self.config.get("general", "hostname")
self.config.set("general", "tls_cert_file", ( self.config.set("general", "tls_cert_file", (
"/etc/letsencrypt/live/{}/fullchain.pem".format(self.hostname))) "/etc/letsencrypt/live/{}/fullchain.pem".format(self.hostname)))
@@ -119,8 +150,13 @@ class LetsEncryptCertificate(CertificateBackend):
def get_backend(config): def get_backend(config):
"""Return the appropriate backend.""" """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 return None
if config.get("certificate", "type") == "letsencrypt": if cert_type == "letsencrypt":
return LetsEncryptCertificate(config) return LetsEncryptCertificate(config)
if cert_type == "manual":
return ManualCertificate(config)
return SelfSignedCertificate(config) return SelfSignedCertificate(config)

View File

@@ -316,6 +316,17 @@ def get_entry_value(entry, interactive):
if entry.get("values") and user_value != "": if entry.get("values") and user_value != "":
user_value = values[int(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 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: if not args.skip_checks:
utils.printcolor("Checking the installer...", utils.BLUE) utils.printcolor("Checking the installer...", utils.BLUE)
checks.handle() checks.handle()
utils.success("Checks complete") utils.success("Checks complete\n")
is_config_file_available, outdate_config = utils.check_config_file( is_config_file_available, outdate_config = utils.check_config_file(
args.configfile, args.interactive, args.upgrade, args.backup, is_restoring) args.configfile, args.interactive, args.upgrade, args.backup, is_restoring)