No description
Find a file
2025-02-16 03:12:39 +01:00
.config Make ansible-lint happy (no functional changes, only codestyle) 2022-05-29 22:50:30 +02:00
.forgejo/workflows fix: add missing community.general dependency 2025-02-15 21:55:59 +01:00
meta chore: migrate to forgejo actions, major ansible-lint refactor 2025-02-15 21:29:09 +01:00
roles chore: lint argument_specs 2025-02-16 03:12:39 +01:00
.ansible-lint chore: migrate to forgejo actions, major ansible-lint refactor 2025-02-15 21:29:09 +01:00
.gitignore chore: lint argument_specs 2025-02-16 03:12:39 +01:00
galaxy.yml fix: add missing community.general dependency 2025-02-15 21:55:59 +01:00
README.md docs: add argument_specs and readme to most roles 2025-02-16 03:05:24 +01:00

Ansible Collection - s3lph.mailserver

Roles

  • s3lph.mailserver.postfix: Postfix mail server
  • s3lph.mailserver.postsrsd: PostSRSd Sender Rewriting Scheme daemon for Postfix
  • s3lph.mailserver.postfixadmin: PostfixAdmin PHP mnmagement interface for mailboxes and aliases
  • s3lph.mailserver.spamassassin: SpamAssassin spam filter
  • s3lph.mailserver.mailman: Mailman 3 mailing list server, management and archive interface
  • s3lph.mailserver.dovecot: Dovecot IMAP server
  • s3lph.mailserver.schleuder: Schleuder PGP-encrypted mailing list server
  • s3lph.mailserver.easywks: EasyWKS server
  • s3lph.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:

  1. ansible-playbook -t role::postfixadmin
  2. ansible-playbook -t role::postfixadmin:bootstrap (tagged with never so needs to be invoked explicitly)
  3. 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:

  1. ansible-playbook -t role::mailman
  2. ansible-playbook -t role::mailman:bootstrap (tagged with never so needs to be invoked explicitly)
  3. 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