Sender verification and virtual alias map from inventory

This commit is contained in:
Julian Rother 2023-03-16 03:42:53 +01:00
parent 8e25214790
commit 53aeb56e54
No known key found for this signature in database
GPG key ID: 8F9B6AE9BAAE4899
6 changed files with 145 additions and 2 deletions

View file

@ -15,7 +15,7 @@ postfix:
transport_maps: []
sender_dependent_relayhost_maps: ''
alias_maps: ''
virtual_alias_maps: ''
virtual_alias_maps: hash:/etc/postfix/virtual
virtual_regex: []
virtual_mailbox_domains: []
mynetworks: []
@ -41,6 +41,12 @@ postfix:
mailboxes: mailMessageStore
check_dovecot_quota: false
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: []
smtpd_milters: []
non_smtpd_milters: []

64
filter_plugins/filters.py Normal file
View 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

View file

@ -95,3 +95,49 @@
with_items: "{{ postfix.postfixmaps }}"
notify:
- 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'

View file

@ -69,7 +69,12 @@ smtpd_helo_restrictions = permit_mynetworks,
smtpd_sender_restrictions = reject_non_fqdn_sender,
reject_unknown_sender_domain,
# reject_sender_login_mismatch, # Disabled because we dont 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_sasl_authenticated
@ -91,12 +96,20 @@ mua_helo_restrictions = permit_mynetworks,
mua_sender_restrictions = reject_non_fqdn_sender,
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_sasl_authenticated
mua_client_restrictions = permit_sasl_authenticated,
reject
smtpd_sender_login_maps = {{ postfix.smtpd_sender_login_maps }}
{% if ("mailbox_transport" in postfix and postfix.mailbox_transport == "dovecot")
or postfix.ldap.enable
%}

View 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
View 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 %}