From 4759146d9951b68b23fa1e6ebb83e24c7c54bed8 Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 21 Jun 2023 21:07:20 +0200 Subject: [PATCH 1/6] Added custom tls cert support --- README.rst | 57 ++++++++++++++++++----- modoboa_installer/config_dict_template.py | 19 ++++++-- modoboa_installer/ssl.py | 35 ++++++++++++-- modoboa_installer/utils.py | 13 ++++++ 4 files changed, 104 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index 0a5577e..33fa18d 100644 --- a/README.rst +++ b/README.rst @@ -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.``. If you want a different @@ -159,22 +159,37 @@ modifications. Finally, run the installer without the ``--stop-after-configfile-check`` option. -Let's Encrypt certificate -------------------------- +Certificate +=========== + +Self-signed +----------- + +It is the default way of the installer, it is however +not recommended for production use. We recommend using +letsencrypt for production. Using Letsencrypt imply that +you accept their Tos (see bellow) + +Letsencrypt +----------- .. warning:: - Please 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. + Please note that by using this option, you aggree to the `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. If you want to generate a valid certificate using `Let's Encrypt `_, edit the ``installer.cfg`` file and modify the following settings:: [certificate] - generate = true type = letsencrypt + tls_cert_file_path = + tls_key_file_path = [letsencrypt] email = admin@example.com @@ -182,6 +197,24 @@ 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. + To do so, please run ``run.py`` with `--stop-after-configfile-check`, + configure your file as desired and apply the configuration as + written bellow. Then run ``run.py`` 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] + 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 diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index bfd8f72..55d4580 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -30,16 +30,25 @@ ConfigDictTemplate = [ { "name": "certificate", "values": [ - { - "option": "generate", - "default": "true", - }, { "option": "type", "default": "self-signed", "customizable": True, "question": "Please choose your certificate type", - "values": ["self-signed", "letsencrypt"], + "value_return": ["manual"], + "values": ["self-signed", "letsencrypt", "manual"], + }, + { + "option": "tls_cert_file_path", + "customizable": True, + "question": "Please enter your certificate fullchain path", + "default": "" + }, + { + "option": "tls_key_file_path", + "customizable": True, + "question": "Please enter your certificate key path", + "default": "" } ], }, diff --git a/modoboa_installer/ssl.py b/modoboa_installer/ssl.py index a76ef67..a4c61a7 100644 --- a/modoboa_installer/ssl.py +++ b/modoboa_installer/ssl.py @@ -25,6 +25,34 @@ class CertificateBackend(object): return True +class ManualCertification(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) + + def generate_cert(self): + 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.""" @@ -119,8 +147,9 @@ class LetsEncryptCertificate(CertificateBackend): def get_backend(config): """Return the appropriate backend.""" - if not config.getboolean("certificate", "generate"): - return None - if config.get("certificate", "type") == "letsencrypt": + cert_type = config.get("certificate", "type") + if cert_type == "letsencrypt": return LetsEncryptCertificate(config) + if cert_type == "manual": + return ManualCertification(config) return SelfSignedCertificate(config) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 2332273..edab3bb 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -316,6 +316,19 @@ def get_entry_value(entry, interactive): if entry.get("values") and user_value != "": user_value = values[int(user_value)] + + condition = ( + entry.get("value_return") and + user_value in entry.get("value_return") + ) + if condition: + 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 From e900e6258f322efd95fcb30919014240118a9e87 Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 21 Jun 2023 21:10:50 +0200 Subject: [PATCH 2/6] Revert generate removing --- modoboa_installer/config_dict_template.py | 4 ++++ modoboa_installer/ssl.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 55d4580..490052e 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -30,6 +30,10 @@ ConfigDictTemplate = [ { "name": "certificate", "values": [ + { + "option": "generate", + "default": "true", + }, { "option": "type", "default": "self-signed", diff --git a/modoboa_installer/ssl.py b/modoboa_installer/ssl.py index a4c61a7..6b61db5 100644 --- a/modoboa_installer/ssl.py +++ b/modoboa_installer/ssl.py @@ -148,6 +148,10 @@ class LetsEncryptCertificate(CertificateBackend): def get_backend(config): """Return the appropriate backend.""" cert_type = config.get("certificate", "type") + condition = (not config.getboolean("certificate", "generate") and + cert_type != "manual") + if condition: + return None if cert_type == "letsencrypt": return LetsEncryptCertificate(config) if cert_type == "manual": From bc88110be650e3a4c202a7ae192f750e6ad0987e Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 21 Jun 2023 21:21:17 +0200 Subject: [PATCH 3/6] Fixed template --- modoboa_installer/config_dict_template.py | 4 ---- modoboa_installer/utils.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 490052e..a7fc685 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -44,14 +44,10 @@ ConfigDictTemplate = [ }, { "option": "tls_cert_file_path", - "customizable": True, - "question": "Please enter your certificate fullchain path", "default": "" }, { "option": "tls_key_file_path", - "customizable": True, - "question": "Please enter your certificate key path", "default": "" } ], diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index edab3bb..a70637b 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -324,7 +324,7 @@ def get_entry_value(entry, interactive): if condition: error(f"{user_value} cannot be set interactively, " "Please configure installer.cfg manually by running " - "'python3 run.py ----stop-after-configfile-check domain'. " + "'python3 run.py --stop-after-configfile-check domain'. " "Check modoboa-installer Readme for more information." ) sys.exit(1) From 469005b52893b5ad7e9bc560e66cd2b2c2941fad Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 22 Jun 2023 09:58:34 +0200 Subject: [PATCH 4/6] Fixed README --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 33fa18d..173d382 100644 --- a/README.rst +++ b/README.rst @@ -187,6 +187,7 @@ If you want to generate a valid certificate using `Let's Encrypt modify the following settings:: [certificate] + generate = true type = letsencrypt tls_cert_file_path = tls_key_file_path = @@ -211,6 +212,7 @@ 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* From 68ecf77045150f45b636aa6092144ef2083d2b75 Mon Sep 17 00:00:00 2001 From: Spitap Date: Fri, 23 Jun 2023 17:40:23 +0200 Subject: [PATCH 5/6] Fixed for upgrade --- modoboa_installer/ssl.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modoboa_installer/ssl.py b/modoboa_installer/ssl.py index 6b61db5..0b161c4 100644 --- a/modoboa_installer/ssl.py +++ b/modoboa_installer/ssl.py @@ -24,6 +24,10 @@ class CertificateBackend(object): return False return True + def generate_cert(self): + """Create a certificate.""" + pass + class ManualCertification(CertificateBackend): """Use certificate provided.""" @@ -46,7 +50,6 @@ class ManualCertification(CertificateBackend): if not path_correct: sys.exit(1) - def generate_cert(self): self.config.set("general", "tls_key_file", self.tls_key_file_path) self.config.set("general", "tls_cert_file", From 18369e238cc84da4fb9b3aeb00b4be3e61f13d07 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 24 Apr 2024 08:28:56 +0200 Subject: [PATCH 6/6] Few updates --- README.rst | 22 +++++++++++----------- modoboa_installer/config_dict_template.py | 2 +- modoboa_installer/ssl.py | 10 +++++----- modoboa_installer/utils.py | 16 +++++++--------- run.py | 2 +- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/README.rst b/README.rst index 173d382..c9b0917 100644 --- a/README.rst +++ b/README.rst @@ -165,19 +165,17 @@ Certificate Self-signed ----------- -It is the default way of the installer, it is however -not recommended for production use. We recommend using -letsencrypt for production. Using Letsencrypt imply that -you accept their Tos (see bellow) +It is the default type of certificate the installer will generate, it +is however not recommended for production use. Letsencrypt ----------- .. warning:: - Please note that by using this option, you aggree to the `ToS + Please note that by using this option, you agree to the `ToS `_ of - letsencrypt and that your IP will be logged (see ToS) + 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. @@ -202,11 +200,13 @@ Manual ------ .. warning:: - It is not possible to configure manual certs interactively. - To do so, please run ``run.py`` with `--stop-after-configfile-check`, - configure your file as desired and apply the configuration as - written bellow. Then run ``run.py`` without - `--stop-after-configfile-check` or `--interactive`. + + 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:: diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index a7fc685..bb7325d 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -39,8 +39,8 @@ ConfigDictTemplate = [ "default": "self-signed", "customizable": True, "question": "Please choose your certificate type", - "value_return": ["manual"], "values": ["self-signed", "letsencrypt", "manual"], + "non_interactive_values": ["manual"], }, { "option": "tls_cert_file_path", diff --git a/modoboa_installer/ssl.py b/modoboa_installer/ssl.py index 0b161c4..4dd7357 100644 --- a/modoboa_installer/ssl.py +++ b/modoboa_installer/ssl.py @@ -7,7 +7,7 @@ from . import package from . import utils -class CertificateBackend(object): +class CertificateBackend: """Base class.""" def __init__(self, config): @@ -29,7 +29,7 @@ class CertificateBackend(object): pass -class ManualCertification(CertificateBackend): +class ManualCertificate(CertificateBackend): """Use certificate provided.""" def __init__(self, *args, **kwargs): @@ -61,7 +61,7 @@ class SelfSignedCertificate(CertificateBackend): 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 @@ -96,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))) @@ -158,5 +158,5 @@ def get_backend(config): if cert_type == "letsencrypt": return LetsEncryptCertificate(config) if cert_type == "manual": - return ManualCertification(config) + return ManualCertificate(config) return SelfSignedCertificate(config) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index a70637b..18725c1 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -317,16 +317,14 @@ def get_entry_value(entry, interactive): if entry.get("values") and user_value != "": user_value = values[int(user_value)] - condition = ( - entry.get("value_return") and - user_value in entry.get("value_return") + 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." ) - if condition: - 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 diff --git a/run.py b/run.py index bdc7b5a..78b9877 100755 --- a/run.py +++ b/run.py @@ -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)