diff --git a/dovecot/templates/conf.d/10-auth.conf.j2 b/dovecot/templates/conf.d/10-auth.conf.j2
index 86b5cf4a17fc01662ec1d8663895e2fb544c8fe8..1da1f350cb0745b9c6e1583fc38556dc7afe5089 100644
--- a/dovecot/templates/conf.d/10-auth.conf.j2
+++ b/dovecot/templates/conf.d/10-auth.conf.j2
@@ -48,7 +48,11 @@ disable_plaintext_auth = yes
 # the standard variables here, eg. %Lu would lowercase the username, %n would
 # drop away the domain if it was given, or "%n-AT-%d" would change the '@' into
 # "-AT-". This translation is done after auth_username_translation changes.
+{% if dovecot_auth_system and not dovecot_auth_virtual %}
+auth_username_format = %Ln
+{% else %}
 #auth_username_format = %Lu
+{% endif %}
 
 # If you want to allow master users to log in by specifying the master
 # username within the normal username string (ie. not using SASL mechanism's
diff --git a/postfix/defaults/main.yml b/postfix/defaults/main.yml
index 1b8e12d5b15303db5f7f33f104ebc547cbbc61dc..3c1adc3d4abd2be75b60b8d69a61a57f29f645af 100644
--- a/postfix/defaults/main.yml
+++ b/postfix/defaults/main.yml
@@ -12,10 +12,14 @@ postfix_tls_key: /etc/ssl/private/privkey.pem
 postfix_tls_configuration: 'previous'
 
 postfix_prefer_lmtp: false
+postfix_enable_dovecot: true
+postfix_enable_submission: true
+postfix_enable_smtps: false
 
 postfix_enable_postscreen: true
 postfix_enable_memcached: false
 postfix_login_suffix: ''
+postfix_luser_relay: ''
 postfix_dnsbl_sites:
   - name: zen.spamhaus.org
   - name: bl.spamcop.net
@@ -43,6 +47,9 @@ postfix_transport_maps: []
 #    protocol: smtp
 #    use_mx: true
 
+postfix_verify_spf: false
+postfix_verify_spf_testmode: true
+postfix_enable_srs: false
 # Note: This requires at least buster-backports or newer.
 postfix_enable_mta_sts: false
 
diff --git a/postfix/tasks/main.yml b/postfix/tasks/main.yml
index fb14d196e91a5effd8c98a40ebe7b12b84b18a3f..0522055b66de37758e2fb3b87413e01cbca74822 100644
--- a/postfix/tasks/main.yml
+++ b/postfix/tasks/main.yml
@@ -79,6 +79,8 @@
     - mail
 
 - import_tasks: mta-sts.yml
+- import_tasks: spf.yml
+- import_tasks: srs.yml
 
 - name: install rt-mailgate if needed
   apt:
@@ -125,7 +127,7 @@
     dest: /etc/postfix/transport
   notify:
     - postmap transport
-  when: postfix_transport_maps|bool
+  when: postfix_transport_maps|count > 0
   tags:
     - postfix
     - mail
diff --git a/postfix/tasks/spf.yml b/postfix/tasks/spf.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d3a4bce23f9f9a9b37af83755cdbd299c59b8423
--- /dev/null
+++ b/postfix/tasks/spf.yml
@@ -0,0 +1,19 @@
+---
+
+- name: ensure we got the SPF policy daemon installed
+  apt:
+    state: "{{ 'present' if postfix_verify_spf else 'absent' }}"
+    name: postfix-policyd-spf-python
+  notify:
+    - restart postfix
+
+- name: ensure the SPF policy daemon is configured
+  template:
+    src: policyd-spf.conf.j2
+    dest: /etc/postfix-policyd-spf-python/policyd-spf.conf
+    owner: root
+    group: root
+    mode: '0644'
+  when: postfix_verify_spf
+  notify:
+    - restart postfix
diff --git a/postfix/tasks/srs.yml b/postfix/tasks/srs.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5d25fc418e9bb998f01418d4d0efd2748f01f374
--- /dev/null
+++ b/postfix/tasks/srs.yml
@@ -0,0 +1,33 @@
+---
+
+- name: ensure we got the SRS daemon installed
+  apt:
+    state: "{{ 'present' if postfix_enable_srs else 'absent' }}"
+    name: postsrsd
+
+- name: ensure there is an (Debian-generated) secrets file
+  file:
+    path: /etc/postsrsd.secret
+    state: file
+    owner: root
+    group: root
+    mode: '0600'
+  when: postfix_enable_srs
+
+- name: ensure the SRS daemon is configured
+  template:
+    src: postsrsd.j2
+    dest: /etc/default/postsrsd
+    owner: root
+    group: root
+    mode: '0644'
+  when: postfix_enable_srs
+  notify:
+    - restart postsrsd
+
+- name: ensure the SRS daemon is up and running
+  service:
+    name: postsrsd
+    enabled: "{{ postfix_enable_srs|string }}"
+    state: "{{ 'started' if postfix_enable_srs else 'stopped' }}"
+  when: postfix_enable_srs
diff --git a/postfix/templates/main.cf.j2 b/postfix/templates/main.cf.j2
index 784102c9d1c32818ba0de246546d2276ab01ed75..e27e9b533ee8669edde8cc9466752648f6a222a2 100644
--- a/postfix/templates/main.cf.j2
+++ b/postfix/templates/main.cf.j2
@@ -7,12 +7,15 @@ myorigin = /etc/mailname
 mydestination = $myhostname localhost {{ postfix_domains | join(" ") }}
 mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ postfix_my_networks|join(" ") }}
 relayhost = {{ postfix_relay_host }}
