567 lines
16 KiB
YAML
567 lines
16 KiB
YAML
---
|
||
# The home-server project produces a multi-purpose setup using Ansible.
|
||
# Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license.
|
||
# Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing.
|
||
|
||
### UPSTREAM BEGIN ⇒ ###
|
||
- name: pull prerequisites from upstream
|
||
include_role: name=etckeeper.inc allow_duplicates=true tasks_from=upstream.yml
|
||
vars:
|
||
msg: Exim
|
||
### ⇐ UPSTREAM BEGIN ###
|
||
|
||
- name: install software
|
||
package:
|
||
name: "{{item}}"
|
||
state: present
|
||
with_items:
|
||
- exim
|
||
- spamassassin
|
||
# sa-compile from spamassassin needs make, gcc.
|
||
- make
|
||
- gcc
|
||
|
||
### UPSTREAM END ⇒ ###
|
||
- name: merge upstream
|
||
include_role: name=etckeeper.inc allow_duplicates=true tasks_from=merge.yml
|
||
vars:
|
||
msg: Exim
|
||
### ⇐ UPSTREAM END ###
|
||
|
||
- name: prepare overriding exim settings
|
||
file:
|
||
name: /etc/systemd/system/exim.service.d
|
||
state: directory
|
||
mode: 0755
|
||
|
||
- name: secure exim systemd settings
|
||
copy:
|
||
content: |
|
||
[Service]
|
||
CapabilityBoundingSet=CAP_AUDIT_WRITE CAP_BLOCK_SUSPEND CAP_CHOWN CAP_FSETID CAP_IPC_LOCK CAP_LEASE CAP_LINUX_IMMUTABLE CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETFCAP CAP_SETPCAP CAP_SETUID CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_PACCT CAP_SYS_RAWIO CAP_WAKE_ALARM
|
||
PrivateTmp=true
|
||
PrivateDevices=true
|
||
ProtectSystem=full
|
||
ProtectHome=true
|
||
NoNewPrivileges=true
|
||
dest: "/etc/systemd/system/exim.service.d/secure-{{nickname}}.conf"
|
||
mode: 0644
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: declare automatic updating and compilation of SpamAssassin rules
|
||
copy:
|
||
content: |
|
||
[Unit]
|
||
Description=Update and compile SpamAssassin rules
|
||
[Service]
|
||
User=root
|
||
Group=spamd
|
||
Type=oneshot
|
||
ExecStart=/usr/bin/vendor_perl/sa-update --allowplugins
|
||
# Exit status: 0 = updated, 1 = no updates available
|
||
SuccessExitStatus=0 1
|
||
ExecStart=/usr/bin/vendor_perl/sa-compile
|
||
SuccessExitStatus=0
|
||
dest: /etc/systemd/system/spamassassin-update.service
|
||
mode: 0644
|
||
notify:
|
||
- restart spamassassin-update.timer
|
||
|
||
- name: plan automatic updating and compilation of SpamAssassin rules
|
||
copy:
|
||
content: |
|
||
[Unit]
|
||
Description=Update and compile SpamAssassin rules
|
||
[Timer]
|
||
OnCalendar=daily
|
||
Persistent=true
|
||
[Install]
|
||
WantedBy=timers.target
|
||
dest: /etc/systemd/system/spamassassin-update.timer
|
||
mode: 0644
|
||
notify:
|
||
- restart spamassassin-update.timer
|
||
|
||
- name: prepare overriding spamassassin settings
|
||
file:
|
||
name: /etc/systemd/system/spamassassin.service.d
|
||
state: directory
|
||
mode: 0755
|
||
|
||
- name: make spamassassin take into account changes in rules
|
||
copy:
|
||
content: |
|
||
[Unit]
|
||
PartOf=spamassassin-update.service
|
||
Wants=spamassassin-update.service
|
||
After=spamassassin-update.service
|
||
dest: /etc/systemd/system/spamassassin.service.d/restart-when-update.conf
|
||
mode: 0644
|
||
notify:
|
||
- restart spamassassin-update.timer
|
||
|
||
- name: secure spamassassin systemd settings
|
||
copy:
|
||
content: |
|
||
[Service]
|
||
ExecStart=
|
||
ExecStart=/usr/bin/vendor_perl/spamd -x -u spamd -g spamd --listen=/run/shared_sockets/spamd
|
||
CapabilityBoundingSet=CAP_AUDIT_WRITE CAP_LEASE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT
|
||
PrivateTmp=true
|
||
PrivateDevices=true
|
||
ProtectSystem=full
|
||
ProtectHome=true
|
||
NoNewPrivileges=true
|
||
dest: /etc/systemd/system/spamassassin.service.d/secure-{{nickname}}.conf
|
||
mode: 0644
|
||
notify:
|
||
- restart spamassassin.service
|
||
|
||
- name: make spamassassin trust local machines
|
||
lineinfile:
|
||
path: /etc/mail/spamassassin/local.cf
|
||
regexp: '^trusted_networks\s'
|
||
line: "trusted_networks {{net_trusted_ranges}}"
|
||
insertafter: '^# trusted_networks'
|
||
notify:
|
||
- restart spamassassin.service
|
||
|
||
- name: be less tolerent with spam
|
||
lineinfile:
|
||
path: /etc/mail/spamassassin/local.cf
|
||
regexp: '^required_score\s'
|
||
line: 'required_score 4.0'
|
||
insertafter: '^# required_score'
|
||
notify:
|
||
- restart spamassassin.service
|
||
|
||
- name: leave spamd when the network is trusted
|
||
lineinfile:
|
||
path: /etc/mail/spamassassin/local.cf
|
||
regexp: '^shortcircuit\s+ALL_TRUSTED\s'
|
||
line: 'shortcircuit ALL_TRUSTED on'
|
||
insertafter: '^# shortcircuit\s+ALL_TRUSTED'
|
||
notify:
|
||
- restart spamassassin.service
|
||
|
||
- name: Aliases LDIF
|
||
ldap_entry:
|
||
server_uri: ldapi://%2Frun%2Fshared_sockets%2Fldapi/
|
||
bind_dn: "cn=root,{{ldap_root}}"
|
||
bind_pw: "{{ldap_rootpw}}"
|
||
dn: 'ou=Aliases,{{ldap_root}}'
|
||
objectClass:
|
||
- top
|
||
- organizationalUnit
|
||
attributes:
|
||
ou: Aliases
|
||
|
||
- name: declare existing aliases
|
||
ldap_entry:
|
||
server_uri: ldapi://%2Frun%2Fshared_sockets%2Fldapi/
|
||
bind_dn: "cn=root,{{ldap_root}}"
|
||
bind_pw: "{{ldap_rootpw}}"
|
||
dn: "cn={{item.alias}},ou=Aliases,{{ldap_root}}"
|
||
objectClass:
|
||
- top
|
||
- nisMailAlias
|
||
attributes:
|
||
cn: "{{item.alias}}"
|
||
with_items: "{{mail_alias_memberships}}"
|
||
|
||
- name: declare existing aliases’ members
|
||
ldap_attr:
|
||
server_uri: ldapi://%2Frun%2Fshared_sockets%2Fldapi/
|
||
bind_dn: "cn=root,{{ldap_root}}"
|
||
bind_pw: "{{ldap_rootpw}}"
|
||
dn: "cn={{item.alias}},ou=Aliases,{{ldap_root}}"
|
||
name: rfc822MailMember
|
||
values: "{{item.member}}"
|
||
state: present
|
||
with_items: "{{mail_alias_memberships}}"
|
||
|
||
- name: declare “dmarc” in system-aliases file
|
||
lineinfile:
|
||
path: /etc/mail/aliases
|
||
regexp: '^(?:#\s*)?dmarc:'
|
||
line: "dmarc: postmaster"
|
||
|
||
- name: finalize system-aliases file
|
||
lineinfile:
|
||
path: /etc/mail/aliases
|
||
regexp: '^(?:#\s*)?root:'
|
||
line: "root: {{mail_forward_root_to}}"
|
||
|
||
- name: send DKIM private key
|
||
copy:
|
||
src: files/{{net_soa}}_dkim.privk.pem
|
||
dest: /etc/mail/{{net_soa}}_dkim.privk.pem
|
||
owner: exim
|
||
group: exim
|
||
mode: 0400
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set smarthost name
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?ROUTER_SMARTHOST\s*='
|
||
line: |
|
||
ROUTER_SMARTHOST={{mail_smtp_smarthost}}
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set log destination
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^log_file_path\s*='
|
||
line: |
|
||
log_file_path = syslog
|
||
insertafter: 'MAIN CONFIGURATION SETTINGS'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: disable log dupplicates
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^syslog_duplication\s*='
|
||
line: |
|
||
syslog_duplication = false
|
||
insertafter: '^log_file_path\s*='
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set LDAP URL
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^ldap_default_servers\s*='
|
||
line: |
|
||
ldap_default_servers = /run/shared_sockets/ldapi : {{SafeZone}}
|
||
insertafter: '^syslog_duplication\s*='
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set local domains
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^domainlist\s+local_domains\s*='
|
||
line: >
|
||
domainlist local_domains = @ :
|
||
{{mail_local_domains | replace(' ', ' : ')}}
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set relay hosts
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^hostlist\s+relay_from_hosts\s*='
|
||
line: >
|
||
hostlist relay_from_hosts = <; ;
|
||
{{net_trusted_ranges | replace(' ', ' ; ')}}
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: use a Unix socket for SpamAssassin
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?spamd_address\s*='
|
||
line: 'spamd_address = /run/shared_sockets/spamd'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: advertise TLS
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?tls_advertise_hosts\s*='
|
||
line: 'tls_advertise_hosts = *'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set TLS parameters for OpenSSL
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: '# {mark} OpenSSL parameters'
|
||
block: |
|
||
tls_require_ciphers = {{tls_ciphers}}
|
||
insertafter: '^tls_advertise_hosts\s*='
|
||
|
||
- name: set TLS certificate
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?tls_certificate\s*='
|
||
line: 'tls_certificate = /etc/mail/exim.crt'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set TLS key
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?tls_privatekey\s*='
|
||
line: 'tls_privatekey = /etc/mail/exim.pem'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set SMTP ports
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?daemon_smtp_ports\s*='
|
||
line: 'daemon_smtp_ports = 25 : 465 : 587'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set TLS port
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?tls_on_connect_ports\s*='
|
||
line: 'tls_on_connect_ports = 465'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set qualified domain
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?qualify_domain\s*='
|
||
line: "qualify_domain = {{net_soa}}"
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: disable reverse-DNS lookups
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?host_lookup\s*='
|
||
line: '# host_lookup = *'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: enable lenient message decoding
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(?:#\s*)?check_rfc2047_length\s*='
|
||
line: 'check_rfc2047_length = false'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set more lenient checks for local email
|
||
replace:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '(control\s*=\s*submission)\s*$'
|
||
replace: '\1/sender_retain'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: allow recipient verification through LMTP
|
||
replace:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '#?(\s*require\s+verify\s*=\s*recipient)$'
|
||
replace: '\1/callout=no_cache'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: deny mail RCPT from SpamHaus SBL
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} SpamHaus SBL ACL'
|
||
block: |
|
||
deny message = rejected because $sender_host_address is in a \
|
||
black list at SpamHaus SBL
|
||
dnslists = sbl.spamhaus.org
|
||
insertbefore: '^\s*#\s*warn\s+dnslists\s*='
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: check mail DATA with SpamAssassin
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} SpamAssassin ACL'
|
||
block: |
|
||
accept condition = ${if >={$message_size}{500k}{yes}{no}}
|
||
accept hosts = +relay_from_hosts
|
||
warn spam = nobody/defer_ok
|
||
add_header = X-Spam_score: $spam_score ($spam_bar)\n\
|
||
X-Spam_score_int: $spam_score_int
|
||
warn spam = nobody/defer_ok
|
||
add_header = X-Spam_report: $spam_report
|
||
condition = ${if >{$spam_score_int}{0}{1}{0}}
|
||
deny spam = nobody:true/defer_ok
|
||
message = This message scored $spam_score spam points.
|
||
condition = ${if >{$spam_score_int}{120}{1}{0}}
|
||
insertbefore: '^\s*#\s*warn\s+spam\s*='
|
||
notify:
|
||
- restart exim.service
|
||
|
||
# TODO: https://github.com/Exim/exim/wiki/SimpleGreylisting (with SPAM≥1.0)
|
||
|
||
- name: use remote_smtp for smarthost delivery
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(\s*transport\s*=)'
|
||
backrefs: true
|
||
line: "\\1 remote_smtp"
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: set IP addresses to be ignored
|
||
lineinfile:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^(\s*ignore_target_hosts\s*=.*::1)(?! ; {{mail_ignore_ip | replace(" ", " ; ")}}$)'
|
||
backrefs: true
|
||
line: "\\1 ; {{mail_ignore_ip | replace(' ', ' ; ')}}"
|
||
when:
|
||
- mail_ignore_ip != ""
|
||
notify:
|
||
- restart exim.service
|
||
|
||
# http://www.openldap.org/lists/openldap-software/200209/msg00792.html
|
||
# http://www.exim.org/exim-html-current/doc/html/spec_html/ch-file_and_database_lookups.html
|
||
# https://www.howtoforge.com/setting-up-a-mail-server-using-exim4-clamav-dovecot-spamassassin-and-many-more-on-debian-p3
|
||
- name: read aliases in OpenLDAP
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} OpenLDAP aliases'
|
||
block: |
|
||
ldap_aliases:
|
||
driver = redirect
|
||
allow_fail
|
||
allow_defer
|
||
data = ${lookup ldap \
|
||
{ldapi:///cn=${quote_ldap_dn:$local_part},ou=Aliases,{{ldap_root}}\
|
||
?rfc822MailMember?sub?\
|
||
(&(objectClass=nisMailAlias))} \
|
||
}
|
||
insertbefore: 'forwarding using traditional .forward'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: disable the userforward router
|
||
replace:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^userforward:\n(?:.+\n)*'
|
||
replace: |
|
||
#userforward:
|
||
# driver = redirect
|
||
# check_local_user
|
||
## local_part_suffix = +* : -*
|
||
## local_part_suffix_optional
|
||
# file = $home/.forward
|
||
## allow_filter
|
||
# no_verify
|
||
# no_expn
|
||
# check_ancestor
|
||
# file_transport = address_file
|
||
# pipe_transport = address_pipe
|
||
# reply_transport = address_reply
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: disable the localuser router
|
||
replace:
|
||
path: /etc/mail/exim.conf
|
||
regexp: '^localuser:\n(?:.+\n)*'
|
||
replace: |
|
||
#localuser:
|
||
# driver = accept
|
||
# check_local_user
|
||
## local_part_suffix = +* : -*
|
||
## local_part_suffix_optional
|
||
# transport = local_delivery
|
||
# cannot_route_message = Unknown user
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: add a router to LMTP
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} LMTP router'
|
||
block: |
|
||
lmtp_user:
|
||
debug_print = "R: lmtp_user for $local_part@$domain"
|
||
driver = manualroute
|
||
domains = +local_domains
|
||
transport = lmtp_transport
|
||
route_list = * {{SafeZone_IP}} byname
|
||
cannot_route_message = Unknown user
|
||
insertbefore: '^#localuser:'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: add a transport for LMTP
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} LMTP transport'
|
||
block: |
|
||
lmtp_transport:
|
||
driver = smtp
|
||
protocol = lmtp
|
||
rcpt_include_affixes
|
||
port = 24
|
||
insertbefore: '^# This transport is used'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: enable DKIM on outgoing emails
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} outgoing DKIM signing'
|
||
block: |
|
||
dkim_canon = relaxed
|
||
dkim_domain = {{net_soa}}
|
||
dkim_private_key = /etc/mail/{{net_soa}}_dkim.privk.pem
|
||
dkim_selector = {{mail_dkim_selector}}
|
||
insertafter: '^\s*driver\s*=\s*smtp\s*$'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: add authenticators based on LDAP
|
||
blockinfile:
|
||
path: /etc/mail/exim.conf
|
||
marker: ' # {mark} LDAP authentication'
|
||
block: |
|
||
plain_server:
|
||
driver = plaintext
|
||
public_name = PLAIN
|
||
server_prompts = :
|
||
server_condition = ${if ldapauth \
|
||
{user="uid=${quote_ldap_dn:$2},ou=Users,{{ldap_root}}" \
|
||
pass=${quote:$3} \
|
||
ldapi:///}{yes}{no}}
|
||
server_set_id = $2
|
||
login_server:
|
||
driver = plaintext
|
||
public_name = LOGIN
|
||
server_prompts = "Username:: : Password::"
|
||
server_condition = ${if ldapauth \
|
||
{user="uid=${quote_ldap_dn:$1},ou=Users,{{ldap_root}}" \
|
||
pass=${quote:$2} \
|
||
ldapi:///}{yes}{no}}
|
||
server_set_id = $1
|
||
insertafter: '^begin\s+authenticators'
|
||
notify:
|
||
- restart exim.service
|
||
|
||
- name: enable spamassassin updater
|
||
systemd:
|
||
daemon_reload: true
|
||
name: spamassassin-update.timer
|
||
enabled: true
|
||
|
||
- name: enable spamassassin
|
||
systemd:
|
||
daemon_reload: true
|
||
name: spamassassin.service
|
||
enabled: true
|
||
|
||
- name: enable exim
|
||
systemd:
|
||
daemon_reload: true
|
||
name: exim.service
|
||
enabled: true
|
||
|
||
### LOCAL COMMIT ⇒ ###
|
||
- name: commit local changes
|
||
include_role: name=etckeeper.inc allow_duplicates=true tasks_from=local.yml
|
||
vars:
|
||
msg: Exim
|
||
### ⇐ LOCAL COMMIT ###
|
||
- meta: flush_handlers
|