Compat with Modoboa 2.4.0

This commit is contained in:
Antoine Nguyen
2025-07-03 10:43:07 +02:00
parent 5fe3e49b9a
commit 53669b48de
12 changed files with 50 additions and 66 deletions

View File

@@ -32,5 +32,8 @@ REMOVED_EXTENSIONS = {
"modoboa-dmarc": "2.1.0", "modoboa-dmarc": "2.1.0",
"modoboa-imap-migration": "2.1.0", "modoboa-imap-migration": "2.1.0",
"modoboa-sievefilters": "2.3.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",
} }

View File

@@ -187,11 +187,7 @@ ConfigDictTemplate = [
}, },
{ {
"option": "extensions", "option": "extensions",
"default": ( "default": ""
"modoboa-amavis "
"modoboa-webmail modoboa-contacts "
"modoboa-radicale"
),
}, },
{ {
"option": "devmode", "option": "devmode",
@@ -303,10 +299,6 @@ ConfigDictTemplate = [
"option": "postmaster_address", "option": "postmaster_address",
"default": "postmaster@%(domain)s", "default": "postmaster@%(domain)s",
}, },
{
"option": "radicale_auth_socket_path",
"default": "/var/run/dovecot/auth-radicale"
},
] ]
}, },
{ {
@@ -409,7 +401,7 @@ ConfigDictTemplate = [
}, },
{ {
"option": "nb_processes", "option": "nb_processes",
"default": "2", "default": "4",
}, },
] ]
}, },

View File

@@ -142,7 +142,7 @@ class Backup:
""" """
Custom config : Custom config :
- DKIM keys: {{keys_storage_dir}} - DKIM keys: {{keys_storage_dir}}
- Radicale collection (calendat, contacts): {{home_dir}} - Radicale collection (calendars, contacts): {{home_dir}}
- Amavis : /etc/amavis/conf.d/99-custom - Amavis : /etc/amavis/conf.d/99-custom
- Postwhite : /etc/postwhite.conf - Postwhite : /etc/postwhite.conf
Feel free to suggest to add others! Feel free to suggest to add others!

View File

@@ -72,24 +72,6 @@ class Dovecot(base.Installer):
"dovecot-core", "create-ssl-cert", "boolean", "false") "dovecot-core", "create-ssl-cert", "boolean", "false")
super().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): def get_template_context(self):
"""Additional variables.""" """Additional variables."""
context = super().get_template_context() context = super().get_template_context()
@@ -113,7 +95,8 @@ class Dovecot(base.Installer):
# Protocols are automatically guessed on debian/ubuntu # Protocols are automatically guessed on debian/ubuntu
protocols = "" 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") hostname = self.config.get("general", "hostname")
oauth2_introspection_url = ( oauth2_introspection_url = (
f"https://{oauth2_client_id}:{oauth2_client_secret}" f"https://{oauth2_client_id}:{oauth2_client_secret}"
@@ -132,9 +115,6 @@ class Dovecot(base.Installer):
"protocols": protocols, "protocols": protocols,
"ssl_protocols": ssl_protocols, "ssl_protocols": ssl_protocols,
"ssl_protocol_parameter": ssl_protocol_parameter, "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 "#", "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 "#",
"oauth2_introspection_url": oauth2_introspection_url "oauth2_introspection_url": oauth2_introspection_url

View File

@@ -131,13 +131,6 @@ service auth {
group = postfix 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. # Auth process is run as this user.
#user = $default_internal_user #user = $default_internal_user
} }

View File

@@ -37,7 +37,13 @@ server {
try_files $uri $uri/ =404; 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/; alias %{app_instance_path}/frontend/;
index index.html; index index.html;
@@ -48,10 +54,5 @@ server {
try_files $uri $uri/ /index.html = 404; try_files $uri $uri/ /index.html = 404;
} }
location / {
include uwsgi_params;
uwsgi_param UWSGI_SCRIPT instance.wsgi:application;
uwsgi_pass modoboa;
}
%{extra_config} %{extra_config}
} }

View File

