diff --git a/plinth/modules/email/__init__.py b/plinth/modules/email/__init__.py index dae3bb869..d56eceffa 100644 --- a/plinth/modules/email/__init__.py +++ b/plinth/modules/email/__init__.py @@ -9,7 +9,7 @@ from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ import plinth.app -from plinth import actions, frontpage, menu +from plinth import actions, cfg, frontpage, menu from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.backups.components import BackupRestore @@ -18,14 +18,22 @@ from plinth.modules.firewall.components import Firewall from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.package import Packages, remove from plinth.signals import domain_added, domain_removed +from plinth.utils import format_lazy -from . import manifest, privileged +from . import aliases, manifest, privileged _description = [ _('This is a complete email server solution using Postfix, Dovecot, ' 'and Rspamd. Postfix sends and receives emails. Dovecot allows ' 'email clients to access your mailbox using IMAP and POP3. Rspamd deals ' 'with spam.'), + format_lazy( + _('Each user on {box_name} gets an email address like ' + 'user@mydomain.example. They will also receive mail from all ' + 'addresses that look like user+foo@mydomain.example. Further, ' + 'they can add aliases to their email address. Necessary aliases ' + 'such as "postmaster" are automatically created pointing to the ' + 'first admin user.'), box_name=_(cfg.box_name)), _('Roundcube app provides web ' 'interface for users to access email.'), _('During installation, any other email servers in the system will be ' @@ -160,6 +168,13 @@ def get_domains(): return [default_domain] if default_domain else [] +def _get_first_admin(): + """Return an admin user in the system or None if non exist.""" + from django.contrib.auth.models import User + users = User.objects.filter(groups__name='admin') + return users[0].username if users else None + + def setup(helper, old_version=None): """Installs and configures module""" @@ -181,6 +196,7 @@ def setup(helper, old_version=None): app.get_component('letsencrypt-email-dovecot').setup_certificates() helper.call('post', privileged.domain.set_domains) helper.call('post', privileged.postfix.setup) + helper.call('post', aliases.setup_common_aliases, _get_first_admin()) helper.call('post', privileged.spam.setup) # Restart daemons diff --git a/plinth/modules/email/aliases.py b/plinth/modules/email/aliases.py index 3804ff290..5d5b3fec8 100644 --- a/plinth/modules/email/aliases.py +++ b/plinth/modules/email/aliases.py @@ -89,3 +89,18 @@ COMMIT; ''' with _get_cursor() as cursor: cursor.executescript(query) + + +def setup_common_aliases(username): + """Create aliases for common mailboxes described in RFC2142. + + See: https://datatracker.ietf.org/doc/html/rfc2142 + """ + aliases = [ + 'mailer-daemon', 'postmaster', 'nobody', 'webmaster', 'www', + 'hostmaster', 'info', 'support', 'abuse', 'noc', 'security' + 'usenet', 'news', 'ftp' + ] + for alias in aliases: + if not exists(alias): + put(username, alias) diff --git a/plinth/modules/email/privileged/postfix.py b/plinth/modules/email/privileged/postfix.py index 0ea743037..9d47c0037 100644 --- a/plinth/modules/email/privileged/postfix.py +++ b/plinth/modules/email/privileged/postfix.py @@ -75,6 +75,9 @@ def _setup_alias_maps(): alias_maps = postconf.get_config(['alias_maps'])['alias_maps'] alias_maps = alias_maps.replace(',', ' ').split(' ') if SQLITE_ALIASES not in alias_maps: - alias_maps.append(SQLITE_ALIASES) + # Prioritize FreedomBox's sqlite based aliases file over /etc/aliases. + # Otherwise, the common aliases will be pointing to 'root' instead of + # first admin user (which is more practical in FreedomBox). + alias_maps = [SQLITE_ALIASES] + alias_maps postconf.set_config({'alias_maps': ' '.join(alias_maps)})