diff --git a/package-postfix_access/defaults/main.yml b/package-postfix_access/defaults/main.yml
new file mode 100644
index 0000000..c4d182e
--- /dev/null
+++ b/package-postfix_access/defaults/main.yml
@@ -0,0 +1,37 @@
+---
+# Default configurations
+# I populate these from external configs; I indicate what they are as inline comments
+
+domain: "{{ blsedomains_admindomain }}" # Base domain name
+postmaster: "root@{{ blsedomains_rootdomain }}" # Postmaster email address
+
+# Roundcube
+smtp_host: "{{ blsecluster_smtphost }}" # The hostname for SMTP access, usually the public name of your mail server
+support_url: "https://www.{{ blsedomains_webdomain }}" # Some website address for Roundcube support
+logo_filename: "bl-logo-roundcube.png" # The Roundcube logo under files/
+roundcube_deskey: "{{ passwd_roundcube_deskey }}" # The Roundcube DES key
+
+# Postfix
+banner_hostname: "{{ ansible_hostname }}.{{ domain }}" # Public hostname of *this* mail host
+myhostname: "{{ banner_hostname }}" # Hostname for Postfix myhostame
+mydomain: "{{ domain }}" # Domain for Postfix mydomain
+mynetworks: "{{ blsecluster_remote1v4 }} {{ blsecluster_remote2v4 }} {{ blsecluster_remote3v4 }} {{ blsecluster_hostsubnetv4 }}" # IP addresses for Postfix mynetworks
+
+# Dovecot
+# Note: SSL listeners aren't provided; HAProxy is expected to do SSL termination for us
+trusted_networks: "{{ blsecluster_hostsubnetv4 }} {{ blsecluster_hostsubnetv6 }}" # Trusted network ranges for Dovecot
+haproxy: yes # Enable HAProxy-specific (Proxy protocol) listeners on ports 10143 and 10110
+
+# LDAP integration (Postfix, Dovecot, Roundcube)
+ldap_host: "{{ blsecluster_ldaphost }}" # The hostname for LDAP access
+ldap_port: 389 # The LDAP port (always non-SSL)
+ldap_basedn: "o=domains,dc=bonilan,dc=net" # The LDAP base DN
+ldap_bind_username: "{{ username_ldap_admin }}" # The LDAP bind user name (usually cn=admin)
+ldap_bind_password: "{{ passwd_ldap_admin }}" # The LDAP bind user password
+
+# MySQL integration (Roundcube)
+mysql_host: "{{ blsecluster_sqlhost }}" # The hostname for MySQL access
+mysql_port: "{{ mysql_client['mail'].port }}" # The port for MySQL access
+mysql_database: "{{ mysql_client['mail'].database }}" # The database name
+mysql_username: "{{ mysql_client['mail'].username }}" # The database user
+mysql_password: "{{ mysql_client['mail'].passwd }}" # The database password
diff --git a/package-postfix_access/files/bl-logo-roundcube.png b/package-postfix_access/files/bl-logo-roundcube.png
new file mode 100644
index 0000000..d47c557
Binary files /dev/null and b/package-postfix_access/files/bl-logo-roundcube.png differ
diff --git a/package-postfix_access/handlers/main.yml b/package-postfix_access/handlers/main.yml
new file mode 100644
index 0000000..fb24197
--- /dev/null
+++ b/package-postfix_access/handlers/main.yml
@@ -0,0 +1,23 @@
+---
+- name: postmap transport
+ command: "postmap /etc/postfix/transport"
+- name: restart amavis
+ service:
+ name: "amavis"
+ state: "restarted"
+- name: restart saslauthd
+ service:
+ name: "saslauthd"
+ state: "restarted"
+- name: restart postfix
+ service:
+ name: "postfix"
+ state: "restarted"
+- name: restart dovecot
+ service:
+ name: "dovecot"
+ state: "restarted"
+- name: restart apache2
+ service:
+ name: "apache2"
+ state: "restarted"
diff --git a/package-postfix_access/tasks/main.yml b/package-postfix_access/tasks/main.yml
new file mode 100644
index 0000000..617ccd8
--- /dev/null
+++ b/package-postfix_access/tasks/main.yml
@@ -0,0 +1,222 @@
+---
+- name: install filtering packages and monitoring components
+ apt:
+ name:
+ - postfix
+ - postfix-ldap
+ - postfix-pcre
+ - dovecot-core
+ - dovecot-imapd
+ - dovecot-pop3d
+ - dovecot-lmtpd
+ - dovecot-sieve
+ - dovecot-managesieved
+ - dovecot-ldap
+ - dovecot-mysql
+ - apache2
+ - libapache2-mod-php
+ - roundcube
+ - roundcube-plugins
+ - php-ldap
+ - php-net-sieve
+ - mailgraph
+ - amavis
+ - spamassassin
+ - clamav-daemon
+ - libnet-dns-perl
+ - libmail-spf-perl
+ - pyzor
+ - razor
+ - pfqueue
+ state: latest
+
+- name: install compression algorithms for scanning
+ apt:
+ name:
+ - p7zip-full
+ - arj
+ - bzip2
+ - cabextract
+ - cpio
+ - file
+ - gzip
+ - lhasa
+ - liblz4-tool
+ - lrzip
+ - lzop
+ - nomarch
+ - pax
+ - rar
+ - rpm
+ - unrar-free
+ - unzip
+ - xz-utils
+ - zip
+ state: latest
+
+# ClamAV
+- name: ensure clamav is in amavis group
+ user:
+ name: "clamav"
+ append: "yes"
+ groups: "amavis"
+
+- name: ensure amavis is in clamav group
+ user:
+ name: "amavis"
+ append: "yes"
+ groups: "clamav"
+
+# Amavis
+- name: install Amavis configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/amavis/conf.d/{{ item }}"
+ notify:
+ - restart amavis
+ with_items:
+ - 15-content_filter_mode
+ - 50-user
+
+# Postfix
+- name: install Postfix main configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/postfix/{{ item }}"
+ notify:
+ - restart postfix
+ with_items:
+ - main.cf
+ - master.cf
+ - helo_access.pcre
+ - transport
+
+- name: map transport
+ command: postmap /etc/postfix/transport
+
+- name: create LDAP config dir
+ file:
+ name: "/etc/postfix/ldap"
+ state: "directory"
+
+- name: install Postfix LDAP configs
+ template:
+ src: "postfix-ldap/{{ item }}.j2"
+ dest: "/etc/postfix/ldap/{{ item }}"
+ mode: "640"
+ group: "postfix"
+ notify:
+ - restart postfix
+ with_items:
+ - catchall_maps.cf
+ - recipient_bcc_maps_domain.cf
+ - recipient_bcc_maps_user.cf
+ - relay_domains.cf
+ - sender_bcc_maps_domain.cf
+ - sender_bcc_maps_user.cf
+ - sender_login_maps.cf
+ - transport_maps_domain.cf
+ - transport_maps_user.cf
+ - virtual_alias_maps.cf
+ - virtual_group_maps.cf
+ - virtual_group_members_maps.cf
+ - virtual_mailbox_domains.cf
+ - virtual_mailbox_maps.cf
+
+- name: link /etc/mailname to /etc/hostname
+ file:
+ dest: "/etc/mailname"
+ src: "/etc/hostname"
+ state: "link"
+ force: "yes"
+
+# Dovecot
+- name: install Dovecot main configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/dovecot/{{ item }}"
+ notify:
+ - restart dovecot
+ with_items:
+ - dovecot.conf
+ - dovecot-ldap.conf
+
+- name: add vmail group
+ group:
+ name: "vmail"
+ gid: "2000"
+ state: "present"
+
+- name: add vmail user
+ user:
+ name: "vmail"
+ home: "/srv/vmail"
+ shell: "/bin/false"
+ uid: "2000"
+ group: "vmail"
+ state: "present"
+
+- name: ensure log ownership
+ file:
+ dest: "/var/log/{{ item }}"
+ owner: "vmail"
+ group: "adm"
+ mode: "644"
+ state: "touch"
+ with_items:
+ - dovecot.log
+ - dovecot-lmtp.log
+
+# Roundcube
+- name: Install roundcube PHP configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/roundcube/{{ item }}"
+ mode: "640"
+ group: "www-data"
+ with_items:
+ - debian-db.php
+ - config.inc.php
+
+- name: Remove default apache2 config
+ file:
+ name: "/etc/apache2/sites-enabled/000-default.conf"
+ state: "absent"
+ notify:
+ - restart apache2
+
+- name: Install roundcube ports config
+ template:
+ src: "ports.conf.j2"
+ dest: "/etc/apache2/ports.conf"
+ notify:
+ - restart apache2
+
+- name: Install roundcube apache2 config
+ template:
+ src: "roundcube.conf.j2"
+ dest: "/etc/roundcube/apache.conf"
+ notify:
+ - restart apache2
+
+- name: create logo dir
+ file:
+ dest: "/var/lib/roundcube/images"
+ state: "directory"
+
+- name: install Roundcube logo
+ copy:
+ src: "{{ logo_filename }}"
+ dest: "/var/lib/roundcube/images/{{ logo_filename }}"
+
+# General
+- name: ensure services are running (and enabled at boot)
+ service:
+ name: "{{ item }}"
+ state: "started"
+ enabled: "yes"
+ with_items:
+ - postfix
+ - amavis
+ - clamav-daemon
+ - dovecot
diff --git a/package-postfix_access/templates/15-content_filter_mode.j2 b/package-postfix_access/templates/15-content_filter_mode.j2
new file mode 100644
index 0000000..8ea341f
--- /dev/null
+++ b/package-postfix_access/templates/15-content_filter_mode.j2
@@ -0,0 +1,12 @@
+use strict;
+
+# Amavis filter configuration
+# {{ ansible_managed }}
+
+@bypass_virus_checks_maps = (
+ \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
+
+@bypass_spam_checks_maps = (
+ \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
+
+1; # ensure a defined return
diff --git a/package-postfix_access/templates/50-user.j2 b/package-postfix_access/templates/50-user.j2
new file mode 100644
index 0000000..3f9136a
--- /dev/null
+++ b/package-postfix_access/templates/50-user.j2
@@ -0,0 +1,12 @@
+use strict;
+
+@local_domains_acl = ( "." );
+$sa_tag_level_deflt = -9999;
+$sa_tag2_level_deflt = 4.5;
+$sa_kill_level_deflt = 4.5;
+$sa_spam_subject_tag = '*** SPAM *** ';
+
+$forward_method = 'smtp:[::1]:10025';
+
+#------------ Do not modify anything below this line -------------
+1; # ensure a defined return
diff --git a/package-postfix_access/templates/config.inc.php.j2 b/package-postfix_access/templates/config.inc.php.j2
new file mode 100644
index 0000000..a6bd5af
--- /dev/null
+++ b/package-postfix_access/templates/config.inc.php.j2
@@ -0,0 +1,111 @@
+ "/images/logo_login_small.png",
+ "elastic:login" => "/images/logo_login.png",
+ "elastic:*[small]" => "/images/logo_small.png",
+ "larry:*" => "/images/larry.png",
+ "login" => "/images/logo_login.png",
+ "[print]" => "/images/logo_print.png",
+);
+$config['skin_logo'] = 'images/{{ logo_filename }}';
+
+$config['des_key'] = '{{ roundcube_deskey }}';
+
+// ----------------------------------
+// PLUGINS
+// ----------------------------------
+$config['plugins'] = array('managesieve');
+$config['create_default_folders'] = true;
+$config['quota_zero_as_unlimited'] = true;
+$config['ldap_public'] = array (
+ 'global_ldap_abook' =>
+ array (
+ 'name' => 'Global LDAP Address Book',
+ 'hosts' =>
+ array (
+ 0 => '{{ ldap_host }}',
+ ),
+ 'port' => {{ ldap_port }},
+ 'use_tls' => false,
+ 'ldap_version' => '3',
+ 'network_timeout' => 10,
+ 'user_specific' => true,
+ 'base_dn' => '{{ ldap_basedn }}',
+ 'bind_dn' => 'mail=%u@%d,ou=Users,domainName=%d,{{ ldap_basedn }}',
+ 'hidden' => false,
+ 'searchonly' => false,
+ 'writable' => false,
+ 'search_fields' =>
+ array (
+ 0 => 'mail',
+ 1 => 'cn',
+ 2 => 'sn',
+ 3 => 'givenName',
+ 4 => 'street',
+ 5 => 'telephoneNumber',
+ 6 => 'mobile',
+ 7 => 'stree',
+ 8 => 'postalCode',
+ ),
+ 'fieldmap' =>
+ array (
+ 'name' => 'cn',
+ 'surname' => 'sn',
+ 'firstname' => 'givenName',
+ 'title' => 'title',
+ 'email' => 'mail:*',
+ 'phone:work' => 'telephoneNumber',
+ 'phone:mobile' => 'mobile',
+ 'street' => 'street',
+ 'zipcode' => 'postalCode',
+ 'locality' => 'l',
+ 'department' => 'departmentNumber',
+ 'notes' => 'description',
+ 'phone:workfax' => 'facsimileTelephoneNumber',
+ 'photo' => 'jpegPhoto',
+ ),
+ 'sort' => 'cn',
+ 'scope' => 'sub',
+ 'filter' => '(&(enabledService=mail)(enabledService=deliver)(enabledService=displayedInGlobalAddressBook)(|(objectClass=mailList)(objectClass=mailAlias)(objectClass=mailUser)))',
+ 'fuzzy_search' => true,
+ 'vlv' => false,
+ 'sizelimit' => '0',
+ 'timelimit' => '0',
+ 'referrals' => false,
+ 'group_filters' =>
+ array (
+ 'departments' =>
+ array (
+ 'name' => 'Mailing Lists',
+ 'scope' => 'sub',
+ 'base_dn' => '{{ ldap_basedn }}',
+ 'filter' => '(&(objectclass=mailList)(accountStatus=active)(enabledService=displayedInGlobalAddressBook))',
+ 'name_attr' => 'cn',
+ 'email' => 'mail',
+ ),
+ ),
+ ),
+);
+$config['autocomplete_addressbooks'] = array('sql', 'global_ldap_abook');
+$config['skin'] = 'elastic';
+$config['addressbook_sort_col'] = 'name';
+$config['draft_autosave'] = 60;
+$config['check_all_folders'] = true;
+$config['autoexpand_threads'] = 2;
+
+include_once("/etc/roundcube/debian-db-roundcube.php");
diff --git a/package-postfix_access/templates/debian-db.php.j2 b/package-postfix_access/templates/debian-db.php.j2
new file mode 100644
index 0000000..191b384
--- /dev/null
+++ b/package-postfix_access/templates/debian-db.php.j2
@@ -0,0 +1,9 @@
+
+ Options +FollowSymLinks
+ # This is needed to parse /var/lib/roundcube/.htaccess. See its
+ # content before setting AllowOverride to None.
+ AllowOverride All
+ = 2.3>
+ Require all granted
+
+
+ Order allow,deny
+ Allow from all
+
+
+
+# Protecting basic directories:
+
+ Options -FollowSymLinks
+ AllowOverride None
+
+
+
+ Options -FollowSymLinks
+ AllowOverride None
+ = 2.3>
+ Require all denied
+
+
+ Order allow,deny
+ Deny from all
+
+
+
+
+ Options -FollowSymLinks
+ AllowOverride None
+ = 2.3>
+ Require all denied
+
+
+ Order allow,deny
+ Deny from all
+
+
+
diff --git a/package-postfix_access/templates/transport.j2 b/package-postfix_access/templates/transport.j2
new file mode 100644
index 0000000..da9f35c
--- /dev/null
+++ b/package-postfix_access/templates/transport.j2
@@ -0,0 +1,3 @@
+# Gmail-specific transfer policy
+# {{ ansible_managed }}
+gmail.com smtp-ipv4:
diff --git a/package-postfix_filter/defaults/main.yml b/package-postfix_filter/defaults/main.yml
new file mode 100644
index 0000000..7e116ab
--- /dev/null
+++ b/package-postfix_filter/defaults/main.yml
@@ -0,0 +1,35 @@
+---
+# Default configurations
+# I populate these from external configs; I indicate what the are as inline comments
+
+# Postfix
+
+# A list of relay domains and their target (square-bracked hostname/IP + port); examples follow
+relay_domains: "{{ blse_relaydomains }}"
+# - domain: "some.domain.tld"
+# relay: "[mail.domain.tld]"
+# - domain: "other.domain.tld"
+# relay: "[secure.domain.tld]:465"
+
+# A list of RBLs to check for rejecting incoming mail
+remote_block_lists:
+ - bl.spamcop.net
+ - zen.spamhaus.org
+ - cbl.abuseat.org
+
+# Enable TLS (literal yes/no only) and, if yes, the cert and key files
+tls_enabled: yes
+tls_cert: "/etc/ssl/{{ ansible_fqdn }}.crt"
+tls_key: "/etc/ssl/{{ ansible_fqdn }}.key"
+
+# Virtual address maps
+virtual_maps:
+ - regex: "/^postmaster@/"
+ map: "root@{{ blsedomains_admindomain }}"
+
+# SpamAssassin
+notify_admin: "joshua@boniface.me" # Administrative address to notify
+notify_method: "smtp:{{ blsecluster_smtphost }}:25"
+custom_sender_scores:
+ - [qr'^(offers)@'i => 1.0]
+ - [qr'^.*@pizzanova.com'i => 1.0]
diff --git a/package-postfix_filter/files/ham-sample/README b/package-postfix_filter/files/ham-sample/README
new file mode 100644
index 0000000..76b5c47
--- /dev/null
+++ b/package-postfix_filter/files/ham-sample/README
@@ -0,0 +1,2 @@
+Populate me with spam-tagged legit emails.
+Then `tar -cvJf ham-sample.txz ham-sample/` in the parent directory.
diff --git a/package-postfix_filter/files/spam-sample/README b/package-postfix_filter/files/spam-sample/README
new file mode 100644
index 0000000..76b5c47
--- /dev/null
+++ b/package-postfix_filter/files/spam-sample/README
@@ -0,0 +1,2 @@
+Populate me with spam-tagged legit emails.
+Then `tar -cvJf ham-sample.txz ham-sample/` in the parent directory.
diff --git a/package-postfix_filter/handlers/main.yml b/package-postfix_filter/handlers/main.yml
new file mode 100644
index 0000000..e7784cc
--- /dev/null
+++ b/package-postfix_filter/handlers/main.yml
@@ -0,0 +1,19 @@
+---
+- name: postmap transport
+ command: "postmap /etc/postfix/transport"
+- name: restart amavis
+ service:
+ name: "amavis"
+ state: "restarted"
+- name: restart saslauthd
+ service:
+ name: "saslauthd"
+ state: "restarted"
+- name: restart postfix
+ service:
+ name: "postfix"
+ state: "restarted"
+- name: restart spamassassin
+ service:
+ name: "spamassassin"
+ state: "restarted"
diff --git a/package-postfix_filter/tasks/main.yml b/package-postfix_filter/tasks/main.yml
new file mode 100644
index 0000000..ba1f684
--- /dev/null
+++ b/package-postfix_filter/tasks/main.yml
@@ -0,0 +1,220 @@
+---
+#
+# Install role packages
+#
+
+- name: install filtering packages and monitoring components
+ apt:
+ name:
+ - postfix
+ - postfix-pcre
+ - mailgraph
+ - amavis
+ - spamassassin
+ - clamav-daemon
+ - libnet-dns-perl
+ - libmail-spf-perl
+ - postfix-policyd-spf-python
+ - pfqueue
+ state: latest
+
+- name: install compression algorithms for scanning
+ apt:
+ name:
+ - p7zip-full
+ - arj
+ - bzip2
+ - cabextract
+ - cpio
+ - file
+ - gzip
+ - lhasa
+ - liblz4-tool
+ - lrzip
+ - lzop
+ - nomarch
+ - pax
+ - rar
+ - rpm
+ - unrar-free
+ - unzip
+ - xz-utils
+ - zip
+ state: latest
+
+#
+# ClamAV
+#
+
+- name: ensure clamav is in amavis group
+ user:
+ name: "clamav"
+ append: "yes"
+ groups: "amavis"
+
+- name: ensure amavis is in clamav group
+ user:
+ name: "amavis"
+ append: "yes"
+ groups: "clamav"
+
+#
+# policyd SPF
+#
+
+- name: install policyd-spf config
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/postfix-policyd-spf-python/{{ item }}"
+ notify:
+ - restart postfix
+ with_items:
+ - "policyd-spf.conf"
+
+#
+# SpamAssassin
+#
+
+- name: install SpamAssassin config
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/spamassassin/{{ item }}"
+ notify:
+ - restart spamassassin
+ - restart amavis
+ with_items:
+ - "local.cf"
+ - "90_customrules.cf"
+
+#
+# Amavis
+#
+
+- name: install Amavis configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/amavis/conf.d/{{ item }}"
+ notify:
+ - restart amavis
+ with_items:
+ - "15-content_filter_mode"
+ - "50-user"
+
+#
+# Postfix
+#
+
+- name: create the Postfix local config dir
+ file:
+ state: directory
+ dest: "/etc/postfix/local"
+
+- name: install the Postfix main configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/postfix/{{ item }}"
+ notify:
+ - restart postfix
+ with_items:
+ - "main.cf"
+ - "master.cf"
+
+- name: install the Postfix local configs
+ template:
+ src: "{{ item }}.j2"
+ dest: "/etc/postfix/local/{{ item }}"
+ notify:
+ - restart postfix
+ with_items:
+ - helo_access
+ - recipient_access
+ - relay_domains
+ - transport
+ - virtual
+
+- name: link /etc/mailname to /etc/hostname
+ file:
+ dest: "/etc/mailname"
+ src: "/etc/hostname"
+ state: "link"
+ force: "yes"
+
+#
+# Verify and enable services
+#
+
+- name: verify and enable services
+ service:
+ name: "{{ item }}"
+ state: "started"
+ enabled: "yes"
+ with_items:
+ - "postfix"
+ - "amavis"
+ - "clamav-daemon"
+
+#
+# SpamAssassin training
+#
+
+- name: download spam sample archive
+ copy:
+ src: "spam-sample.txz"
+ dest: "/var/cache/spam-sample.txz"
+ owner: "root"
+ group: "root"
+ mode: "400"
+ register: spamsample
+
+- name: make temporary directory
+ command: "mktemp -d"
+ register: tempdirspam
+ when: spamsample.changed
+
+- name: extract spam sample archive to temporary directory
+ unarchive:
+ remote_src: "yes"
+ src: "/var/cache/spam-sample.txz"
+ dest: "{{ tempdirspam.stdout }}/"
+ when: spamsample.changed
+
+- name: sa-learn from the spam sample
+ command: "sa-learn --spam {{ tempdirspam.stdout }}/spam-sample/"
+ when: spamsample.changed
+
+- name: remove temporary directory
+ file:
+ dest: "{{ tempdirspam.stdout }}"
+ state: "absent"
+ when: spamsample.changed
+
+- name: download ham sample archive
+ copy:
+ src: "ham-sample.txz"
+ dest: "/var/cache/ham-sample.txz"
+ owner: "root"
+ group: "root"
+ mode: "400"
+ register: hamsample
+
+- name: make temporary directory
+ command: "mktemp -d"
+ register: tempdirham
+ when: hamsample.changed
+
+- name: extract ham sample archive to temporary directory
+ unarchive:
+ remote_src: "yes"
+ src: "/var/cache/ham-sample.txz"
+ dest: "{{ tempdirham.stdout }}/"
+ when: hamsample.changed
+
+- name: sa-learn from the ham sample
+ command: "sa-learn --ham {{ tempdirham.stdout }}/ham-sample/"
+ when: hamsample.changed
+
+- name: remove temporary directory
+ file:
+ dest: "{{ tempdirham.stdout }}"
+ state: "absent"
+ when: hamsample.changed
diff --git a/package-postfix_filter/templates/15-content_filter_mode.j2 b/package-postfix_filter/templates/15-content_filter_mode.j2
new file mode 100644
index 0000000..8ea341f
--- /dev/null
+++ b/package-postfix_filter/templates/15-content_filter_mode.j2
@@ -0,0 +1,12 @@
+use strict;
+
+# Amavis filter configuration
+# {{ ansible_managed }}
+
+@bypass_virus_checks_maps = (
+ \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
+
+@bypass_spam_checks_maps = (
+ \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
+
+1; # ensure a defined return
diff --git a/package-postfix_filter/templates/50-user.j2 b/package-postfix_filter/templates/50-user.j2
new file mode 100644
index 0000000..50125ae
--- /dev/null
+++ b/package-postfix_filter/templates/50-user.j2
@@ -0,0 +1,40 @@
+use strict;
+
+@local_domains_acl = ( "." );
+$sa_tag_level_deflt = -9999;
+$sa_tag2_level_deflt = 5;
+$sa_kill_level_deflt = 9999;
+$sa_spam_subject_tag = '*** SPAM *** ';
+$final_spam_destiny = 'D_PASS';
+
+$bad_header_quarantine_method = undef;
+
+$notify_method = '{{ notify_method }}';
+
+$newvirus_admin = '{{ notify_admin }}';
+$virus_admin = '{{ notify_admin }}';
+$spam_admin = '{{ notify_admin }}';
+
+$banned_admin = \@virus_admin_maps; # for compatibility with pre-2.2.1
+$bad_header_admin = \@virus_admin_maps; # for compatibility with pre-2.2.1
+@newvirus_admin_maps = (\$newvirus_admin);
+@virus_admin_maps = (\%virus_admin, \$virus_admin);
+@spam_admin_maps = (\%spam_admin, \$spam_admin);
+@banned_admin_maps = (\$banned_admin);
+@bad_header_admin_maps= (\$bad_header_admin);
+
+{% if custom_sender_scores is defined %}
+# Custom sender_map scores
+@score_sender_maps = ({
+ '.' => [
+ new_RE(
+{% for score in custom_sender_scores %}
+ {{ score }},
+{% endfor %}
+ ),
+ ],
+});
+{% endif %}
+
+#------------ Do not modify anything below this line -------------
+1; # ensure a defined return
diff --git a/package-postfix_filter/templates/90_customrules.cf.j2 b/package-postfix_filter/templates/90_customrules.cf.j2
new file mode 100644
index 0000000..92bacf9
--- /dev/null
+++ b/package-postfix_filter/templates/90_customrules.cf.j2
@@ -0,0 +1,7 @@
+# Adjustments to default SpamAssassin scoring
+# {{ ansible_managed }}
+score RCVD_IN_PSBL 3.5 # Increase default score significantly
+score URIBL_BLACK 3.5 # Increase default score significantly
+score LOTS_OF_MONEY 2 # Increase default score a little
+score ADVANCE_FEE_4_NEW 2 # Increase default score a little
+score RDNS_NONE 0.5 # Decrease default score a little
diff --git a/package-postfix_filter/templates/helo_access.j2 b/package-postfix_filter/templates/helo_access.j2
new file mode 100644
index 0000000..266c7fb
--- /dev/null
+++ b/package-postfix_filter/templates/helo_access.j2
@@ -0,0 +1,2 @@
+/^\[[0-9]{1,3}(\.[0-9]{1,3}){3}\]$/ DUNNO announced self using an address literal
+/^[0-9]{1,3}(\.[0-9]{1,3}){3}$/ REJECT announced self with an IP address instead of a domain name
diff --git a/package-postfix_filter/templates/local.cf.j2 b/package-postfix_filter/templates/local.cf.j2
new file mode 100644
index 0000000..73e0a67
--- /dev/null
+++ b/package-postfix_filter/templates/local.cf.j2
@@ -0,0 +1,27 @@
+# SpamAssassin local config
+# {{ ansible_managed }}
+
+report_safe 1
+required_score 4.5
+
+use_bayes 1
+bayes_auto_learn 1
+bayes_auto_learn_threshold_nonspam -0.1
+bayes_auto_learn_threshold_spam 9.0
+
+score BAYES_00 -4
+score BAYES_05 -2
+score BAYES_80 2
+score BAYES_95 6
+score BAYES_99 8
+
+bayes_ignore_header X-Bogosity
+bayes_ignore_header X-Spam-Flag
+bayes_ignore_header X-Spam-Status
+
+bayes_path /var/spamassassin/bayes_db/bayes
+bayes_file_mode 0777
+
+skip_rbl_checks 0
+ok_languages all
+ok_locales all
diff --git a/package-postfix_filter/templates/main.cf.j2 b/package-postfix_filter/templates/main.cf.j2
new file mode 100644
index 0000000..9b71172
--- /dev/null
+++ b/package-postfix_filter/templates/main.cf.j2
@@ -0,0 +1,91 @@
+# Main Postfix configuration
+# {{ ansible_managed }}
+
+myorigin = /etc/mailname
+myhostname = {{ ansible_fqdn }}
+
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+message_size_limit = 26214400
+mailbox_size_limit = 0
+default_process_limit = 1000
+recipient_delimiter = +
+inet_interfaces = all
+
+smtpd_banner = {{ ansible_fqdn }} ESMTP $mail_name (Debian/GNU)
+biff = no
+append_dot_mydomain = no
+delay_warning_time = 48h
+maximal_queue_lifetime = 14d
+bounce_queue_lifetime = 14d
+readme_directory = no
+compatibility_level = 2
+
+smtpd_use_tls={{ tls_enabled }}
+smtpd_tls_dh1024_param_file = /etc/ssl/dhparams.pem
+smtpd_tls_cert_file={{ tls_cert }}
+smtpd_tls_key_file={{ tls_key }}
+smtpd_tls_ask_ccert = yes
+smtpd_tls_received_header = yes
+smtpd_tls_loglevel = 1
+smtpd_tls_session_cache_database = btree:$data_directory/smtpd_scache
+smtpd_tls_security_level = may
+smtpd_tls_protocols = !SSLv2,!SSLv3
+smtpd_tls_ciphers = medium
+smtpd_tls_exclude_ciphers = RC4, CAMELLIA, SEED, 3DES
+
+smtp_use_tls={{ tls_enabled }}
+smtp_tls_cert_file={{ tls_cert }}
+smtp_tls_key_file={{ tls_key }}
+smtp_tls_session_cache_database = btree:$data_directory/smtp_scache
+smtp_tls_loglevel = 1
+smtp_tls_security_level = may
+smtp_tls_protocols = $smtpd_tls_protocols
+smtp_tls_ciphers = $smtpd_tls_ciphers
+smtp_tls_exclude_ciphers = $smtpd_tls_exclude_ciphers
+
+mydestination =
+local_recipient_maps =
+alias_maps =
+alias_database =
+virtual_alias_maps = pcre:$config_directory/local/virtual
+local_transport = error:local mail delivery is disabled
+transport_maps = pcre:$config_directory/local/transport
+relay_domains = $config_directory/local/relay_domains
+
+content_filter = smtp-amavis:[127.0.0.1]:10024
+smtpd_client_recipient_rate_limit = 250
+strict_rfc821_envelopes = yes
+receive_override_options = no_address_mappings
+policyd-spf_time_limit = 3600
+smtpd_relay_restrictions =
+ permit_mynetworks
+ reject_unauth_destination
+ check_policy_service unix:private/policyd-spf
+{% for rbl in remote_block_lists %}
+ reject_rbl_client {{ rbl }}
+{% endfor %}
+ warn_if_reject reject_unknown_client
+
+smtpd_helo_required = yes
+smtpd_helo_restrictions =
+ check_helo_access pcre:$config_directory/local/helo_access
+ reject_invalid_hostname
+
+smtpd_sender_restrictions =
+ check_sender_mx_access cidr:$config_directory/local/mx_access
+ reject_unknown_sender_domain
+ reject_non_fqdn_sender
+ check_sender_access pcre:$config_directory/local/sender_access
+
+smtpd_recipient_restrictions =
+ reject_unknown_recipient_domain
+ reject_non_fqdn_recipient
+ reject_unauth_pipelining
+ reject_unauth_destination
+ check_policy_service unix:private/policyd-spf
+ check_recipient_access pcre:$config_directory/local/recipient_access
+ reject_unverified_recipient
+
+smtpd_data_restrictions =
+ reject_multi_recipient_bounce
+ reject_unauth_pipelining
diff --git a/package-postfix_filter/templates/master.cf.j2 b/package-postfix_filter/templates/master.cf.j2
new file mode 100644
index 0000000..dea0256
--- /dev/null
+++ b/package-postfix_filter/templates/master.cf.j2
@@ -0,0 +1,62 @@
+# Postfix master process configuration file
+# {{ ansible_managed }}
+
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (yes) (never) (100)
+# ==========================================================================
+smtp inet n - y - - smtpd
+pickup unix n - y 60 1 pickup
+ -o content_filter=
+ -o receive_override_options=no_header_body_checks
+cleanup unix n - y - 0 cleanup
+qmgr unix n - n 300 1 qmgr
+tlsmgr unix - - y 1000? 1 tlsmgr
+rewrite unix - - y - - trivial-rewrite
+bounce unix - - y - 0 bounce
+defer unix - - y - 0 bounce
+trace unix - - y - 0 bounce
+verify unix - - y - 1 verify
+flush unix n - y 1000? 0 flush
+proxymap unix - - n - - proxymap
+proxywrite unix - - n - 1 proxymap
+smtp unix - - y - - smtp
+relay unix - - y - - smtp
+showq unix n - y - - showq
+error unix - - y - - error
+retry unix - - y - - error
+discard unix - - y - - discard
+local unix - n n - - local
+virtual unix - n n - - virtual
+lmtp unix - - y - - lmtp
+anvil unix - - y - 1 anvil
+scache unix - - y - 1 scache
+
+policyd-spf unix - n n - 0 spawn
+ user=policyd-spf argv=/usr/bin/policyd-spf
+
+smtp-amavis unix - - y - 2 smtp
+ -o smtp_data_done_timeout=1200
+ -o smtp_send_xforward_command=yes
+ -o disable_dns_lookups=yes
+ -o max_use=20
+
+127.0.0.1:10025 inet n - y - - smtpd
+ -o content_filter=
+ -o local_recipient_maps=
+ -o relay_recipient_maps=
+ -o smtpd_restriction_classes=
+ -o smtpd_delay_reject=no
+ -o smtpd_client_restrictions=permit_mynetworks,reject
+ -o smtpd_helo_restrictions=
+ -o smtpd_sender_restrictions=
+ -o smtpd_recipient_restrictions=permit_mynetworks,reject
+ -o smtpd_data_restrictions=reject_unauth_pipelining
+ -o smtpd_end_of_data_restrictions=
+ -o mynetworks=127.0.0.0/8
+ -o smtpd_error_sleep_time=0
+ -o smtpd_soft_error_limit=1001
+ -o smtpd_hard_error_limit=1000
+ -o smtpd_client_connection_count_limit=0
+ -o smtpd_client_connection_rate_limit=0
+ -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
diff --git a/package-postfix_filter/templates/policyd-spf.conf.j2 b/package-postfix_filter/templates/policyd-spf.conf.j2
new file mode 100644
index 0000000..cccd84a
--- /dev/null
+++ b/package-postfix_filter/templates/policyd-spf.conf.j2
@@ -0,0 +1,12 @@
+# policyd SPF config
+# {{ ansible_managed }}
+
+debugLevel = 1
+
+HELO_reject = False
+Mail_From_reject = False
+
+PermError_reject = False
+TempError_Defer = False
+
+skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
diff --git a/package-postfix_filter/templates/recipient_access.j2 b/package-postfix_filter/templates/recipient_access.j2
new file mode 100644
index 0000000..eeee382
--- /dev/null
+++ b/package-postfix_filter/templates/recipient_access.j2
@@ -0,0 +1,4 @@
+/[%!@].*[%!@]/ REJECT sender-specified routing in recipient address
+/&.*@/ REJECT invalid user
+/^(daemon|bin|sys|sync|games|man|lp|news|uucp|proxy|www-data|backup|list|irc|gnats|nobody)@/ REJECT reserved system user
+/^(ntp|sshd|munin|postfix|clamav|sqlgrey|policyd-spf|bind|statd|freerad|mysql|smokeping|systemd-.+|)@/ REJECT reserved system user
diff --git a/package-postfix_filter/templates/relay_domains.j2 b/package-postfix_filter/templates/relay_domains.j2
new file mode 100644
index 0000000..16dbac2
--- /dev/null
+++ b/package-postfix_filter/templates/relay_domains.j2
@@ -0,0 +1,3 @@
+{% for domain in relay_domains %}
+{{ domain.domain }}
+{% endfor %}
diff --git a/package-postfix_filter/templates/transport.j2 b/package-postfix_filter/templates/transport.j2
new file mode 100644
index 0000000..80a2133
--- /dev/null
+++ b/package-postfix_filter/templates/transport.j2
@@ -0,0 +1,3 @@
+{% for domain in relay_domains %}
+/@{{ domain.domain }}$/ relay:{{ domain.relay }}
+{% endfor %}
diff --git a/package-postfix_filter/templates/virtual.j2 b/package-postfix_filter/templates/virtual.j2
new file mode 100644
index 0000000..d89602a
--- /dev/null
+++ b/package-postfix_filter/templates/virtual.j2
@@ -0,0 +1,3 @@
+{% for map in virtual_maps %}
+{{ map.regex }} {{ map.map }}
+{% endfor %}