# Ansible Collection - s3lph.mailserver ## Roles - `s3lph.mailserver.postfix`: [Postfix][postfix] mail server - `s3lph.mailserver.postsrsd`: [PostSRSd][postsrsd] Sender Rewriting Scheme daemon for Postfix - `s3lph.mailserver.postfixadmin`: [PostfixAdmin][postfixadmin] PHP mnmagement interface for mailboxes and aliases - `s3lph.mailserver.spamassassin`: [SpamAssassin][spamassassin] spam filter - `s3lph.mailserver.mailman`: [Mailman 3][mailman] mailing list server, management and archive interface - `s3lph.mailserver.dovecot`: [Dovecot][dovecot] IMAP server - `s3lph.mailserver.schleuder`: [Schleuder][schleuder] PGP-encrypted mailing list server - `s3lph.mailserver.easywks`: [EasyWKS][easywks] server - `s3lph.mailserver.multischleuder`: [Multischleuder][multischleuder] Schleuder management ## Status of This Collection This collection is designed to work with Debian >= 11, but it should work on any Debian-based distribution. The roles in this collection are currently mainly designed to work in unison with each other. They can be used to set up a production-ready mail server IF setting up at least Postfix, PostfixAdmin and Dovecot. If only some parts are used, there may be some restrictions in place, such as: - Virtual mail domains currently can't be configured - Overriding defaults for master.cf entries currently is rather cumbersome (see below) Documentation is not yet where it should be, but it's getting there. ## Example Setup The following example is a collection of host vars that will get your mail stack up and running. ### Postfix ```yaml # The default "3.6" is too recent for e.g. Debian buster postfix_compatibility_level: 2 # General domain settings postfix_mydomain: example.org postfix_myorigin: "$myhostname" # Set up TLS postfix_smtpd_tls_cert_file: "/etc/ssl/certs/{{ inventory_hostname }}.crt" postfix_smtpd_tls_key_file: "/etc/ssl/private/{{ inventory_hostname }}.key" # The smtpd_*_restrictions in this role are fairly restrictive. # If you need to override this, you can do so e.g. through this: postfix_smtpd_client_restrictions: - reject_unauth_pipelining - permit_mynetworks - reject_non_fqdn_sender - reject_unknown_sender_domain - permit # This goes into /etc/aliases postfix_aliases: root: me abuse: me postmaster: you hostmaster: me you them # Unfortunately I haven't come up with a better way to manage master.cf # entries yet, so this will have to do for now: postfix_master_processes: | # Override TLS certificate for the submission port {%- set x = postfix_default_master_processes.submission.options.append("-o smtpd_tls_cert_file=/etc/ssl/certs/smtp.example.org.crt") -%} {%- set x = postfix_default_master_processes.submission.options.append("-o smtpd_tls_key_file=/etc/ssl/private/smtp.example.org.key") -%} {{ postfix_default_master_processes }} ``` Also includes support for postfix-policyd-spf-python. To enable, set ```yaml postfix_policyd_spf_enable: yes postfix_smtpd_recipient_restrictions: # ... - reject_unauth_destination - check_policy_service unix:private/policyd-spf # ... ``` Run `ansible-playbook -t role::postfix` to deploy. ### PostSRSd ```yaml # Tell Postfix to pass all mails through PostSRSd postfix_srsd_enable: yes # Use a domain that your MX can send and receive mail for postsrsd_domain: srs.example.org ``` Run `ansible-playbook -t role::postfix:config,role::postsrsd` to deploy. Note that Postfix must already be up and running with a valid config, because `PostSRSd` uses the `postconf` command to obtain additional information. ### PostfixAdmin ```yaml # Configure Postfix to get virtual mail config from PostfixAdmin's # MariaDB backend postfix_postfixadmin_enable: yes # Postfix is running chrooted, and bind-mounting the socket into # /var/spool/postfix is messy postfixadmin_database_postfix_hosts: 127.0.0.1 # Configure global aliases postfixadmin_admin_name: You postfixadmin_admin_email: postmaster@example.org # Configure mail delivery transports (only required if other # transports than "virtual" are used) postfixadmin_transport: yes postfixadmin_transport_options: # Need to use LMTP instead of virtual delivery for Sieve to work - lmtp:unix:private/dovecot-lmtp # Base URL is needed to call the bootstrap API postfixadmin_base_url: https://example.org/postfixadmin # If set to yes/true, this option permits login for inactive users, but only if the service is NOT smtp. # This permits disabled users to still read their mail, but will not allow them to send mail. postfixadmin_permit_inactive_user_nosmtp: yes ``` As this role involves some secrets, you should put the following variables into a protected location, e.g. an ansible-vault encrypted file: ```yaml postfixadmin_setup_password: super secure setup password # Admin user used for invoking the bootstrap API endpoint postfixadmin_bootstrap_admin_username: postmaster@example.org postfixadmin_bootstrap_admin_password: another secure password # The postfixadmin user will be able to read/write postfixadmin_database_password: 2097uogd94WNayeo # The postfix and dovecot users will be readonly postfixadmin_database_postfix_password: Oh5r0RJcVzDIqjP4 postfixadmin_database_dovecot_password: c9yFPDDBuL3ft4FD ``` The web server config is not (yet) managed with Ansible. You can e.g. set up Apache with PHP integration and alias a URL path to the PostfixAdmin webroot: ```apache2 Alias /admin /opt/postfixadmin/postfixadmin/public ``` If MariaDB is running on the same host, you can bootstrap the database and its users automatically: 1. `ansible-playbook -t role::postfixadmin` 1. `ansible-playbook -t role::postfixadmin:bootstrap` (tagged with `never` so needs to be invoked explicitly) 1. `ansible-playbook -t role::postfix:config` ### SpamAssassin ```yaml # Tell postfix to pass mails through spamassassin postfix_spamassassin_enable: yes # Configure spamassassin daemon for using only global config spamassassin_spamd_user: debian-spamd spamassassin_nouser_config: yes # Enable daily rule update spamassassin_enable_cron: yes # Configure internal network addresses spamassassin_internal_networks: - "2001:db8:42::10" - "10.42.0.10" spamassassin_trusted_networks: - "2001:db8:23::10" - "10.23.0.10" # Additional SpamAssassin config, e.g. rule definitions or overrides postfix_spamassassin_enable: | # Override score for a rule score FREEMAIL_FORGED_REPLYTO 3.5 # Rule that checks for common german load-related spammy words body __LOANS_GERMAN_1 /darlehen/i body __LOANS_GERMAN_2 /kredite?/i body __LOANS_GERMAN_3 /zinss.{0,3}tz/i meta LOANS_GERMAN ((__LOANS_GERMAN_1 + __LOANS_GERMAN_2 + __LOANS_GERMAN_3) > 1) describe LOANS_GERMAN Loans offered in German score LOANS_GERMAN 2.0 ``` Run `ansible-playbook -t role::postfix:config,role::postsrsd` to deploy. ### Mailman 3 ```yaml # Tell postfix to pass mailing list addresses to mailman postfix_mailman_enable: yes # Postfix is running chrooted, and bind-mounting the socket into # /var/spool/postfix is messy mailman_database_postfix_hosts: 127.0.0.1 # Configure names, URLs and mail addresses mailman_sitename: lists.example.org mailman_site_owner: mailman@example.org mailman_web_base_url: https://lists.example.org/ mailman_web_admin_name: Mailman Admin mailman_web_admin_email: mailman@example.org # Initial admin account to create on bootstrap mailman_superuser_name: root mailman_superuser_email: mailman@example.org ``` As this role involves some secrets, you should put the following variables into a protected location, e.g. an ansible-vault encrypted file: ```yaml # Generate secure passwords for all these values mailman_mariadb_password: mailman_webservice_admin_pass: mailman_web_database_password: mailman_superuser_password: # <-- you'll need this later to log in mailman_hyperkitty_api_key: mailman_web_secret_key: mailman_database_postfix_password: ``` If MariaDB is running on the same host, you can bootstrap the database and its users automatically: 1. `ansible-playbook -t role::mailman` 1. `ansible-playbook -t role::mailman:bootstrap` (tagged with `never` so needs to be invoked explicitly) 1. `ansible-playbook -t role::postfix:config` ### Dovecot ```yaml dovecot_hostname: imap.example.org dovecot_tls_cert_filename: /etc/ssl/certs/imap.example.org.crt dovecot_tls_key_filename: /etc/ssl/private/imap.example.org.key # Enable Pigeonhole Sieve/ManageSieve dovecot_enable_pigeonhole: yes dovecot_enable_pigeonhole_managesieve: yes # Adapt to your virtual mail home dovecot_pigeonhole_sieve: 'file:/home/virtual/%d/%n/sieve;active=/home/virtual/%d/%n/.dovecot.sieve' ``` Run `ansible-playbook -t role::dovecot` to deploy ### Schleuder TODO: Documentation/Example ### EasyWKS ```yaml # Register a "gpgwks" postfix transport in master.cf, which pipes # mail into easywks process. LMTP should be preferred though! #postfix_easywks_pipe_transport: yes # When the easywks package is available in your system's package # sources, set this to no. Otherwise the package is downloaded from # the project upstream releases at Gitlab. #easywks_download: no # Simple /etc/easywks.yml encoded as a multiline string easywks_config: | mailing_method: smtp smtp: host: localhost port: 8025 lmtpd: host: "::1" port: 8024 domains: example.org: submission_address: webkey@example.org example.net: submission_address: wks@example.net ``` Run `ansible-playbook -t role::postfix:config,role::easywks` to deploy ### Multischleuder ```yaml # When the multischleuder package is available in your system's package # sources, set this to no. Otherwise the package is downloaded from # the project upstream releases at Gitlab. #multischleuder_download: no # Simple /etc/multischleuder/multischleuder.yml encoded as a multiline string mutlischleuder_config: | api: url: "https://localhost:4443" token: "thetoken" cafile: /etc/multischleuder/schleuder-ca.pem # See https://gitlab.com/s3lph/multischleuder for the full config... ``` Run `ansible-playbook -t role::multischleuder` to deploy [postfix]: http://www.postfix.org/ [postsrsd]: https://github.com/roehling/postsrsd [postfixadmin]: https://github.com/postfixadmin/postfixadmin [spamassassin]: https://spamassassin.apache.org/ [mailman]: http://list.org/ [dovecot]: https://dovecot.org/ [schleuder]: https://schleuder.org/ [easywks]: https://gitlab.com/s3lph/easywks [multischleuder]: https://gitlab.com/s3lph/multischleuder