From 53669b48de7ce85341a547ed2583380fcb06841b Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 3 Jul 2025 10:43:07 +0200 Subject: [PATCH] Compat with Modoboa 2.4.0 --- modoboa_installer/compatibility_matrix.py | 5 +++- modoboa_installer/config_dict_template.py | 12 ++-------- modoboa_installer/scripts/backup.py | 2 +- modoboa_installer/scripts/dovecot.py | 24 ++----------------- .../files/dovecot/conf.d/10-master.conf.tpl | 7 ------ .../scripts/files/nginx/modoboa.conf.tpl | 13 +++++----- .../scripts/files/postfix/master.cf.tpl | 5 ---- .../scripts/files/radicale/config.tpl | 4 ++-- .../scripts/files/uwsgi/modoboa.ini.tpl | 1 + modoboa_installer/scripts/modoboa.py | 7 +----- modoboa_installer/scripts/radicale.py | 17 ++++++++----- modoboa_installer/utils.py | 19 +++++++++++++++ 12 files changed, 50 insertions(+), 66 deletions(-) diff --git a/modoboa_installer/compatibility_matrix.py b/modoboa_installer/compatibility_matrix.py index 5fc9da8..9377c49 100644 --- a/modoboa_installer/compatibility_matrix.py +++ b/modoboa_installer/compatibility_matrix.py @@ -32,5 +32,8 @@ REMOVED_EXTENSIONS = { "modoboa-dmarc": "2.1.0", "modoboa-imap-migration": "2.1.0", "modoboa-sievefilters": "2.3.0", - "modoboa-postfix-autoreply": "2.3.0" + "modoboa-postfix-autoreply": "2.3.0", + "modoboa-contacts": "2.4.0", + "modoboa-radicale": "2.4.0", + "modoboa-webmail": "2.4.0", } diff --git a/modoboa_installer/config_dict_template.py b/modoboa_installer/config_dict_template.py index 2436e3b..72e4ebd 100644 --- a/modoboa_installer/config_dict_template.py +++ b/modoboa_installer/config_dict_template.py @@ -187,11 +187,7 @@ ConfigDictTemplate = [ }, { "option": "extensions", - "default": ( - "modoboa-amavis " - "modoboa-webmail modoboa-contacts " - "modoboa-radicale" - ), + "default": "" }, { "option": "devmode", @@ -303,10 +299,6 @@ ConfigDictTemplate = [ "option": "postmaster_address", "default": "postmaster@%(domain)s", }, - { - "option": "radicale_auth_socket_path", - "default": "/var/run/dovecot/auth-radicale" - }, ] }, { @@ -409,7 +401,7 @@ ConfigDictTemplate = [ }, { "option": "nb_processes", - "default": "2", + "default": "4", }, ] }, diff --git a/modoboa_installer/scripts/backup.py b/modoboa_installer/scripts/backup.py index 285f9a5..9ef9f5c 100644 --- a/modoboa_installer/scripts/backup.py +++ b/modoboa_installer/scripts/backup.py @@ -142,7 +142,7 @@ class Backup: """ Custom config : - DKIM keys: {{keys_storage_dir}} - - Radicale collection (calendat, contacts): {{home_dir}} + - Radicale collection (calendars, contacts): {{home_dir}} - Amavis : /etc/amavis/conf.d/99-custom - Postwhite : /etc/postwhite.conf Feel free to suggest to add others! diff --git a/modoboa_installer/scripts/dovecot.py b/modoboa_installer/scripts/dovecot.py index 2559b70..6c38e85 100644 --- a/modoboa_installer/scripts/dovecot.py +++ b/modoboa_installer/scripts/dovecot.py @@ -72,24 +72,6 @@ class Dovecot(base.Installer): "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 - def get_template_context(self): """Additional variables.""" context = super().get_template_context() @@ -113,7 +95,8 @@ class Dovecot(base.Installer): # Protocols are automatically guessed on debian/ubuntu protocols = "" - oauth2_client_id, oauth2_client_secret = self.create_oauth2_app() + oauth2_client_id, oauth2_client_secret = utils.create_oauth2_app( + "Dovecot", "dovecot", self.config) hostname = self.config.get("general", "hostname") oauth2_introspection_url = ( f"https://{oauth2_client_id}:{oauth2_client_secret}" @@ -132,9 +115,6 @@ class Dovecot(base.Installer): "protocols": protocols, "ssl_protocols": ssl_protocols, "ssl_protocol_parameter": ssl_protocol_parameter, - "radicale_user": self.config.get("radicale", "user"), - "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 diff --git a/modoboa_installer/scripts/files/dovecot/conf.d/10-master.conf.tpl b/modoboa_installer/scripts/files/dovecot/conf.d/10-master.conf.tpl index 604b5d7..59d48ab 100644 --- a/modoboa_installer/scripts/files/dovecot/conf.d/10-master.conf.tpl +++ b/modoboa_installer/scripts/files/dovecot/conf.d/10-master.conf.tpl @@ -131,13 +131,6 @@ service auth { group = postfix } - # Radicale auth - %{radicale_enabled}unix_listener %{radicale_auth_socket_path} { - %{radicale_enabled} mode = 0666 - %{radicale_enabled} user = %{radicale_user} - %{radicale_enabled} group = %{radicale_user} - %{radicale_enabled}} - # Auth process is run as this user. #user = $default_internal_user } diff --git a/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl b/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl index 9e60227..725402c 100644 --- a/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl +++ b/modoboa_installer/scripts/files/nginx/modoboa.conf.tpl @@ -37,7 +37,13 @@ server { try_files $uri $uri/ =404; } - location ^~ /new-admin { + location ~ ^/(api|accounts) { + include uwsgi_params; + uwsgi_param UWSGI_SCRIPT instance.wsgi:application; + uwsgi_pass modoboa; + } + + location / { alias %{app_instance_path}/frontend/; index index.html; @@ -48,10 +54,5 @@ server { try_files $uri $uri/ /index.html = 404; } - location / { - include uwsgi_params; - uwsgi_param UWSGI_SCRIPT instance.wsgi:application; - uwsgi_pass modoboa; - } %{extra_config} } diff --git a/modoboa_installer/scripts/files/postfix/master.cf.tpl b/modoboa_installer/scripts/files/postfix/master.cf.tpl index 514ddae..72b2369 100644 --- a/modoboa_installer/scripts/files/postfix/master.cf.tpl +++ b/modoboa_installer/scripts/files/postfix/master.cf.tpl @@ -124,11 +124,6 @@ mailman unix - n n - - pipe flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user} -# Modoboa autoreply service -# -autoreply unix - n n - - pipe - flags= user=%{dovecot_mailboxes_owner}:%{dovecot_mailboxes_owner} argv=%{modoboa_venv_path}/bin/python %{modoboa_instance_path}/manage.py autoreply $sender $mailbox - # Amavis return path # %{amavis_enabled}127.0.0.1:10025 inet n - n - - smtpd diff --git a/modoboa_installer/scripts/files/radicale/config.tpl b/modoboa_installer/scripts/files/radicale/config.tpl index e0b420c..e08e0f9 100644 --- a/modoboa_installer/scripts/files/radicale/config.tpl +++ b/modoboa_installer/scripts/files/radicale/config.tpl @@ -71,7 +71,7 @@ # Authentication method # Value: none | htpasswd | remote_user | http_x_remote_user -type = dovecot +type = radicale_modoboa_auth_oauth2 # Htpasswd filename # htpasswd_filename = users @@ -85,7 +85,7 @@ type = dovecot # Incorrect authentication delay (seconds) #delay = 1 -dovecot_socket = %{auth_socket_path} +oauth2_introspection_endpoint = %{oauth2_introspection_url} [rights] diff --git a/modoboa_installer/scripts/files/uwsgi/modoboa.ini.tpl b/modoboa_installer/scripts/files/uwsgi/modoboa.ini.tpl index 9d27e21..674ce4c 100644 --- a/modoboa_installer/scripts/files/uwsgi/modoboa.ini.tpl +++ b/modoboa_installer/scripts/files/uwsgi/modoboa.ini.tpl @@ -13,4 +13,5 @@ socket = %uwsgi_socket_path chmod-socket = 660 vacuum = true single-interpreter = True +max-requests = 5000 buffer-size = 8192 diff --git a/modoboa_installer/scripts/modoboa.py b/modoboa_installer/scripts/modoboa.py index e35f409..c2d01bf 100644 --- a/modoboa_installer/scripts/modoboa.py +++ b/modoboa_installer/scripts/modoboa.py @@ -56,9 +56,6 @@ class Modoboa(base.Installer): self.amavis_enabled = True else: self.extensions.remove("modoboa-amavis") - if "modoboa-radicale" in self.extensions: - if not self.config.getboolean("radicale", "enabled"): - self.extensions.remove("modoboa-radicale") self.dovecot_enabled = self.config.getboolean("dovecot", "enabled") self.opendkim_enabled = self.config.getboolean("opendkim", "enabled") self.dkim_cron_enabled = False @@ -243,8 +240,6 @@ class Modoboa(base.Installer): ), "dovecot_mailboxes_owner": ( self.config.get("dovecot", "mailboxes_owner")), - "radicale_enabled": ( - "" if "modoboa-radicale" in extensions else "#"), "opendkim_user": self.config.get("opendkim", "user"), "minutes": random.randint(1, 59), "hours": f"{random_hour},{random_hour+12}", @@ -276,7 +271,7 @@ class Modoboa(base.Installer): "pdfcredentials": { "storage_dir": pdf_storage_dir }, - "modoboa_radicale": { + "calendars": { "server_location": "https://{}/radicale/".format( self.config.get("general", "hostname")), "rights_file_path": "{}/rights".format( diff --git a/modoboa_installer/scripts/radicale.py b/modoboa_installer/scripts/radicale.py index 4fe6ca2..89d27c5 100644 --- a/modoboa_installer/scripts/radicale.py +++ b/modoboa_installer/scripts/radicale.py @@ -33,7 +33,7 @@ class Radicale(base.Installer): """Prepare a dedicated virtualenv.""" python.setup_virtualenv(self.venv_path, sudo_user=self.user) packages = [ - "Radicale", "pytz" + "Radicale", "pytz", "radicale-modoboa-auth-oauth2" ] python.install_packages(packages, self.venv_path, sudo_user=self.user) python.install_package_from_repository( @@ -43,17 +43,22 @@ class Radicale(base.Installer): def get_template_context(self): """Additional variables.""" - context = super(Radicale, self).get_template_context() - radicale_auth_socket_path = self.config.get( - "dovecot", "radicale_auth_socket_path") + context = super().get_template_context() + oauth2_client_id, oauth2_client_secret = utils.create_oauth2_app( + "Radicale", "radicale", self.config) + hostname = self.config.get("general", "hostname") + oauth2_introspection_url = ( + f"https://{oauth2_client_id}:{oauth2_client_secret}" + f"@{hostname}/api/o/introspect/" + ) context.update({ - "auth_socket_path": radicale_auth_socket_path + "oauth2_introspection_url": oauth2_introspection_url, }) return context def get_config_files(self): """Return appropriate path.""" - config_files = super(Radicale, self).get_config_files() + config_files = super().get_config_files() if package.backend.FORMAT == "deb": path = "supervisor=/etc/supervisor/conf.d/radicale.conf" else: diff --git a/modoboa_installer/utils.py b/modoboa_installer/utils.py index 18725c1..a9cd5f4 100644 --- a/modoboa_installer/utils.py +++ b/modoboa_installer/utils.py @@ -12,6 +12,7 @@ import stat import string import subprocess import sys +import uuid try: import configparser except ImportError: @@ -485,3 +486,21 @@ 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 create_oauth2_app(app_name: str, client_id: str, config) -> tuple[str, str]: + """Create a application for Oauth2 authentication.""" + # FIXME: how can we check that application already exists ? + venv_path = config.get("modoboa", "venv_path") + python_path = os.path.join(venv_path, "bin", "python") + instance_path = config.get("modoboa", "instance_path") + script_path = os.path.join(instance_path, "manage.py") + client_secret = str(uuid.uuid4()) + cmd = ( + f"{python_path} {script_path} createapplication " + f"--name={app_name} --skip-authorization " + f"--client-id={client_id} --client-secret={client_secret} " + f"confidential client-credentials" + ) + exec_cmd(cmd) + return client_id, client_secret