-{% if postfix_transport_maps|bool %}
+{% if postfix_transport_maps|count > 0 %}
 transport_maps = cdb:/etc/postfix/transport
 {% endif %}
+{% if postfix_luser_relay != "" %}
+luser_relay = {{ postfix_luser_relay }}
+local_recipient_maps =
+{% endif %}
 
-{% if not postfix_satellite_only %}
-
+{% if not postfix_satellite_only and postfix_enable_dovecot %}
 {% if postfix_domains|count > 0 %}
 {% if postfix_prefer_lmtp %}
 mailbox_transport = lmtp:unix:private/dovecot-lmtp
@@ -25,7 +28,6 @@ smtpd_sender_login_maps = proxy:pcre:/etc/postfix/login_maps.pcre
 smtpd_sasl_type = dovecot
 smtpd_sasl_path = private/auth
 smtpd_sasl_auth_enable = yes
-
 {% endif %}
 
 append_dot_mydomain = no
@@ -45,8 +47,20 @@ smtpd_relay_restrictions =
 	permit_mynetworks
 	permit_sasl_authenticated
 	defer_unauth_destination
+{% if postfix_verify_spf %}
+smtpd_recipient_restrictions=
+     permit_mynetworks
+     permit_sasl_authenticated
+     reject_unauth_destination
+     check_policy_service unix:private/policy-spf
+{% endif %}
+{% if postfix_enable_srs %}
+sender_canonical_maps = tcp:127.0.0.1:10001
+sender_canonical_classes = envelope_sender
+recipient_canonical_maps = tcp:127.0.0.1:10002
+recipient_canonical_classes= envelope_recipient,header_recipient
+{% endif %}
 
-smtpd_use_tls = yes
 smtp_tls_security_level = may
 smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
 smtpd_tls_security_level = may
@@ -86,7 +100,7 @@ alias_maps = cdb:/etc/aliases
 alias_database = cdb:/etc/aliases
 virtual_alias_maps = cdb:/etc/postfix/virtual
 
-{% if postfix_virtual_domains|count > 0 %}
+{% if postfix_virtual_domains|count > 0 and postfix_enable_dovecot %}
 virtual_mailbox_domains = {{ postfix_virtual_domains | join(", ") }}
 virtual_mailbox_base = /var/vmail/
 virtual_mailbox_limit = 512000000
diff --git a/postfix/templates/master.cf.j2 b/postfix/templates/master.cf.j2
index 2d4a4ffaf6422475a0381eabe0ac06f5a7c246c5..38e4c54c51d8b33556a180283f80db63edcb955a 100644
--- a/postfix/templates/master.cf.j2
+++ b/postfix/templates/master.cf.j2
@@ -21,6 +21,7 @@ smtpd     pass  -       -       y       -       -       smtpd
 {% endif %}
 dnsblog   unix  -       -       y       -       0       dnsblog
 tlsproxy  unix  -       -       y       -       0       tlsproxy
+{% if postfix_enable_submission %}
 submission inet n       -       y       -       -       smtpd
   -o smtpd_sasl_security_options=noanonymous
   -o smtpd_sasl_local_domain=$myhostname
@@ -40,17 +41,17 @@ submission inet n       -       y       -       -       smtpd
 #  -o smtpd_recipient_restrictions=
 #  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
 #  -o milter_macro_daemon_name=ORIGINATING
