feat(role): Initial commit
This commit is contained in:
parent
35a7aa165a
commit
78e80101b3
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.idea
|
48
README.md
48
README.md
@ -1,3 +1,47 @@
|
||||
# role-common-send_only_mta
|
||||
[//]: # (SPDX-License-Identifier: MIT)
|
||||
# Role Name
|
||||
|
||||
Configure Postfix on Debian as a send-only Mail Transfer Agent
|
||||
role-common-send_only_mta
|
||||
|
||||
# Description
|
||||
|
||||
Configure Postfix on Debian as a send-only Mail Transfer Agent.
|
||||
|
||||
# Requirements
|
||||
|
||||
Your target machines must be Debian.
|
||||
|
||||
# Role Variables
|
||||
|
||||
Per [defaults/main.yml](defaults/main.yml) this role expects both a recipient e-mail address and login credentials for an SMTP mail submission server to be available in a HashiCorp Vault instance. Feel free to override these variables as needed with host or group vars. If you stick with HashiCorp Vault the default path for SMTP credentials is `settings/comms/e-mail/default/sender`, the default path for a recipient address is `settings/comms/e-mail/default/recipient`.
|
||||
|
||||
## Sender data
|
||||
|
||||
- `addr-spec`: Your sender e-mail address rendered as an [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.4.1) `addr-spec` string such as `noreply@example.com`.
|
||||
- `credentials-password-sasl-smtp-auth-login`: A password that Postfix will use to log in to the SMTP mail submission server via the `AUTH LOGIN` SMTP SASL authentication mechanism.
|
||||
- `credentials-username`: The SMTP username needed to log in to the SMTP mail submission server.
|
||||
- `submission-server-fqdn`: Your upstream SMTP mail submission server's fully qualified domain name such as `smtp.example.com`.
|
||||
- `submission-server-port`: The TCP port you want Postfix to use to connect to the SMTP mail submission server.
|
||||
|
||||
## Recipient data
|
||||
|
||||
- `addr-spec`: Same as above, a recipient e-mail address rendered as an [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.4.1) `addr-spec` string such as `noreply@example.com`.
|
||||
|
||||
# Dependencies
|
||||
|
||||
None.
|
||||
|
||||
# Example Playbook
|
||||
|
||||
In your `playbook.yml` call it like so:
|
||||
|
||||
```
|
||||
- name: 'Awesome playbook'
|
||||
hosts: all
|
||||
roles:
|
||||
- 'role-common-send_only_mta'
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
|
7
defaults/main.yml
Normal file
7
defaults/main.yml
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
somta__e_mail_default_recipient_addr_spec: '{{ lookup(''hashi_vault'', ''secret=secret/data/settings/comms/e-mail/default/recipient:addr-spec'') }}'
|
||||
somta__e_mail_default_sender_addr_spec: '{{ lookup(''hashi_vault'', ''secret=secret/data/settings/comms/e-mail/default/sender:addr-spec'') }}'
|
||||
somta__e_mail_default_sender_credentials_password_smtp_auth_login: '{{ lookup(''hashi_vault'', ''secret=secret/data/settings/comms/e-mail/default/sender:credentials-password-sasl-smtp-auth-login'') }}'
|
||||
somta__e_mail_default_sender_credentials_username: '{{ lookup(''hashi_vault'', ''secret=secret/data/settings/comms/e-mail/default/sender:credentials-username'') }}'
|
||||
somta__e_mail_default_sender_submission_server_fqdn: '{{ lookup(''hashi_vault'', ''secret=secret/data/settings/comms/e-mail/default/sender:submission-server-fqdn'') }}'
|
||||
somta__e_mail_default_sender_submission_server_port: '{{ lookup(''hashi_vault'', ''secret=secret/data/settings/comms/e-mail/default/sender:submission-server-port'') }}'
|
24
handlers/main.yml
Normal file
24
handlers/main.yml
Normal file
@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
- name: 'Rebuild Postfix lookup tables'
|
||||
loop_control:
|
||||
loop_var: 'somta_postfix_postmap_handler'
|
||||
label: 'Rebuild ''/etc/postfix/{{ somta_postfix_postmap_handler.lookup_table_source_file }}'' lookup table'
|
||||
loop:
|
||||
- { lookup_table_source_file: 'sasl_passwd.j2' }
|
||||
- { lookup_table_source_file: 'sender_canonical_maps.j2' }
|
||||
- { lookup_table_source_file: 'smtp_header_checks' }
|
||||
ansible.builtin.shell: |
|
||||
postmap '/etc/postfix/{{ somta_postfix_postmap_handler.lookup_table_source_file }}'
|
||||
listen: 'Ensure that a Mail Transfer Agent is running with newest config'
|
||||
|
||||
- name: 'Rebuild e-mail aliases lookup tables'
|
||||
ansible.builtin.shell: |
|
||||
newaliases
|
||||
listen: 'Ensure that a Mail Transfer Agent is running with newest config'
|
||||
|
||||
- name: 'Restart postfix.service'
|
||||
ansible.builtin.service:
|
||||
name: 'postfix.service'
|
||||
state: 'restarted'
|
||||
enabled: true
|
||||
listen: 'Ensure that a Mail Transfer Agent is running with newest config'
|
2
tasks/main.yml
Normal file
2
tasks/main.yml
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
- import_tasks: postfix-setup.yml
|
78
tasks/postfix-setup.yml
Normal file
78
tasks/postfix-setup.yml
Normal file
@ -0,0 +1,78 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
- name: 'If OS is a Linux flavor install Postfix'
|
||||
when: ansible_facts['system'] | lower == 'linux'
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- 'postfix'
|
||||
- 'postfix-pcre'
|
||||
state: 'present'
|
||||
|
||||
- name: 'Ensure Postfix lookup table files exist with correct perms'
|
||||
loop_control:
|
||||
loop_var: 'somta_postfix_postconf_lookup_table'
|
||||
label: 'Copy lookup table file ''/etc/postfix/{{ somta_postfix_postconf_lookup_table.file }}'' and set perms'
|
||||
loop:
|
||||
- { mode: '0600', file: 'sasl_passwd' }
|
||||
- { mode: '0644', file: 'sender_canonical_maps' }
|
||||
- { mode: '0644', file: 'smtp_header_checks' }
|
||||
ansible.builtin.template:
|
||||
src: 'etc/postfix/{{ somta_postfix_postconf_lookup_table.file }}.j2'
|
||||
dest: '/etc/postfix/{{ somta_postfix_postconf_lookup_table.file }}'
|
||||
mode: '{{ somta_postfix_postconf_lookup_table.mode }}'
|
||||
notify:
|
||||
- 'Ensure that a Mail Transfer Agent is running with newest config'
|
||||
|
||||
- name: 'Add e-mail alias for user ''root'''
|
||||
ansible.builtin.lineinfile:
|
||||
path: '/etc/aliases'
|
||||
insertafter: 'EOF'
|
||||
regexp: '^root:.*'
|
||||
line: 'root: {{ somta__e_mail_default_recipient_addr_spec }}'
|
||||
notify:
|
||||
- 'Ensure that a Mail Transfer Agent is running with newest config'
|
||||
|
||||
# Add our own config block to the end of Postfix' main.cf file. In
|
||||
# 'ansible.builtin.blockinfile' we use the default 'marker' param '#
|
||||
# {mark} ANSIBLE MANAGED BLOCK'. We 'insertafter: EOF' so we know for a
|
||||
# fact that our config block is the bottommost thing in main.cf. The
|
||||
# next task 'ansible.builtin.replace' uses the marker string as an
|
||||
# anchor to comment out any duplicate parameters /before/ the marker.
|
||||
- name: 'Configure Postfix main.cf to SMTP-deliver e-mails to an upstream mail gateway'
|
||||
ansible.builtin.blockinfile:
|
||||
block: "{{ lookup('ansible.builtin.template', 'etc/postfix/main.cf.blockinfile.j2') }}"
|
||||
path: '/etc/postfix/main.cf'
|
||||
create: true
|
||||
insertafter: 'EOF'
|
||||
prepend_newline: true
|
||||
notify:
|
||||
- 'Ensure that a Mail Transfer Agent is running with newest config'
|
||||
|
||||
- name: 'In Postfix main.cf comment out params managed by this playbook; Postfix doesn''t like dupes'
|
||||
loop_control:
|
||||
label: 'Comment out unmanaged occurrences of param ''{{ item | regex_replace(''^(#\s?)?(?P<param>[^=\s]+)([^\r\n\f]*)'', ''\g<param>'') }}'''
|
||||
# Look up file content from our main.cf config template file. Split the
|
||||
# result by line delimiters into a list that contains each line as a
|
||||
# list item via Python string splitlines() method. Now that we have a
|
||||
# list apply the Jinja2 'select' filter to it. For each list item filter
|
||||
# it by using the Jinja2 built-in test 'search' against it to search for
|
||||
# an occurrence of the equals sign '=' in that list item. When a config
|
||||
# line (i.e. a list item) does not contain an equals sign we reject it
|
||||
# thus pruning it from the list. We lastly generate a new list from our
|
||||
# result, one that only contains lines where an equals sign appears.
|
||||
loop: '{{ lookup(''ansible.builtin.template'', ''etc/postfix/main.cf.blockinfile.j2'').splitlines() | select(''search'', ''='') | list }}'
|
||||
ansible.builtin.replace:
|
||||
path: '/etc/postfix/main.cf'
|
||||
before: '.*?# BEGIN ANSIBLE MANAGED BLOCK'
|
||||
# regex_replace each {{ item }}. Instead of one complete line from
|
||||
# the main.cf template file we only want the name of each parameter;
|
||||
# that's whatever appears in front of the first equals sign ('=') in
|
||||
# that line minus any comment markers ('#') we may have put in our
|
||||
# our main.cf template. Store the param name in a named capture
|
||||
# group (?P<param>...) - with a capital letter P because this
|
||||
# behavior is a Python-specific regex extension
|
||||
# (https://stackoverflow.com/a/10060065) - and lastly reuse
|
||||
# '\g<param>' as our 'regexp:' string.
|
||||
regexp: '^(#\s?)?({{ item | regex_replace(''^(#\s?)?(?P<param>[^=\s]+)([^\r\n\f]*)'', ''\g<param>'') }})'
|
||||
replace: '# \2'
|
||||
notify:
|
||||
- 'Ensure that a Mail Transfer Agent is running with newest config'
|
29
templates/etc/postfix/main.cf.blockinfile.j2
Normal file
29
templates/etc/postfix/main.cf.blockinfile.j2
Normal file
@ -0,0 +1,29 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Per 'man 5 postconfig': When the same parameter is defined multiple
|
||||
# times, only the last instance is remembered.
|
||||
#
|
||||
# While that's true we still do not want to duplicate params that
|
||||
# originally exist in main.cf. If we did and inevitably set our params
|
||||
# to different values than the default main.cf params Postfix tools such
|
||||
# as mailq/sendmail, postqueue etc. would warn us that we're duplicating
|
||||
# and reconfiguring params multiple times. Tools would begin reporting
|
||||
# 'warning: main.cf, line <n>: overriding earlier entry'. This is a
|
||||
# useful feature for when some config /actually/ goes wrong so we don't
|
||||
# want to contaminate troubleshooting by forcing this behavior with our
|
||||
# Ansible-managed config.
|
||||
#
|
||||
# For each of the params below we've gone ahead and commented out the
|
||||
# same param where it appeared before this Ansible-managed config block.
|
||||
# That way we're avoiding param duplicates.
|
||||
myhostname = {{ ansible_fqdn }}
|
||||
mydestination = $myhostname, localhost.$mydomain, localhost
|
||||
inet_interfaces = loopback-only
|
||||
relayhost = [{{ somta__e_mail_default_sender_submission_server_fqdn }}]:{{ somta__e_mail_default_sender_submission_server_port }}
|
||||
sender_canonical_classes = envelope_sender, header_sender
|
||||
sender_canonical_maps = pcre:/etc/postfix/sender_canonical_maps
|
||||
smtp_header_checks = pcre:/etc/postfix/smtp_header_checks
|
||||
smtp_sasl_auth_enable = yes
|
||||
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
|
||||
smtp_sasl_tls_security_options = noanonymous
|
||||
smtp_tls_security_level = encrypt
|
||||
# debug_peer_list = example.com, mail.example.net, 1.2.3.4
|
2
templates/etc/postfix/sasl_passwd.j2
Normal file
2
templates/etc/postfix/sasl_passwd.j2
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
[{{ somta__e_mail_default_sender_submission_server_fqdn }}]:{{ somta__e_mail_default_sender_submission_server_port }} {{ somta__e_mail_default_sender_credentials_username }}:{{ somta__e_mail_default_sender_credentials_password_smtp_auth_login }}
|
2
templates/etc/postfix/sender_canonical_maps.j2
Normal file
2
templates/etc/postfix/sender_canonical_maps.j2
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
/.+/ {{ somta__e_mail_default_sender_addr_spec }}
|
9
templates/etc/postfix/smtp_header_checks.j2
Normal file
9
templates/etc/postfix/smtp_header_checks.j2
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# In our 'From:' header reuse any mail display name then append our
|
||||
# official sender e-mail address.
|
||||
/^From:[[:space:]]*(.+?)([[:space:]]*<)/ REPLACE From: "${1}" <{{ somta__e_mail_default_sender_addr_spec }}>
|
||||
|
||||
# Hide the sender's IP and user agent in the Received header
|
||||
# https://wiki.archlinux.org/title/Postfix
|
||||
/^Received:.*/ IGNORE
|
||||
/^User-Agent:.*/ IGNORE
|
Loading…
x
Reference in New Issue
Block a user