--- # 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 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: disable the dnslookup router replace: path: /etc/mail/exim.conf regexp: '^dnslookup:\n(?:.+\n)*' replace: | # dnslookup: # driver = dnslookup # domains = ! +local_domains # transport = remote_smtp # ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 # no_more notify: - restart exim.service - name: enable the smarthost router replace: path: /etc/mail/exim.conf regexp: '^#\s*smarthost:\n(?:.+\n)*' replace: | smarthost: driver = manualroute domains = ! +local_domains transport = remote_smtp route_list = * ISP.SMART.HOST ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 no_more notify: - restart exim.service - name: set the smarthost hostname lineinfile: path: /etc/mail/exim.conf regexp: '^(\s*route_list\s*=)' backrefs: true line: "\\1 * {{mail_smtp_smarthost}}" 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