-#smtps     inet  n       -       y       -       -       smtpd
-#  -o syslog_name=postfix/smtps
-#  -o smtpd_tls_wrappermode=yes
-#  -o smtpd_sasl_auth_enable=yes
-#  -o smtpd_reject_unlisted_recipient=no
-#  -o smtpd_client_restrictions=$mua_client_restrictions
-#  -o smtpd_helo_restrictions=$mua_helo_restrictions
-#  -o smtpd_sender_restrictions=$mua_sender_restrictions
-#  -o smtpd_recipient_restrictions=
-#  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-#  -o milter_macro_daemon_name=ORIGINATING
+{% endif %}
+{% if postfix_enable_smtps %}
+smtps     inet  n       -       y       -       -       smtpd
+  -o syslog_name=postfix/smtps
+  -o smtpd_tls_wrappermode=yes
+  -o smtpd_sasl_auth_enable=yes
+  -o smtpd_reject_unlisted_recipient=no
+  -o smtpd_recipient_restrictions=
+  -o smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
+  -o milter_macro_daemon_name=ORIGINATING
+{% endif %}
 #628	  inet  n       -       y       -       -       qmqpd
 {% endif %}
 pickup    unix  n       -       y       60      1       pickup
@@ -94,7 +95,12 @@ postlog   unix-dgram n  -       n       -       1       postlogd
 # maildrop. See the Postfix MAILDROP_README file for details.
 # Also specify in main.cf: maildrop_destination_recipient_limit=1
 
-{% if not postfix_satellite_only %}
+{% if postfix_verify_spf %}
+policy-spf unix -       n       n       -       0       spawn
+  user=nobody argv=/usr/bin/policyd-spf
+{% endif %}
+
+{% if not postfix_satellite_only and postfix_enable_dovecot %}
 dovecot   unix  -       n       n       -       -       pipe
   flags=DRhu user=5001:5000 argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -a ${original_recipient} -d ${user}@${nexthop}
 
diff --git a/postfix/templates/policyd-spf.conf.j2 b/postfix/templates/policyd-spf.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f7a781fd31b77dc986c7d9d2eb842953b6607060
--- /dev/null
+++ b/postfix/templates/policyd-spf.conf.j2
@@ -0,0 +1,13 @@
+#  For a fully commented sample config file see policyd-spf.conf.commented
+
+debugLevel = 1
+TestOnly = {{ '1' if postfix_verify_spf_testmode else '0' }}
+
+HELO_reject = Fail
+Mail_From_reject = Fail
+
+PermError_reject = False
+TempError_Defer = False
+
+skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
+
diff --git a/postfix/templates/postsrsd.j2 b/postfix/templates/postsrsd.j2
new file mode 100644
index 0000000000000000000000000000000000000000..3bde86a9ae6f55c51903e59a5b8c07c6cfb6a5e2
--- /dev/null
+++ b/postfix/templates/postsrsd.j2
@@ -0,0 +1,55 @@
+# Default settings for postsrsd
+
+# Local domain name.
+# Addresses are rewritten to originate from this domain. The default value
+# is taken from `postconf -h mydomain` and probably okay.
+#
+SRS_DOMAIN={{ ansible_fqdn.split(".")[1:]|join(".") }}
+
+# Exclude additional domains.
+# You may list domains which shall not be subjected to address rewriting.
+# If a domain name starts with a dot, it matches all subdomains, but not
+# the domain itself. Separate multiple domains by space or comma.
+#
+#SRS_EXCLUDE_DOMAINS=.example.com,example.org
+
+# First separator character after SRS0 or SRS1.
+# Can be one of: -+=
+SRS_SEPARATOR==
+
+# Secret key to sign rewritten addresses.
+# When postsrsd is installed for the first time, a random secret is generated
+# and stored in /etc/postsrsd.secret. For most installations, that's just fine.
+#
+SRS_SECRET=/etc/postsrsd.secret
+
+# Length of hash to be used in rewritten addresses
+SRS_HASHLENGTH=4
+
+# Minimum length of hash to accept when validating return addresses.
+# When increasing SRS_HASHLENGTH, set this to its previous value and
+# wait for the duration of SRS return address validity (21 days) before
+# increading this value as well.
+SRS_HASHMIN=4
+
+# Local ports for TCP list.
+# These ports are used to bind the TCP list for postfix. If you change
+# these, you have to modify the postfix settings accordingly. The ports
+# are bound to the loopback interface, and should never be exposed on
+# the internet.
+#
+SRS_FORWARD_PORT=10001
+SRS_REVERSE_PORT=10002
+
+# Drop root privileges and run as another user after initialization.
+# This is highly recommended as postsrsd handles untrusted input.
+#
+RUN_AS=postsrsd
+
+# Bind to this address
+#
+SRS_LISTEN_ADDR=127.0.0.1
+
+# Jail daemon in chroot environment
+CHROOT=/var/lib/postsrsd
+