@@ -124,11 +124,6 @@ mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user} ${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 return path
# #
%{amavis_enabled}127.0.0.1:10025 inet n - n - - smtpd %{amavis_enabled}127.0.0.1:10025 inet n - n - - smtpd

View File

@@ -71,7 +71,7 @@
# Authentication method # Authentication method
# Value: none | htpasswd | remote_user | http_x_remote_user # Value: none | htpasswd | remote_user | http_x_remote_user
type = dovecot type = radicale_modoboa_auth_oauth2
# Htpasswd filename # Htpasswd filename
# htpasswd_filename = users # htpasswd_filename = users
@@ -85,7 +85,7 @@ type = dovecot
# Incorrect authentication delay (seconds) # Incorrect authentication delay (seconds)
#delay = 1 #delay = 1
dovecot_socket = %{auth_socket_path} oauth2_introspection_endpoint = %{oauth2_introspection_url}
[rights] [rights]

View File

@@ -13,4 +13,5 @@ socket = %uwsgi_socket_path
chmod-socket = 660 chmod-socket = 660
vacuum = true vacuum = true
single-interpreter = True single-interpreter = True
max-requests = 5000
buffer-size = 8192 buffer-size = 8192

View File

@@ -56,9 +56,6 @@ class Modoboa(base.Installer):
self.amavis_enabled = True self.amavis_enabled = True
else: else:
self.extensions.remove("modoboa-amavis") 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.dovecot_enabled = self.config.getboolean("dovecot", "enabled")
self.opendkim_enabled = self.config.getboolean("opendkim", "enabled") self.opendkim_enabled = self.config.getboolean("opendkim", "enabled")
self.dkim_cron_enabled = False self.dkim_cron_enabled = False
@@ -243,8 +240,6 @@ class Modoboa(base.Installer):
), ),
"dovecot_mailboxes_owner": ( "dovecot_mailboxes_owner": (
self.config.get("dovecot", "mailboxes_owner")), self.config.get("dovecot", "mailboxes_owner")),
"radicale_enabled": (
"" if "modoboa-radicale" in extensions else "#"),
"opendkim_user": self.config.get("opendkim", "user"), "opendkim_user": self.config.get("opendkim", "user"),
"minutes": random.randint(1, 59), "minutes": random.randint(1, 59),
"hours": f"{random_hour},{random_hour+12}", "hours": f"{random_hour},{random_hour+12}",
@@ -276,7 +271,7 @@ class Modoboa(base.Installer):
"pdfcredentials": { "pdfcredentials": {
"storage_dir": pdf_storage_dir "storage_dir": pdf_storage_dir
}, },
"modoboa_radicale": { "calendars": {
"server_location": "https://{}/radicale/".format( "server_location": "https://{}/radicale/".format(
self.config.get("general", "hostname")), self.config.get("general", "hostname")),
"rights_file_path": "{}/rights".format( "rights_file_path": "{}/rights".format(

View File

@@ -33,7 +33,7 @@ class Radicale(base.Installer):
"""Prepare a dedicated virtualenv.""" """Prepare a dedicated virtualenv."""
python.setup_virtualenv(self.venv_path, sudo_user=self.user) python.setup_virtualenv(self.venv_path, sudo_user=self.user)
packages = [ packages = [
"Radicale", "pytz" "Radicale", "pytz", "radicale-modoboa-auth-oauth2"
] ]
python.install_packages(packages, self.venv_path, sudo_user=self.user) python.install_packages(packages, self.venv_path, sudo_user=self.user)
python.install_package_from_repository( python.install_package_from_repository(
@@ -43,17 +43,22 @@ class Radicale(base.Installer):
def get_template_context(self): def get_template_context(self):
"""Additional variables.""" """Additional variables."""
context = super(Radicale, self).get_template_context() context = super().get_template_context()
radicale_auth_socket_path = self.config.get( oauth2_client_id, oauth2_client_secret = utils.create_oauth2_app(
"dovecot", "radicale_auth_socket_path") "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({ context.update({
"auth_socket_path": radicale_auth_socket_path "oauth2_introspection_url": oauth2_introspection_url,
}) })
return context return context
def get_config_files(self): def get_config_files(self):
"""Return appropriate path.""" """Return appropriate path."""
config_files = super(Radicale, self).get_config_files() config_files = super().get_config_files()
if package.backend.FORMAT == "deb": if package.backend.FORMAT == "deb":
path = "supervisor=/etc/supervisor/conf.d/radicale.conf" path = "supervisor=/etc/supervisor/conf.d/radicale.conf"
else: else:

View File

@@ -12,6 +12,7 @@ import stat
import string import string
import subprocess import subprocess
import sys import sys
import uuid
try: try:
import configparser import configparser
except ImportError: except ImportError:
@@ -485,3 +486,21 @@ def validate_backup_path(path: str, silent_mode: bool):
mkdir_safe(os.path.join(backup_path, dir), mkdir_safe(os.path.join(backup_path, dir),
stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3]) stat.S_IRWXU | stat.S_IRWXG, pw[2], pw[3])
return backup_path 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