From c7499e5733598a056f9bef1f3a656bd92754b9ff Mon Sep 17 00:00:00 2001 From: Spitap Date: Tue, 4 Apr 2023 17:34:48 +0200 Subject: [PATCH 01/47] Added Rspamd installation --- modoboa_installer/config_dict_template.py | 39 ++++++++- .../scripts/files/nginx/modoboa.conf.tpl | 7 ++ .../scripts/files/postfix/main.cf.tpl | 25 +++--- .../files/rspamd/local.d/antivirus.conf.tpl | 11 +++ .../rspamd/local.d/dkim_signing.conf.tpl | 3 + .../files/rspamd/local.d/greylisting.conf.tpl | 2 + .../files/rspamd/local.d/mx_check.conf.tpl | 1 + .../scripts/files/rspamd/local.d/rbl.conf.tpl | 6 ++ .../scripts/files/rspamd/local.d/spf.conf.tpl | 6 ++ .../rspamd/local.d/worker-controller.inc | 1 + .../rspamd/local.d/worker-controller.inc.tpl | 1 + .../rspamd/local.d/worker-normal.inc.tpl | 1 + .../files/rspamd/local.d/worker-proxy.inc.tpl | 3 + modoboa_installer/scripts/postfix.py | 4 +- modoboa_installer/scripts/rspamd.py | 82 +++++++++++++++++++ 15 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/mx_check.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/rbl.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/spf.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/worker-normal.inc.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/worker-proxy.inc.tpl create mode 100644 modoboa_installer/scripts/rspamd.py diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 2436e3b5..c9ca67aa 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -229,12 +229,45 @@ def is_email(user_input): ] }, { - "name": "amavis", + "name": "rspamd", "values": [ { "option": "enabled", "default": "true", }, + { + "option": "password", + "default": make_password, + } + { + "option": "dnsbl", + "default": "true", + }, + { + "option": "dkim_keys_storage_dir", + "default": "/var/lib/dkim" + }, + { + "option": "keys_path_map", + "default": "/var/lib/dkim/keys.path.map" + }, + { + "option": "selectors_path_map", + "default": "/var/lib/dkim/selectors.path.map" + }, + { + "option": "greylisting", + "default": "true" + } + ], + }, + { + "name": "amavis", + "values": [ + { + "option": "enabled", + "default": "false", + }, { "option": "user", "default": "amavis", @@ -374,7 +407,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default": "true", + "default": "false", }, { "option": "config_dir", @@ -443,7 +476,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default": "true", + "default": "false", }, { "option": "user", diff --git a/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl b/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl index 9e60227c..5a1abeb5 100644 --- a/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl +++ b/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl @@ -48,6 +48,13 @@ server { try_files $uri $uri/ /index.html = 404; } +%{rspamd_enabled} location /rspamd/ { +%{rspamd_enabled} proxy_pass http://localhost:11334/; +%{rspamd_enabled} +%{rspamd_enabled} proxy_set_header Host $host; +%{rspamd_enabled} proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +%{rspamd_enabled} } + location / { include uwsgi_params; uwsgi_param UWSGI_SCRIPT instance.wsgi:application; diff --git a/modoboa_installer/scripts/files/postfix/main.cf.tpl b/modoboa_installer/scripts/files/postfix/main.cf.tpl index 294c2a01..c112975e 100644 --- a/modoboa_installer/scripts/files/postfix/main.cf.tpl +++ b/modoboa_installer/scripts/files/postfix/main.cf.tpl @@ -122,6 +122,11 @@ strict_rfc821_envelopes = yes %{opendkim_enabled}milter_default_action = accept %{opendkim_enabled}milter_content_timeout = 30s +# Rspamd setup +%{rspamd_enabled}smtpd_milters = inet:localhost:11332 +%{rspamd_enabled}milter_default_action = accept +%{rspamd_enabled}milter_protocol = 6 + # List of authorized senders smtpd_sender_login_maps = proxy:%{db_driver}:/etc/postfix/sql-sender-login-map.cf @@ -142,18 +147,18 @@ smtpd_recipient_restrictions = ## Postcreen settings # -postscreen_access_list = - permit_mynetworks - cidr:/etc/postfix/postscreen_spf_whitelist.cidr -postscreen_blacklist_action = enforce +%{rspamd_disabled}postscreen_access_list = +%{rspamd_disabled} permit_mynetworks +%{rspamd_disabled} cidr:/etc/postfix/postscreen_spf_whitelist.cidr +%{rspamd_disabled}postscreen_blacklist_action = enforce # Use some DNSBL -postscreen_dnsbl_sites = - zen.spamhaus.org=127.0.0.[2..11]*3 - bl.spameatingmonkey.net=127.0.0.2*2 - bl.spamcop.net=127.0.0.2 -postscreen_dnsbl_threshold = 3 -postscreen_dnsbl_action = enforce +%{rspamd_disabled}postscreen_dnsbl_sites = +%{rspamd_disabled} zen.spamhaus.org=127.0.0.[2..11]*3 +%{rspamd_disabled} bl.spameatingmonkey.net=127.0.0.2*2 +%{rspamd_disabled} bl.spamcop.net=127.0.0.2 +%{rspamd_disabled}postscreen_dnsbl_threshold = 3 +%{rspamd_disabled}postscreen_dnsbl_action = enforce postscreen_greet_banner = Welcome, please wait... postscreen_greet_action = enforce diff --git a/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl new file mode 100644 index 00000000..235ea0f0 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl @@ -0,0 +1,11 @@ +clamav { + symbol = "CLAM_VIRUS"; + type = "clamav"; + servers = "127.0.0.1:3310"; + patterns { + # symbol_name = "pattern"; + JUST_EICAR = '^Eicar-Test-Signature$'; + } +} + + diff --git a/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl new file mode 100644 index 00000000..0025c3b7 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl @@ -0,0 +1,3 @@ +try_fallback = false; +selector_map = "%selectors_path_map"; +path_map = "%keys_path_map"; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl new file mode 100644 index 00000000..cc44e3ab --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl @@ -0,0 +1,2 @@ +servers = "127.0.0.1:6379"; +%{postwhite_enabled}whitelisted_ip = "/etc/postfix/postscreen_spf_whitelist.cidr" diff --git a/modoboa_installer/scripts/files/rspamd/local.d/mx_check.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/mx_check.conf.tpl new file mode 100644 index 00000000..1ead4eee --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/mx_check.conf.tpl @@ -0,0 +1 @@ +enabled = true; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/rbl.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/rbl.conf.tpl new file mode 100644 index 00000000..35b23ba5 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/rbl.conf.tpl @@ -0,0 +1,6 @@ +# to disable all predefined rules if the user doesn't want dnsbl + +url_whitelist = []; + +rbls { +} diff --git a/modoboa_installer/scripts/files/rspamd/local.d/spf.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/spf.conf.tpl new file mode 100644 index 00000000..85a98bc0 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/spf.conf.tpl @@ -0,0 +1,6 @@ +spf_cache_size = 1k; +spf_cache_expire = 1d; +max_dns_nesting = 10; +max_dns_requests = 30; +min_cache_ttl = 5m; +disable_ipv6 = false; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc b/modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc new file mode 100644 index 00000000..8490a181 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc @@ -0,0 +1 @@ +enable_password = %controller_password diff --git a/modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc.tpl b/modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc.tpl new file mode 100644 index 00000000..8490a181 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc.tpl @@ -0,0 +1 @@ +enable_password = %controller_password diff --git a/modoboa_installer/scripts/files/rspamd/local.d/worker-normal.inc.tpl b/modoboa_installer/scripts/files/rspamd/local.d/worker-normal.inc.tpl new file mode 100644 index 00000000..a6ee8317 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/worker-normal.inc.tpl @@ -0,0 +1 @@ +enabled = false; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/worker-proxy.inc.tpl b/modoboa_installer/scripts/files/rspamd/local.d/worker-proxy.inc.tpl new file mode 100644 index 00000000..f64333fc --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/worker-proxy.inc.tpl @@ -0,0 +1,3 @@ +upstream "local" { + self_scan = yes; +} diff --git a/modoboa_installer/scripts/postfix.py b/modoboa_installer/scripts/postfix.py index 6a2d7438..ba1fcbc7 100644 --- a/modoboa_installer/scripts/postfix.py +++ b/modoboa_installer/scripts/postfix.py @@ -60,7 +60,9 @@ def get_template_context(self): "modoboa_instance_path": self.config.get( "modoboa", "instance_path"), "opendkim_port": self.config.get( - "opendkim", "port") + "opendkim", "port"), + "rspamd_disabled": "" if not self.config.get( + "rspamd", "enabled") else "#" }) return context diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py new file mode 100644 index 00000000..5e7640b1 --- /dev/null +++ b/modoboa_installer/scripts/rspamd.py @@ -0,0 +1,82 @@ +"""Amavis related functions.""" + +import os + +from .. import package +from .. import utils + +from . import base +from . import backup, install + + +class Rspamd(base.Installer): + + """Rspamd installer.""" + + appname = "rspamd" + packages = { + "deb": [ + "rspamd", "redis" + ] + } + config_files = ["local.d/dkim_signing.conf", + "local.d/mx_check.conf", + "local.d/spf.conf", + "local.d/worker-controller.inc", + "local.d/worker-normal.inc", + "local.d/worker-proxy.inc"] + + @property + def config_dir(self): + """Return appropriate config dir.""" + return "/etc/rspamd" + + def get_config_files(self): + """Return appropriate config files.""" + _config_files = self.config_files + if self.config.get("clamav", "enabled"): + _config_files.append("local.d/antivirus.conf") + if self.app_config["dnsbl"]: + _config_files.append("local.d/greylisting.conf") + if not self.app_config["dnsbl"]: + _config_files.append("local.d/rbl.conf") + return _config_files + + def get_template_context(self): + _context = super().get_template_context() + code, controller_password = utils.exec_cmd( + r"rspamadm pw -p {}".format(self.app_config["password"])) + if code != 0: + utils.error("Error setting rspamd password. " + "Please make sure it is not 'q1' or 'q2'." + "Storing the password in plain. See" + "https://rspamd.com/doc/quickstart.html#setting-the-controller-password") + _context["controller_password"] = password + else: + _context["controller_password"] = controller_password + return _context + + def custom_backup(self, path): + """Backup custom configuration if any.""" + custom_config_dir = os.path.join(self.config_dir, + "/local.d/") + custom_backup_dir = os.path.join(path, "/rspamd/") + local_files = [f for f in os.listdir(custom_config_dir) + if os.path.isfile(custom_config_dir, f) + ] + for file in local_files: + utils.copy_file(file, custom_backup_dir) + if len(local_files) != 0: + utils.success("Rspamd custom configuration saved!") + + def restore(self): + """Restore custom config files.""" + custom_config_dir = os.path.join(self.config_dir, + "/local.d/") + custom_backup_dir = os.path.join(path, "/rspamd/") + backed_up_files = [f for f in os.listdir(custom_backup_dir) + if os.path.isfile(custom_backup_dir, f) + ] + for file in backed_up_files: + utils.copy_file(file, custom_config_dir) + utils.success("Custom Rspamd configuration restored.") From f38b6e7416cfd6f986390ba4aab18703d44f0d0b Mon Sep 17 00:00:00 2001 From: Spitap Date: Tue, 4 Apr 2023 17:48:56 +0200 Subject: [PATCH 02/47] Fixed dict, few fixes --- modoboa_installer/config_dict_template.py | 3 ++- modoboa_installer/scripts/modoboa.py | 21 ++++++++++++++------- modoboa_installer/scripts/rspamd.py | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index c9ca67aa..52e92098 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -189,6 +189,7 @@ def is_email(user_input): "option": "extensions", "default": ( "modoboa-amavis " + "modoboa-rspamd " "modoboa-webmail modoboa-contacts " "modoboa-radicale" ), @@ -238,7 +239,7 @@ def is_email(user_input): { "option": "password", "default": make_password, - } + }, { "option": "dnsbl", "default": "true", diff --git a/modoboa_installer/scripts/modoboa.py b/modoboa_installer/scripts/modoboa.py index 1904a144..30b936e2 100644 --- a/modoboa_installer/scripts/modoboa.py +++ b/modoboa_installer/scripts/modoboa.py @@ -49,13 +49,10 @@ def __init__(self, *args, **kwargs): self.instance_path = self.config.get("modoboa", "instance_path") self.extensions = self.config.get("modoboa", "extensions").split() self.devmode = self.config.getboolean("modoboa", "devmode") - # Sanity check for amavis - self.amavis_enabled = False - if "modoboa-amavis" in self.extensions: - if self.config.getboolean("amavis", "enabled"): - self.amavis_enabled = True - else: - self.extensions.remove("modoboa-amavis") + # Sanity check for amavis and rspamd + self.amavis_enabled = sanity_check("modoboa-amavis", "amavis") + sanity_check("modoboa-rspamd", "rspamd") + if "modoboa-radicale" in self.extensions: if not self.config.getboolean("radicale", "enabled"): self.extensions.remove("modoboa-radicale") @@ -63,6 +60,16 @@ def __init__(self, *args, **kwargs): self.opendkim_enabled = self.config.getboolean("opendkim", "enabled") self.dkim_cron_enabled = False + def sanity_check(extension, plugin): + # Sanity check for plugin requirements + enabled = False + if extension in self.extensions: + if self.config.getboolean(plugin, "enabled"): + enabled = True + else: + self.extensions.remove(extension) + return enabled + def is_extension_ok_for_version(self, extension, version): """Check if extension can be installed with this modo version.""" version = utils.convert_version_to_int(version) diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 5e7640b1..cc34f21f 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -31,6 +31,23 @@ def config_dir(self): """Return appropriate config dir.""" return "/etc/rspamd" + def install_config_files(self): + """Make sure config directory exists.""" + user = self.config.get("modoboa", "user") + pw = pwd.getpwnam(user) + targets = [ + [self.app_config["dkim_keys_storage_dir"], pw[2], pw[3]] + ] + for target in targets: + if not os.path.exists(target[0]): + utils.mkdir( + target[0], + stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | + stat.S_IROTH | stat.S_IXOTH, + target[1], target[2] + ) + super().install_config_files() + def get_config_files(self): """Return appropriate config files.""" _config_files = self.config_files From e8cf67509abf01a49404ffa24eabd0ad9d13751f Mon Sep 17 00:00:00 2001 From: Spitap Date: Tue, 4 Apr 2023 23:17:49 +0200 Subject: [PATCH 03/47] Better configuration --- installer.cfg | 131 ++++++++++++++++++ modoboa_installer/config_dict_template.py | 2 - .../scripts/files/postfix/main.cf.tpl | 17 +-- .../scripts/files/postfix/master.cf.tpl | 3 +- .../files/rspamd/local.d/antivirus.conf.tpl | 11 +- ...greylisting.conf.tpl => greylist.conf.tpl} | 1 + .../files/rspamd/local.d/metrics.conf.tpl | 20 +++ .../rspamd/local.d/milter_headers.conf.tpl | 33 +++++ .../rspamd/local.d/worker-controller.inc | 1 - modoboa_installer/scripts/postfix.py | 2 +- modoboa_installer/scripts/rspamd.py | 19 ++- 11 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 installer.cfg rename modoboa_installer/scripts/files/rspamd/local.d/{greylisting.conf.tpl => greylist.conf.tpl} (73%) create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/metrics.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl delete mode 100644 modoboa_installer/scripts/files/rspamd/local.d/worker-controller.inc diff --git a/installer.cfg b/installer.cfg new file mode 100644 index 00000000..62f6c282 --- /dev/null +++ b/installer.cfg @@ -0,0 +1,131 @@ +[general] +hostname = mail.%(domain)s + +[certificate] +generate = true +type = letsencrypt + +[letsencrypt] +email = aa@aa.fr + +[database] +engine = postgres +host = 127.0.0.1 +install = true + +[postgres] +user = postgres +password = + +[mysql] +user = root +password = DPnHqZYHZ3gegiVT +charset = utf8 +collation = utf8_general_ci + +[fail2ban] +enabled = true +config_dir = /etc/fail2ban +max_retry = 20 +ban_time = 3600 +find_time = 30 + +[modoboa] +user = modoboa +home_dir = /srv/modoboa +venv_path = %(home_dir)s/env +instance_path = %(home_dir)s/instance +timezone = Europe/Paris +dbname = modoboa +dbuser = modoboa +dbpassword = Zj3PY6G2M8Hw6Gig +extensions = modoboa-rspamd modoboa-pdfcredentials modoboa-postfix-autoreply modoboa-sievefilters modoboa-webmail modoboa-contacts modoboa-radicale +devmode = false + +[automx] +enabled = true +user = automx +config_dir = /etc +home_dir = /srv/automx +venv_path = %(home_dir)s/env +instance_path = %(home_dir)s/instance + +[rspamd] +enabled = true +password = B7ugujmFa2LLwu93 +dnsbl = true +dkim_keys_storage_dir = /var/lib/dkim +keys_path_map = /var/lib/dkim/keys.path.map +selectors_path_map = /var/lib/dkim/selectors.path.map +greylisting = true + +[amavis] +enabled = false +user = amavis +max_servers = 2 +dbname = amavis +dbuser = amavis +dbpassword = YSidxAfIqPC191Ir + +[clamav] +enabled = true +user = clamav + +[dovecot] +enabled = true +config_dir = /etc/dovecot +user = dovecot +home_dir = /srv/vmail +mailboxes_owner = vmail +extra_protocols = +postmaster_address = postmaster@%(domain)s +radicale_auth_socket_path = /var/run/dovecot/auth-radicale + +[nginx] +enabled = true +config_dir = /etc/nginx + +[razor] +enabled = true +config_dir = /etc/razor + +[postfix] +enabled = true +config_dir = /etc/postfix +message_size_limit = 11534336 + +[postwhite] +enabled = true +config_dir = /etc + +[spamassassin] +enabled = false +config_dir = /etc/mail/spamassassin +dbname = spamassassin +dbuser = spamassassin +dbpassword = s44EHekTTwOboebX + +[uwsgi] +enabled = true +config_dir = /etc/uwsgi +nb_processes = 2 + +[radicale] +enabled = true +user = radicale +config_dir = /etc/radicale +home_dir = /srv/radicale +venv_path = %(home_dir)s/env + +[opendkim] +enabled = false +user = opendkim +config_dir = /etc +port = 12345 +keys_storage_dir = /var/lib/dkim +dbuser = opendkim +dbpassword = acTggtM3vZeVBYRn + +[backup] +default_path = ./modoboa_backup/ + diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 52e92098..0abd442a 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -288,8 +288,6 @@ def is_email(user_input): { "option": "dbpassword", "default": make_password, - "customizable": True, - "question": "Please enter amavis db password" }, ], }, diff --git a/modoboa_installer/scripts/files/postfix/main.cf.tpl b/modoboa_installer/scripts/files/postfix/main.cf.tpl index c112975e..dd110bbd 100644 --- a/modoboa_installer/scripts/files/postfix/main.cf.tpl +++ b/modoboa_installer/scripts/files/postfix/main.cf.tpl @@ -124,6 +124,7 @@ strict_rfc821_envelopes = yes # Rspamd setup %{rspamd_enabled}smtpd_milters = inet:localhost:11332 +%{rspamd_enabled}non_smtpd_milters = inet:localhost:11332 %{rspamd_enabled}milter_default_action = accept %{rspamd_enabled}milter_protocol = 6 @@ -160,14 +161,14 @@ smtpd_recipient_restrictions = %{rspamd_disabled}postscreen_dnsbl_threshold = 3 %{rspamd_disabled}postscreen_dnsbl_action = enforce -postscreen_greet_banner = Welcome, please wait... -postscreen_greet_action = enforce +%{rspamd_disabled}postscreen_greet_banner = Welcome, please wait... +%{rspamd_disabled}postscreen_greet_action = enforce -postscreen_pipelining_enable = yes -postscreen_pipelining_action = enforce +%{rspamd_disabled}postscreen_pipelining_enable = yes +%{rspamd_disabled}postscreen_pipelining_action = enforce -postscreen_non_smtp_command_enable = yes -postscreen_non_smtp_command_action = enforce +%{rspamd_disabled}postscreen_non_smtp_command_enable = yes +%{rspamd_disabled}postscreen_non_smtp_command_action = enforce -postscreen_bare_newline_enable = yes -postscreen_bare_newline_action = enforce +%{rspamd_disabled}postscreen_bare_newline_enable = yes +%{rspamd_disabled}postscreen_bare_newline_action = enforce diff --git a/modoboa_installer/scripts/files/postfix/master.cf.tpl b/modoboa_installer/scripts/files/postfix/master.cf.tpl index 514ddae7..293b9590 100644 --- a/modoboa_installer/scripts/files/postfix/master.cf.tpl +++ b/modoboa_installer/scripts/files/postfix/master.cf.tpl @@ -9,7 +9,8 @@ # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== -smtp inet n - - - 1 postscreen +%{rspamd_disabled}smtp inet n - - - 1 postscreen +%{rspamd_enabled}smtp inet n - - - - smtpd smtpd pass - - - - - smtpd %{amavis_enabled} -o smtpd_proxy_filter=inet:[127.0.0.1]:10024 %{amavis_enabled} -o smtpd_proxy_options=speed_adjust diff --git a/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl index 235ea0f0..9aafe74a 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl @@ -1,11 +1,14 @@ clamav { + scan_mime_parts = true; + scan_text_mime = true; + scan_image_mime = true; + symbol = "CLAM_VIRUS"; type = "clamav"; - servers = "127.0.0.1:3310"; + servers = "/var/run/clamd.amavisd/clamd.sock"; + patterns { # symbol_name = "pattern"; - JUST_EICAR = '^Eicar-Test-Signature$'; + JUST_EICAR = "Test.EICAR"; } } - - diff --git a/modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl similarity index 73% rename from modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl rename to modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl index cc44e3ab..cf6c0360 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/greylisting.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl @@ -1,2 +1,3 @@ +%{greylisting_disabled}enabled = false; servers = "127.0.0.1:6379"; %{postwhite_enabled}whitelisted_ip = "/etc/postfix/postscreen_spf_whitelist.cidr" diff --git a/modoboa_installer/scripts/files/rspamd/local.d/metrics.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/metrics.conf.tpl new file mode 100644 index 00000000..896e7466 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/metrics.conf.tpl @@ -0,0 +1,20 @@ +actions { + reject = 15; # normal value is 15, 150 so it will never be rejected + add_header = 6; # set to 0.1 for testing, 6 for normal operation. + rewrite_subject = 8; # Default: 8 + greylist = 4; # Default: 4 +} + +group "antivirus" { + symbol "JUST_EICAR" { + weight = 10; + description = "Eicar test signature"; + } + symbol "CLAM_VIRUS_FAIL" { + weight = 0; + } + symbol "CLAM_VIRUS" { + weight = 10; + description = "ClamAV found a Virus"; + } +} diff --git a/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl new file mode 100644 index 00000000..de91d0ba --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl @@ -0,0 +1,33 @@ +use = ["x-spam-status", "my-x-spam-score" ,"x-virus","authentication-results" ]; +extended_spam_headers = false; +skip_local = false; +skip_authenticated = false; + +# Write the score as a header +custom { + my-x-spam-score = < Date: Tue, 4 Apr 2023 23:50:08 +0200 Subject: [PATCH 04/47] Removed installer.cfg --- .gitignore | 2 + installer.cfg | 131 -------------------------------------------------- 2 files changed, 2 insertions(+), 131 deletions(-) delete mode 100644 installer.cfg diff --git a/.gitignore b/.gitignore index e12944af..2a204eeb 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,5 @@ target/ # PyCharm .idea/ + +installer.cfg \ No newline at end of file diff --git a/installer.cfg b/installer.cfg deleted file mode 100644 index 62f6c282..00000000 --- a/installer.cfg +++ /dev/null @@ -1,131 +0,0 @@ -[general] -hostname = mail.%(domain)s - -[certificate] -generate = true -type = letsencrypt - -[letsencrypt] -email = aa@aa.fr - -[database] -engine = postgres -host = 127.0.0.1 -install = true - -[postgres] -user = postgres -password = - -[mysql] -user = root -password = DPnHqZYHZ3gegiVT -charset = utf8 -collation = utf8_general_ci - -[fail2ban] -enabled = true -config_dir = /etc/fail2ban -max_retry = 20 -ban_time = 3600 -find_time = 30 - -[modoboa] -user = modoboa -home_dir = /srv/modoboa -venv_path = %(home_dir)s/env -instance_path = %(home_dir)s/instance -timezone = Europe/Paris -dbname = modoboa -dbuser = modoboa -dbpassword = Zj3PY6G2M8Hw6Gig -extensions = modoboa-rspamd modoboa-pdfcredentials modoboa-postfix-autoreply modoboa-sievefilters modoboa-webmail modoboa-contacts modoboa-radicale -devmode = false - -[automx] -enabled = true -user = automx -config_dir = /etc -home_dir = /srv/automx -venv_path = %(home_dir)s/env -instance_path = %(home_dir)s/instance - -[rspamd] -enabled = true -password = B7ugujmFa2LLwu93 -dnsbl = true -dkim_keys_storage_dir = /var/lib/dkim -keys_path_map = /var/lib/dkim/keys.path.map -selectors_path_map = /var/lib/dkim/selectors.path.map -greylisting = true - -[amavis] -enabled = false -user = amavis -max_servers = 2 -dbname = amavis -dbuser = amavis -dbpassword = YSidxAfIqPC191Ir - -[clamav] -enabled = true -user = clamav - -[dovecot] -enabled = true -config_dir = /etc/dovecot -user = dovecot -home_dir = /srv/vmail -mailboxes_owner = vmail -extra_protocols = -postmaster_address = postmaster@%(domain)s -radicale_auth_socket_path = /var/run/dovecot/auth-radicale - -[nginx] -enabled = true -config_dir = /etc/nginx - -[razor] -enabled = true -config_dir = /etc/razor - -[postfix] -enabled = true -config_dir = /etc/postfix -message_size_limit = 11534336 - -[postwhite] -enabled = true -config_dir = /etc - -[spamassassin] -enabled = false -config_dir = /etc/mail/spamassassin -dbname = spamassassin -dbuser = spamassassin -dbpassword = s44EHekTTwOboebX - -[uwsgi] -enabled = true -config_dir = /etc/uwsgi -nb_processes = 2 - -[radicale] -enabled = true -user = radicale -config_dir = /etc/radicale -home_dir = /srv/radicale -venv_path = %(home_dir)s/env - -[opendkim] -enabled = false -user = opendkim -config_dir = /etc -port = 12345 -keys_storage_dir = /var/lib/dkim -dbuser = opendkim -dbpassword = acTggtM3vZeVBYRn - -[backup] -default_path = ./modoboa_backup/ - From e433b3405288a3b453f6c6459c89d73e156813fa Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 5 Apr 2023 18:09:54 +0200 Subject: [PATCH 05/47] fixed test --- tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests.py b/tests.py index 6f8fdf64..e77d6ae5 100644 --- a/tests.py +++ b/tests.py @@ -99,7 +99,7 @@ def test_updating_configfile(self, mock_user_input): def test_interactive_mode_letsencrypt(self, mock_user_input): """Check interactive mode.""" mock_user_input.side_effect = [ - "1", "admin@example.test", "0", "", "", "", "", "" + "1", "admin@example.test", "0", "", "", "", "" ] with open(os.devnull, "w") as fp: sys.stdout = fp @@ -126,8 +126,8 @@ def test_configfile_loading(self, mock_user_input): "example.test"]) self.assertTrue(os.path.exists(self.cfgfile)) self.assertIn( - "modoboa automx amavis clamav dovecot nginx razor postfix" - " postwhite spamassassin uwsgi", + "fail2ban modoboa automx rspamd clamav dovecot nginx razor " + "postfix postwhite uwsgi radicale", out.getvalue() ) self.assertNotIn("It seems that your config file is outdated.", From d8c116ad0243651f2030e021617df2241e6c07c2 Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 21 Jun 2023 16:04:38 +0200 Subject: [PATCH 06/47] updated rspamd config --- modoboa_installer/config_dict_template.py | 8 +++++ modoboa_installer/scripts/clamav.py | 7 ++-- .../files/rspamd/local.d/antivirus.conf.tpl | 5 +-- .../files/rspamd/local.d/greylist.conf.tpl | 1 - .../files/rspamd/local.d/groups.conf.tpl | 5 +++ .../files/rspamd/local.d/redis.conf.tpl | 2 ++ .../files/rspamd/local.d/settings.conf.tpl | 8 +++++ modoboa_installer/scripts/postfix.py | 14 ++++++-- modoboa_installer/scripts/rspamd.py | 35 +++++++++++++++++-- 9 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/groups.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/redis.conf.tpl create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/settings.conf.tpl diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 0abd442a..e8721587 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -259,6 +259,14 @@ def is_email(user_input): { "option": "greylisting", "default": "true" + }, + { + "option": "whitelist_auth", + "default": "true" + }, + { + "option": "whitelist_auth_weigth", + "default": "-5" } ], }, diff --git a/modoboa_installer/scripts/clamav.py b/modoboa_installer/scripts/clamav.py index 2ff48680..a62eda69 100644 --- a/modoboa_installer/scripts/clamav.py +++ b/modoboa_installer/scripts/clamav.py @@ -42,9 +42,10 @@ def post_run(self): """Additional tasks.""" if package.backend.FORMAT == "deb": user = self.config.get(self.appname, "user") - system.add_user_to_group( - user, self.config.get("amavis", "user") - ) + if self.config.get("amavis", "enabled").lower() == "true": + system.add_user_to_group( + user, self.config.get("amavis", "user") + ) pattern = ( "s/^AllowSupplementaryGroups false/" "AllowSupplementaryGroups true/") diff --git a/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl index 9aafe74a..5e50a4ee 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/antivirus.conf.tpl @@ -2,10 +2,11 @@ clamav { scan_mime_parts = true; scan_text_mime = true; scan_image_mime = true; - + retransmits = 2; + timeout = 30; symbol = "CLAM_VIRUS"; type = "clamav"; - servers = "/var/run/clamd.amavisd/clamd.sock"; + servers = "127.0.0.1:3310" patterns { # symbol_name = "pattern"; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl index cf6c0360..bf90f46b 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/greylist.conf.tpl @@ -1,3 +1,2 @@ %{greylisting_disabled}enabled = false; servers = "127.0.0.1:6379"; -%{postwhite_enabled}whitelisted_ip = "/etc/postfix/postscreen_spf_whitelist.cidr" diff --git a/modoboa_installer/scripts/files/rspamd/local.d/groups.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/groups.conf.tpl new file mode 100644 index 00000000..0e10663b --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/groups.conf.tpl @@ -0,0 +1,5 @@ +symbols { + "WHITELIST_AUTHENTICATED" { + weight = %whitelist_auth_weigth; + } +} diff --git a/modoboa_installer/scripts/files/rspamd/local.d/redis.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/redis.conf.tpl new file mode 100644 index 00000000..6b6c00dd --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/redis.conf.tpl @@ -0,0 +1,2 @@ +write_servers = "localhost"; +read_servers = "localhost"; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/settings.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/settings.conf.tpl new file mode 100644 index 00000000..1eae1c05 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/settings.conf.tpl @@ -0,0 +1,8 @@ +authenticated { + priority = high; + authenticated = yes; + apply { + groups_disabled = ["rbl", "spf"]; + } +%{whitelist_auth_enabled} symbols ["WHITELIST_AUTHENTICATED"]; +} diff --git a/modoboa_installer/scripts/postfix.py b/modoboa_installer/scripts/postfix.py index 19e64270..ea56b75e 100644 --- a/modoboa_installer/scripts/postfix.py +++ b/modoboa_installer/scripts/postfix.py @@ -103,8 +103,18 @@ def post_run(self): utils.exec_cmd("postalias {}".format(aliases_file)) # Postwhite - install("postwhite", self.config, self.upgrade, self.archive_path) + condition = ( + not self.config.getboolean("rspamd", "enabled") and + self.config.getboolean("postwhite", "enabled") + ) + if condition: + install("postwhite", self.config, self.upgrade, self.archive_path) def backup(self, path): """Launch postwhite backup.""" - backup("postwhite", self.config, path) + condition = ( + not self.config.getboolean("rspamd", "enabled") and + self.config.getboolean("postwhite", "enabled") + ) + if condition: + backup("postwhite", self.config, path) diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 1a4fbc0f..7313ffa1 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -4,6 +4,7 @@ from .. import package from .. import utils +from .. import system from . import base from . import backup, install @@ -34,6 +35,29 @@ def config_dir(self): """Return appropriate config dir.""" return "/etc/rspamd" + def install_packages(self): + status, codename = utils.exec_cmd("lsb_release -c -s") + + if codename.lower() in ["bionic", "bookworm", "bullseye", "buster", + "focal", "jammy", "jessie", "sid", "stretch", + "trusty", "wheezy", "xenial"]: + utils.mkdir_safe("/etc/apt/keyrings") + + if codename.lower() == "bionic": + package.backend.install("software-properties-common") + utils.exec_cmd("add-apt-repository ppa:ubuntu-toolchain-r/test") + + utils.exec_cmd("wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -") + utils.exec_cmd(f"echo \"deb http://apt.llvm.org/{codename}/ llvm-toolchain-{codename}-16 main\" | sudo tee /etc/apt/sources.list.d/llvm-16.list") + utils.exec_cmd(f"echo \"deb-src http://apt.llvm.org/{codename}/ llvm-toolchain-{codename}-16 main\" | sudo tee -a /etc/apt/sources.list.d/llvm-16.list") + + utils.exec_cmd("wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rspamd.gpg > /dev/null") + utils.exec_cmd(f"echo \"deb [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ {codename} main\" | sudo tee /etc/apt/sources.list.d/rspamd.list") + utils.exec_cmd(f"echo \"deb-src [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ {codename} main\" | sudo tee -a /etc/apt/sources.list.d/rspamd.list") + package.backend.update() + + return super().install_packages() + def install_config_files(self): """Make sure config directory exists.""" user = self.config.get("modoboa", "user") @@ -58,6 +82,8 @@ def get_config_files(self): _config_files.append("local.d/antivirus.conf") if self.app_config["dnsbl"].lower() == "true": _config_files.append("local.d/rbl.conf") + if self.app_config["whitelist_auth"].lower() == "true": + _config_files.append("local.d/groups.conf") return _config_files def get_template_context(self): @@ -72,13 +98,16 @@ def get_template_context(self): _context["controller_password"] = password else: _context["controller_password"] = controller_password - _context["greylisting_disabled"] = "" if not self.app_config["greylisting"] else "#" - if not self.app_config["greylisting"]: - _context["postwhite_enabled"] = "#" + _context["greylisting_disabled"] = "" if not self.app_config["greylisting"].lower() == "true" else "#" + _context["whitelist_auth_enabled"] = "" if self.app_config["whitelist_auth"].lower() == "true" else "#" return _context def post_run(self): """Additional tasks.""" + system.add_user_to_group( + self.config.get("modoboa", "user"), + "_rspamd" + ) if self.config("clamav", "enabled"): install("clamav", self.config, self.upgrade, self.archive_path) From c0718864669c9bfe2890ac2af9a35ff1ffb21cba Mon Sep 17 00:00:00 2001 From: Spitap Date: Tue, 5 Sep 2023 11:32:16 +0200 Subject: [PATCH 07/47] fix --- modoboa_installer/scripts/modoboa.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modoboa_installer/scripts/modoboa.py b/modoboa_installer/scripts/modoboa.py index 30b936e2..1e8763bf 100644 --- a/modoboa_installer/scripts/modoboa.py +++ b/modoboa_installer/scripts/modoboa.py @@ -50,8 +50,8 @@ def __init__(self, *args, **kwargs): self.extensions = self.config.get("modoboa", "extensions").split() self.devmode = self.config.getboolean("modoboa", "devmode") # Sanity check for amavis and rspamd - self.amavis_enabled = sanity_check("modoboa-amavis", "amavis") - sanity_check("modoboa-rspamd", "rspamd") + self.amavis_enabled = self.sanity_check("modoboa-amavis", "amavis") + self.sanity_check("modoboa-rspamd", "rspamd") if "modoboa-radicale" in self.extensions: if not self.config.getboolean("radicale", "enabled"): @@ -60,7 +60,7 @@ def __init__(self, *args, **kwargs): self.opendkim_enabled = self.config.getboolean("opendkim", "enabled") self.dkim_cron_enabled = False - def sanity_check(extension, plugin): + def sanity_check(self, extension, plugin): # Sanity check for plugin requirements enabled = False if extension in self.extensions: From 798e522b6782dd43ef2cc4e18b00b2336070c7d2 Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 6 Sep 2023 12:46:20 +0200 Subject: [PATCH 08/47] App incompatibility detection, updated for 2.2.0 --- modoboa_installer/compatibility_matrix.py | 7 +++++ modoboa_installer/config_dict_template.py | 6 ++--- .../files/modoboa/supervisor-rq-dkim.tpl | 2 +- modoboa_installer/scripts/modoboa.py | 26 +++++++++++++++++++ modoboa_installer/scripts/rspamd.py | 3 +-- modoboa_installer/utils.py | 13 ++++++++++ run.py | 7 +++-- 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/modoboa_installer/compatibility_matrix.py b/modoboa_installer/compatibility_matrix.py index 5fc9da88..46de78a0 100644 --- a/modoboa_installer/compatibility_matrix.py +++ b/modoboa_installer/compatibility_matrix.py @@ -34,3 +34,10 @@ "modoboa-sievefilters": "2.3.0", "modoboa-postfix-autoreply": "2.3.0" } + +APP_INCOMPATIBILITY = { + "opendkim": ["rspamd"], + "amavis": ["rspamd"], + "postwhite": ["rspamd"], + "spamassassin": ["rspamd"] +} diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index e8721587..7b8569c2 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -249,11 +249,11 @@ def is_email(user_input): "default": "/var/lib/dkim" }, { - "option": "keys_path_map", + "option": "key_map_path", "default": "/var/lib/dkim/keys.path.map" }, { - "option": "selectors_path_map", + "option": "selector_map_path", "default": "/var/lib/dkim/selectors.path.map" }, { @@ -401,7 +401,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default": "true", + "default": "false", }, { "option": "config_dir", diff --git a/modoboa_installer/scripts/files/modoboa/supervisor-rq-dkim.tpl b/modoboa_installer/scripts/files/modoboa/supervisor-rq-dkim.tpl index 42d9bd07..531650ea 100644 --- a/modoboa_installer/scripts/files/modoboa/supervisor-rq-dkim.tpl +++ b/modoboa_installer/scripts/files/modoboa/supervisor-rq-dkim.tpl @@ -3,7 +3,7 @@ autostart=true autorestart=true command=%{venv_path}/bin/python %{home_dir}/instance/manage.py rqworker dkim directory=%{home_dir} -user=%{opendkim_user} +user=%{dkim_user} redirect_stderr=true numprocs=1 stopsignal=TERM diff --git a/modoboa_installer/scripts/modoboa.py b/modoboa_installer/scripts/modoboa.py index 1e8763bf..c883d9d6 100644 --- a/modoboa_installer/scripts/modoboa.py +++ b/modoboa_installer/scripts/modoboa.py @@ -137,6 +137,22 @@ def _setup_venv(self): f"https://raw.githubusercontent.com/modoboa/modoboa/{modoboa_version}/{db_file}", venv=self.venv_path) # Dev mode: + db_package = [] + # We need to install db package afterward to check for installed modoboa version + if self.dbengine == "postgres": + if self.modoboa_2_2_or_greater: + db_package = ["psycopg[binary]\>3.1.7"] + else: + db_package = ["psycopg2-binary\<2.9"] + else: + db_package = "mysqlclient" + python.install_packages( + db_package, self.venv_path, + upgrade=self.upgrade, + sudo_user=self.user, + beta=self.config.getboolean("modoboa", "install_beta") + ) + if self.devmode: python.install_package_from_remote_requirements( f"https://raw.githubusercontent.com/modoboa/modoboa/{modoboa_version}/dev-requirements.txt", @@ -267,6 +283,7 @@ def get_template_context(self): "radicale_enabled": ( "" if "modoboa-radicale" in extensions else "#"), "opendkim_user": self.config.get("opendkim", "user"), + "dkim_user": "_rspamd" if self.config.getboolean("rspamd", "enabled") else self.config.get("opendkim", "user") "minutes": random.randint(1, 59), "hours": f"{random_hour},{random_hour+12}", "modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#", @@ -310,6 +327,15 @@ def apply_settings(self): if self.config.getboolean("opendkim", "enabled"): settings["admin"]["dkim_keys_storage_dir"] = ( self.config.get("opendkim", "keys_storage_dir")) + + if self.config.getboolean("rspamd", "enabled"): + settings["admin"]["dkim_keys_storage_dir"] = ( + self.config.get("rspamd", "dkim_keys_storage_dir")) + settings["modoboa_rspamd"]["key_map_path"] = ( + self.config.get("rspamd", "key_map_path")) + settings["modoboa_rspamd"]["selector_map_path"] = ( + self.config.get("rspamd", "selector_map_path")) + settings = json.dumps(settings) query = ( "UPDATE core_localconfig SET _parameters='{}'" diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 7313ffa1..d0c9bd15 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -60,8 +60,7 @@ def install_packages(self): def install_config_files(self): """Make sure config directory exists.""" - user = self.config.get("modoboa", "user") - pw = pwd.getpwnam(user) + pw = pwd.getpwnam("_rspamd") targets = [ [self.app_config["dkim_keys_storage_dir"], pw[2], pw[3]] ] diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 18725c15..ff8785dd 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -18,6 +18,7 @@ import ConfigParser as configparser from . import config_dict_template +from . import compatibility_matrix.APP_INCOMPATIBILITY ENV = {} @@ -485,3 +486,15 @@ def validate_backup_path(path: str, silent_mode: bool): mkdir_safe(os.path.join(backup_path, dir), stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3]) return backup_path + +def check_app_compatibility(section, config): + """Check that the app can be installed in regards to other enabled apps.""" + incompatible_app = [] + if section in APP_INCOMPATIBILITY.keys(): + for app in APP_INCOMPATIBILITY[section]: + if config.getboolean(app, "enabled"): + error(f"{section} cannont be installed if {app} is enabled. " + "Please disable one of them.") + incompatible_app.append(app) + return len(incompatible_app) == 0 + diff --git a/run.py b/run.py index 22cfa145..fa8c60ca 100755 --- a/run.py +++ b/run.py @@ -22,14 +22,13 @@ PRIMARY_APPS = [ - "amavis", "fail2ban", "modoboa", "automx", "radicale", "uwsgi", "nginx", - "opendkim", + "rspamd", "postfix", "dovecot" ] @@ -261,6 +260,7 @@ def main(input_args): # Show concerned components components = [] + incompatible_app_detected = False for section in config.sections(): if section in ["general", "database", "mysql", "postgres", "certificate", "letsencrypt", "backup"]: @@ -268,7 +268,10 @@ def main(input_args): if (config.has_option(section, "enabled") and not config.getboolean(section, "enabled")): continue + incompatible_app_detected = utils.check_app_compatibility(section, config) components.append(section) + if incompatible_app_detected: + sys.exit(0) utils.printcolor(" ".join(components), utils.YELLOW) if not args.force: answer = utils.user_input("Do you confirm? (Y/n) ") From 064d7e5960cb2f97d5fd8ef9a7d0c8a205d84b2d Mon Sep 17 00:00:00 2001 From: Spitap Date: Wed, 6 Sep 2023 12:47:54 +0200 Subject: [PATCH 09/47] import fix --- modoboa_installer/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index ff8785dd..877c42fc 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -18,7 +18,7 @@ import ConfigParser as configparser from . import config_dict_template -from . import compatibility_matrix.APP_INCOMPATIBILITY +from .compatibility_matrix import APP_INCOMPATIBILITY ENV = {} From 9298f210831e6c06420da1f06e786ee661de3799 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Tue, 12 Sep 2023 16:58:23 +0200 Subject: [PATCH 10/47] Few fixes --- modoboa_installer/scripts/modoboa.py | 6 +++--- run.py | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modoboa_installer/scripts/modoboa.py b/modoboa_installer/scripts/modoboa.py index c883d9d6..cfea5f4f 100644 --- a/modoboa_installer/scripts/modoboa.py +++ b/modoboa_installer/scripts/modoboa.py @@ -140,8 +140,8 @@ def _setup_venv(self): db_package = [] # We need to install db package afterward to check for installed modoboa version if self.dbengine == "postgres": - if self.modoboa_2_2_or_greater: - db_package = ["psycopg[binary]\>3.1.7"] + if self.modoboa_2_2_or_greater: + db_package = ["psycopg[binary]\>3.1.7"] else: db_package = ["psycopg2-binary\<2.9"] else: @@ -283,7 +283,7 @@ def get_template_context(self): "radicale_enabled": ( "" if "modoboa-radicale" in extensions else "#"), "opendkim_user": self.config.get("opendkim", "user"), - "dkim_user": "_rspamd" if self.config.getboolean("rspamd", "enabled") else self.config.get("opendkim", "user") + "dkim_user": "_rspamd" if self.config.getboolean("rspamd", "enabled") else self.config.get("opendkim", "user"), "minutes": random.randint(1, 59), "hours": f"{random_hour},{random_hour+12}", "modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#", diff --git a/run.py b/run.py index fa8c60ca..6f7e689f 100755 --- a/run.py +++ b/run.py @@ -260,7 +260,6 @@ def main(input_args): # Show concerned components components = [] - incompatible_app_detected = False for section in config.sections(): if section in ["general", "database", "mysql", "postgres", "certificate", "letsencrypt", "backup"]: @@ -268,10 +267,10 @@ def main(input_args): if (config.has_option(section, "enabled") and not config.getboolean(section, "enabled")): continue - incompatible_app_detected = utils.check_app_compatibility(section, config) + incompatible_app_detected = not utils.check_app_compatibility(section, config) + if incompatible_app_detected: + sys.exit(0) components.append(section) - if incompatible_app_detected: - sys.exit(0) utils.printcolor(" ".join(components), utils.YELLOW) if not args.force: answer = utils.user_input("Do you confirm? (Y/n) ") From dc354434dc6dfdd6a91527c6702250c40f5f032c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 09:50:40 +0200 Subject: [PATCH 11/47] Fixed wrong settings initialization --- modoboa_installer/scripts/modoboa.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modoboa_installer/scripts/modoboa.py b/modoboa_installer/scripts/modoboa.py index cfea5f4f..bc753c27 100644 --- a/modoboa_installer/scripts/modoboa.py +++ b/modoboa_installer/scripts/modoboa.py @@ -331,10 +331,10 @@ def apply_settings(self): if self.config.getboolean("rspamd", "enabled"): settings["admin"]["dkim_keys_storage_dir"] = ( self.config.get("rspamd", "dkim_keys_storage_dir")) - settings["modoboa_rspamd"]["key_map_path"] = ( - self.config.get("rspamd", "key_map_path")) - settings["modoboa_rspamd"]["selector_map_path"] = ( - self.config.get("rspamd", "selector_map_path")) + settings["modoboa_rspamd"] = { + "key_map_path": self.config.get("rspamd", "key_map_path"), + "selector_map_path": self.config.get("rspamd", "selector_map_path") + } settings = json.dumps(settings) query = ( From 210efd5bc9005e6ac788a4261787f0eb2751ab09 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 10:10:07 +0200 Subject: [PATCH 12/47] Fixed issues in rspamd script --- modoboa_installer/config_dict_template.py | 4 +++ modoboa_installer/scripts/rspamd.py | 37 ++++++++++++----------- modoboa_installer/utils.py | 13 +++++++- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 7b8569c2..82ea0953 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -236,6 +236,10 @@ def is_email(user_input): "option": "enabled", "default": "true", }, + { + "option": "user", + "default": "_rspamd", + }, { "option": "password", "default": make_password, diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index d0c9bd15..2c82043d 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -1,17 +1,18 @@ """Amavis related functions.""" import os +import pwd +import stat from .. import package from .. import utils from .. import system from . import base -from . import backup, install +from . import install class Rspamd(base.Installer): - """Rspamd installer.""" appname = "rspamd" @@ -36,11 +37,8 @@ def config_dir(self): return "/etc/rspamd" def install_packages(self): - status, codename = utils.exec_cmd("lsb_release -c -s") - - if codename.lower() in ["bionic", "bookworm", "bullseye", "buster", - "focal", "jammy", "jessie", "sid", "stretch", - "trusty", "wheezy", "xenial"]: + debian_based_dist, codename = utils.is_dist_debian_based() + if debian_based_dist: utils.mkdir_safe("/etc/apt/keyrings") if codename.lower() == "bionic": @@ -60,7 +58,8 @@ def install_packages(self): def install_config_files(self): """Make sure config directory exists.""" - pw = pwd.getpwnam("_rspamd") + user = self.config.get(self.appname, "user") + pw = pwd.getpwnam(user) targets = [ [self.app_config["dkim_keys_storage_dir"], pw[2], pw[3]] ] @@ -94,7 +93,7 @@ def get_template_context(self): "Please make sure it is not 'q1' or 'q2'." "Storing the password in plain. See" "https://rspamd.com/doc/quickstart.html#setting-the-controller-password") - _context["controller_password"] = password + _context["controller_password"] = self.app_config["password"] else: _context["controller_password"] = controller_password _context["greylisting_disabled"] = "" if not self.app_config["greylisting"].lower() == "true" else "#" @@ -103,10 +102,11 @@ def get_template_context(self): def post_run(self): """Additional tasks.""" + user = self.config.get(self.appname, "user") system.add_user_to_group( self.config.get("modoboa", "user"), - "_rspamd" - ) + user + ) if self.config("clamav", "enabled"): install("clamav", self.config, self.upgrade, self.archive_path) @@ -127,10 +127,11 @@ def restore(self): """Restore custom config files.""" custom_config_dir = os.path.join(self.config_dir, "/local.d/") - custom_backup_dir = os.path.join(path, "/rspamd/") - backed_up_files = [f for f in os.listdir(custom_backup_dir) - if os.path.isfile(custom_backup_dir, f) - ] - for file in backed_up_files: - utils.copy_file(file, custom_config_dir) - utils.success("Custom Rspamd configuration restored.") + custom_backup_dir = os.path.join(self.archive_path, "/rspamd/") + backed_up_files = [ + f for f in os.listdir(custom_backup_dir) + if os.path.isfile(custom_backup_dir, f) + ] + for f in backed_up_files: + utils.copy_file(f, custom_config_dir) + utils.success("Custom Rspamd configuration restored.") diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 877c42fc..7ee62360 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -102,6 +102,17 @@ def dist_name(): return dist_info()[0].lower() +def is_dist_debian_based() -> (bool, str): + """Check if current OS is Debian based or not.""" + status, codename = exec_cmd("lsb_release -c -s") + codename = codename.lower() + return codename in [ + "bionic", "bookworm", "bullseye", "buster", + "focal", "jammy", "jessie", "sid", "stretch", + "trusty", "wheezy", "xenial" + ], codename + + def mkdir(path, mode, uid, gid): """Create a directory.""" if not os.path.exists(path): @@ -487,6 +498,7 @@ def validate_backup_path(path: str, silent_mode: bool): stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3]) return backup_path + def check_app_compatibility(section, config): """Check that the app can be installed in regards to other enabled apps.""" incompatible_app = [] @@ -497,4 +509,3 @@ def check_app_compatibility(section, config): "Please disable one of them.") incompatible_app.append(app) return len(incompatible_app) == 0 - From 136c8d9471bbf2b1be1c8ee9fd36b48aea54d1d8 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 10:18:12 +0200 Subject: [PATCH 13/47] Fixed wrong setting names --- modoboa_installer/config_dict_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 82ea0953..694988c3 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -253,11 +253,11 @@ def is_email(user_input): "default": "/var/lib/dkim" }, { - "option": "key_map_path", + "option": "keys_map_path", "default": "/var/lib/dkim/keys.path.map" }, { - "option": "selector_map_path", + "option": "selectors_map_path", "default": "/var/lib/dkim/selectors.path.map" }, { From 6301e4c0cb37d06d78e27f85dbefe945c5c87aaa Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 10:21:19 +0200 Subject: [PATCH 14/47] Consistency for variable names --- modoboa_installer/config_dict_template.py | 4 ++-- .../scripts/files/rspamd/local.d/dkim_signing.conf.tpl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 694988c3..82ea0953 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -253,11 +253,11 @@ def is_email(user_input): "default": "/var/lib/dkim" }, { - "option": "keys_map_path", + "option": "key_map_path", "default": "/var/lib/dkim/keys.path.map" }, { - "option": "selectors_map_path", + "option": "selector_map_path", "default": "/var/lib/dkim/selectors.path.map" }, { diff --git a/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl index 0025c3b7..d3a51748 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl @@ -1,3 +1,3 @@ try_fallback = false; -selector_map = "%selectors_path_map"; -path_map = "%keys_path_map"; +selector_map = "%selector_path_map"; +path_map = "%key_path_map"; From 56e8bcecf2b53b18383944d3bf2b52581c4258ce Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 10:25:00 +0200 Subject: [PATCH 15/47] Fixed wrong setting names! --- .../scripts/files/rspamd/local.d/dkim_signing.conf.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl index d3a51748..3dcf9923 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/dkim_signing.conf.tpl @@ -1,3 +1,3 @@ try_fallback = false; -selector_map = "%selector_path_map"; -path_map = "%key_path_map"; +selector_map = "%selector_map_path"; +path_map = "%key_map_path"; From dc3a0f02cb3bce2ee9f1304948334f1f8b353ec2 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 10:33:02 +0200 Subject: [PATCH 16/47] Escape % character in config file --- .../scripts/files/rspamd/local.d/milter_headers.conf.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl index de91d0ba..f3c489ae 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl @@ -11,7 +11,7 @@ custom { -- return no error return nil, -- header(s) to add - {['X-Spam-Score'] = string.format('%.2f', sc[1])}, + {['X-Spam-Score'] = string.format('%%.2f', sc[1])}, -- header(s) to remove {['X-Spam-Score'] = 1}, -- metadata to store From 625985aa20447059647b296ade3a0c00e1c209db Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 10:41:45 +0200 Subject: [PATCH 17/47] Fixed wrong access to config option --- modoboa_installer/scripts/rspamd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 2c82043d..452038d7 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -107,7 +107,7 @@ def post_run(self): self.config.get("modoboa", "user"), user ) - if self.config("clamav", "enabled"): + if self.config.getboolean("clamav", "enabled"): install("clamav", self.config, self.upgrade, self.archive_path) def custom_backup(self, path): From c768a4ac7d7a8d178dfd834d4b2af3858abe2c70 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 14 Sep 2023 13:27:24 +0200 Subject: [PATCH 18/47] Fixed tests --- tests.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests.py b/tests.py index e77d6ae5..cfb0d142 100644 --- a/tests.py +++ b/tests.py @@ -127,11 +127,12 @@ def test_configfile_loading(self, mock_user_input): self.assertTrue(os.path.exists(self.cfgfile)) self.assertIn( "fail2ban modoboa automx rspamd clamav dovecot nginx razor " - "postfix postwhite uwsgi radicale", + "postfix uwsgi radicale", out.getvalue() ) - self.assertNotIn("It seems that your config file is outdated.", - out.getvalue() + self.assertNotIn( + "It seems that your config file is outdated.", + out.getvalue() ) @patch("modoboa_installer.utils.user_input") From b687c13e79987f775f0e6bcc6b12bfa1693e9c17 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 21 Sep 2023 09:53:59 +0200 Subject: [PATCH 19/47] Make rspamd installation work --- modoboa_installer/scripts/rspamd.py | 4 ---- modoboa_installer/utils.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 452038d7..87b36e40 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -45,10 +45,6 @@ def install_packages(self): package.backend.install("software-properties-common") utils.exec_cmd("add-apt-repository ppa:ubuntu-toolchain-r/test") - utils.exec_cmd("wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -") - utils.exec_cmd(f"echo \"deb http://apt.llvm.org/{codename}/ llvm-toolchain-{codename}-16 main\" | sudo tee /etc/apt/sources.list.d/llvm-16.list") - utils.exec_cmd(f"echo \"deb-src http://apt.llvm.org/{codename}/ llvm-toolchain-{codename}-16 main\" | sudo tee -a /etc/apt/sources.list.d/llvm-16.list") - utils.exec_cmd("wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rspamd.gpg > /dev/null") utils.exec_cmd(f"echo \"deb [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ {codename} main\" | sudo tee /etc/apt/sources.list.d/rspamd.list") utils.exec_cmd(f"echo \"deb-src [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ {codename} main\" | sudo tee -a /etc/apt/sources.list.d/rspamd.list") diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 7ee62360..1bdd3f80 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -105,7 +105,7 @@ def dist_name(): def is_dist_debian_based() -> (bool, str): """Check if current OS is Debian based or not.""" status, codename = exec_cmd("lsb_release -c -s") - codename = codename.lower() + codename = codename.strip().lower() return codename in [ "bionic", "bookworm", "bullseye", "buster", "focal", "jammy", "jessie", "sid", "stretch", From 1e05f07103ae5a2f493bc2c42c6fad98317a2bde Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 21 Sep 2023 09:55:23 +0200 Subject: [PATCH 20/47] Convert codename to str --- modoboa_installer/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 1bdd3f80..3faf3737 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -105,7 +105,7 @@ def dist_name(): def is_dist_debian_based() -> (bool, str): """Check if current OS is Debian based or not.""" status, codename = exec_cmd("lsb_release -c -s") - codename = codename.strip().lower() + codename = codename.decode().strip().lower() return codename in [ "bionic", "bookworm", "bullseye", "buster", "focal", "jammy", "jessie", "sid", "stretch", From b49ba6d089c0d159f9dd511966633c3576218a69 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 21 Sep 2023 10:05:45 +0200 Subject: [PATCH 21/47] Fixed wrong call to mkdir_safe --- modoboa_installer/scripts/rspamd.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 87b36e40..49af4a83 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -39,7 +39,11 @@ def config_dir(self): def install_packages(self): debian_based_dist, codename = utils.is_dist_debian_based() if debian_based_dist: - utils.mkdir_safe("/etc/apt/keyrings") + utils.mkdir_safe( + "/etc/apt/keyrings", + stat.S_IRWXU | stat.S_IRUSR | stat.S_IXUSR, + 0, 0 + ) if codename.lower() == "bionic": package.backend.install("software-properties-common") From 473019c49c2497e82eeaa436d971f22cf03ef89d Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 21 Sep 2023 10:24:43 +0200 Subject: [PATCH 22/47] Better custom repo installation --- modoboa_installer/package.py | 26 ++++++++++++++++++++++++-- modoboa_installer/scripts/rspamd.py | 14 +++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/modoboa_installer/package.py b/modoboa_installer/package.py index 58ee940c..195d0345 100644 --- a/modoboa_installer/package.py +++ b/modoboa_installer/package.py @@ -49,7 +49,29 @@ def prepare_system(self): def restore_system(self): utils.exec_cmd("rm -f {}".format(self.policy_file)) - def update(self, force=False): + def add_custom_repository(self, + name: str, + url: str, + key_url: str, + codename: str, + with_source: bool = True): + key_file = f"/etc/apt/keyrings/{name}.gpg" + utils.exec_cmd( + f"wget -O - {key_url} | gpg --dearmor | sudo tee {key_file} > /dev/null" + ) + line_types = ["deb"] + if with_source: + line_types.append("deb-src") + for line_type in line_types: + line = ( + f"{line_type} [arch=amd64 signed-by={key_file}] " + f"{url} {codename} main" + ) + target_file = f"/etc/apt/source.list.d/{name}.list" + utils.exec_cmd(f'echo "{line} | sude tee {target_file}') + self.index_updated = False + + def update(self): """Update local cache.""" if self.index_updated and not force: return @@ -89,7 +111,7 @@ class RPMPackage(Package): def __init__(self, dist_name): """Initialize backend.""" - super(RPMPackage, self).__init__(dist_name) + super().__init__(dist_name) if "centos" in dist_name: self.install("epel-release") diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 49af4a83..105f0361 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -41,17 +41,21 @@ def install_packages(self): if debian_based_dist: utils.mkdir_safe( "/etc/apt/keyrings", - stat.S_IRWXU | stat.S_IRUSR | stat.S_IXUSR, + stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | + stat.S_IROTH | stat.S_IXOTH, 0, 0 ) - if codename.lower() == "bionic": + if codename == "bionic": package.backend.install("software-properties-common") utils.exec_cmd("add-apt-repository ppa:ubuntu-toolchain-r/test") - utils.exec_cmd("wget -O- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rspamd.gpg > /dev/null") - utils.exec_cmd(f"echo \"deb [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ {codename} main\" | sudo tee /etc/apt/sources.list.d/rspamd.list") - utils.exec_cmd(f"echo \"deb-src [arch=amd64 signed-by=/etc/apt/keyrings/rspamd.gpg] http://rspamd.com/apt-stable/ {codename} main\" | sudo tee -a /etc/apt/sources.list.d/rspamd.list") + package.backend.add_custom_repository( + "rspamd", + "http://rspamd.com/apt-stable/", + "https://rspamd.com/apt-stable/gpg.key", + codename + ) package.backend.update() return super().install_packages() From 8a45ead64a5128af990f515a40f48ce3bc78759a Mon Sep 17 00:00:00 2001 From: Spitap Date: Mon, 25 Sep 2023 11:40:26 +0200 Subject: [PATCH 23/47] Updated config and interactive mode --- .gitignore | 5 ++- modoboa_installer/config_dict_template.py | 32 ++++++++++++++--- modoboa_installer/scripts/rspamd.py | 2 +- modoboa_installer/utils.py | 44 ++++++++++++++--------- 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 2a204eeb..3f88a5e9 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,7 @@ target/ # PyCharm .idea/ -installer.cfg \ No newline at end of file +#KDE +*.kdev4 + +installer.cfg diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 82ea0953..b3adc718 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -27,6 +27,22 @@ def is_email(user_input): } ] }, + { + "name": "antispam", + "values": [ + { + "option": "enabled", + "default": "true", + "question": "Do you want to setup an antispam utility?" + }, + { + "option": "type", + "default": "rspamd", + "question": "Please select your antispam utility", + "values": ["rspamd", "amavis"] + } + ] + }, { "name": "certificate", "values": [ @@ -50,7 +66,7 @@ def is_email(user_input): }, { "name": "letsencrypt", - "if": "certificate.type=letsencrypt", + "if": ["certificate.type=letsencrypt"], "values": [ { "option": "email", @@ -85,7 +101,7 @@ def is_email(user_input): }, { "name": "postgres", - "if": "database.engine=postgres", + "if": ["database.engine=postgres"], "values": [ { "option": "user", @@ -101,7 +117,7 @@ def is_email(user_input): }, { "name": "mysql", - "if": "database.engine=mysql", + "if": ["database.engine=mysql"], "values": [ { "option": "user", @@ -276,9 +292,11 @@ def is_email(user_input): }, { "name": "amavis", + "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", + "default-if": "true", "default": "false", }, { @@ -371,7 +389,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default": "true", + "default": "false", }, { "option": "config_dir", @@ -402,9 +420,11 @@ def is_email(user_input): }, { "name": "postwhite", + "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", + "default-if": "true", "default": "false", }, { @@ -415,9 +435,11 @@ def is_email(user_input): }, { "name": "spamassassin", + "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", + "default-if": "true", "default": "false", }, { @@ -484,9 +506,11 @@ def is_email(user_input): }, { "name": "opendkim", + "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", + "default-if": "true", "default": "false", }, { diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 105f0361..4fcd9c99 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -1,4 +1,4 @@ -"""Amavis related functions.""" +"""Rspamd related functions.""" import os import pwd diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 3faf3737..e9ef5578 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -184,25 +184,29 @@ def copy_from_template(template, dest, context): fp.write(ConfigFileTemplate(buf).substitute(context)) -def check_config_file(dest, interactive=False, upgrade=False, backup=False, restore=False): +def check_config_file(dest, + interactive=False, + upgrade=False, + backup=False, + restore=False): """Create a new installer config file if needed.""" is_present = True if os.path.exists(dest): return is_present, update_config(dest, False) if upgrade: - printcolor( + error( "You cannot upgrade an existing installation without a " - "configuration file.", RED) + "configuration file.") sys.exit(1) elif backup: is_present = False - printcolor( + error( "Your configuration file hasn't been found. A new one will be generated. " - "Please edit it with correct password for the databases !", RED) + "Please edit it with correct password for the databases !") elif restore: - printcolor( + error( "You cannot restore an existing installation without a " - f"configuration file. (file : {dest} has not been found...", RED) + f"configuration file. (file : {dest} has not been found...") sys.exit(1) printcolor( @@ -309,10 +313,17 @@ def validate(value, config_entry): def get_entry_value(entry, interactive): - if callable(entry["default"]): - default_value = entry["default"]() + if entry.get("default-if") is not None and interactive: + # In case in interactive we try to look for a default-if + default_entry = entry["default-if"] + else: + default_entry = entry["default"] + + if callable(default_entry): + default_value = default_entry() else: - default_value = entry["default"] + default_value = default_entry + user_value = None if entry.get("customizable") and interactive: while (user_value != '' and not validate(user_value, entry)): @@ -348,13 +359,14 @@ def load_config_template(interactive): config = configparser.ConfigParser() # only ask about options we need, else still generate default for section in tpl_dict: + interactive_section = interactive if "if" in section: - config_key, value = section.get("if").split("=") - section_name, option = config_key.split(".") - interactive_section = ( - config.get(section_name, option) == value and interactive) - else: - interactive_section = interactive + for condition in section.get("if"): + config_key, value = condition.split("=") + section_name, option = config_key.split(".") + interactive_section = interactive_section and ( + config.get(section_name, option) == value) + config.add_section(section["name"]) for config_entry in section["values"]: value = get_entry_value(config_entry, interactive_section) From 09fd2b76a55672fe79fb930aaa658caa82f96392 Mon Sep 17 00:00:00 2001 From: Spitap Date: Mon, 25 Sep 2023 11:52:45 +0200 Subject: [PATCH 24/47] Fixed config --- modoboa_installer/config_dict_template.py | 10 ++++++++++ modoboa_installer/scripts/files/modoboa/crontab.tpl | 1 + 2 files changed, 11 insertions(+) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index b3adc718..5d33e126 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -33,11 +33,14 @@ def is_email(user_input): { "option": "enabled", "default": "true", + "customizable": True, + "values": ["true", "false"], "question": "Do you want to setup an antispam utility?" }, { "option": "type", "default": "rspamd", + "customizable": True, "question": "Please select your antispam utility", "values": ["rspamd", "amavis"] } @@ -201,6 +204,13 @@ def is_email(user_input): "customizable": True, "question": "Please enter Modoboa db password", }, + { + "option": "cron_error_recipient", + "default": "root", + "customizable": True, + "question": + "Please enter a mail recipient for cron error reports" + }, { "option": "extensions", "default": ( diff --git a/modoboa_installer/scripts/files/modoboa/crontab.tpl b/modoboa_installer/scripts/files/modoboa/crontab.tpl index 84d9427e..f5d36d2a 100644 --- a/modoboa_installer/scripts/files/modoboa/crontab.tpl +++ b/modoboa_installer/scripts/files/modoboa/crontab.tpl @@ -3,6 +3,7 @@ # PYTHON=%{venv_path}/bin/python INSTANCE=%{instance_path} +MAILTO=%{cron_error_recipient} # Operations on mailboxes %{dovecot_enabled}* * * * * %{dovecot_mailboxes_owner} $PYTHON $INSTANCE/manage.py handle_mailbox_operations From bde57b073701f0c70fc83615179a31c069333e15 Mon Sep 17 00:00:00 2001 From: Spitap Date: Mon, 25 Sep 2023 12:09:38 +0200 Subject: [PATCH 25/47] Added possibility of if directive in each entry --- modoboa_installer/config_dict_template.py | 3 ++- modoboa_installer/utils.py | 26 +++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 5d33e126..df5f0137 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -42,7 +42,8 @@ def is_email(user_input): "default": "rspamd", "customizable": True, "question": "Please select your antispam utility", - "values": ["rspamd", "amavis"] + "values": ["rspamd", "amavis"], + "if": ["antispam.enabled=true"] } ] }, diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index e9ef5578..4ef298c6 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -292,6 +292,16 @@ def random_key(l=16): return key +def check_if_condition(config, entry): + """Check if the "if" directive is present and computes it""" + section_if = True + for condition in entry: + config_key, value = condition.split("=") + section_name, option = config_key.split(".") + section_if = config.get(section_name, option) == value + return section_if + + def validate(value, config_entry): if value is None: return False @@ -361,15 +371,19 @@ def load_config_template(interactive): for section in tpl_dict: interactive_section = interactive if "if" in section: - for condition in section.get("if"): - config_key, value = condition.split("=") - section_name, option = config_key.split(".") - interactive_section = interactive_section and ( - config.get(section_name, option) == value) + condition = check_if_condition(config, section["if"]) + interactive_section = condition and interactive config.add_section(section["name"]) for config_entry in section["values"]: - value = get_entry_value(config_entry, interactive_section) + if config_entry.get("if") is not None: + interactive_section = (interactive_section and + check_if_condition( + config, config_entry["if"] + ) + ) + value = get_entry_value(config_entry, + interactive_section) config.set(section["name"], config_entry["option"], value) return config From 5e476f0b85593427d4225ad2010f51ea9817ae2a Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 10:57:49 +0200 Subject: [PATCH 26/47] Fixed new source bug, removed bionic, added dynamic defaults --- modoboa_installer/config_dict_template.py | 19 ++++++++----------- modoboa_installer/package.py | 2 +- modoboa_installer/scripts/rspamd.py | 4 ---- modoboa_installer/utils.py | 19 ++++++++----------- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index df5f0137..dc166405 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -258,10 +258,11 @@ def is_email(user_input): }, { "name": "rspamd", + "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", - "default": "true", + "default": ["antispam.enabled=true", "antispam.type=amavis"], }, { "option": "user", @@ -270,6 +271,8 @@ def is_email(user_input): { "option": "password", "default": make_password, + "customizable": True, + "question": "Please enter Rspamd interface password", }, { "option": "dnsbl", @@ -303,12 +306,10 @@ def is_email(user_input): }, { "name": "amavis", - "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", - "default-if": "true", - "default": "false", + "default": ["antispam.enabled=true", "antispam.type=amavis"], }, { "option": "user", @@ -431,12 +432,10 @@ def is_email(user_input): }, { "name": "postwhite", - "if": ["antispam.enabled=true", "antispam.type=amavis"], "values": [ { "option": "enabled", - "default-if": "true", - "default": "false", + "default": ["antispam.enabled=true", "antispam.type=amavis"], }, { "option": "config_dir", @@ -450,8 +449,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default-if": "true", - "default": "false", + "default": ["antispam.enabled=true", "antispam.type=amavis"], }, { "option": "config_dir", @@ -521,8 +519,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default-if": "true", - "default": "false", + "default": ["antispam.enabled=true", "antispam.type=amavis"], }, { "option": "user", diff --git a/modoboa_installer/package.py b/modoboa_installer/package.py index 195d0345..b25392e3 100644 --- a/modoboa_installer/package.py +++ b/modoboa_installer/package.py @@ -57,7 +57,7 @@ def add_custom_repository(self, with_source: bool = True): key_file = f"/etc/apt/keyrings/{name}.gpg" utils.exec_cmd( - f"wget -O - {key_url} | gpg --dearmor | sudo tee {key_file} > /dev/null" + f"wget -O - {key_url} | gpg --dearmor | tee {key_file} > /dev/null" ) line_types = ["deb"] if with_source: diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 4fcd9c99..0ecbf480 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -46,10 +46,6 @@ def install_packages(self): 0, 0 ) - if codename == "bionic": - package.backend.install("software-properties-common") - utils.exec_cmd("add-apt-repository ppa:ubuntu-toolchain-r/test") - package.backend.add_custom_repository( "rspamd", "http://rspamd.com/apt-stable/", diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 4ef298c6..811d6d19 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -322,18 +322,14 @@ def validate(value, config_entry): return True -def get_entry_value(entry, interactive): - if entry.get("default-if") is not None and interactive: - # In case in interactive we try to look for a default-if - default_entry = entry["default-if"] - else: - default_entry = entry["default"] - +def get_entry_value(entry, interactive, config): + default_entry = entry("default") + if type(default_entry) is type(list()): + default_value = check_if_condition(config, default_entry) if callable(default_entry): - default_value = default_entry() + default_value = entry["default"]() else: default_value = default_entry - user_value = None if entry.get("customizable") and interactive: while (user_value != '' and not validate(user_value, entry)): @@ -383,7 +379,8 @@ def load_config_template(interactive): ) ) value = get_entry_value(config_entry, - interactive_section) + interactive_section, + config) config.set(section["name"], config_entry["option"], value) return config @@ -531,7 +528,7 @@ def check_app_compatibility(section, config): if section in APP_INCOMPATIBILITY.keys(): for app in APP_INCOMPATIBILITY[section]: if config.getboolean(app, "enabled"): - error(f"{section} cannont be installed if {app} is enabled. " + error(f"{section} cannot be installed if {app} is enabled. " "Please disable one of them.") incompatible_app.append(app) return len(incompatible_app) == 0 From 95a0cac2d9db41301db1514c74f490cb08d2a4d9 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 11:30:16 +0200 Subject: [PATCH 27/47] Fixed capped default choice, removed old py2 code --- modoboa_installer/utils.py | 20 +++++----------- run.py | 47 +++++--------------------------------- 2 files changed, 12 insertions(+), 55 deletions(-) diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 811d6d19..4d96009e 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -1,5 +1,6 @@ """Utility functions.""" +import configparser import contextlib import datetime import getpass @@ -12,10 +13,6 @@ import string import subprocess import sys -try: - import configparser -except ImportError: - import ConfigParser as configparser from . import config_dict_template from .compatibility_matrix import APP_INCOMPATIBILITY @@ -34,12 +31,7 @@ class FatalError(Exception): def user_input(message): """Ask something to the user.""" - try: - from builtins import input - except ImportError: - answer = raw_input(message) - else: - answer = input(message) + answer = input(message) return answer @@ -322,8 +314,8 @@ def validate(value, config_entry): return True -def get_entry_value(entry, interactive, config): - default_entry = entry("default") +def get_entry_value(entry: dict, interactive: bool, config: configparser.ConfigParser) -> string: + default_entry = entry["default"] if type(default_entry) is type(list()): default_value = check_if_condition(config, default_entry) if callable(default_entry): @@ -480,7 +472,7 @@ def validate_backup_path(path: str, silent_mode: bool): if not path_exists: if not silent_mode: create_dir = input( - f"\"{path}\" doesn't exist, would you like to create it? [Y/n]\n" + f"\"{path}\" doesn't exist, would you like to create it? [y/N]\n" ).lower() if silent_mode or (not silent_mode and create_dir.startswith("y")): @@ -495,7 +487,7 @@ def validate_backup_path(path: str, silent_mode: bool): if len(os.listdir(path)) != 0: if not silent_mode: delete_dir = input( - "Warning: backup directory is not empty, it will be purged if you continue... [Y/n]\n").lower() + "Warning: backup directory is not empty, it will be purged if you continue... [y/N]\n").lower() if silent_mode or (not silent_mode and delete_dir.startswith("y")): try: diff --git a/run.py b/run.py index 6f7e689f..2aa5d4b5 100755 --- a/run.py +++ b/run.py @@ -11,7 +11,6 @@ import ConfigParser as configparser import sys -import checks from modoboa_installer import compatibility_matrix from modoboa_installer import constants from modoboa_installer import package @@ -37,12 +36,6 @@ def installation_disclaimer(args, config): """Display installation disclaimer.""" hostname = config.get("general", "hostname") - utils.printcolor( - "Notice:\n" - "It is recommanded to run this installer on a FRESHLY installed server.\n" - "(ie. with nothing special already installed on it)\n", - utils.CYAN - ) utils.printcolor( "Warning:\n" "Before you start the installation, please make sure the following " @@ -53,7 +46,7 @@ def installation_disclaimer(args, config): hostname.replace(".{}".format(args.domain), ""), hostname ), - utils.YELLOW + utils.CYAN ) utils.printcolor( "Your mail server will be installed with the following components:", @@ -119,9 +112,6 @@ def backup_system(config, args): utils.copy_file(args.configfile, backup_path) # Backup applications for app in PRIMARY_APPS: - if app == "dovecot" and args.no_mail: - utils.printcolor("Skipping mail backup", utils.BLUE) - continue scripts.backup(app, config, backup_path) @@ -173,17 +163,11 @@ def main(input_args): help="For script usage, do not require user interaction " "backup will be saved at ./modoboa_backup/Backup_M_Y_d_H_M " "if --backup-path is not provided") - parser.add_argument( - "--no-mail", action="store_true", default=False, - help="Disable mail backup (save space)") parser.add_argument( "--restore", type=str, metavar="path", help="Restore a previously backup up modoboa instance on a NEW machine. " "You MUST provide backup directory" - ), - parser.add_argument( - "--skip-checks", action="store_true", default=False, - help="Skip the checks the installer performs initially") + ) parser.add_argument("domain", type=str, help="The main domain of your future mail server") args = parser.parse_args(input_args) @@ -204,12 +188,6 @@ def main(input_args): utils.success("Welcome to Modoboa installer!\n") - # Checks - if not args.skip_checks: - utils.printcolor("Checking the installer...", utils.BLUE) - checks.handle() - 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) @@ -221,12 +199,12 @@ def main(input_args): # Check if config is outdated and ask user if it needs to be updated if is_config_file_available and outdate_config: answer = utils.user_input("It seems that your config file is outdated. " - "Would you like to update it? (Y/n) ") - if not answer or answer.lower().startswith("y"): + "Would you like to update it? (y/N) ") + if answer.lower().startswith("y"): config_file_update_complete(utils.update_config(args.configfile)) if not args.stop_after_configfile_check: - answer = utils.user_input("Would you like to stop to review the updated config? (Y/n)") - if not answer or answer.lower().startswith("y"): + answer = utils.user_input("Would you like to stop to review the updated config? (y/N)") + if answer.lower().startswith("y"): return else: utils.error("You might encounter unexpected errors ! " @@ -300,19 +278,6 @@ def main(input_args): "Restore complete! You can enjoy Modoboa at https://{} (same credentials as before)" .format(config.get("general", "hostname")) ) - utils.success( - "\n" - "Modoboa is a free software maintained by volunteers.\n" - "You like the project and want it to be sustainable?\n" - "Then don't wait anymore and go sponsor it here:\n" - ) - utils.printcolor( - "https://github.com/sponsors/modoboa\n", - utils.YELLOW - ) - utils.success( - "Thank you for your help :-)\n" - ) if __name__ == "__main__": From ec7674bd44834f4e5a344955f3b050bb55caf304 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 11:42:58 +0200 Subject: [PATCH 28/47] small fix --- modoboa_installer/config_dict_template.py | 2 +- modoboa_installer/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index dc166405..ee8b1734 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -258,7 +258,7 @@ def is_email(user_input): }, { "name": "rspamd", - "if": ["antispam.enabled=true", "antispam.type=amavis"], + "if": ["antispam.enabled=true", "antispam.type=rspamd"], "values": [ { "option": "enabled", diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 4d96009e..1088bd37 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -317,8 +317,8 @@ def validate(value, config_entry): def get_entry_value(entry: dict, interactive: bool, config: configparser.ConfigParser) -> string: default_entry = entry["default"] if type(default_entry) is type(list()): - default_value = check_if_condition(config, default_entry) - if callable(default_entry): + default_value = str(check_if_condition(config, default_entry)).lower() + elif callable(default_entry): default_value = entry["default"]() else: default_value = default_entry From 43d8b198f325a4859f423509c87a920ec86207df Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 11:49:57 +0200 Subject: [PATCH 29/47] small fix part 2 --- modoboa_installer/config_dict_template.py | 2 +- run.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index ee8b1734..30455bd7 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -262,7 +262,7 @@ def is_email(user_input): "values": [ { "option": "enabled", - "default": ["antispam.enabled=true", "antispam.type=amavis"], + "default": ["antispam.enabled=true", "antispam.type=rspamd"], }, { "option": "user", diff --git a/run.py b/run.py index 2aa5d4b5..7ae4f7c7 100755 --- a/run.py +++ b/run.py @@ -239,7 +239,7 @@ def main(input_args): # Show concerned components components = [] for section in config.sections(): - if section in ["general", "database", "mysql", "postgres", + if section in ["general", "antispam", "database", "mysql", "postgres", "certificate", "letsencrypt", "backup"]: continue if (config.has_option(section, "enabled") and From 1ae86716e2394f8e8d685bec27c482d5f0ee26c6 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 12:22:57 +0200 Subject: [PATCH 30/47] Bug fix --- modoboa_installer/package.py | 5 ++++- modoboa_installer/scripts/rspamd.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modoboa_installer/package.py b/modoboa_installer/package.py index b25392e3..483e2e96 100644 --- a/modoboa_installer/package.py +++ b/modoboa_installer/package.py @@ -2,6 +2,8 @@ import re +from os.path import isfile as file_exists + from . import utils @@ -68,7 +70,8 @@ def add_custom_repository(self, f"{url} {codename} main" ) target_file = f"/etc/apt/source.list.d/{name}.list" - utils.exec_cmd(f'echo "{line} | sude tee {target_file}') + tee_option = "-a" if file_exists(target_file) else "" + utils.exec_cmd(f'echo "{line}" | sude tee {tee_option} {target_file}') self.index_updated = False def update(self): diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 0ecbf480..6c03103c 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -95,6 +95,7 @@ def get_template_context(self): "https://rspamd.com/doc/quickstart.html#setting-the-controller-password") _context["controller_password"] = self.app_config["password"] else: + controller_password = controller_password.decode().replace("\n", "") _context["controller_password"] = controller_password _context["greylisting_disabled"] = "" if not self.app_config["greylisting"].lower() == "true" else "#" _context["whitelist_auth_enabled"] = "" if self.app_config["whitelist_auth"].lower() == "true" else "#" From 08f71fb1b863ad94f6e3af76f4a84d23921f97de Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 12:31:47 +0200 Subject: [PATCH 31/47] small fix part 3 --- modoboa_installer/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/package.py b/modoboa_installer/package.py index 483e2e96..c10dacf9 100644 --- a/modoboa_installer/package.py +++ b/modoboa_installer/package.py @@ -71,7 +71,7 @@ def add_custom_repository(self, ) target_file = f"/etc/apt/source.list.d/{name}.list" tee_option = "-a" if file_exists(target_file) else "" - utils.exec_cmd(f'echo "{line}" | sude tee {tee_option} {target_file}') + utils.exec_cmd(f'echo "{line}" | tee {tee_option} {target_file}') self.index_updated = False def update(self): From 7cec24fad6c2e110cf37814d5010a0dba0876de0 Mon Sep 17 00:00:00 2001 From: Spitap Date: Sun, 15 Oct 2023 12:40:39 +0200 Subject: [PATCH 32/47] small fix part 4 --- modoboa_installer/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/package.py b/modoboa_installer/package.py index c10dacf9..a2719d5b 100644 --- a/modoboa_installer/package.py +++ b/modoboa_installer/package.py @@ -69,7 +69,7 @@ def add_custom_repository(self, f"{line_type} [arch=amd64 signed-by={key_file}] " f"{url} {codename} main" ) - target_file = f"/etc/apt/source.list.d/{name}.list" + target_file = f"/etc/apt/sources.list.d/{name}.list" tee_option = "-a" if file_exists(target_file) else "" utils.exec_cmd(f'echo "{line}" | tee {tee_option} {target_file}') self.index_updated = False From 1e36d1453441b6d36b943288c53e00170cb07a93 Mon Sep 17 00:00:00 2001 From: Spitfireap Date: Fri, 12 Jan 2024 18:08:28 +0100 Subject: [PATCH 33/47] added sieve rule to move spam to junk folder --- modoboa_installer/config_dict_template.py | 6 +++++- modoboa_installer/scripts/dovecot.py | 12 +++++++++++- .../scripts/files/dovecot/conf.d/90-sieve.conf | 2 +- .../conf.d/custom_after_sieve/spam-to-junk.sieve.tpl | 4 ++++ 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 modoboa_installer/scripts/files/dovecot/conf.d/custom_after_sieve/spam-to-junk.sieve.tpl diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 30455bd7..79083d94 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -379,7 +379,11 @@ def is_email(user_input): }, { "option": "radicale_auth_socket_path", - "default": "/var/run/dovecot/auth-radicale" + "default": "/var/run/dovecot/auth-radicale", + }, + { + "option": "move_spam_to_junk", + "default": "true", }, ] }, diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 2559b70a..721b43d2 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -40,7 +40,13 @@ def setup_user(self): def get_config_files(self): """Additional config files.""" - return self.config_files + [ + _config_files = self.config_files + + if self.app_config["move_spam_to_junk"]: + _config_files += ["conf.d/90-sieve.conf", + "conf.d/custom_after_sieve/spam-to-junk.sieve"] + + return _config_files + [ "dovecot-sql-{}.conf.ext=dovecot-sql.conf.ext" .format(self.dbengine), "dovecot-sql-master-{}.conf.ext=dovecot-sql-master.conf.ext" @@ -165,6 +171,10 @@ def post_run(self): utils.exec_cmd("chmod 600 /etc/dovecot/conf.d/10-ssl-keys.try") # Add mailboxes user to dovecot group for modoboa mailbox commands. # See https://github.com/modoboa/modoboa/issues/2157. + if self.app_config["move_spam_to_junk"]: + # Compile sieve script + sieve_file = "/etc/dovecot/conf.d/custom_after_sieve/spam-to-junk.sieve" + utils.exec_cmd(f"/usr/bin/sievec {sieve_file}") system.add_user_to_group(self.mailboxes_owner, 'dovecot') def restart_daemon(self): diff --git a/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf b/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf index 35a9f5d3..dd76330f 100644 --- a/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf +++ b/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf @@ -38,7 +38,7 @@ plugin { # Identical to sieve_before, only the specified scripts are executed after the # user's script (only when keep is still in effect!). Multiple script file or # directory paths can be specified by appending an increasing number. - #sieve_after = + {%dovecot_enabled}sieve_after = /etc/dovecot/conf.d/custom_after_sieve #sieve_after2 = #sieve_after2 = (etc...) diff --git a/modoboa_installer/scripts/files/dovecot/conf.d/custom_after_sieve/spam-to-junk.sieve.tpl b/modoboa_installer/scripts/files/dovecot/conf.d/custom_after_sieve/spam-to-junk.sieve.tpl new file mode 100644 index 00000000..8eb572cd --- /dev/null +++ b/modoboa_installer/scripts/files/dovecot/conf.d/custom_after_sieve/spam-to-junk.sieve.tpl @@ -0,0 +1,4 @@ +require "fileinto"; +if header :contains "X-Spam-Status" "Yes" { + fileinto "Junk"; +} From c5b3a25413ff1667eab9facc731c715e3656f8b7 Mon Sep 17 00:00:00 2001 From: Spitfireap Date: Fri, 12 Jan 2024 18:26:35 +0100 Subject: [PATCH 34/47] Made 90-sieve a template --- modoboa_installer/scripts/dovecot.py | 44 ++++--------------- .../{90-sieve.conf => 90-sieve.conf.tpl} | 0 2 files changed, 8 insertions(+), 36 deletions(-) rename modoboa_installer/scripts/files/dovecot/conf.d/{90-sieve.conf => 90-sieve.conf.tpl} (100%) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 721b43d2..9caa38bd 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -4,7 +4,6 @@ import os import pwd import shutil -import uuid from .. import database from .. import package @@ -15,6 +14,7 @@ class Dovecot(base.Installer): + """Dovecot installer.""" appname = "dovecot" @@ -27,9 +27,8 @@ class Dovecot(base.Installer): } config_files = [ "dovecot.conf", "dovecot-dict-sql.conf.ext", "conf.d/10-ssl.conf", - "conf.d/10-master.conf", "conf.d/20-lmtp.conf", "conf.d/10-ssl-keys.try", - "conf.d/dovecot-oauth2.conf.ext" - ] + "conf.d/10-master.conf", "conf.d/20-lmtp.conf", + "conf.d/10-ssl-keys.try", "conf.d/90-sieve.conf"] with_user = True def setup_user(self): @@ -43,8 +42,7 @@ def get_config_files(self): _config_files = self.config_files if self.app_config["move_spam_to_junk"]: - _config_files += ["conf.d/90-sieve.conf", - "conf.d/custom_after_sieve/spam-to-junk.sieve"] + _config_files += ["conf.d/custom_after_sieve/spam-to-junk.sieve"] return _config_files + [ "dovecot-sql-{}.conf.ext=dovecot-sql.conf.ext" @@ -76,29 +74,11 @@ def install_packages(self): self.backports_codename = "bookworm" package.backend.preconfigure( "dovecot-core", "create-ssl-cert", "boolean", "false") - super().install_packages() - - def create_oauth2_app(self): - """Create a application for Oauth2 authentication.""" - # FIXME: how can we check that application already exists ? - venv_path = self.config.get("modoboa", "venv_path") - python_path = os.path.join(venv_path, "bin", "python") - instance_path = self.config.get("modoboa", "instance_path") - script_path = os.path.join(instance_path, "manage.py") - client_id = "dovecot" - client_secret = str(uuid.uuid4()) - cmd = ( - f"{python_path} {script_path} createapplication " - f"--name=Dovecot --skip-authorization " - f"--client-id={client_id} --client-secret={client_secret} " - f"confidential client-credentials" - ) - utils.exec_cmd(cmd) - return client_id, client_secret + super(Dovecot, self).install_packages() def get_template_context(self): """Additional variables.""" - context = super().get_template_context() + context = super(Dovecot, self).get_template_context() pw_mailbox = pwd.getpwnam(self.mailboxes_owner) dovecot_package = {"deb": "dovecot-core", "rpm": "dovecot"} ssl_protocol_parameter = "ssl_protocols" @@ -119,13 +99,6 @@ def get_template_context(self): # Protocols are automatically guessed on debian/ubuntu protocols = "" - oauth2_client_id, oauth2_client_secret = self.create_oauth2_app() - hostname = self.config.get("general", "hostname") - oauth2_introspection_url = ( - f"https://{oauth2_client_id}:{oauth2_client_secret}" - f"@{hostname}/api/o/introspect/" - ) - context.update({ "db_driver": self.db_driver, "mailboxes_owner_uid": pw_mailbox[2], @@ -142,8 +115,7 @@ def get_template_context(self): "radicale_auth_socket_path": os.path.basename( self.config.get("dovecot", "radicale_auth_socket_path")), "modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#", - "not_modoboa_2_2_or_greater": "" if not self.modoboa_2_2_or_greater else "#", - "oauth2_introspection_url": oauth2_introspection_url + "not_modoboa_2_2_or_greater": "" if not self.modoboa_2_2_or_greater else "#" }) return context @@ -173,7 +145,7 @@ def post_run(self): # See https://github.com/modoboa/modoboa/issues/2157. if self.app_config["move_spam_to_junk"]: # Compile sieve script - sieve_file = "/etc/dovecot/conf.d/custom_after_sieve/spam-to-junk.sieve" + sieve_file = f"{self.config_dir}/conf.d/custom_after_sieve/spam-to-junk.sieve" utils.exec_cmd(f"/usr/bin/sievec {sieve_file}") system.add_user_to_group(self.mailboxes_owner, 'dovecot') diff --git a/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf b/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf.tpl similarity index 100% rename from modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf rename to modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf.tpl From c98e6cde5ed204ee4207b1d6ab42a4e6c796f779 Mon Sep 17 00:00:00 2001 From: Spitfireap Date: Fri, 12 Jan 2024 18:38:18 +0100 Subject: [PATCH 35/47] Made junk sieve optional --- modoboa_installer/scripts/dovecot.py | 5 +++-- .../scripts/files/dovecot/conf.d/90-sieve.conf.tpl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 9caa38bd..5d33c4e6 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -78,7 +78,7 @@ def install_packages(self): def get_template_context(self): """Additional variables.""" - context = super(Dovecot, self).get_template_context() + context = super().get_template_context() pw_mailbox = pwd.getpwnam(self.mailboxes_owner) dovecot_package = {"deb": "dovecot-core", "rpm": "dovecot"} ssl_protocol_parameter = "ssl_protocols" @@ -115,7 +115,8 @@ def get_template_context(self): "radicale_auth_socket_path": os.path.basename( self.config.get("dovecot", "radicale_auth_socket_path")), "modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#", - "not_modoboa_2_2_or_greater": "" if not self.modoboa_2_2_or_greater else "#" + "not_modoboa_2_2_or_greater": "" if not self.modoboa_2_2_or_greater else "#", + "do_move_spam_to_junk": "" if self.app_config["move_spam_to_junk"] else "#" }) return context diff --git a/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf.tpl b/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf.tpl index dd76330f..480d2c2f 100644 --- a/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf.tpl +++ b/modoboa_installer/scripts/files/dovecot/conf.d/90-sieve.conf.tpl @@ -38,7 +38,7 @@ plugin { # Identical to sieve_before, only the specified scripts are executed after the # user's script (only when keep is still in effect!). Multiple script file or # directory paths can be specified by appending an increasing number. - {%dovecot_enabled}sieve_after = /etc/dovecot/conf.d/custom_after_sieve + %{do_move_spam_to_junk}sieve_after = /etc/dovecot/conf.d/custom_after_sieve #sieve_after2 = #sieve_after2 = (etc...) From a6cc0caceb1b5d60e6fd313ff9d9ee71bc656f6a Mon Sep 17 00:00:00 2001 From: Spitfireap Date: Fri, 12 Jan 2024 18:52:40 +0100 Subject: [PATCH 36/47] create sieve dir if needed --- modoboa_installer/scripts/dovecot.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 5d33c4e6..6590a0f0 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -4,6 +4,7 @@ import os import pwd import shutil +import stat from .. import database from .. import package @@ -120,6 +121,17 @@ def get_template_context(self): }) return context + def install_config_files(self): + """Create sieve dir if needed.""" + if self.app_config["move_spam_to_junk"]: + utils.mkdir_safe( + f"{self.config_dir}/conf.d/custom_after_sieve", + stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | + stat.S_IROTH | stat.S_IXOTH, + 0, 0 + ) + super().install_config_files() + def post_run(self): """Additional tasks.""" if self.dbengine == "postgres": From c9c4cafd8b4eaa35e4a118028f4d0aa8708ab6ff Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 23 Feb 2024 11:15:41 +0100 Subject: [PATCH 37/47] Fixed file copy issue --- modoboa_installer/scripts/dovecot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 6590a0f0..03328554 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -148,7 +148,8 @@ def post_run(self): self.get_file_path("fix_modoboa_postgres_schema.sql") ) for f in glob.glob("{}/*".format(self.get_file_path("conf.d"))): - utils.copy_file(f, "{}/conf.d".format(self.config_dir)) + if os.path.isfile(f): + utils.copy_file(f, "{}/conf.d".format(self.config_dir)) # Make postlogin script executable utils.exec_cmd("chmod +x /usr/local/bin/postlogin.sh") # Only root should have read access to the 10-ssl-keys.try From fb9f854893fce1b4fb1b0a3340883a08344234c5 Mon Sep 17 00:00:00 2001 From: Spitfireap Date: Thu, 3 Oct 2024 11:36:55 +0200 Subject: [PATCH 38/47] Update after rebase --- modoboa_installer/disclaimers.py | 51 +++++++++++++++++++ modoboa_installer/scripts/dovecot.py | 28 +++++++++-- run.py | 74 +++++++++------------------- 3 files changed, 97 insertions(+), 56 deletions(-) create mode 100644 modoboa_installer/disclaimers.py diff --git a/modoboa_installer/disclaimers.py b/modoboa_installer/disclaimers.py new file mode 100644 index 00000000..9faff69b --- /dev/null +++ b/modoboa_installer/disclaimers.py @@ -0,0 +1,51 @@ +from . import utils + + +def installation_disclaimer(args, config): + """Display installation disclaimer.""" + hostname = config.get("general", "hostname") + utils.printcolor( + "Notice:\n" + "It is recommanded to run this installer on a FRESHLY installed server.\n" + "(ie. with nothing special already installed on it)\n", + utils.CYAN + ) + utils.printcolor( + "Warning:\n" + "Before you start the installation, please make sure the following " + "DNS records exist for domain '{}':\n" + " {} IN A \n" + " @ IN MX {}.\n".format( + args.domain, + hostname.replace(".{}".format(args.domain), ""), + hostname + ), + utils.YELLOW + ) + utils.printcolor( + "Your mail server will be installed with the following components:", + utils.BLUE) + + +def upgrade_disclaimer(config): + """Display upgrade disclaimer.""" + utils.printcolor( + "Your mail server is about to be upgraded and the following components" + " will be impacted:", utils.BLUE + ) + + +def backup_disclaimer(): + """Display backup disclamer. """ + utils.printcolor( + "Your mail server will be backed up locally.\n" + " !! You should really transfer the backup somewhere else...\n" + " !! Custom configuration (like for postfix) won't be saved.", utils.BLUE) + + +def restore_disclaimer(): + """Display restore disclamer. """ + utils.printcolor( + "You are about to restore a previous installation of Modoboa.\n" + "If a new version has been released in between, please update your database!", + utils.BLUE) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 03328554..0463142f 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -28,8 +28,9 @@ class Dovecot(base.Installer): } config_files = [ "dovecot.conf", "dovecot-dict-sql.conf.ext", "conf.d/10-ssl.conf", - "conf.d/10-master.conf", "conf.d/20-lmtp.conf", - "conf.d/10-ssl-keys.try", "conf.d/90-sieve.conf"] + "conf.d/10-master.conf", "conf.d/20-lmtp.conf", "conf.d/10-ssl-keys.try", + "conf.d/dovecot-oauth2.conf.ext" + ] with_user = True def setup_user(self): @@ -75,7 +76,25 @@ def install_packages(self): self.backports_codename = "bookworm" package.backend.preconfigure( "dovecot-core", "create-ssl-cert", "boolean", "false") - super(Dovecot, self).install_packages() + super().install_packages() + + def create_oauth2_app(self): + """Create a application for Oauth2 authentication.""" + # FIXME: how can we check that application already exists ? + venv_path = self.config.get("modoboa", "venv_path") + python_path = os.path.join(venv_path, "bin", "python") + instance_path = self.config.get("modoboa", "instance_path") + script_path = os.path.join(instance_path, "manage.py") + client_id = "dovecot" + client_secret = str(uuid.uuid4()) + cmd = ( + f"{python_path} {script_path} createapplication " + f"--name=Dovecot --skip-authorization " + f"--client-id={client_id} --client-secret={client_secret} " + f"confidential client-credentials" + ) + utils.exec_cmd(cmd) + return client_id, client_secret def get_template_context(self): """Additional variables.""" @@ -117,7 +136,8 @@ def get_template_context(self): self.config.get("dovecot", "radicale_auth_socket_path")), "modoboa_2_2_or_greater": "" if self.modoboa_2_2_or_greater else "#", "not_modoboa_2_2_or_greater": "" if not self.modoboa_2_2_or_greater else "#", - "do_move_spam_to_junk": "" if self.app_config["move_spam_to_junk"] else "#" + "do_move_spam_to_junk": "" if self.app_config["move_spam_to_junk"] else "#", + "oauth2_introspection_url": oauth2_introspection_url }) return context diff --git a/run.py b/run.py index 7ae4f7c7..b12bfb47 100755 --- a/run.py +++ b/run.py @@ -18,6 +18,7 @@ from modoboa_installer import ssl from modoboa_installer import system from modoboa_installer import utils +from modoboa_installer import disclaimers PRIMARY_APPS = [ @@ -33,53 +34,9 @@ ] -def installation_disclaimer(args, config): - """Display installation disclaimer.""" - hostname = config.get("general", "hostname") - utils.printcolor( - "Warning:\n" - "Before you start the installation, please make sure the following " - "DNS records exist for domain '{}':\n" - " {} IN A \n" - " @ IN MX {}.\n".format( - args.domain, - hostname.replace(".{}".format(args.domain), ""), - hostname - ), - utils.CYAN - ) - utils.printcolor( - "Your mail server will be installed with the following components:", - utils.BLUE) - - -def upgrade_disclaimer(config): - """Display upgrade disclaimer.""" - utils.printcolor( - "Your mail server is about to be upgraded and the following components" - " will be impacted:", utils.BLUE - ) - - -def backup_disclaimer(): - """Display backup disclamer. """ - utils.printcolor( - "Your mail server will be backed up locally.\n" - " !! You should really transfer the backup somewhere else...\n" - " !! Custom configuration (like for postfix) won't be saved.", utils.BLUE) - - -def restore_disclaimer(): - """Display restore disclamer. """ - utils.printcolor( - "You are about to restore a previous installation of Modoboa.\n" - "If a new version has been released in between, please update your database!", - utils.BLUE) - - def backup_system(config, args): """Launch backup procedure.""" - backup_disclaimer() + disclaimers.backup_disclaimer() backup_path = None if args.silent_backup: if not args.backup_path: @@ -199,12 +156,12 @@ def main(input_args): # Check if config is outdated and ask user if it needs to be updated if is_config_file_available and outdate_config: answer = utils.user_input("It seems that your config file is outdated. " - "Would you like to update it? (y/N) ") - if answer.lower().startswith("y"): + "Would you like to update it? (Y/n) ") + if not answer or answer.lower().startswith("y"): config_file_update_complete(utils.update_config(args.configfile)) if not args.stop_after_configfile_check: - answer = utils.user_input("Would you like to stop to review the updated config? (y/N)") - if answer.lower().startswith("y"): + answer = utils.user_input("Would you like to stop to review the updated config? (Y/n)") + if not answer or answer.lower().startswith("y"): return else: utils.error("You might encounter unexpected errors ! " @@ -229,12 +186,12 @@ def main(input_args): # Display disclaimer python 3 linux distribution if args.upgrade: - upgrade_disclaimer(config) + disclaimers.upgrade_disclaimer(config) elif args.restore: - restore_disclaimer() + disclaimers.restore_disclaimer() scripts.restore_prep(args.restore) else: - installation_disclaimer(args, config) + disclaimers.installation_disclaimer(args, config) # Show concerned components components = [] @@ -278,6 +235,19 @@ def main(input_args): "Restore complete! You can enjoy Modoboa at https://{} (same credentials as before)" .format(config.get("general", "hostname")) ) + utils.success( + "\n" + "Modoboa is a free software maintained by volunteers.\n" + "You like the project and want it to be sustainable?\n" + "Then don't wait anymore and go sponsor it here:\n" + ) + utils.printcolor( + "https://github.com/sponsors/modoboa\n", + utils.YELLOW + ) + utils.success( + "Thank you for your help :-)\n" + ) if __name__ == "__main__": From 58047d3bd9f379039b59fe32923e823488ba902f Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 11:57:02 +0100 Subject: [PATCH 39/47] imported checks --- run.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/run.py b/run.py index b12bfb47..a647ab4b 100755 --- a/run.py +++ b/run.py @@ -11,6 +11,7 @@ import ConfigParser as configparser import sys +from . import checks from modoboa_installer import compatibility_matrix from modoboa_installer import constants from modoboa_installer import package @@ -69,6 +70,9 @@ def backup_system(config, args): utils.copy_file(args.configfile, backup_path) # Backup applications for app in PRIMARY_APPS: + if app == "dovecot" and args.no_mail: + utils.printcolor("Skipping mail backup", utils.BLUE) + continue scripts.backup(app, config, backup_path) @@ -145,6 +149,12 @@ def main(input_args): utils.success("Welcome to Modoboa installer!\n") + # Checks + if not args.skip_checks: + utils.printcolor("Checking the installer...", utils.BLUE) + checks.handle() + 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) From 12b53978570a38caaff36acd760d486c52937186 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 11:58:08 +0100 Subject: [PATCH 40/47] fix import --- checks.py => modoboa_installer/checks.py | 0 run.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename checks.py => modoboa_installer/checks.py (100%) diff --git a/checks.py b/modoboa_installer/checks.py similarity index 100% rename from checks.py rename to modoboa_installer/checks.py diff --git a/run.py b/run.py index a647ab4b..90e243a6 100755 --- a/run.py +++ b/run.py @@ -11,7 +11,7 @@ import ConfigParser as configparser import sys -from . import checks +from modoboa_installer import checks from modoboa_installer import compatibility_matrix from modoboa_installer import constants from modoboa_installer import package From 321219d8ecc9d7cf7f636541a6844a563b795634 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 12:01:18 +0100 Subject: [PATCH 41/47] imported arguments --- run.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/run.py b/run.py index 90e243a6..78cd78b7 100755 --- a/run.py +++ b/run.py @@ -124,11 +124,17 @@ def main(input_args): help="For script usage, do not require user interaction " "backup will be saved at ./modoboa_backup/Backup_M_Y_d_H_M " "if --backup-path is not provided") + parser.add_argument( + "--no-mail", action="store_true", default=False, + help="Disable mail backup (save space)") parser.add_argument( "--restore", type=str, metavar="path", help="Restore a previously backup up modoboa instance on a NEW machine. " "You MUST provide backup directory" ) + parser.add_argument( + "--skip-checks", action="store_true", default=False, + help="Skip the checks the installer performs initially") parser.add_argument("domain", type=str, help="The main domain of your future mail server") args = parser.parse_args(input_args) From 7ea73baadaec8bf3b22910661d9d62190d48aa52 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 12:16:06 +0100 Subject: [PATCH 42/47] Fixed indentation --- modoboa_installer/scripts/dovecot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 0463142f..efc74dbd 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -79,7 +79,7 @@ def install_packages(self): super().install_packages() def create_oauth2_app(self): - """Create a application for Oauth2 authentication.""" + """Create a application for Oauth2 authentication.""" # FIXME: how can we check that application already exists ? venv_path = self.config.get("modoboa", "venv_path") python_path = os.path.join(venv_path, "bin", "python") From 8e14bf72e6ea8c36245519a88b1c121f7fbba1de Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 12:22:41 +0100 Subject: [PATCH 43/47] Fixed dovecot --- modoboa_installer/checks.py | 2 +- modoboa_installer/scripts/dovecot.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modoboa_installer/checks.py b/modoboa_installer/checks.py index a8022a74..5f2cb0fd 100644 --- a/modoboa_installer/checks.py +++ b/modoboa_installer/checks.py @@ -26,7 +26,7 @@ def check_version(): "Check README file for instructions about how to update.\n" "No support will be provided without an up-to-date installer!" ) - answer = utils.user_input("Continue anyway? (Y/n) ") + answer = utils.user_input("Continue anyway? (y/N) ") if not answer.lower().startswith("y"): sys.exit(0) else: diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index efc74dbd..191475a1 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -119,6 +119,13 @@ def get_template_context(self): # Protocols are automatically guessed on debian/ubuntu protocols = "" + oauth2_client_id, oauth2_client_secret = self.create_oauth2_app() + hostname = self.config.get("general", "hostname") + oauth2_introspection_url = ( + f"https://{oauth2_client_id}:{oauth2_client_secret}" + f"@{hostname}/api/o/introspect/" + ) + context.update({ "db_driver": self.db_driver, "mailboxes_owner_uid": pw_mailbox[2], From f6c6c3eb103101d96986e94d90c86aa828afa102 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 12:26:17 +0100 Subject: [PATCH 44/47] Fixed dovecot #2 --- modoboa_installer/scripts/dovecot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 191475a1..c00ca64c 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -5,6 +5,7 @@ import pwd import shutil import stat +import uuid from .. import database from .. import package From 1161cbd15b9168499dd902949aac5307c830a775 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 12:36:02 +0100 Subject: [PATCH 45/47] Added rspamd dashboard info --- run.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/run.py b/run.py index 78cd78b7..e4e8f9a7 100755 --- a/run.py +++ b/run.py @@ -246,6 +246,10 @@ def main(input_args): "Congratulations! You can enjoy Modoboa at https://{} (admin:password)" .format(config.get("general", "hostname")) ) + if config.get("rspamd", "enabled"): + utils.success( + f"You can also enjoy rspamd at https://{config.get("general", "hostname")} ({config.get("rspamd", "password")})" + ) else: utils.success( "Restore complete! You can enjoy Modoboa at https://{} (same credentials as before)" From 3fa0c37bdb5ab452cde932f67f6c3621f3548d71 Mon Sep 17 00:00:00 2001 From: Spitap Date: Thu, 31 Oct 2024 12:48:50 +0100 Subject: [PATCH 46/47] Added Arc signing --- modoboa_installer/scripts/files/rspamd/local.d/arc.conf | 3 +++ modoboa_installer/scripts/rspamd.py | 1 + run.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/arc.conf diff --git a/modoboa_installer/scripts/files/rspamd/local.d/arc.conf b/modoboa_installer/scripts/files/rspamd/local.d/arc.conf new file mode 100644 index 00000000..3dcf9923 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/arc.conf @@ -0,0 +1,3 @@ +try_fallback = false; +selector_map = "%selector_map_path"; +path_map = "%key_map_path"; diff --git a/modoboa_installer/scripts/rspamd.py b/modoboa_installer/scripts/rspamd.py index 6c03103c..3890f28e 100644 --- a/modoboa_installer/scripts/rspamd.py +++ b/modoboa_installer/scripts/rspamd.py @@ -22,6 +22,7 @@ class Rspamd(base.Installer): ] } config_files = ["local.d/dkim_signing.conf", + "local.d/arc.conf", "local.d/mx_check.conf", "local.d/spf.conf", "local.d/worker-controller.inc", diff --git a/run.py b/run.py index e4e8f9a7..4dab5a31 100755 --- a/run.py +++ b/run.py @@ -248,7 +248,7 @@ def main(input_args): ) if config.get("rspamd", "enabled"): utils.success( - f"You can also enjoy rspamd at https://{config.get("general", "hostname")} ({config.get("rspamd", "password")})" + f"You can also enjoy rspamd at https://{config.get("general", "hostname")}/rspamd ({config.get("rspamd", "password")})" ) else: utils.success( From 53d98bdbef54ee8fb699ebae1efe2d45767c476e Mon Sep 17 00:00:00 2001 From: Spitfireap Date: Wed, 18 Dec 2024 17:41:03 +0100 Subject: [PATCH 47/47] Updated ARC --- .../scripts/files/rspamd/local.d/arc.conf.tpl | 3 +++ .../rspamd/local.d/milter_headers.conf.tpl | 19 +------------------ 2 files changed, 4 insertions(+), 18 deletions(-) create mode 100644 modoboa_installer/scripts/files/rspamd/local.d/arc.conf.tpl diff --git a/modoboa_installer/scripts/files/rspamd/local.d/arc.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/arc.conf.tpl new file mode 100644 index 00000000..3dcf9923 --- /dev/null +++ b/modoboa_installer/scripts/files/rspamd/local.d/arc.conf.tpl @@ -0,0 +1,3 @@ +try_fallback = false; +selector_map = "%selector_map_path"; +path_map = "%key_map_path"; diff --git a/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl b/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl index f3c489ae..e0b37433 100644 --- a/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl +++ b/modoboa_installer/scripts/files/rspamd/local.d/milter_headers.conf.tpl @@ -1,25 +1,8 @@ -use = ["x-spam-status", "my-x-spam-score" ,"x-virus","authentication-results" ]; +use = ["x-spam-status","x-virus","authentication-results" ]; extended_spam_headers = false; skip_local = false; skip_authenticated = false; -# Write the score as a header -custom { - my-x-spam-score = <