Getting started

No local mail by default

The nullmailer service does not provide support for local mail - all mail is forwarded to the configured SMTP servers for further processing. If you need more advanced SMTP configuration, you should check out the debops.postfix role which can configure the Postfix MTA. This also means that in a new environment, you should prepare at least 1 host as the central mail hub for your network, or use an already existing SMTP server for relaying mail messages.

The debops.nullmailer role is designed to allow automatic switch to a different SMTP server - if it detects a postfix package installed on a host, it will automatically disable configuration of the nullmailer service to not interfere with existing Postfix configuration.

All mail directed to the local recipients will be forwarded to the root UNIX account on the upstream SMTP mail relay - if it's Postfix, then this will be a local UNIX account. There you can deal with the e-mail messages as you see fit - forward them to a virtual root@<domain> account, or to other people, filter them, etc.

Local sender and recipient addresses without specified FQDN domain will have the host's FQDN set in their e-mail address "domain" part. This might be not desirable when you use multiple hosts behind a mail relay and send messages to external recipients. In that case, in the Postfix service on the mail relay you can configure domain masquerading to mask the internal hostnames.

If you use the debops.postfix role to manage the mail relay, you can do that with the following configuration in the Ansible inventory:

postfix__maincf:

  - name: 'masquerade_domains'
    value: [ 'example.org' ]

  - name: 'local_header_rewrite_clients'
    value: [ 'permit_inet_interfaces, 'permit_mynetworks',
             'permit_sasl_authenticated' ]

  - name: 'masquerade_exceptions'
    value: [ 'MAILER-DAEMON', 'postmaster', 'root' ]

This will mask any.thing.example.org in the e-mail addresses of senders and recipients and will convert them to example.org. The exceptions will ensure that the mail from root account is not rewritten and points to the correct host.

Default SMTP relay

The role detects the preferred upstream SMTP relay by using DNS SRV Records for the following service:

_smtp._tcp.{{ nullmailer__domain }} (default port 25)

At the moment, only a single SRV resource record is supported.

If the SRV record is not found, the role will fall back to using static domain names, based on the host domain (nullmailer__domain):

SMTP:  smtp.example.org:25

For details on how to configure DNS SRV records, see DNS SRV Records. Alternatively, you can override the SRV based detection by defining a relay host in the Ansible inventory:

# ansible/inventory/group_vars/all/nullmailer.yml

nullmailer__relayhost: '<FQDN address of mail server>'

See Overriding DNS SRV Queries for more details.

LDAP integration

If the LDAP environment is configured on the host, the debops.nullmailer role will automatically use it to create an LDAP service account in the directory. The relayhost configuration will have SMTP authentication enabled with the nullmailer@<host.example.org> username, which can then be used by the relay server via SASL authentication to find the corresponding nullmailer account in the LDAP directory and authenticate the service.

See the debops.postldap Ansible role documentation for details about configuring Postfix mail relay with LDAP directory support.

Example inventory

The debops.nullmailer role is included by default in the common DebOps playbook and you don't need to add a host to a custom inventory group to activate it.

Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.nullmailer role:

---

- name: Manage nullmailer SMTP server
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_all_hosts', 'debops_service_nullmailer' ]
  become: True

  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'

  pre_tasks:

    - name: Prepare nullmailer environment
      import_role:
        name: 'nullmailer'
        tasks_from: 'main_env'
      tags: [ 'role::nullmailer', 'role::ferm', 'role::tcpwrappers' ]

  roles:

    - role: python
      tags: [ 'role::python', 'skip::python', 'role::ldap' ]
      python__dependent_packages3:
        - '{{ ldap__python__dependent_packages3 }}'
      python__dependent_packages2:
        - '{{ ldap__python__dependent_packages2 }}'

    - role: ldap
      tags: [ 'role::ldap', 'skip::ldap' ]
      ldap__dependent_tasks:
        - '{{ nullmailer__ldap__dependent_tasks }}'

    - role: ferm
      tags: [ 'role::ferm', 'skip::ferm' ]
      ferm__dependent_rules:
        - '{{ nullmailer__ferm__dependent_rules }}'

    - role: tcpwrappers
      tags: [ 'role::tcpwrappers', 'skip::tcpwrappers' ]
      tcpwrappers__dependent_allow:
        - '{{ nullmailer__tcpwrappers__dependent_allow }}'

    - role: nullmailer
      tags: [ 'role::nullmailer', 'skip::nullmailer' ]