Sender verification and virtual alias map from inventory
This commit is contained in:
parent
8e25214790
commit
53aeb56e54
6 changed files with 145 additions and 2 deletions
|
|
@ -15,7 +15,7 @@ postfix:
|
||||||
transport_maps: []
|
transport_maps: []
|
||||||
sender_dependent_relayhost_maps: ''
|
sender_dependent_relayhost_maps: ''
|
||||||
alias_maps: ''
|
alias_maps: ''
|
||||||
virtual_alias_maps: ''
|
virtual_alias_maps: hash:/etc/postfix/virtual
|
||||||
virtual_regex: []
|
virtual_regex: []
|
||||||
virtual_mailbox_domains: []
|
virtual_mailbox_domains: []
|
||||||
mynetworks: []
|
mynetworks: []
|
||||||
|
|
@ -41,6 +41,12 @@ postfix:
|
||||||
mailboxes: mailMessageStore
|
mailboxes: mailMessageStore
|
||||||
check_dovecot_quota: false
|
check_dovecot_quota: false
|
||||||
postfixmaps: []
|
postfixmaps: []
|
||||||
|
aliases: []
|
||||||
|
sender_verification:
|
||||||
|
enable: false # enable/disable the restriction, 'test' to warn instead of reject
|
||||||
|
match_sender_domain: ''
|
||||||
|
allow_sender_domains: []
|
||||||
|
smtpd_sender_login_maps: hash:/etc/postfix/sender_logins
|
||||||
add_header_checks: []
|
add_header_checks: []
|
||||||
smtpd_milters: []
|
smtpd_milters: []
|
||||||
non_smtpd_milters: []
|
non_smtpd_milters: []
|
||||||
|
|
|
||||||
64
filter_plugins/filters.py
Normal file
64
filter_plugins/filters.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
def validate_aliases(aliases):
|
||||||
|
for alias in aliases:
|
||||||
|
unknown_attributes = alias.keys() - {'addresses', 'senders', 'receivers', 'senders_and_receivers'}
|
||||||
|
if unknown_attributes:
|
||||||
|
raise ValueError('Unknown alias attributes: ' + ', '.join(unknown_attributes))
|
||||||
|
for address in alias['addresses']:
|
||||||
|
if not address.islower():
|
||||||
|
raise ValueError(f'Aliases are not case-sensitive. Make "{address}" all-lowercase!')
|
||||||
|
if address.strip() != address:
|
||||||
|
raise ValueError(f'Remove surrounding whitespace in "{address}"!')
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'postfix_resolve_senders': self.postfix_resolve_senders,
|
||||||
|
'postfix_resolve_receivers': self.postfix_resolve_receivers,
|
||||||
|
}
|
||||||
|
|
||||||
|
def postfix_resolve_senders(self, aliases, match_sender_domain, allow_sender_domains=tuple()):
|
||||||
|
validate_aliases(aliases)
|
||||||
|
sender_suffix = '@' + match_sender_domain
|
||||||
|
address_sender_address_map = {}
|
||||||
|
for alias in aliases:
|
||||||
|
for address in alias['addresses']:
|
||||||
|
address_sender_address_map.setdefault(address, set())
|
||||||
|
address_sender_address_map[address] |= set(alias.get('senders', []))
|
||||||
|
address_sender_address_map[address] |= set(alias.get('senders_and_receivers', []))
|
||||||
|
for _ in range(100):
|
||||||
|
done = True
|
||||||
|
for address, senders in address_sender_address_map.items():
|
||||||
|
for sender in set(senders):
|
||||||
|
if sender.endswith(sender_suffix):
|
||||||
|
continue
|
||||||
|
done = False
|
||||||
|
if sender not in address_sender_address_map:
|
||||||
|
raise Exception(f'Address does not resolve to sender address: {sender}')
|
||||||
|
senders.remove(sender)
|
||||||
|
senders |= address_sender_address_map[sender]
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
if not done:
|
||||||
|
raise Exception('Recursion limit reached')
|
||||||
|
|
||||||
|
address_sender_name_map = {}
|
||||||
|
for address, senders in address_sender_address_map.items():
|
||||||
|
address_sender_name_map[address] = set()
|
||||||
|
for sender in senders:
|
||||||
|
username = sender[:-len(sender_suffix)]
|
||||||
|
address_sender_name_map[address].add(username)
|
||||||
|
for domain in allow_sender_domains:
|
||||||
|
address_sender_name_map[address].add(f'{username}@{domain}')
|
||||||
|
return address_sender_name_map
|
||||||
|
|
||||||
|
def postfix_resolve_receivers(self, aliases):
|
||||||
|
validate_aliases(aliases)
|
||||||
|
address_receiver_address_map = {}
|
||||||
|
for alias in aliases:
|
||||||
|
for address in alias['addresses']:
|
||||||
|
address_receiver_address_map.setdefault(address, set())
|
||||||
|
address_receiver_address_map[address] |= set(alias.get('receivers', []))
|
||||||
|
address_receiver_address_map[address] |= set(alias.get('senders_and_receivers', []))
|
||||||
|
return address_receiver_address_map
|
||||||
|
|
@ -95,3 +95,49 @@
|
||||||
with_items: "{{ postfix.postfixmaps }}"
|
with_items: "{{ postfix.postfixmaps }}"
|
||||||
notify:
|
notify:
|
||||||
- restart postfix
|
- restart postfix
|
||||||
|
|
||||||
|
- name: create virtual table
|
||||||
|
template:
|
||||||
|
src: virtual.in.j2
|
||||||
|
dest: /etc/postfix/virtual.in
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
register: create_virtual_table
|
||||||
|
|
||||||
|
- name: check virtual table db
|
||||||
|
loop:
|
||||||
|
- /etc/postfix/virtual.in
|
||||||
|
- /etc/postfix/virtual.db
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: '{{ item }}'
|
||||||
|
register: virtual_table_stats
|
||||||
|
|
||||||
|
- name: update virtual table db
|
||||||
|
when: create_virtual_table.changed
|
||||||
|
or not virtual_table_stats.results[1].stat.exists
|
||||||
|
or virtual_table_stats.results[0].stat.mtime > virtual_table_stats.results[1].stat.mtime
|
||||||
|
ansible.builtin.shell: 'postmap hash:/etc/postfix/virtual.in && mv /etc/postfix/virtual.in.db /etc/postfix/virtual.db'
|
||||||
|
|
||||||
|
- name: create sender logins table
|
||||||
|
template:
|
||||||
|
src: sender_logins.in.j2
|
||||||
|
dest: /etc/postfix/sender_logins.in
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
register: create_sender_logins_table
|
||||||
|
|
||||||
|
- name: check sender logins table db
|
||||||
|
loop:
|
||||||
|
- /etc/postfix/sender_logins.in
|
||||||
|
- /etc/postfix/sender_logins.db
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: '{{ item }}'
|
||||||
|
register: sender_logins_table_stats
|
||||||
|
|
||||||
|
- name: update sender logins table db
|
||||||
|
when: create_sender_logins_table.changed
|
||||||
|
or not sender_logins_table_stats.results[1].stat.exists
|
||||||
|
or sender_logins_table_stats.results[0].stat.mtime > sender_logins_table_stats.results[1].stat.mtime
|
||||||
|
ansible.builtin.shell: 'postmap hash:/etc/postfix/sender_logins.in && mv /etc/postfix/sender_logins.in.db /etc/postfix/sender_logins.db'
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,12 @@ smtpd_helo_restrictions = permit_mynetworks,
|
||||||
|
|
||||||
smtpd_sender_restrictions = reject_non_fqdn_sender,
|
smtpd_sender_restrictions = reject_non_fqdn_sender,
|
||||||
reject_unknown_sender_domain,
|
reject_unknown_sender_domain,
|
||||||
# reject_sender_login_mismatch, # Disabled because we don’t map correctly
|
{% if postfix.sender_verification.enable == 'test' -%}
|
||||||
|
warn_if_reject,
|
||||||
|
reject_authenticated_sender_login_mismatch,
|
||||||
|
{% elif postfix.sender_verification.enable -%}
|
||||||
|
reject_authenticated_sender_login_mismatch,
|
||||||
|
{% endif -%}
|
||||||
permit_mynetworks,
|
permit_mynetworks,
|
||||||
permit_sasl_authenticated
|
permit_sasl_authenticated
|
||||||
|
|
||||||
|
|
@ -91,12 +96,20 @@ mua_helo_restrictions = permit_mynetworks,
|
||||||
|
|
||||||
mua_sender_restrictions = reject_non_fqdn_sender,
|
mua_sender_restrictions = reject_non_fqdn_sender,
|
||||||
reject_unknown_sender_domain,
|
reject_unknown_sender_domain,
|
||||||
|
{% if postfix.sender_verification.enable == 'test' -%}
|
||||||
|
warn_if_reject,
|
||||||
|
reject_authenticated_sender_login_mismatch,
|
||||||
|
{% elif postfix.sender_verification.enable -%}
|
||||||
|
reject_authenticated_sender_login_mismatch,
|
||||||
|
{% endif -%}
|
||||||
permit_mynetworks,
|
permit_mynetworks,
|
||||||
permit_sasl_authenticated
|
permit_sasl_authenticated
|
||||||
|
|
||||||
mua_client_restrictions = permit_sasl_authenticated,
|
mua_client_restrictions = permit_sasl_authenticated,
|
||||||
reject
|
reject
|
||||||
|
|
||||||
|
smtpd_sender_login_maps = {{ postfix.smtpd_sender_login_maps }}
|
||||||
|
|
||||||
{% if ("mailbox_transport" in postfix and postfix.mailbox_transport == "dovecot")
|
{% if ("mailbox_transport" in postfix and postfix.mailbox_transport == "dovecot")
|
||||||
or postfix.ldap.enable
|
or postfix.ldap.enable
|
||||||
%}
|
%}
|
||||||
|
|
|
||||||
7
templates/sender_logins.in.j2
Normal file
7
templates/sender_logins.in.j2
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
{% for alias, senders in postfix.aliases|postfix_resolve_senders(postfix.sender_verification.match_sender_domain, postfix.sender_verification.allow_sender_domains)|dictsort if senders %}
|
||||||
|
{{ alias }}
|
||||||
|
{% for sender in senders|sort %}
|
||||||
|
{{ sender }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
7
templates/virtual.in.j2
Normal file
7
templates/virtual.in.j2
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
{% for alias, receivers in postfix.aliases|postfix_resolve_receivers|dictsort if receivers %}
|
||||||
|
{{ alias }}
|
||||||
|
{% for receiver in receivers|sort %}
|
||||||
|
{{ receiver }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue