.config | ||
.forgejo/workflows | ||
meta | ||
roles | ||
.ansible-lint | ||
.gitignore | ||
galaxy.yml | ||
README.md |
Ansible Collection - s3lph.mailserver
Roles
s3lph.mailserver.postfix
: Postfix mail servers3lph.mailserver.postsrsd
: PostSRSd Sender Rewriting Scheme daemon for Postfixs3lph.mailserver.postfixadmin
: PostfixAdmin PHP mnmagement interface for mailboxes and aliasess3lph.mailserver.spamassassin
: SpamAssassin spam filters3lph.mailserver.mailman
: Mailman 3 mailing list server, management and archive interfaces3lph.mailserver.dovecot
: Dovecot IMAP servers3lph.mailserver.schleuder
: Schleuder PGP-encrypted mailing list servers3lph.mailserver.easywks
: EasyWKS servers3lph.mailserver.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
# 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
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
# 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
# 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:
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:
Alias /admin /opt/postfixadmin/postfixadmin/public
If MariaDB is running on the same host, you can bootstrap the database and its users automatically:
ansible-playbook -t role::postfixadmin
ansible-playbook -t role::postfixadmin:bootstrap
(tagged withnever
so needs to be invoked explicitly)ansible-playbook -t role::postfix:config
SpamAssassin
# 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
# 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:
# 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:
ansible-playbook -t role::mailman
ansible-playbook -t role::mailman:bootstrap
(tagged withnever
so needs to be invoked explicitly)ansible-playbook -t role::postfix:config
Dovecot
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
# 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
# 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