summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ansible.cfg2
-rw-r--r--certs/dkim/0ef2a7235861d65c872faf4e72b29a29:jakmedlem.se.pub9
-rw-r--r--certs/dkim/138abf7e73c88d8dc67ca2d26881bc81:guilhem.se.pub9
-rw-r--r--certs/dkim/564736f16aac6a05b50ea67fd6259e16:hemskaklubben.se.pub9
-rw-r--r--certs/dkim/5d30c523ff3622ed454230a16a11ddf6.guilhem.user:debian.org.pub9
-rw-r--r--certs/dkim/5fd8ba74ecb12069964e21a0ba90a516:tevs.net.pub9
-rw-r--r--certs/dkim/79992d8659ce1c2d3f5a9ad20d167c15:r0x.se.pub9
-rw-r--r--certs/dkim/9552b222c0c258daf13bd410f6b5a159:ljhms.se.pub9
-rw-r--r--certs/dkim/a4b2e822cfcf594acd24f44587590eb1:gbg.cmsmarx.org.pub9
-rw-r--r--certs/dkim/aa813339234ce48d3b3bbfa334fbf48e:dubre.me.pub9
-rw-r--r--certs/dkim/caf0355abffeda8264045c3730362147:himmelkanten.se.pub9
-rw-r--r--certs/dkim/ccb92aa8f79aa6d76b2a9d6ecf6b30e6:vimmelkanten.se.pub9
-rw-r--r--certs/dkim/d32231afe345182ae1a9b376fa912dca:guilhem.org.pub9
-rw-r--r--certs/dkim/d3df4ddda85e3c927621b1b02a9cbb85:lists.fripost.org.pub9
-rw-r--r--certs/dkim/f032227401564da2cee5d5d0965969c4:hemdal.se.pub9
-rwxr-xr-xcerts/gencerts.sh14
-rw-r--r--certs/ipsec/benjamin.pem14
-rw-r--r--certs/ipsec/levante.pem14
-rw-r--r--certs/ssh_known_hosts4
-rw-r--r--group_vars/all.yml49
-rw-r--r--lib/action_plugins/fetch_cmd.py2
-rw-r--r--lib/modules/mysql_user2495
-rw-r--r--lib/modules/openldap62
-rw-r--r--lib/modules/postmulti4
-rw-r--r--production19
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/10-logging.conf85
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/10-mail.conf45
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/10-ssl.conf33
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/15-lda.conf2
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/20-imap.conf29
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/90-plugin.conf5
-rw-r--r--roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext2
-rw-r--r--roles/IMAP/files/etc/dovecot/dovecot-ldap.conf.ext11
-rw-r--r--roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service10
-rwxr-xr-xroles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl104
-rwxr-xr-xroles/IMAP/files/usr/local/bin/list-users.pl45
-rw-r--r--roles/IMAP/tasks/imap.yml17
-rw-r--r--roles/IMAP/tasks/mda.yml2
-rw-r--r--roles/IMAP/tasks/spam.yml4
-rw-r--r--roles/IMAP/templates/etc/dovecot/conf.d/10-master.conf.j230
-rw-r--r--roles/IMAP/templates/etc/dovecot/conf.d/15-lda.conf0
-rw-r--r--roles/IMAP/templates/etc/postfix/main.cf.j22
-rw-r--r--roles/LDAP-provider/tasks/main.yml3
-rw-r--r--roles/MSA/files/etc/postfix/anonymize_sender.pcre3
-rw-r--r--roles/MSA/files/etc/systemd/system/postfix-sender-login.service6
-rwxr-xr-xroles/MSA/files/usr/local/bin/postfix-sender-login.pl13
-rw-r--r--roles/MSA/tasks/main.yml30
-rw-r--r--roles/MSA/templates/etc/postfix/main.cf.j210
l---------roles/MSA/templates/etc/postfix/smtp_tls_policy.j21
-rw-r--r--roles/MX/files/etc/opendmarc.conf116
-rw-r--r--roles/MX/files/etc/systemd/system/opendmarc.service.d/override.conf17
-rw-r--r--roles/MX/files/etc/systemd/system/opendmarc.socket10
-rw-r--r--roles/MX/handlers/main.yml9
-rw-r--r--roles/MX/tasks/main.yml45
-rw-r--r--roles/MX/templates/etc/postfix/main.cf.j27
-rw-r--r--roles/MX/templates/etc/postfix/virtual/transport.j24
-rw-r--r--roles/amavis/tasks/main.yml4
-rw-r--r--roles/amavis/templates/etc/amavis/conf.d/50-user.j23
-rw-r--r--roles/bacula-dir/files/etc/systemd/system/bacula-director.service.d/override.conf (renamed from roles/bacula-dir/files/etc/systemd/system/bacula-director.service)14
-rw-r--r--roles/bacula-dir/tasks/main.yml18
-rw-r--r--roles/bacula-dir/templates/etc/bacula/bacula-dir.conf.j2228
-rw-r--r--roles/bacula-sd/files/etc/systemd/system/bacula-sd.service.d/override.conf (renamed from roles/bacula-sd/files/etc/systemd/system/bacula-sd.service)14
-rw-r--r--roles/bacula-sd/tasks/main.yml14
-rw-r--r--roles/bacula-sd/templates/etc/bacula/bacula-sd.conf.j24
-rwxr-xr-xroles/common-LDAP/files/usr/local/sbin/slapcat-all.sh33
-rw-r--r--roles/common-LDAP/tasks/main.yml10
-rw-r--r--roles/common-LDAP/templates/etc/default/slapd.j24
-rw-r--r--roles/common-LDAP/templates/etc/ldap/database.ldif.j2127
-rw-r--r--roles/common-SQL/files/etc/mysql/mariadb.conf.d/99-user.cnf4
-rw-r--r--roles/common-SQL/files/etc/mysql/my.cnf131
-rw-r--r--roles/common-SQL/tasks/main.yml14
-rw-r--r--roles/common-web/files/etc/nginx/sites-available/default6
-rw-r--r--roles/common-web/files/etc/nginx/snippets/acme-challenge.conf4
-rw-r--r--roles/common-web/files/etc/nginx/snippets/fastcgi-php.conf17
-rw-r--r--roles/common-web/files/etc/nginx/snippets/fastcgi.conf9
-rw-r--r--roles/common-web/files/etc/nginx/snippets/ssl.conf18
-rw-r--r--roles/common-web/tasks/main.yml16
-rw-r--r--roles/common/files/etc/apt/apt.conf.d/50unattended-upgrades111
-rw-r--r--roles/common/files/etc/apt/listchanges.conf3
-rw-r--r--roles/common/files/etc/fail2ban/action.d/nftables-allports.local6
-rw-r--r--roles/common/files/etc/fail2ban/fail2ban.local11
-rw-r--r--roles/common/files/etc/fail2ban/filter.d/dovecot.conf39
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/common-local120
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/dovecot-local30
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/postfix-local72
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/strongswan-local15
-rw-r--r--roles/common/files/etc/logcheck/logcheck.conf13
-rw-r--r--roles/common/files/etc/logcheck/violations.ignore.d/logcheck-sudo10
-rw-r--r--roles/common/files/etc/logrotate.d/fripost-mail4
-rw-r--r--roles/common/files/etc/rkhunter.conf250
-rw-r--r--roles/common/files/etc/rsyslog.conf12
-rw-r--r--roles/common/files/etc/strongswan.d/charon.conf68
-rw-r--r--roles/common/files/etc/strongswan.d/charon/socket-default.conf3
-rw-r--r--roles/common/files/etc/systemd/system/bacula-fd.service25
-rw-r--r--roles/common/files/etc/systemd/system/bacula-fd.service.d/override.conf13
-rw-r--r--roles/common/files/etc/systemd/system/fail2ban.service.d/override.conf5
-rw-r--r--roles/common/files/etc/systemd/system/munin-node.service.d/override.conf14
-rw-r--r--roles/common/files/etc/systemd/system/stunnel4@.service5
-rwxr-xr-xroles/common/files/usr/local/sbin/update-firewall16
-rw-r--r--roles/common/handlers/main.yml11
-rw-r--r--roles/common/tasks/bacula.yml14
-rw-r--r--roles/common/tasks/fail2ban.yml20
-rw-r--r--roles/common/tasks/ipsec.yml5
-rw-r--r--roles/common/tasks/logging.yml1
-rw-r--r--roles/common/tasks/main.yml4
-rw-r--r--roles/common/tasks/munin-node.yml56
-rw-r--r--roles/common/tasks/ntp.yml32
-rw-r--r--roles/common/tasks/smart.yml7
-rw-r--r--roles/common/tasks/stunnel.yml12
-rw-r--r--roles/common/tasks/sysctl.yml10
-rw-r--r--roles/common/templates/etc/apt/sources.list.j28
-rw-r--r--roles/common/templates/etc/bacula/bacula-fd.conf.j212
-rw-r--r--roles/common/templates/etc/clamav/freshclam.conf.j21
-rw-r--r--roles/common/templates/etc/fail2ban/jail.local.j26
-rw-r--r--roles/common/templates/etc/ipsec.conf.j24
-rw-r--r--roles/common/templates/etc/munin/munin-node.conf.j24
-rw-r--r--roles/common/templates/etc/munin/plugin-conf.d/munin-node.j22
-rwxr-xr-xroles/common/templates/etc/nftables.conf.j2220
-rw-r--r--roles/common/templates/etc/ntp.conf.j214
-rw-r--r--roles/common/templates/etc/postfix/main.cf.j22
-rw-r--r--roles/common/templates/etc/postfix/master.cf.j26
-rw-r--r--roles/common/templates/etc/systemd/timesyncd.conf.d/fripost.conf.j29
-rw-r--r--roles/common/templates/etc/unbound/unbound.conf.j22
-rw-r--r--roles/git/files/etc/nginx/sites-available/git54
-rw-r--r--roles/git/files/etc/systemd/system/cgit.service23
-rw-r--r--roles/git/files/etc/systemd/system/cgit.socket11
-rw-r--r--roles/git/files/etc/systemd/system/git-http-backend.service21
-rw-r--r--roles/git/files/etc/systemd/system/git-http-backend.socket11
-rw-r--r--roles/git/files/etc/uwsgi/apps-available/cgit.ini6
-rw-r--r--roles/git/files/etc/uwsgi/apps-available/git-http-backend.ini8
-rw-r--r--roles/git/handlers/main.yml13
-rw-r--r--roles/git/tasks/cgit.yml96
-rw-r--r--roles/git/tasks/gitolite.yml2
-rw-r--r--roles/lacme/files/etc/lacme/lacme.conf119
-rw-r--r--roles/lacme/tasks/main.yml9
-rw-r--r--roles/lists/files/etc/nginx/sites-available/sympa35
-rw-r--r--roles/lists/files/etc/sympa/sympa/sympa.conf28
-rw-r--r--roles/lists/files/etc/systemd/system/wwsympa.service9
-rw-r--r--roles/lists/tasks/mail.yml2
-rw-r--r--roles/lists/tasks/sympa.yml4
-rw-r--r--roles/lists/templates/etc/postfix/main.cf.j22
-rw-r--r--roles/lists/templates/etc/sympa/robot.conf.j23
-rw-r--r--roles/munin-master/files/etc/nginx/sites-available/munin4
-rw-r--r--roles/munin-master/tasks/main.yml11
-rw-r--r--roles/munin-master/templates/etc/munin/munin.conf.j24
-rw-r--r--roles/nextcloud/files/etc/cron.d/nextcloud2
-rw-r--r--roles/nextcloud/files/etc/logrotate.d/nextcloud9
-rw-r--r--roles/nextcloud/files/etc/nginx/sites-available/nextcloud51
-rw-r--r--roles/nextcloud/files/etc/php/fpm/pool.d/nextcloud.conf24
-rw-r--r--roles/nextcloud/handlers/main.yml7
-rw-r--r--roles/nextcloud/tasks/main.yml136
-rw-r--r--roles/out/tasks/main.yml19
-rw-r--r--roles/out/templates/etc/postfix/main.cf.j25
-rw-r--r--roles/out/templates/etc/postfix/smtp_tls_policy.j212
-rw-r--r--roles/webmail/files/etc/cron.d/roundcube-core7
-rw-r--r--roles/webmail/files/etc/nginx/sites-available/roundcube43
-rw-r--r--roles/webmail/files/etc/php/fpm/pool.d/roundcube.conf22
-rw-r--r--roles/webmail/files/etc/roundcube/plugins/authres_status/config.inc.php6
-rw-r--r--roles/webmail/files/etc/roundcube/plugins/html5_notifier/config.inc.php6
-rw-r--r--roles/webmail/files/etc/roundcube/plugins/jqueryui/config.inc.php11
-rw-r--r--roles/webmail/files/etc/roundcube/plugins/password/config.inc.php2
-rw-r--r--roles/webmail/files/etc/roundcube/plugins/thunderbird_labels/config.inc.php5
-rw-r--r--roles/webmail/files/etc/systemd/system/stunnel4@ldap.socket11
-rw-r--r--roles/webmail/files/usr/share/roundcube/program/resources/fripost_logo_black.png (renamed from roles/webmail/files/usr/share/roundcube/skins/classic/images/fripost_logo.png)bin6567 -> 6567 bytes
-rw-r--r--roles/webmail/files/usr/share/roundcube/program/resources/fripost_logo_white.png (renamed from roles/webmail/files/usr/share/roundcube/skins/larry/images/fripost_logo.png)bin5454 -> 5454 bytes
-rw-r--r--roles/webmail/handlers/main.yml10
-rw-r--r--roles/webmail/tasks/ldap.yml34
-rw-r--r--roles/webmail/tasks/main.yml2
-rw-r--r--roles/webmail/tasks/roundcube.yml143
-rw-r--r--roles/webmail/templates/etc/roundcube/plugins/managesieve/config.inc.php.j24
-rw-r--r--roles/webmail/templates/etc/stunnel/ldap.conf.j2 (renamed from roles/webmail/files/etc/stunnel/ldap.conf)32
-rw-r--r--roles/wiki/files/etc/nginx/sites-available/website19
-rw-r--r--roles/wiki/files/etc/nginx/sites-available/wiki21
-rw-r--r--roles/wiki/files/etc/systemd/system/ikiwiki.service23
-rw-r--r--roles/wiki/files/etc/systemd/system/ikiwiki.socket11
-rw-r--r--roles/wiki/files/var/lib/ikiwiki/IkiWiki/Plugin/pandoc.pm55
-rw-r--r--roles/wiki/files/var/lib/ikiwiki/fripost-wiki.setup3
-rw-r--r--roles/wiki/handlers/main.yml9
-rw-r--r--roles/wiki/tasks/main.yml39
179 files changed, 2826 insertions, 1936 deletions
diff --git a/ansible.cfg b/ansible.cfg
index fac3b72..09c8745 100644
--- a/ansible.cfg
+++ b/ansible.cfg
@@ -39,6 +39,6 @@ become_ask_pass=True
[ssh_connection]
-control_path = ~/.ssh/S.ansible-%%C
+control_path = ${XDG_RUNTIME_DIR}/ssh-ansible-%%C
ssh_args = -oHashKnownHosts=no -oUserKnownHostsFile=./certs/ssh_known_hosts -oStrictHostKeyChecking=yes -oControlMaster=auto -oControlPersist=60s
pipelining = True
diff --git a/certs/dkim/0ef2a7235861d65c872faf4e72b29a29:jakmedlem.se.pub b/certs/dkim/0ef2a7235861d65c872faf4e72b29a29:jakmedlem.se.pub
new file mode 100644
index 0000000..07b06bc
--- /dev/null
+++ b/certs/dkim/0ef2a7235861d65c872faf4e72b29a29:jakmedlem.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA631Sqr0EH+zzmS8g1I6f
+JziGSCos24MbNjt//uqJVdkSEO6syumWePmYLzVz/G4PhFl4D1Y3ZtbXS8bR+BZ6
+MJNy+U+nGfyqo/MOxEPXIT/tRVa/8Ikniq1mvasIhX+TZllCf86wLCo061bgn9cH
+IiZS6zeSgxGbLWEQdSE7Kb/Pd0vzH2piaVEA4MzZQyonguqMlm3AU4kQ0k8pMp6k
+caMSUr7WxEPE6k5p5ah+mJ54fAvezkXyBs7oDuc3SrExkv2mfjBifN+GlGzMsYbb
+iQl4ixfxPAw1pLwP4/sPDhdIZf0vQcrSgxuEMGmFr23BD7moCLXn0lXxalICkz7m
+xwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/138abf7e73c88d8dc67ca2d26881bc81:guilhem.se.pub b/certs/dkim/138abf7e73c88d8dc67ca2d26881bc81:guilhem.se.pub
new file mode 100644
index 0000000..7996cdb
--- /dev/null
+++ b/certs/dkim/138abf7e73c88d8dc67ca2d26881bc81:guilhem.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27ioPU45Jca7x1J37gnI
+MOYTxxWE2H/EhL0SiMUROTphBPGYPyqJMmVCb3ff30Uf1tNv4g2UudWkMmoyHfgB
+hS7B07U+kK+q1gIDzcdQyGv1NN4efYtzo7iT6aNEArSP7vxa+OW1pltF1MQz5tnS
+UNFagc34cmQcN3QQXJW8XPMrQfDCm5QYgjXKamQ59GQuH+H6awNi0jGoEmlkwZad
+ZZ0O2Ly7eP/VPrmjDjP0QfC3pUjiFVa7ZPeL89aUuhQv40//7UxuGWuRKH/S7Rna
+tn3P059zXbK73Gg4ZdxLp9FPoORAyKxTq4F0ekSDc64zwNzTuNMusysBzAf3g2WS
+LwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/564736f16aac6a05b50ea67fd6259e16:hemskaklubben.se.pub b/certs/dkim/564736f16aac6a05b50ea67fd6259e16:hemskaklubben.se.pub
new file mode 100644
index 0000000..8f0a4bd
--- /dev/null
+++ b/certs/dkim/564736f16aac6a05b50ea67fd6259e16:hemskaklubben.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzdBZEho4wiYXL2RcxE3f
+ELcE7p2gjJD+gCIEIY6a7R6TM+GDxY/uLRXJuMpaw3Ar23R9Dj9I4Pu8k/MpskXU
+XQPwpOlnY18Dg/AYJR23RMBOZl8x6AGrO7GxP1R21l/9Y+ZHcuORPnIvRyLgX9aj
+k8iUwWaHfRo+rIyDTsPGQ6O8Zi1mCjlV+iuY1EO2AE88MNh6DiBlZlAH0dhLD9nw
+2AIz6oDQb9PNuUMfW6CBILWK45ETaBkAXqSJjcxzNbRov7AkHNDXx4Vw/s9AE2n+
+On7QHuojPtz4rJh83nInCFVuHXF9ZYR/MYBFLTBa9QzN6W/p7igulc4pn0Bn8STD
+RQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/5d30c523ff3622ed454230a16a11ddf6.guilhem.user:debian.org.pub b/certs/dkim/5d30c523ff3622ed454230a16a11ddf6.guilhem.user:debian.org.pub
new file mode 100644
index 0000000..bc14abf
--- /dev/null
+++ b/certs/dkim/5d30c523ff3622ed454230a16a11ddf6.guilhem.user:debian.org.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvGwfj6qfh15Nr/fLITGU
+ES9Vxtjqz6XR8uYifT9P6n80yVLLF0mCVvekSsH8Y8ZShmDmlqrNAM8i5MyKSGTq
+nMTABFlXa0bCHSjY3MNTG0PEn2yUGuc2IsmZWps8oaut5AGirEHoKjx/p2TpcFlD
+8DsyojlFbsIYmAQe65nfliDM8Qiu7NgJlghfRTTBTeXJ2yijrPymBFhJ1z1GWxUm
+nJHzj4I5ySvOmNgCVfv6nP6OjaGOdso8hiS6rse84evRlczFp+IQ/3ErP4HyNZlS
+gHFwRzcENwFdiZ9H3KjM5MUwUJ9ifyy1n2bH/5LOVEsqzmbVJqwrTi1gIP3JQL9z
+EwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/5fd8ba74ecb12069964e21a0ba90a516:tevs.net.pub b/certs/dkim/5fd8ba74ecb12069964e21a0ba90a516:tevs.net.pub
new file mode 100644
index 0000000..c4c5317
--- /dev/null
+++ b/certs/dkim/5fd8ba74ecb12069964e21a0ba90a516:tevs.net.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6xXJyURaqp+OD7VETPP
+CykL9Mwl90uFQOGl6HElV9uZwAhKekmVQka8JrMTZ5KpgVG+ro/NgnRGP8poQB9A
+poXRovX6kqwibzkh9Y/mcEW9UJg3WFc/VfOHUoKyqKtFrWwAPbTzLqC9idqEr1P/
+dLoU5i0Qhp/fWJJg0cnEg3V+6LZtmRjZwGBWKqrSaJENGAAUe0eqm5kJGfNHPagB
+Dg85o6fuTnz3ydXJk0+JaZc96jiXQzDlya0H+QbfMjBOOg7VjNzbLaDDQnFBLaY0
+tsGGcNqyS1cC8rR5Pv/1SY9fvjXBnu3//tuWuJYeHS5Q7wx84yZ03VcvVsA8yjro
+GwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/79992d8659ce1c2d3f5a9ad20d167c15:r0x.se.pub b/certs/dkim/79992d8659ce1c2d3f5a9ad20d167c15:r0x.se.pub
new file mode 100644
index 0000000..0b5de26
--- /dev/null
+++ b/certs/dkim/79992d8659ce1c2d3f5a9ad20d167c15:r0x.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0fHwmvlTzoFX3AYvh6JZ
+t2P2vS1dDRaMB6sVXEoHjdesrvmkYCYkMvboAA1Sv34ZOqoc0E6WhSsiwEP4tRdl
+WkIQhOFr85KR3sdj3KtepngBexl4arx8FcE55mbDErzaHdmMZVbITJDNfl002DiD
+j2U35R81pTOT1M7j8eJPAF2yu2FzJG/hw98ZQWwbkgyZrIcdtTmi95uWnsjGbfcM
+Iq9YBPQItt0g/tGF379+r1D4md50UQz5SbqzsZ36O73UhY1CYo+ZFZH5dHtEl8am
+mnGy0QSdMHvwd+emPXo5bZHYSKqUqt/dDJ7NkYUnXiDpFbnCyt3s/UJ4T5VAyMQg
+CQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/9552b222c0c258daf13bd410f6b5a159:ljhms.se.pub b/certs/dkim/9552b222c0c258daf13bd410f6b5a159:ljhms.se.pub
new file mode 100644
index 0000000..cbf348c
--- /dev/null
+++ b/certs/dkim/9552b222c0c258daf13bd410f6b5a159:ljhms.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzzcLgdRi0hh0X1aXWA5R
+oa92nKQCAf26aClwuXCyl9mfJXhIWPrw/tobpl1RCrRXUT7d/3qQ/cboacjuPbIB
+E8U6a3D0S9usWg9qY31fNMQoqKQPk/lXbfAfeiJCccVUdaXqEtMErqj8+XTnd4jJ
+EfA/P/p956w3bdZAH+4jEtrXgf7NErn7YJcusK2HQp1clOzV+kBo9bABqjhXHL2L
+rhtWRUgO3ODaiPmUl1VAFjepjJYb6gXSQOuquJdlKYfOwyxcVgKA1caADeWS51em
+hc/MOob5DKL99S3IjecicaBffTArijW5PcJLfGGKHIkAxmTWo/YsTA3hxSmnpGna
+1wIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/a4b2e822cfcf594acd24f44587590eb1:gbg.cmsmarx.org.pub b/certs/dkim/a4b2e822cfcf594acd24f44587590eb1:gbg.cmsmarx.org.pub
new file mode 100644
index 0000000..e4b2949
--- /dev/null
+++ b/certs/dkim/a4b2e822cfcf594acd24f44587590eb1:gbg.cmsmarx.org.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmO04P51AngD41CpgrTm
+5Mjs4M4iT10In9JF9X2l83opAoiGDoTPElcXxmmGvOvwbUV1Q2sNb2/WKLhNvlRp
+EgZiBqBd9Gf1Rf9OxpoQlfZjdhbQ5UPhxma4f/saN3RNNvAgDH+TE6DltAvZF+kW
+sjRlBabVdB+vPHBwfSBIEHvzS2rJLwx8jzzsWJQpBRkjBcueX2v4AmPIw2Lc0RJh
+/76a63xyeyGXcvKzHrBlhZPl+P6Yz5KXOE9eGJmdS9ahArZTUs5ACSwWlSrJyDUo
+hT7UeuDp0/J3HMKY51CsMwTLUxfrUoNMIAyJTJg20XeqkYzymMG5hgFilhdJdS4C
+PwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/aa813339234ce48d3b3bbfa334fbf48e:dubre.me.pub b/certs/dkim/aa813339234ce48d3b3bbfa334fbf48e:dubre.me.pub
new file mode 100644
index 0000000..92f7479
--- /dev/null
+++ b/certs/dkim/aa813339234ce48d3b3bbfa334fbf48e:dubre.me.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArZQr93FM5Ob62mmMlE2r
+pPnRJCSnaKobSedlAjdvN3frKp7jI38ygRf+9tlj40k8XPES9CYcqmN3EGCZsOTF
+JxcZvFbSQIj+1YDKiefUm6ezGbOWdSOYHY0Ztycl9pJeUFWFFXrqhk6jBs8kt3gb
+siwLD2mgXUauV+PRumoxkzLz7oH+wnNlAEcyhzhor4sHN+2oYfr/KBHfS6X1UcVp
+Tn6mGv71OAzI0Ae3VgJ+uIfNA5rcm0iBpVMIZL2z4KYNbDUl3ah7H46/W0SI+53E
+1aUsq4mHccDZV5CtMjncM16O1iqVRbA0BBvt1lxgd42YInGd88eM9Bp6GuyBuSFr
+GwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/caf0355abffeda8264045c3730362147:himmelkanten.se.pub b/certs/dkim/caf0355abffeda8264045c3730362147:himmelkanten.se.pub
new file mode 100644
index 0000000..f0f4827
--- /dev/null
+++ b/certs/dkim/caf0355abffeda8264045c3730362147:himmelkanten.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoJ2pK0ZGuPMzXm9yUXjS
+A8CYffODp8qGl+C4Hg3F3EbBvv5VP92ALwVh3bsA63Zn4TXQ2yKztK/czoRvmTBw
+qfpZABMdkLbokffIALHrouz3e0oNxrfNcH/H/vB0AjjtHvENfOLlhBBB178HuHpa
+YVKAItiD5T0rQ3soTXRYt5Lc/TjXLMfiN8MXxVKNc5FtHi8KFZLDMXC4wfBbkkyD
+iJGryG2p3XFTCn03nEOdpLb291K/tnE6BOdDsIkgN0OHwWv/SefSgiyyJtTYvdlN
+7Cknr0uu6vjjEGhvkF3q7HzIqqlA8OwiTWuqegnOvsOR3ueIly5DIZ/cgAuhpOgY
+0QIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/ccb92aa8f79aa6d76b2a9d6ecf6b30e6:vimmelkanten.se.pub b/certs/dkim/ccb92aa8f79aa6d76b2a9d6ecf6b30e6:vimmelkanten.se.pub
new file mode 100644
index 0000000..51ce588
--- /dev/null
+++ b/certs/dkim/ccb92aa8f79aa6d76b2a9d6ecf6b30e6:vimmelkanten.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Raz+2M5r1t/bKiFYRZx
+e3HsciNmoMIHy1+UaXq7gnGHmSFxOwDquItmrsmXZBLPbul34NnOcXjmz2sOYDNR
+bAbKWGp/Gp1Tw0d8wWGScOI8QJLayVqmnnJW2INzYQ3XmXXlVJu0U8659W/jIBrE
+GHtVGus9X46tNI1fMQjRxTuJ9fKp4cjVuUCpZM4U8+YAyqPI+uGkRtacYRpoHcrR
+/OeN6/rx63XLgvjW8woihdoUo2S2FAqMvHRKSVwrkoMl9C3yNUUZTq/ddzUPd/jm
+skzgGqkgePSwMM04TE4fW395GFaZwhh0opecwZS2KV9MAPjVtnm/PnTLluAQLBeG
+4QIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/d32231afe345182ae1a9b376fa912dca:guilhem.org.pub b/certs/dkim/d32231afe345182ae1a9b376fa912dca:guilhem.org.pub
new file mode 100644
index 0000000..360fe90
--- /dev/null
+++ b/certs/dkim/d32231afe345182ae1a9b376fa912dca:guilhem.org.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArcDGkR2PB0DAySR3Rel5
+qGjrMYInAwPAbyZ6SFYjjKZxEGFqkgyweUJATxa17rFZ2AWU6M0TKKEgCCDWYasa
+NfQUJd5yRcZksEhLU3S3dKNj5OmtbgFdtV3rtw8Eavxv6rSsYkzyzcQTSQDKkkGf
+CMSwWAkWYVTxRhdqRdbv+rQv47ykzymBKSU2PTkg3u/oWE0ebtSM9O4Q6AzSNiWM
+X4ClWndCpK+04WrR8GaWfsQiWpQHzwidTq8ybZXzHjU8wBl44/N5R4RytB9AFhiM
+31gnQ7yQ0JAt9vVrnzF8WNNAiSbtMukygsP9lem9ZTKCjPgKrr8IajKbdlwV49My
+GwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/d3df4ddda85e3c927621b1b02a9cbb85:lists.fripost.org.pub b/certs/dkim/d3df4ddda85e3c927621b1b02a9cbb85:lists.fripost.org.pub
new file mode 100644
index 0000000..e879336
--- /dev/null
+++ b/certs/dkim/d3df4ddda85e3c927621b1b02a9cbb85:lists.fripost.org.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuo5CbOjxGWmBKtAHWujA
+Jt13eluCYkki0gY8mF8/EJdMIrkJIRBitHHTI0Dbue1jSlD/xaAxIjeDa0okhfrR
+9XHc6cO5kImYgcdE6/0kdqy+UIo9c66i2RBtOu6luyHAMMXqUEaS8DxhxZv8v2Bx
+Ufotx9c1xIFz0h0z2Jlbvq8JFhEDFJY2Gj452lM9aRiADqHt9NeNqhBQyK+3dydx
+6VoTSj72tiEE28l/iT7EVYdAuHKNNDNBQgZBdWHqb+zZg3F5VpH/A5qfOykdXApc
+8gY4bUEK60eDT5h52o3Hguaq1hsP3AqBuOlZ4RVmtchn7tmu/9QT3S0QENfW7waM
+kQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/dkim/f032227401564da2cee5d5d0965969c4:hemdal.se.pub b/certs/dkim/f032227401564da2cee5d5d0965969c4:hemdal.se.pub
new file mode 100644
index 0000000..2aed357
--- /dev/null
+++ b/certs/dkim/f032227401564da2cee5d5d0965969c4:hemdal.se.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPW6X6CAplQ0He8CyqUs
+Ob5RnoukZtVllZsHJxvFCPjsXq4ZcPZRyNkyBo71XkDG0IBD5lCXYJk0dO/3iLMP
+fOXl9g250sZkaa1lZTdUu19Jv0Sitegql2MF6WMmt/ntPnIIaG4yq/w40ylnF8HJ
+flfN5MvPBnW2ltzzfBwUdOEEXlCsZ+tFp0s6qPrfz9dnqN0TIwFfRui3ohHGox1o
+5tKGoX3HLQ+pFyOg16dyegCXLezBwcy7RfymUnG5H0BeOrQlNZyQoSX0G/Fh4WGe
+NYyFVoWg8oYrry59zow7gEITGoYABDI/DGfMQIJMA6XHXauvhnGzFuY5Ae4ElsKU
+0QIDAQAB
+-----END PUBLIC KEY-----
diff --git a/certs/gencerts.sh b/certs/gencerts.sh
index b796339..3314757 100755
--- a/certs/gencerts.sh
+++ b/certs/gencerts.sh
@@ -26,8 +26,8 @@ x509fpr() {
host="${msg%%,*}"; host="${host%% *}"; host="${host#\`}"
pub="$DIR/${host%%:*}.pub"
spki=$(openssl pkey -pubin -outform DER <"$pub" | openssl dgst -sha256 | sed -nr 's/^[^=]+=\s*//p')
- [ "$typ" = mdwn ] && printf '\n[%s](https://crt.sh/?spkisha256=%s&iCAID=16418&exclude=expired)\n\n' "$msg" "$spki" \
- || printf '\n%s\n\n: X.509: https://crt.sh/?spkisha256=%s&iCAID=16418&exclude=expired\n SPKI:\n' \
+ [ "$typ" = mdwn ] && printf '\n[%s](https://crt.sh/?spkisha256=%s&exclude=expired)\n\n' "$msg" "$spki" \
+ || printf '\n%s\n\n: X.509: https://crt.sh/?spkisha256=%s&exclude=expired\n SPKI:\n' \
"$(printf '%s' "$msg" | tr -d '`' )" "$spki"
[ "$typ" = mdwn ] && indent=":${indent#?}"
for h in sha1 sha256; do
@@ -124,10 +124,10 @@ admin@fripost.org
These certificates are all issued by the Let's Encrypt Certificate
-Authority, and are submitted to Certificate Transparency logs. You can
-view all issued Let's Encrypt certificates at crt.sh:
+Authority, and are submitted to Certificate Transparency logs. You can
+view all issued certificates at crt.sh:
- https://crt.sh/?Identity=%25fripost.org&iCAID=16418
+ https://crt.sh/?Identity=fripost.org
The SPKI of our X.509 certificates are also available in PEM format at:
@@ -155,8 +155,8 @@ the [signed version of this page](/certs.asc).)
These certificates are all issued by the [Let's Encrypt Certificate
Authority](https://letsencrypt.org), and are submitted to [Certificate
Transparency logs](https://www.certificate-transparency.org).
-You can view all issued Let's Encrypt certificates at
-[crt.sh](https://crt.sh/?Identity=%25fripost.org&iCAID=16418).
+You can view all issued certificates at
+[crt.sh](https://crt.sh/?Identity=fripost.org).
The SPKI of our X.509 certificates are also available in PEM format
under our [Git repository]($VCS_BROWSER/tree/certs/public),
from which this fingerprint list was [generated]($VCS_BROWSER/tree/certs/gencerts.sh), at
diff --git a/certs/ipsec/benjamin.pem b/certs/ipsec/benjamin.pem
deleted file mode 100644
index bfb094e..0000000
--- a/certs/ipsec/benjamin.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwOODNQ5sdVXFrzAeo9bC
-hbauUP69uXoc6OP/l1xB9kjzmErEnoAlVjKO05nUE6Uus03/RkEPdyaMCfKarAhb
-FHaowtylUjUcIsVJkGsem4vRtuLv929vLx4TdL8BN5NCMsXOecoI5z//lfJ4YVfp
-mLQ+OUM8kWNcHOPRpnLLZq/Pwvn93WbzWmxlcmVZUwq66f0N9zBSk8678TikZGx2
-dJ/HZwigswo0PSxTIbvE2eoDdFohi9RrBxpXTnsxCAXpFIV7SLobw+tQvuv+r2oK
-5oGOnHIGmJZWVC3bRIb+PPELeB1g3TfNz7bP5PRKpXnP0cdK/0J2A+vQqArr8ACs
-gzxsKUb7t9OASLH14fQ25FJ3nsc+CS9snXIxJourd5d2cyhMe3xBo0tzPLC8sc3m
-wIyuz60o0pOjvIfzlYyldtYk3CTCVKMs1UpLnea8DDIvzhWn+TLX2yAKS/KNG0Tw
-72aLc86ZUVKV0+fkwjRWtIAWSJQZL/tOl4iDyU+T9dG9dDR1KlsfW0JBGTkyZOLZ
-rSBVQvDj/aUQjgc8e54MghJsS5QdAvD2rTO5liqB8YzHY77Nj2d4f5kqBHj41Kwt
-GOQT4nXYI+rdOpkmkMj5kOGoeRICSv+eszXADnHHtoPS73rjej0gseibSvvm9n3i
-Kkd5mm2N2oZ9Q5pF52CUFfMCAwEAAQ==
------END PUBLIC KEY-----
diff --git a/certs/ipsec/levante.pem b/certs/ipsec/levante.pem
new file mode 100644
index 0000000..06af7c3
--- /dev/null
+++ b/certs/ipsec/levante.pem
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0wHLeQbEamVileAuqKmI
+lJDBtXFDqK90G/cRmNY+2G3Iy3qzbyPQ2oqZLWwXLOs4M2PGwgzeaNAVWxONVa7C
+Hxaj0R1fV06JsqNwWX5P6u5NAy/lz0Z3GtWr9WWttHVn3+d6EpZR2j00pzgOlk4O
+3LeZiVg4NnX0Tf9uWSk1EmFmQjZQX0AcVx9HOvNZS7/uoWU25pOhJu5TZ4b0FARe
+LU5kA1VzCdeRclMA2YfhPP93W6bHvqaaMMy9EdBefkbNiJZP+DxoLSCRsI2Cc3eI
+xVPnXj6un2Yjdhuku1rxfFkPuhEjU6yNfI/SPmX/VPmSzy4SDMUDjdn81+TC1RHO
+gjCD83a69AbMW6qv0/5sYpbZlbNi8q+UV/tZcp740tS4cRCZqEQOsL4CZWioXMfi
+hTAhX9E65u0GCDjcXw2ydV05omTNqndq/vV8tTE0Opj/AX5EGgfRPxvc92K1piLZ
+aq406XSpc009A9Y0OAFbF1gEFMM1qtp93+1osnSH3sNpIB1ufzHOsobEnd8Jtkdh
+4M38Ohreaw4xUSryT8oqvGVY/ObTVA+vnJIgv+GmCTRzvmkTIjaYgUn0/LvscreJ
+njaVcBZbL2v8zujJoSXCdRc7SCRJ9JlEIFxlaoEqAa6zexVMQW8+8bDtoIjr25xK
+XIm9iuIlo/vi9f6QxaMjitECAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/certs/ssh_known_hosts b/certs/ssh_known_hosts
index 0eebead..33f8d24 100644
--- a/certs/ssh_known_hosts
+++ b/certs/ssh_known_hosts
@@ -1,7 +1,5 @@
antilop.fripost.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCmmS3Cs0ldlSEfgaL1ltfWpu2xA+1vj2pP5/zNp0aCdwqKoD/wYIa9T2kZKTzFC3J65v8E3fn/tRyDRolqhCeXWDrfBgJstLGH4ZpDMblbDjWnYmz6SY86TvE15BqWqrzDxDgcewHZlCmAxUETBgFNHSVyb1OXsfW4OmqLilbkTxY6v2QzKYzxfZTWOK2yGTO+dnT6dCN54CCItsbExLlDgqQhS/cTitL4MwpPOnMZ1cNiVyTwfDu8sFK/mDA2O+chgmplQemmuSLqnBKlcpnERcySQTetSX/bnZBvCVsDzg1tFn0Zg0PzgPCYauQMRgpk7J4jcv8P0VCK31ppuGsY4a+snvKiHUnFK7DRrJcnqSpRfwV1uL3t0khfBYiogl+zoQJ4G2yE2vKiFHVSe8LKSjgSOcnn4VwvqBNFARXMEF3Wvy72d9ZX+0kzbzp3r95vcWwqMeYDG9QEDI5lgENdhzxTcZD3Pn1hYxf862NO6UIBQW4uUhEI54QLb4bsjl/ZJ1l9KPB7bH4n58tizXlXweimzlJA+PeUzfA5BFhKjIiX9fJkVMcIFi84JmbjGMgw/B9VHewhblN+0Q8UO1PqDSlFIhE3NfWCI+1wMH2Um+zv/7M5aJYfZJvuPsvOx6D9lXOns8+kSNcdYdjkbOsgq+tdHG0eWkBuC7CRdmiKzw==
antilop.fripost.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ6R2d9q2fFtu7P4Br7z141ccR8yhY+hgyi2ylNvrcgQ
-benjamin.skangas.se ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArhPJlY+UhU0CILq7EBDLFpZFIemGJsW+d2euZyyYKbZppEtLQHIhXpiW8de1MErT3bkOeS8v8L8v0ZQLvlI/uN6i4yuTDPcf2qYTnoL4P5lzNLDIyNq6YRd26FId0M1A9YJz6t9mORb/Opb3Nq033iz40T2VJ1iJPHlCAcGOyjuxfcaiIrgPWPsKShQNdLkp5k3V0EnJoraB+bgvDfBBH5Cs4cab3EMeWBeZXB2rrICRyKZkm2dXFdDGp9UgujEQazWF0uXKMVZw1A4ZeTKc6GN66Icz5ceBTnJu38pI8ogreVyyKV5WNHeJBTpKhIT6vNQSeu98y1hrF6jIXPuQkw==
-benjamin.skangas.se ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN7Y77PUARUSb10sGZE4/W+ULh+0AFbzThQzBVspa7zS
civett.fripost.org,git.fripost.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCmlIwAthHdsufAedoBpABqvdrRI6HK7Rl02gC/ErEmtF1TvLsO2IbC2H0jEl8l6yL6eADkmQP9N7xQvpr8EH8v6AKiU6ufo3lih0Xv/OYtyVN2TdD9zzJ6Y0PxeiBaMRM0blMnfxwcfGe6Q61YQE2sbRnfUHINbEODVw5nMBxc7349x6qvsIeleL6cgEA7WWAZvDgucxXZv5IgTBT1amWDfZIadvzz6s5C6EnrGm/XrrVo7P8yD7tT3wcMFEW65S4tx9xE54Z3UydSstiJ+ZMDv4hJsZ6LYj9hgpMDWRiIZxiq/f2AoV87D54T7nSI4txkeXVPgaS7loxPtaJOzRo0prnV3yYuBq/8HaVhAOKTOK2SYFUUNay5BduxrfrQiRIqpL3uUIBA3kyVxR2YQ+FVPDHcve294CPvtTj5IYffMxJnQhkXCFSMgHYFUt9NSKqpq/9Ti3MImUtqgTq7oubwCDeltIKmiOUF9EHTM25ukmcJDvH+r5fyuOddB4/h2D/RJJ75SvKhJnz/H/pIhtYnEDaSEa3Xv9bk6KGj4Y3s49km3lcLOHE909ov0B2m1xFDT3pAiQ2v68hHrbbM/luA/sura5+Ic0mRIA9ZfsTCU1HvBAHM3AtItbrgNAvQfbPlvbZ/6hvlDW0L2CaWNSWO/mc3lcQS5UQC44T1/zkd4w==
civett.fripost.org,git.fripost.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINCKzoiQ3Ue81kQKl3t1mE2MDuS2ffVfNpNgTI0xKF5B
elefant.fripost.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDQnJ9OqxH1Xe49NYUfriyHs4mz05ECMLADT0VKNGkBAG67DzRL0QyEdBaKM7tUvgBeL9NdtXgt9OUBbCd9aDE45W2Y51r6A94TQqzRtEmlneprEWMuMKigXBpx/Y7l5XyocNvPFBgFM6l3mpoQEX13dJtCtEq/EvF/GGmAjYBUsG2LGbNZdotKyjWOtoSYZRCrON0xyUrQNt/JEw9RdmftHNqvaa04Kj17WZRAg2NT98rniIMhkMMkHr8ONE69YbomP/hfmnwjUb3VHbqSNPdhSdW3+WW1sBAe2+0UMsPurQ+qEO3c/sn0BUg+y+f2If7GVv8hSQP5GEixx9q0aVJ62MJfrXarZRPQWhN5CRcP2gWrUZEG/GnLDixzBid3cP8dEOAXBslZS7wP2GCLYqE9vj/hQ6Wl1kecMsKGEYKfll3GjtzYvi2F1HkdhxhzETw/d+sVk9plVRCHI758uIkXDrcULO5QXzOUGRdytS15IihbpJil4anh9mgqfxN9DpnZ9ZPeWnWfu2qpwjxTlylayHT0d4v+t9XqYI577jy0/9yxFbALYzDEzYxrbp7oh8YQ4olrgjtWaflwSnz1kYtOrl6n5cKYbwIGLv9mGFWyBurpU5aSnyuyzmUYDYciCO0/xjEtb1Xntne+Mb4tbZT8m9AlSv39yOQ0oYZySvrg4Q==
@@ -12,3 +10,5 @@ mistral.fripost.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzGR8+jafHmXZF7b7DuOe8
mistral.fripost.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKtVVGS/t8LBTinXuDIlVthaOTq9fyP79j1nBOchF4A4
calima.fripost.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWqe+XY9I+a4hemK5wIlwbqdDFC+jMP+nJ0SA5wX+5Lsu1sdj2FO4ziNZ0zluLA/YLyGawaqWhMWSBvDLtYa4KAv/kwzuc0Zifj6KfeBYhQnWaUZWIJp4y0KvZyaw1/QBYyea56j93zI4H0Ea9ay1jPL3kPTF9x8ynKNi34PhrEpXrXzvv9jrCgKwrwG1s5iqznzE5Rg0xJQIoKSOJXE+3xAbAA9ZGYtaFemMG+fcm67isGPYKS7DBmaMEsAQF0ri/qNsQOo7vMhw5lmYRNzehq74GL/njXzugp8cmClRGGk0YNWA0b9qfzHRYocX25OzAEQ1JE3b3cvctVeZcimqj
calima.fripost.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJbr+FgV+fnwbDsFJ/oiM79ku3V8N+SQwxuHxODIpsmk
+levante.fripost.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkZEj63yRdHiCD/pDRQWW3zmeGt4opXZ8RaIZw0ne1k6L8D+KF1UH9jnmjOoCMnx6BwJU4BsMaywlzIg2a8eDZgQwF1V+NddflHosQyUh086xGVxNTIwMjpywUPYYR5O/Cx2pdSHHK6k9KyWzJv7hfZ6fFBh5b+vBUOLBsismAHI4tBx4DiKwhitBbdZfB+pS+8DEEZatEauD59qCFC8+S/WoSzLchF/d0Qc8plUtCL+wv+bLM6rxkBmfqwsRYw/Py9QT5XDUHCQdUCxyGQV3H7iNgE4R6jiKnF0f3LxV1iJtGm6etoK0KbCvnQbLLfn53qMtdgPvqi+i5J87h+srF
+levante.fripost.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBACec3QwQifr7Pu9ZCgkcv+vYPPAEp0PeYOWpMvOca1
diff --git a/group_vars/all.yml b/group_vars/all.yml
index 4fcfc39..0c19c4c 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -2,6 +2,9 @@
non_free_packages:
elefant:
- firmware-bnx2
+ levante:
+ # for tg3
+ - firmware-misc-nonfree
# Virtual (non-routable) IPv4 subnet for IPsec. It is always nullrouted
# in the absence of xfrm lookup (i.e., when there is no matching IPsec
@@ -11,7 +14,7 @@ ipsec:
# Virtual (non-routable) addresses for IPsec. They all need to be
# distinct and belong to the above subnet 'ipsec_subnet'.
antilop: 172.16.0.1
- benjamin: 172.16.0.2
+ levante: 172.16.0.2
civett: 172.16.0.3
elefant: 172.16.0.4
giraff: 172.16.0.5
@@ -45,7 +48,7 @@ postfix_instance:
, addr: "{{ (groups.all | length > 1) | ternary( ipsec[ hostvars[groups.lists[0]].inventory_hostname_short ], '127.0.0.1') }}"
, port: 2527 }
-imapsvr_addr: "{{ postfix_instance.IMAP.addr | ipaddr }}"
+imapsvr_addr: "{{ postfix_instance.IMAP.addr | ansible.utils.ipaddr }}"
dkim_keys:
giraff:
@@ -55,6 +58,48 @@ dkim_keys:
d: fripost.org
# selector (randomly generated with `xxd -p -l16 </dev/urandom`)
s: 8f00fb94ec6c37aacb48bd43e073f9b7
+ "lists.fripost.org":
+ d: lists.fripost.org
+ s: d3df4ddda85e3c927621b1b02a9cbb85
+ "guilhem@debian.org":
+ d: debian.org
+ s: 5d30c523ff3622ed454230a16a11ddf6.guilhem.user
+ "guilhem.org":
+ d: guilhem.org
+ s: d32231afe345182ae1a9b376fa912dca
+ "guilhem.se":
+ d: guilhem.se
+ s: 138abf7e73c88d8dc67ca2d26881bc81
+ "hemdal.se":
+ d: hemdal.se
+ s: f032227401564da2cee5d5d0965969c4
+ "tevs.net":
+ d: tevs.net
+ s: 5fd8ba74ecb12069964e21a0ba90a516
+ "jakmedlem.se":
+ d: jakmedlem.se
+ s: 0ef2a7235861d65c872faf4e72b29a29
+ "gbg.cmsmarx.org":
+ d: gbg.cmsmarx.org
+ s: a4b2e822cfcf594acd24f44587590eb1
+ "r0x.se":
+ d: r0x.se
+ s: 79992d8659ce1c2d3f5a9ad20d167c15
+ "ljhms.se":
+ d: ljhms.se
+ s: 9552b222c0c258daf13bd410f6b5a159
+ "dubre.me":
+ d: dubre.me
+ s: aa813339234ce48d3b3bbfa334fbf48e
+ "himmelkanten.se":
+ d: himmelkanten.se
+ s: caf0355abffeda8264045c3730362147
+ "vimmelkanten.se":
+ d: vimmelkanten.se
+ s: ccb92aa8f79aa6d76b2a9d6ecf6b30e6
+ "hemskaklubben.se":
+ d: hemskaklubben.se
+ s: 564736f16aac6a05b50ea67fd6259e16
"~": # catch-all, for our virtual domains
d: x.fripost.org
s: 9df9cdc7e101629b5003b587945afa70
diff --git a/lib/action_plugins/fetch_cmd.py b/lib/action_plugins/fetch_cmd.py
index 57d7220..93960eb 100644
--- a/lib/action_plugins/fetch_cmd.py
+++ b/lib/action_plugins/fetch_cmd.py
@@ -50,7 +50,7 @@ class ActionModule(ActionBase):
local_checksum = checksum(dest)
# calculate checksum for the remote file, don't bother if using become as slurp will be used
- remote_checksum = self._remote_checksum(stdout, all_vars=task_vars)
+ remote_checksum = self._execute_remote_stat(stdout, all_vars=task_vars, follow=True).get('checksum')
if remote_checksum != local_checksum:
makedirs_safe(os.path.dirname(dest))
diff --git a/lib/modules/mysql_user2 b/lib/modules/mysql_user2
deleted file mode 100644
index dc9a69e..0000000
--- a/lib/modules/mysql_user2
+++ /dev/null
@@ -1,495 +0,0 @@
-#!/usr/bin/python3
-
-# (c) 2012, Mark Theunissen <mark.theunissen@gmail.com>
-# Sponsored by Four Kitchens http://fourkitchens.com.
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-DOCUMENTATION = '''
----
-module: mysql_user2
-short_description: Adds or removes a user from a MySQL database.
-description:
- - Adds or removes a user from a MySQL database.
-version_added: "0.6"
-options:
- name:
- description:
- - name of the user (role) to add or remove
- required: true
- default: null
- password:
- description:
- - set the user's password
- required: false
- default: null
- host:
- description:
- - the 'host' part of the MySQL username
- required: false
- default: localhost
- login_user:
- description:
- - The username used to authenticate with
- required: false
- default: null
- login_password:
- description:
- - The password used to authenticate with
- required: false
- default: null
- login_host:
- description:
- - Host running the database
- required: false
- default: localhost
- login_port:
- description:
- - Port of the MySQL server
- required: false
- default: 3306
- version_added: '1.4'
- login_unix_socket:
- description:
- - The path to a Unix domain socket for local connections
- required: false
- default: null
- priv:
- description:
- - "MySQL privileges string in the format: C(db.table:priv1,priv2)"
- required: false
- default: null
- append_privs:
- description:
- - Append the privileges defined by priv to the existing ones for this
- user instead of overwriting existing ones.
- required: false
- choices: [ "yes", "no" ]
- default: "no"
- version_added: "1.4"
- state:
- description:
- - Whether the user should exist. When C(absent), removes
- the user.
- required: false
- default: present
- choices: [ "present", "absent" ]
- check_implicit_admin:
- description:
- - Check if mysql allows login as root/nopassword before trying supplied credentials.
- required: false
- type: bool
- default: no
- version_added: "1.3"
-notes:
- - Requires the MySQLdb Python package on the remote host. For Ubuntu, this
- is as easy as apt-get install python-mysqldb.
- - Both C(login_password) and C(login_username) are required when you are
- passing credentials. If none are present, the module will attempt to read
- the credentials from C(~/.my.cnf), and finally fall back to using the MySQL
- default login of 'root' with no password.
- - "MySQL server installs with default login_user of 'root' and no password. To secure this user
- as part of an idempotent playbook, you must create at least two tasks: the first must change the root user's password,
- without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing
- the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from
- the file."
-
-requirements: [ "ConfigParser", "MySQLdb" ]
-author: Mark Theunissen
-'''
-
-EXAMPLES = """
-# Create database user with name 'bob' and password '12345' with all database privileges
-- mysql_user: name=bob password=12345 priv=*.*:ALL state=present
-
-# Ensure no user named 'sally' exists, also passing in the auth credentials.
-- mysql_user: login_user=root login_password=123456 name=sally state=absent
-
-# Example privileges string format
-mydb.*:INSERT,UPDATE/anotherdb.*:SELECT/yetanotherdb.*:ALL
-
-# Example using login_unix_socket to connect to server
-- mysql_user: name=root password=abc123 login_unix_socket=/var/run/mysqld/mysqld.sock
-
-# Example .my.cnf file for setting the root password
-# Note: don't use quotes around the password, because the mysql_user module
-# will include them in the password but the mysql client will not
-
-[client]
-user=root
-password=n<_665{vS43y
-"""
-
-import ConfigParser
-import getpass
-import tempfile
-try:
- import MySQLdb
-except ImportError:
- mysqldb_found = False
-else:
- mysqldb_found = True
-
-# ===========================================
-# MySQL module specific support methods.
-#
-
-def user_exists(cursor, user, host):
- cursor.execute("SELECT count(*) FROM user WHERE user = %s AND host = %s", (user,host))
- count = cursor.fetchone()
- return count[0] > 0
-
-def load_plugin(cursor, plugin, soname):
- cursor.execute("SELECT count(*) FROM information_schema.plugins WHERE plugin_name = %s", plugin)
- count = cursor.fetchone()
- if count[0] == 0:
- if soname is None:
- module.fail_json(msg="missing plugin 'soname' parameter")
- cursor.execute("INSTALL PLUGIN %s SONAME %s", (plugin, soname))
-
-def user_add(cursor, user, host, password, new_priv, auth_plugin, soname):
- if password is None:
- # Automatically loaded on first first use.
- load_plugin(cursor, auth_plugin, soname)
- cursor.execute("CREATE USER %s@%s IDENTIFIED WITH %s", (user,host,auth_plugin))
- else:
- cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
- if new_priv is not None:
- for db_table, priv in new_priv.iteritems():
- privileges_grant(cursor, user,host,db_table,priv)
- return True
-
-def user_mod(cursor, user, host, password, new_priv, append_privs, auth_plugin):
- changed = False
- grant_option = False
-
- # Handle plugin.
- if auth_plugin is not None:
- cursor.execute("SELECT plugin FROM user WHERE user = %s AND host = %s", (user,host))
- if cursor.fetchone()[0] != auth_plugin:
- # Sadly there is no proper way to updade the authentication plugin:
- # http://bugs.mysql.com/bug.php?id=67449
- cursor.execute( "UPDATE user SET plugin = %s, password = '' WHERE user = %s AND host = %s"
- , (auth_plugin,user,host))
- cursor.execute("FLUSH PRIVILEGES")
- changed = True
-
- # Handle passwords.
- if password is not None:
- cursor.execute("SELECT password FROM user WHERE user = %s AND host = %s", (user,host))
- current_pass_hash = cursor.fetchone()
- cursor.execute("SELECT PASSWORD(%s)", (password,))
- new_pass_hash = cursor.fetchone()
- if current_pass_hash[0] != new_pass_hash[0]:
- cursor.execute("SET PASSWORD FOR %s@%s = PASSWORD(%s)", (user,host,password))
- changed = True
-
- # Handle privileges.
- if new_priv is not None:
- curr_priv = privileges_get(cursor, user,host)
-
- # If the user has privileges on a db.table that doesn't appear at all in
- # the new specification, then revoke all privileges on it.
- for db_table, priv in curr_priv.iteritems():
- # If the user has the GRANT OPTION on a db.table, revoke it first.
- if "GRANT" in priv:
- grant_option = True
- if db_table not in new_priv:
- if user != "root" and "PROXY" not in priv and not append_privs:
- privileges_revoke(cursor, user,host,db_table,grant_option)
- changed = True
-
- # If the user doesn't currently have any privileges on a db.table, then
- # we can perform a straight grant operation.
- for db_table, priv in new_priv.iteritems():
- if db_table not in curr_priv:
- privileges_grant(cursor, user,host,db_table,priv)
- changed = True
-
- # If the db.table specification exists in both the user's current privileges
- # and in the new privileges, then we need to see if there's a difference.
- db_table_intersect = set(new_priv.keys()) & set(curr_priv.keys())
- for db_table in db_table_intersect:
- priv_diff = set(new_priv[db_table]) ^ set(curr_priv[db_table])
- if (len(priv_diff) > 0):
- privileges_revoke(cursor, user,host,db_table,grant_option)
- privileges_grant(cursor, user,host,db_table,new_priv[db_table])
- changed = True
-
- return changed
-
-def user_delete(cursor, user, host):
- cursor.execute("DROP USER %s@%s", (user,host))
- return True
-
-def privileges_get(cursor, user,host):
- """ MySQL doesn't have a better method of getting privileges aside from the
- SHOW GRANTS query syntax, which requires us to then parse the returned string.
- Here's an example of the string that is returned from MySQL:
-
- GRANT USAGE ON *.* TO 'user'@'localhost' IDENTIFIED BY 'pass';
-
- This function makes the query and returns a dictionary containing the results.
- The dictionary format is the same as that returned by privileges_unpack() below.
- """
- output = {}
- cursor.execute("SHOW GRANTS FOR %s@%s", (user,host))
- grants = cursor.fetchall()
-
- def pick(x):
- if x == 'ALL PRIVILEGES':
- return 'ALL'
- else:
- return x
-
- for grant in grants:
- res = re.match("GRANT (.+) ON (.+) TO '.+'@'.+'( IDENTIFIED BY PASSWORD '.+')? ?(.*)", grant[0])
- if res is None:
- module.fail_json(msg="unable to parse the MySQL grant string")
- privileges = res.group(1).split(", ")
- privileges = [ pick(x) for x in privileges]
- if "WITH GRANT OPTION" in res.group(4):
- privileges.append('GRANT')
- db = res.group(2)
- output[db] = privileges
- return output
-
-def privileges_unpack(priv):
- """ Take a privileges string, typically passed as a parameter, and unserialize
- it into a dictionary, the same format as privileges_get() above. We have this
- custom format to avoid using YAML/JSON strings inside YAML playbooks. Example
- of a privileges string:
-
- mydb.*:INSERT,UPDATE/anotherdb.*:SELECT/yetanother.*:ALL
-
- The privilege USAGE stands for no privileges, so we add that in on *.* if it's
- not specified in the string, as MySQL will always provide this by default.
- """
- output = {}
- for item in priv.split('/'):
- pieces = item.split(':')
- if pieces[0].find('.') != -1:
- pieces[0] = pieces[0].split('.')
- for idx, piece in enumerate(pieces):
- if pieces[0][idx] != "*":
- pieces[0][idx] = "`" + pieces[0][idx] + "`"
- pieces[0] = '.'.join(pieces[0])
-
- output[pieces[0]] = [ g.strip() for g in pieces[1].upper().split(',') ]
-
- if '*.*' not in output:
- output['*.*'] = ['USAGE']
-
- return output
-
-def privileges_revoke(cursor, user,host,db_table,grant_option):
- if grant_option:
- query = "REVOKE GRANT OPTION ON %s FROM '%s'@'%s'" % (db_table,user,host)
- cursor.execute(query)
- query = "REVOKE ALL PRIVILEGES ON %s FROM '%s'@'%s'" % (db_table,user,host)
- cursor.execute(query)
-
-def privileges_grant(cursor, user,host,db_table,priv):
-
- priv_string = ",".join(filter(lambda x: x != 'GRANT', priv))
- query = "GRANT %s ON %s TO '%s'@'%s'" % (priv_string,db_table,user,host)
- if 'GRANT' in priv:
- query = query + " WITH GRANT OPTION"
- cursor.execute(query)
-
-
-def strip_quotes(s):
- """ Remove surrounding single or double quotes
-
- >>> print strip_quotes('hello')
- hello
- >>> print strip_quotes('"hello"')
- hello
- >>> print strip_quotes("'hello'")
- hello
- >>> print strip_quotes("'hello")
- 'hello
-
- """
- single_quote = "'"
- double_quote = '"'
-
- if s.startswith(single_quote) and s.endswith(single_quote):
- s = s.strip(single_quote)
- elif s.startswith(double_quote) and s.endswith(double_quote):
- s = s.strip(double_quote)
- return s
-
-
-def config_get(config, section, option):
- """ Calls ConfigParser.get and strips quotes
-
- See: http://dev.mysql.com/doc/refman/5.0/en/option-files.html
- """
- return strip_quotes(config.get(section, option))
-
-
-def _safe_cnf_load(config, path):
-
- data = {'user':'', 'password':''}
-
- # read in user/pass
- f = open(path, 'r')
- for line in f.readlines():
- line = line.strip()
- if line.startswith('user='):
- data['user'] = line.split('=', 1)[1].strip()
- if line.startswith('password=') or line.startswith('pass='):
- data['password'] = line.split('=', 1)[1].strip()
- f.close()
-
- # write out a new cnf file with only user/pass
- fh, newpath = tempfile.mkstemp(prefix=path + '.')
- f = open(newpath, 'wb')
- f.write('[client]\n')
- f.write('user=%s\n' % data['user'])
- f.write('password=%s\n' % data['password'])
- f.close()
-
- config.readfp(open(newpath))
- os.remove(newpath)
- return config
-
-def load_mycnf():
- config = ConfigParser.RawConfigParser()
- mycnf = os.path.expanduser('~/.my.cnf')
- if not os.path.exists(mycnf):
- return False
- try:
- config.readfp(open(mycnf))
- except (IOError):
- return False
- except:
- config = _safe_cnf_load(config, mycnf)
-
- # We support two forms of passwords in .my.cnf, both pass= and password=,
- # as these are both supported by MySQL.
- try:
- passwd = config_get(config, 'client', 'password')
- except (ConfigParser.NoOptionError):
- try:
- passwd = config_get(config, 'client', 'pass')
- except (ConfigParser.NoOptionError):
- return False
-
- # If .my.cnf doesn't specify a user, default to user login name
- try:
- user = config_get(config, 'client', 'user')
- except (ConfigParser.NoOptionError):
- user = getpass.getuser()
- creds = dict(user=user,passwd=passwd)
- return creds
-
-def connect(module, login_user, login_password):
- if module.params["login_unix_socket"]:
- db_connection = MySQLdb.connect(host=module.params["login_host"], unix_socket=module.params["login_unix_socket"], user=login_user, passwd=login_password, db="mysql")
- else:
- db_connection = MySQLdb.connect(host=module.params["login_host"], port=int(module.params["login_port"]), user=login_user, passwd=login_password, db="mysql")
- return db_connection.cursor()
-
-# ===========================================
-# Module execution.
-#
-
-def main():
- module = AnsibleModule(
- argument_spec = dict(
- login_user=dict(default=None),
- login_password=dict(default=None, no_log=True),
- login_host=dict(default="localhost"),
- login_port=dict(default=3306, type='int'),
- login_unix_socket=dict(default=None),
- user=dict(required=True, aliases=['name']),
- password=dict(default=None, no_log=True, type='str'),
- host=dict(default="localhost"),
- state=dict(default="present", choices=["absent", "present"]),
- priv=dict(default=None),
- append_privs=dict(default=False, type="bool"),
- check_implicit_admin=dict(default=False, type="bool"),
- auth_plugin=dict(default=None),
- soname=dict(default=None)
- )
- )
- user = module.params["user"]
- password = module.params["password"]
- host = module.params["host"]
- state = module.params["state"]
- priv = module.params["priv"]
- check_implicit_admin = module.params['check_implicit_admin']
- append_privs = module.boolean(module.params["append_privs"])
- auth_plugin = module.params['auth_plugin']
- soname = module.params['soname']
-
- if not mysqldb_found:
- module.fail_json(msg="the python mysqldb module is required")
-
- if priv is not None:
- try:
- priv = privileges_unpack(priv)
- except:
- module.fail_json(msg="invalid privileges string")
-
- # Either the caller passes both a username and password with which to connect to
- # mysql, or they pass neither and allow this module to read the credentials from
- # ~/.my.cnf.
- login_password = module.params["login_password"]
- login_user = module.params["login_user"]
- if login_user is None and login_password is None:
- mycnf_creds = load_mycnf()
- if mycnf_creds is False:
- login_user = "root"
- login_password = ""
- else:
- login_user = mycnf_creds["user"]
- login_password = mycnf_creds["passwd"]
- elif login_password is None or login_user is None:
- module.fail_json(msg="when supplying login arguments, both login_user and login_password must be provided")
-
- cursor = None
- try:
- if check_implicit_admin:
- try:
- cursor = connect(module, 'root', '')
- except:
- pass
-
- if not cursor:
- cursor = connect(module, login_user, login_password)
- except Exception as e:
- module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials")
-
- if state == "present":
- if user_exists(cursor, user, host):
- changed = user_mod(cursor, user, host, password, priv, append_privs, auth_plugin)
- else:
- if (password is None and auth_plugin is None) or (password is not None and auth_plugin is not None):
- module.fail_json(msg="password xor auth_plugin is required when adding a user")
- changed = user_add(cursor, user, host, password, priv, auth_plugin, soname)
- elif state == "absent":
- if user_exists(cursor, user, host):
- changed = user_delete(cursor, user, host)
- else:
- changed = False
- module.exit_json(changed=changed, user=user)
-
-# this is magic, see lib/ansible/module_common.py
-#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
-main()
diff --git a/lib/modules/openldap b/lib/modules/openldap
index 9afe1f1..c09e791 100644
--- a/lib/modules/openldap
+++ b/lib/modules/openldap
@@ -49,10 +49,10 @@ indexedAttributes = frozenset([
# ('%s' in the attribute value is replaced with the value of the source
# entry.)
indexedDN = {
- 'olcSchemaConfig': [('cn', '{*}%s')],
- 'olcMdbConfig': [('olcDbDirectory', '%s' )],
- 'olcOverlayConfig': [('olcOverlay', '%s' )],
- 'olcMonitorConfig': [],
+ b'olcSchemaConfig': [('cn', '{*}%s')],
+ b'olcMdbConfig': [('olcDbDirectory', '%s' )],
+ b'olcOverlayConfig': [('olcOverlay', '%s' )],
+ b'olcMonitorConfig': [],
}
# Allow for flexible ACLs for user using SASL's EXTERNAL mechanism.
@@ -60,23 +60,23 @@ indexedDN = {
# "gidNumber=106+uidNumber=102,cn=peercred,cn=external,cn=auth" where
# 102 is postfix's UID and 106 its primary GID.
# (Regular expressions are not allowed.)
-sasl_ext_re = re.compile( r"""(?P<start>\sby\s+dn(?:\.exact)?)=
- (?P<quote>['\"]?)username=(?P<user>[a-z][-a-z0-9_]*),
+sasl_ext_re = re.compile( b"""(?P<start>\sby\s+dn(?:\.exact)?)=
+ (?P<quote>['\"]?)username=(?P<user>_?[a-z][-a-z0-9_]*),
(?P<end>cn=peercred,cn=external,cn=auth)
(?P=quote)\s"""
, re.VERBOSE )
-multispaces = re.compile( r"\s+" )
+multispaces = re.compile( b"\s+" )
pwd_dict = {}
def acl_sasl_ext(m):
- u = m.group('user')
+ u = m.group('user').decode("utf-8")
if u not in pwd_dict.keys():
pwd_dict[u] = pwd.getpwnam(u)
- return '%s="gidNumber=%d+uidNumber=%d,%s" ' % ( m.group('start')
- , pwd_dict[u].pw_gid
- , pwd_dict[u].pw_uid
- , m.group('end')
- )
+ return b'%s="gidNumber=%d+uidNumber=%d,%s" ' % ( m.group('start')
+ , pwd_dict[u].pw_gid
+ , pwd_dict[u].pw_uid
+ , m.group('end')
+ )
# Run the given callback on each DN seen. If its return value is not
@@ -109,14 +109,14 @@ def flexibleSearch(module, l, dn, entry):
scope = ldap.SCOPE_ONELEVEL
f = []
for c in idxClasses:
- f.append ( filter_format('objectClass=%s', [c]) )
+ f.append ( filter_format('objectClass=%s', [c.decode("utf-8")]) )
for a,v in indexedDN[c]:
if a == h:
v2 = t
elif a not in entry.keys() or len(entry[a]) > 1:
module.fail_json(msg="Multiple values found! This is a bug. Please report.")
else:
- v2 = entry[a][0]
+ v2 = entry[a][0].decode("utf-8")
f.append ( filter_format(a+'='+v, [v2]) )
if len(f) == 1:
f = f[0]
@@ -139,7 +139,7 @@ def processEntry(module, l, dn, entry):
for x in indexedAttributes.intersection(entry.keys()):
# remove useless extra spaces in ACLs etc
- entry[x] = list(map( partial(multispaces.sub, ' '), entry[x] ))
+ entry[x] = list(map( partial(multispaces.sub, b' '), entry[x] ))
r = flexibleSearch( module, l, dn, entry )
if r is None:
@@ -156,7 +156,7 @@ def processEntry(module, l, dn, entry):
d,e = r
fst = str2dn(dn).pop(0)[0][0]
diff = []
- for a,v in e.iteritems():
+ for a,v in e.items():
if a not in entry.keys():
if a != fst:
# delete all values except for the first attribute,
@@ -169,7 +169,7 @@ def processEntry(module, l, dn, entry):
entry[a] = list(map ( partial(sasl_ext_re.sub, acl_sasl_ext)
, entry[a] ))
# add explicit indices in the entry from the LDIF
- entry[a] = list(map( (lambda x: '{%d}%s' % x)
+ entry[a] = list(map( (lambda x: b'{%d}%s' % x)
, zip(range(len(entry[a])),entry[a])))
if v != entry[a]:
diff.append(( ldap.MOD_REPLACE, a, entry[a] ))
@@ -231,31 +231,31 @@ def getDN_DB(module, l, a, v, attrlist=['']):
# Convert a *.schema file into *.ldif format. The algorithm can be found
# in /etc/ldap/schema/openldap.ldif .
def slapd_to_ldif(src, name):
- s = open( src, 'r' )
+ s = open( src, 'rb' )
d = tempfile.NamedTemporaryFile(delete=False)
atexit.register(lambda: os.unlink( d.name ))
- d.write('dn: cn=%s,cn=schema,cn=config\n' % name)
- d.write('objectClass: olcSchemaConfig\n')
+ d.write(b'dn: cn=%s,cn=schema,cn=config\n' % name.encode("utf-8"))
+ d.write(b'objectClass: olcSchemaConfig\n')
- re1 = re.compile( r'^objectIdentifier\s(.*)', re.I )
- re2 = re.compile( r'^objectClass\s(.*)', re.I )
- re3 = re.compile( r'^attributeType\s(.*)', re.I )
- reSp = re.compile( r'^\s+' )
+ re1 = re.compile( b'^objectIdentifier\s(.*)', re.I )
+ re2 = re.compile( b'^objectClass\s(.*)', re.I )
+ re3 = re.compile( b'^attributeType\s(.*)', re.I )
+ reSp = re.compile( b'^\s+' )
for line in s.readlines():
- if line == '\n':
- line = '#\n'
+ if line == b'\n':
+ line = b'#\n'
m1 = re1.match(line)
m2 = re2.match(line)
m3 = re3.match(line)
if m1 is not None:
- line = 'olcObjectIdentifier: %s' % m1.group(1)
+ line = b'olcObjectIdentifier: %s' % m1.group(1)
elif m2 is not None:
- line = 'olcObjectClasses: %s' % m2.group(1)
+ line = b'olcObjectClasses: %s' % m2.group(1)
elif m3 is not None:
- line = 'olcAttributeTypes: %s' % m3.group(1)
+ line = b'olcAttributeTypes: %s' % m3.group(1)
- d.write( reSp.sub(line, ' ') )
+ d.write( reSp.sub(line, b' ') )
s.close()
diff --git a/lib/modules/postmulti b/lib/modules/postmulti
index 3c0a522..e6f58e3 100644
--- a/lib/modules/postmulti
+++ b/lib/modules/postmulti
@@ -30,7 +30,7 @@ def postconf(k, instance=None):
cmd.extend([ os.path.join(os.sep, 'usr', 'sbin', 'postconf')
, '-h', k ])
- return subprocess.check_output(cmd, stderr=subprocess.STDOUT).rstrip()
+ return subprocess.check_output(cmd, stderr=subprocess.STDOUT).rstrip().decode("utf-8")
# To destroy an existing instance:
@@ -55,7 +55,7 @@ def main():
enable = postconf('multi_instance_enable')
wrapper = postconf('multi_instance_wrapper')
- if enable != "yes" or not wrapper:
+ if enable != "yes" or wrapper == "":
# Initiate postmulti
changed = True
if module.check_mode:
diff --git a/production b/production
index ec655ab..a35d2a6 100644
--- a/production
+++ b/production
@@ -13,8 +13,8 @@ antilop.fripost.org geoip=se
[civett]
civett.fripost.org geoip=se mxno=2
-[benjamin]
-benjamin.skangas.se geoip=se
+[levante]
+levante.fripost.org geoip=se
[calima]
calima.fripost.org geoip=se
@@ -49,10 +49,10 @@ IMAP
giraff
[bacula_dir:children]
-benjamin
+levante
[bacula_sd:children]
-benjamin
+levante
# webmail.fripost.org
[webmail:children]
@@ -72,17 +72,20 @@ wiki
calima
[munin_master:children]
-benjamin
+levante
+[backports:children]
+webmail
+
# machines behind NAT
[NATed:children]
-benjamin
+levante
# hostnames resolving to a dynamic IP
[DynDNS:children]
-benjamin
+levante
# need dhcp client
[dhclient:children]
-benjamin
+levante
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/10-logging.conf b/roles/IMAP/files/etc/dovecot/conf.d/10-logging.conf
deleted file mode 100644
index 848fe69..0000000
--- a/roles/IMAP/files/etc/dovecot/conf.d/10-logging.conf
+++ /dev/null
@@ -1,85 +0,0 @@
-##
-## Log destination.
-##
-
-# Log file to use for error messages. "syslog" logs to syslog,
-# /dev/stderr logs to stderr.
-#log_path = syslog
-
-# Log file to use for informational messages. Defaults to log_path.
-#info_log_path =
-# Log file to use for debug messages. Defaults to info_log_path.
-#debug_log_path =
-
-# Syslog facility to use if you're logging to syslog. Usually if you don't
-# want to use "mail", you'll use local0..local7. Also other standard
-# facilities are supported.
-#syslog_facility = mail
-
-##
-## Logging verbosity and debugging.
-##
-
-# Log unsuccessful authentication attempts and the reasons why they failed.
-#auth_verbose = no
-
-# In case of password mismatches, log the attempted password. Valid values are
-# no, plain and sha1. sha1 can be useful for detecting brute force password
-# attempts vs. user simply trying the same password over and over again.
-# You can also truncate the value to n chars by appending ":n" (e.g. sha1:6).
-#auth_verbose_passwords = no
-
-# Even more verbose logging for debugging purposes. Shows for example SQL
-# queries.
-#auth_debug = no
-
-# In case of password mismatches, log the passwords and used scheme so the
-# problem can be debugged. Enabling this also enables auth_debug.
-#auth_debug_passwords = no
-
-# Enable mail process debugging. This can help you figure out why Dovecot
-# isn't finding your mails.
-#mail_debug = no
-
-# Show protocol level SSL errors.
-#verbose_ssl = no
-
-# mail_log plugin provides more event logging for mail processes.
-plugin {
- # Events to log. Also available: flag_change append
- #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
- # Available fields: uid, box, msgid, from, subject, size, vsize, flags
- # size and vsize are available only for expunge and copy events.
- #mail_log_fields = uid box msgid size
-}
-
-##
-## Log formatting.
-##
-
-# Prefix for each line written to log file. % codes are in strftime(3)
-# format.
-log_timestamp = "%Y-%m-%d %H:%M:%S "
-
-# Space-separated list of elements we want to log. The elements which have
-# a non-empty variable value are joined together to form a comma-separated
-# string.
-#login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c
-
-# Login log format. %s contains login_log_format_elements string, %$ contains
-# the data we want to log.
-#login_log_format = %$: %s
-
-# Log prefix for mail processes. See doc/wiki/Variables.txt for list of
-# possible variables you can use.
-#mail_log_prefix = "%s(%u): "
-
-# Format to use for logging mail deliveries. See doc/wiki/Variables.txt for
-# list of all variables you can use. Some of the common ones include:
-# %$ - Delivery status message (e.g. "saved to INBOX")
-# %m - Message-ID
-# %s - Subject
-# %f - From address
-# %p - Physical size
-# %w - Virtual size
-#deliver_log_format = msgid=%m: %$
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/10-mail.conf b/roles/IMAP/files/etc/dovecot/conf.d/10-mail.conf
index a781402..d74b026 100644
--- a/roles/IMAP/files/etc/dovecot/conf.d/10-mail.conf
+++ b/roles/IMAP/files/etc/dovecot/conf.d/10-mail.conf
@@ -118,7 +118,7 @@ mail_gid = vmail
# Group to enable temporarily for privileged operations. Currently this is
# used only with INBOX when either its initial creation or dotlocking fails.
# Typically this is set to "mail" to give access to /var/mail.
-#mail_privileged_group =
+mail_privileged_group =
# Grant access to these supplementary groups for mail processes. Typically
# these are used to set up access to shared mailboxes. Note that it may be
@@ -172,7 +172,10 @@ mail_server_admin = mailto:postmaster@fripost.org
# methods. NFS users: flock doesn't work, remember to change mmap_disable.
#lock_method = fcntl
-# Directory in which LDA/LMTP temporarily stores incoming mails >128 kB.
+# Directory where mails can be temporarily stored. Usually it's used only for
+# mails larger than >= 128 kB. It's used by various parts of Dovecot, for
+# example LDA/LMTP while delivering large mails or zlib plugin for keeping
+# uncompressed mails.
#mail_temp_dir = /tmp
# Valid UID range for users, defaults to 500 and above. This is mostly
@@ -219,7 +222,7 @@ first_valid_uid = 1
# Space separated list of plugins to load for all services. Plugins specific to
# IMAP, LDA, etc. are added to this list in their own .conf files.
-mail_plugins = quota stats virtual zlib
+mail_plugins = quota virtual zlib
##
## Mailbox handling optimizations
@@ -227,7 +230,16 @@ mail_plugins = quota stats virtual zlib
# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are
# also required for IMAP NOTIFY extension to be enabled.
-mailbox_list_index = yes
+#mailbox_list_index = yes
+
+# Trust mailbox list index to be up-to-date. This reduces disk I/O at the cost
+# of potentially returning out-of-date results after e.g. server crashes.
+# The results will be automatically fixed once the folders are opened.
+#mailbox_list_index_very_dirty_syncs = yes
+
+# Should INBOX be kept up-to-date in the mailbox list index? By default it's
+# not, because most of the mailbox accesses will open INBOX anyway.
+#mailbox_list_index_include_inbox = no
# The minimum number of mails in a mailbox before updates are done to cache
# file. This allows optimizing Dovecot's behavior to do less disk writes at
@@ -255,6 +267,19 @@ mailbox_list_index = yes
# These should exist only after Dovecot dies in the middle of saving mails.
#mail_temp_scan_interval = 1w
+# How many slow mail accesses sorting can perform before it returns failure.
+# With IMAP the reply is: NO [LIMIT] Requested sort would have taken too long.
+# The untagged SORT reply is still returned, but it's likely not correct.
+#mail_sort_max_read_count = 0
+
+protocol !indexer-worker {
+ # If folder vsize calculation requires opening more than this many mails from
+ # disk (i.e. mail sizes aren't in cache already), return failure and finish
+ # the calculation via indexer process. Disabled by default. This setting must
+ # be 0 for indexer-worker processes.
+ #mail_vsize_bg_after_count = 0
+}
+
##
## Maildir-specific settings
##
@@ -354,7 +379,7 @@ mailbox_list_index = yes
##
# Maximum dbox file size until it's rotated.
-#mdbox_rotate_size = 2M
+#mdbox_rotate_size = 10M
# Maximum dbox file age until it's rotated. Typically in days. Day begins
# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled.
@@ -390,3 +415,13 @@ mail_attachment_fs = sis-queue /home/mail/attachments/queue:posix
# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}.
# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits
mail_attachment_hash = %{sha256}
+
+# Settings to control adding $HasAttachment or $HasNoAttachment keywords.
+# By default, all MIME parts with Content-Disposition=attachment, or inlines
+# with filename parameter are consired attachments.
+# add-flags-on-save - Add the keywords when saving new mails.
+# content-type=type or !type - Include/exclude content type. Excluding will
+# never consider the matched MIME part as attachment. Including will only
+# negate an exclusion (e.g. content-type=!foo/* content-type=foo/bar).
+# exclude-inlined - Exclude any Content-Disposition=inline MIME part.
+#mail_attachment_detection_options =
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/10-ssl.conf b/roles/IMAP/files/etc/dovecot/conf.d/10-ssl.conf
index 250eec5..adeb879 100644
--- a/roles/IMAP/files/etc/dovecot/conf.d/10-ssl.conf
+++ b/roles/IMAP/files/etc/dovecot/conf.d/10-ssl.conf
@@ -27,10 +27,11 @@ ssl_key = </etc/dovecot/ssl/imap.fripost.org.key
#ssl_require_crl = yes
# Directory and/or file for trusted SSL CA certificates. These are used only
-# when Dovecot needs to act as an SSL client (e.g. imapc backend). The
-# directory is usually /etc/ssl/certs in Debian-based systems and the file is
-# /etc/pki/tls/cert.pem in RedHat-based systems.
-#ssl_client_ca_dir =
+# when Dovecot needs to act as an SSL client (e.g. imapc backend or
+# submission service). The directory is usually /etc/ssl/certs in
+# Debian-based systems and the file is /etc/pki/tls/cert.pem in
+# RedHat-based systems.
+ssl_client_ca_dir = /etc/ssl/certs
#ssl_client_ca_file =
# Request client to send a certificate. If you also want to require it, set
@@ -42,14 +43,23 @@ ssl_key = </etc/dovecot/ssl/imap.fripost.org.key
# auth_ssl_username_from_cert=yes.
#ssl_cert_username_field = commonName
-# DH parameters length to use.
-ssl_dh_parameters_length = 2048
+# SSL DH parameters
+# Generate new params with `openssl dhparam -out /etc/dovecot/dh.pem 4096`
+# Or migrate from old ssl-parameters.dat file with the command dovecot
+# gives on startup when ssl_dh is unset.
+ssl_dh = </etc/ssl/dhparams.pem
-# SSL protocols to use
-#ssl_protocols = !SSLv3
+# Minimum SSL protocol version to use. Potentially recognized values are SSLv3,
+# TLSv1, TLSv1.1, and TLSv1.2, depending on the OpenSSL version used.
+ssl_min_protocol = TLSv1.2
# SSL ciphers to use
-ssl_cipher_list = HIGH:!aNULL:!eNULL:!3DES:!MD5:@STRENGTH
+ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
+
+# Colon separated list of elliptic curves to use. Empty value (the default)
+# means use the defaults from the SSL library. P-521:P-384:P-256 would be an
+# example of a valid value.
+#ssl_curve_list =
# Prefer the server's order of ciphers over client's.
#ssl_prefer_server_ciphers = no
@@ -58,5 +68,6 @@ ssl_cipher_list = HIGH:!aNULL:!eNULL:!3DES:!MD5:@STRENGTH
#ssl_crypto_device =
# SSL extra options. Currently supported options are:
-# no_compression - Disable compression.
-ssl_options = no_compression
+# compression - Enable compression.
+# no_ticket - Disable SSL session tickets.
+#ssl_options =
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/15-lda.conf b/roles/IMAP/files/etc/dovecot/conf.d/15-lda.conf
index 2a7bd27..4bfd8a5 100644
--- a/roles/IMAP/files/etc/dovecot/conf.d/15-lda.conf
+++ b/roles/IMAP/files/etc/dovecot/conf.d/15-lda.conf
@@ -3,7 +3,7 @@
##
# Address to use when sending rejection mails.
-# Default is postmaster@<your domain>. %d expands to recipient domain.
+# Default is postmaster@%d. %d expands to recipient domain.
#postmaster_address =
# Hostname to use in various parts of sent mails (e.g. in Message-Id) and
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/20-imap.conf b/roles/IMAP/files/etc/dovecot/conf.d/20-imap.conf
index 3ddedce..de1fbbb 100644
--- a/roles/IMAP/files/etc/dovecot/conf.d/20-imap.conf
+++ b/roles/IMAP/files/etc/dovecot/conf.d/20-imap.conf
@@ -21,10 +21,17 @@ imap_hibernate_timeout = 15s
# %{fetch_body_count} - Number of mails with mail body data sent to client
# %{fetch_body_bytes} - Number of bytes with mail body data sent to client
# %{deleted} - Number of mails where client added \Deleted flag
-# %{expunged} - Number of mails that client expunged
+# %{expunged} - Number of mails that client expunged, which does not
+# include automatically expunged mails
+# %{autoexpunged} - Number of mails that were automatically expunged after
+# client disconnected
# %{trashed} - Number of mails that client copied/moved to the
# special_use=\Trash mailbox.
-#imap_logout_format = in=%i out=%o
+# %{appended} - Number of mails saved during the session
+#imap_logout_format = in=%i out=%o deleted=%{deleted} expunged=%{expunged} \
+# trashed=%{trashed} hdr_count=%{fetch_hdr_count} \
+# hdr_bytes=%{fetch_hdr_bytes} body_count=%{fetch_body_count} \
+# body_bytes=%{fetch_body_bytes}
# Override the IMAP CAPABILITY response. If the value begins with '+',
# add the given capabilities on top of the defaults (e.g. +XFOO XBAR).
@@ -65,9 +72,25 @@ imap_hibernate_timeout = 15s
# Host allowed in URLAUTH URLs sent by client. "*" allows all.
#imap_urlauth_host =
+# Enable IMAP LITERAL- extension (replaces LITERAL+)
+#imap_literal_minus = no
+
+# What happens when FETCH fails due to some internal error:
+# disconnect-immediately:
+# The FETCH is aborted immediately and the IMAP client is disconnected.
+# disconnect-after:
+# The FETCH runs for all the requested mails returning as much data as
+# possible. The client is finally disconnected without a tagged reply.
+# no-after:
+# Same as disconnect-after, but tagged NO reply is sent instead of
+# disconnecting the client. If the client attempts to FETCH the same failed
+# mail more than once, the client is disconnected. This is to avoid clients
+# from going into infinite loops trying to FETCH a broken mail.
+#imap_fetch_failure = disconnect-immediately
+
protocol imap {
# Space separated list of plugins to load (default is global mail_plugins).
- mail_plugins = $mail_plugins imap_stats imap_zlib
+ mail_plugins = $mail_plugins imap_zlib
# Maximum number of IMAP connections allowed for a user from each IP address.
# NOTE: The username is compared case-sensitively.
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/90-plugin.conf b/roles/IMAP/files/etc/dovecot/conf.d/90-plugin.conf
index 9583b6d..52a81ca 100644
--- a/roles/IMAP/files/etc/dovecot/conf.d/90-plugin.conf
+++ b/roles/IMAP/files/etc/dovecot/conf.d/90-plugin.conf
@@ -23,11 +23,6 @@ plugin {
quota = count:User quota
quota_vsizes = yes
- # how often to session statistics
- stats_refresh = 30 secs
- # track per-IMAP command statistics
- stats_track_cmds = yes
-
zlib_save = gz
zlib_save_level = 6
}
diff --git a/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext b/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext
index ecd7134..a054ffe 100644
--- a/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext
+++ b/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext
@@ -2,7 +2,7 @@
# conf.d/auth-dict.conf.ext
# Dictionary URI
-uri = proxy:/var/run/dovecot/auth-proxy:
+uri = proxy:/run/dovecot/auth-proxy:
# Username iteration prefix. Keys under this are assumed to contain usernames.
iterate_prefix = userdb/
diff --git a/roles/IMAP/files/etc/dovecot/dovecot-ldap.conf.ext b/roles/IMAP/files/etc/dovecot/dovecot-ldap.conf.ext
index 1b97a0e..a455616 100644
--- a/roles/IMAP/files/etc/dovecot/dovecot-ldap.conf.ext
+++ b/roles/IMAP/files/etc/dovecot/dovecot-ldap.conf.ext
@@ -130,7 +130,7 @@ pass_attrs =
#pass_attrs = uid=user,userPassword=password,\
# homeDirectory=userdb_home,uidNumber=userdb_uid,gidNumber=userdb_gid
-# Filter for password lookups (ignored for auth binds)
+# Filter for password lookups
pass_filter = (&(objectClass=FripostVirtualUser)(fvl=%n)(fripostIsStatusActive=TRUE))
# Attributes and filter to get a list of all users
@@ -140,3 +140,12 @@ pass_filter = (&(objectClass=FripostVirtualUser)(fvl=%n)(fripostIsStatusActive=T
# Default password scheme. "{scheme}" before password overrides this.
# List of supported schemes is in: http://wiki2.dovecot.org/Authentication
#default_pass_scheme = CRYPT
+
+# By default all LDAP lookups are performed by the auth master process.
+# If blocking=yes, auth worker processes are used to perform the lookups.
+# Each auth worker process creates its own LDAP connection so this can
+# increase parallelism. With blocking=no the auth master process can
+# keep 8 requests pipelined for the LDAP connection, while with blocking=yes
+# each connection has a maximum of 1 request running. For small systems the
+# blocking=no is sufficient and uses less resources.
+#blocking = no
diff --git a/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service b/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service
index d20f9c2..3ac0b31 100644
--- a/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service
+++ b/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service
@@ -4,8 +4,7 @@ After=dovecot.target
Requires=dovecot-auth-proxy.socket
[Service]
-User=vmail
-Group=vmail
+User=_dovecot-auth-proxy
StandardInput=null
SyslogFacility=mail
ExecStart=/usr/local/bin/dovecot-auth-proxy.pl
@@ -13,14 +12,13 @@ ExecStart=/usr/local/bin/dovecot-auth-proxy.pl
# Hardening
NoNewPrivileges=yes
PrivateDevices=yes
-ProtectSystem=strict
-ProtectHome=read-only
-PrivateDevices=yes
PrivateNetwork=yes
+ProtectHome=yes
+ProtectSystem=strict
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
-RestrictAddressFamilies=
+RestrictAddressFamilies=AF_UNIX
[Install]
WantedBy=multi-user.target
diff --git a/roles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl b/roles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl
index 5b2c74e..39d3762 100755
--- a/roles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl
+++ b/roles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl
@@ -1,8 +1,8 @@
-#!/usr/bin/perl
+#!/usr/bin/perl -T
#----------------------------------------------------------------------
# Dovecot userdb lookup proxy table for user iteration
-# Copyright © 2017 Guilhem Moulin <guilhem@fripost.org>
+# Copyright © 2017,2020 Guilhem Moulin <guilhem@fripost.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,14 +21,22 @@
use warnings;
use strict;
-use Errno 'EINTR';
+use Errno qw/EINTR/;
+use Net::LDAPI;
+use Net::LDAP::Constant qw/LDAP_CONTROL_PAGED LDAP_SUCCESS/;
+use Net::LDAP::Control::Paged ();
+use Net::LDAP::Util qw/ldap_explode_dn/;
+use Authen::SASL;
+
+my $BASE = "ou=virtual,dc=fripost,dc=org";
# clean up PATH
$ENV{PATH} = join ':', qw{/usr/bin /bin};
delete @ENV{qw/IFS CDPATH ENV BASH_ENV/};
-# number of pre-forked servers
+# number of pre-forked servers and maximum requests per worker
my $nProc = 1;
+my $maxRequests = 1;
sub server();
# fdopen(3) the file descriptor FD
@@ -37,12 +45,6 @@ die "This service must be socket-activated.\n"
and defined $ENV{LISTEN_FDS} and $ENV{LISTEN_FDS} == 1;
open my $S, '+<&=', 3 or die "fdopen: $!";
-do {
- my $dir = (getpwnam('vmail'))[7] // die "No such user: vmail";
- $dir .= '/virtual';
- chdir($dir) or die "chdir($dir): $!";
-};
-
my @CHILDREN;
for (my $i = 0; $i < $nProc-1; $i++) {
my $pid = fork() // die "fork: $!";
@@ -61,7 +63,7 @@ exit $?;
#############################################################################
sub server() {
- for (my $n = 0; $n < 1; $n++) {
+ for (my $n = 0; $n < $maxRequests; $n++) {
accept(my $conn, $S) or do {
next if $! == EINTR;
die "accept: $!";
@@ -74,7 +76,7 @@ sub server() {
next;
}
# <major-version> <minor-version> <value type>
- unless ($1 == 2 and $2 == 1 and $3 == 0) {
+ unless ($1 == 2 and $2 == 2 and $3 == 0) {
warn "Unsupported protocol version $1.$2 (or value type $3)\n";
close $conn or warn "Can't close: $!";
next;
@@ -94,10 +96,20 @@ sub server() {
sub fail($;$) {
my ($fh, $msg) = @_;
$fh->printflush("F\n");
- warn "$msg\n" if defined $msg;
+ print STDERR $msg, "\n" if defined $msg;
}
-# list all users, even the inactive ones
+sub dn2user($) {
+ my $dn = shift;
+ $dn = ldap_explode_dn($dn, casefold => "lower");
+ if (defined $dn and $#$dn == 4
+ and defined (my $l = $dn->[0]->{fvl})
+ and defined (my $d = $dn->[1]->{fvd})) {
+ return $l ."@". $d;
+ }
+}
+
+# list all users (even the inactive ones)
sub iterate($$$$) {
my ($fh, $flags, $max_rows, $prefix) = @_;
unless ($flags == 0) {
@@ -105,27 +117,55 @@ sub iterate($$$$) {
return;
}
- opendir my $dh, '.' or do {
- fail($fh => "opendir: $!");
- return;
- };
- my $count = 0;
- while (defined (my $d = readdir $dh)) {
- next if $d eq '.' or $d eq '..';
- opendir my $dh, $d or do {
- fail($fh => "opendir: $!");
- return;
- };
- while (defined (my $l = readdir $dh) and ($max_rows <= 0 or $count < $max_rows)) {
- next if $l eq '.' or $l eq '..';
- my $user = $l.'@'.$d;
- next unless $user =~ /\A[a-zA-Z0-9\.\-_@]+\z/; # skip invalid user names
+ my $ldap = Net::LDAPI::->new();
+ $ldap->bind( undef, sasl => Authen::SASL::->new(mechanism => "EXTERNAL") )
+ or do { fail($fh => "Error: Couldn't bind"); return; };
+ my $page = Net::LDAP::Control::Paged::->new(size => 100);
+
+ my $callback = sub($$) {
+ my ($mesg, $entry) = @_;
+ return unless defined $entry;
+
+ my $dn = $entry->dn();
+ if (defined (my $user = dn2user($dn))) {
$fh->printf("O%s%s\t\n", $prefix, $user);
- $count++;
+ } else {
+ print STDERR "Couldn't extract username from dn: ", $dn, "\n";
}
- closedir $dh or warn "closedir: $!";
+ $mesg->pop_entry;
+ };
+
+ my @search_args = (
+ base => $BASE,
+ , scope => "children"
+ , deref => "never"
+ , filter => "(objectClass=FripostVirtualUser)"
+ , sizelimit => $max_rows
+ , control => [$page]
+ , callback => $callback
+ , attrs => ["1.1"]
+ );
+
+ my $cookie;
+ while (1) {
+ my $mesg = $ldap->search(@search_args);
+ last unless $mesg->code == LDAP_SUCCESS;
+
+ my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last;
+ $cookie = $resp->cookie();
+ goto SEARCH_DONE unless defined $cookie and length($cookie) > 0;
+
+ $page->cookie($cookie);
+ }
+
+ if (defined $cookie and length($cookie) > 0) {
+ fail($fh => "Abnormal exit from LDAP search, aborting");
+ $page->cookie($cookie);
+ $page->size(0);
+ $ldap->search(@search_args);
}
- closedir $dh or warn "closedir: $!";
+ SEARCH_DONE:
+ $ldap->unbind();
$fh->printflush("\n");
}
diff --git a/roles/IMAP/files/usr/local/bin/list-users.pl b/roles/IMAP/files/usr/local/bin/list-users.pl
deleted file mode 100755
index 1bcab35..0000000
--- a/roles/IMAP/files/usr/local/bin/list-users.pl
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/perl
-
-# Copyright © 2017 Guilhem Moulin <guilhem@fripost.org>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-use warnings;
-use strict;
-use Net::LDAPI;
-use Net::LDAP::Util qw/ldap_explode_dn escape_dn_value/;
-use Authen::SASL;
-
-my $BASE = 'ou=virtual,dc=fripost,dc=org';
-
-my $LDAP = Net::LDAPI::->new();
-$LDAP->bind( undef, sasl => Authen::SASL::->new(mechanism => 'EXTERNAL') )
- or die "Error: Couldn't bind";
-
-my $mesg = $LDAP->search( base => $BASE, scope => 'children', deref => 'never'
- , filter => '(objectClass=FripostVirtualUser)'
- , attrs => ['1.1']
- );
-die $mesg->error if $mesg->code;
-
-while (defined (my $entry = $mesg->pop_entry())) {
- my $dn = $entry->dn() // next;
- $dn = ldap_explode_dn($dn, casefold => 'lower');
- next unless defined $dn and $#$dn == 4;
- my $l = $dn->[0]->{fvl} // next;
- my $d = $dn->[1]->{fvd} // next;
- printf "%s@%s\n", $l, $d;
-}
-
-$LDAP->unbind;
diff --git a/roles/IMAP/tasks/imap.yml b/roles/IMAP/tasks/imap.yml
index 231c759..4a157af 100644
--- a/roles/IMAP/tasks/imap.yml
+++ b/roles/IMAP/tasks/imap.yml
@@ -18,6 +18,13 @@
password=!
state=present
+- name: Install Net::LDAP and Authen::SASL
+ apt: pkg={{ packages }}
+ vars:
+ packages:
+ - libnet-ldap-perl
+ - libauthen-sasl-perl
+
- name: Copy dovecot auth proxy
copy: src=usr/local/bin/dovecot-auth-proxy.pl
dest=/usr/local/bin/dovecot-auth-proxy.pl
@@ -30,6 +37,15 @@
tags:
- sysctl
+- name: Create '_dovecot-auth-proxy' user
+ user: name=_dovecot-auth-proxy system=yes
+ group=nogroup
+ createhome=no
+ home=/nonexistent
+ shell=/usr/sbin/nologin
+ password=!
+ state=present
+
- name: Copy dovecot auth proxy systemd unit files
copy: src=etc/systemd/system/{{ item }}
dest=/etc/systemd/system/{{ item }}
@@ -123,7 +139,6 @@
register: r1
with_items:
- conf.d/10-auth.conf
- - conf.d/10-logging.conf
- conf.d/10-mail.conf
- conf.d/10-ssl.conf
- conf.d/15-mailboxes.conf
diff --git a/roles/IMAP/tasks/mda.yml b/roles/IMAP/tasks/mda.yml
index f705fe7..0e8690d 100644
--- a/roles/IMAP/tasks/mda.yml
+++ b/roles/IMAP/tasks/mda.yml
@@ -3,7 +3,7 @@
vars:
packages:
- postfix
- - postfix-ldap
+ - postfix-lmdb
- name: Configure Postfix
template: src=etc/postfix/{{ item }}.j2
diff --git a/roles/IMAP/tasks/spam.yml b/roles/IMAP/tasks/spam.yml
index c275b55..d70ccc9 100644
--- a/roles/IMAP/tasks/spam.yml
+++ b/roles/IMAP/tasks/spam.yml
@@ -63,8 +63,8 @@
- name: Create a 'amavis' SQL user
# This *must* be the user we run spamd as
# See https://svn.apache.org/repos/asf/spamassassin/trunk/sql/README.bayes
- mysql_user2: >
- name=amavis password= auth_plugin=auth_socket
+ mysql_user: >
+ name=amavis password= plugin=auth_socket
priv="spamassassin.awl: SELECT,INSERT,UPDATE,DELETE
/spamassassin.bayes_seen: SELECT,INSERT, DELETE
/spamassassin.bayes_token: SELECT,INSERT,UPDATE,DELETE
diff --git a/roles/IMAP/templates/etc/dovecot/conf.d/10-master.conf.j2 b/roles/IMAP/templates/etc/dovecot/conf.d/10-master.conf.j2
index 8eef8a1..d61c11b 100644
--- a/roles/IMAP/templates/etc/dovecot/conf.d/10-master.conf.j2
+++ b/roles/IMAP/templates/etc/dovecot/conf.d/10-master.conf.j2
@@ -4,7 +4,7 @@
# Default VSZ (virtual memory size) limit for service processes. This is mainly
# intended to catch and kill processes that leak memory before they eat up
# everything.
-default_vsz_limit = 512M
+default_vsz_limit = 1024M
# Login user is internally used by login processes. This is the most untrusted
# user in Dovecot system. It shouldn't have access to anything at all.
@@ -53,6 +53,19 @@ service pop3-login {
}
}
+service stats {
+ unix_listener stats-writer {
+ user = vmail
+ mode = 0600
+ }
+}
+
+service submission-login {
+ inet_listener submission {
+ port = 0
+ }
+}
+
service lmtp {
user = vmail
@@ -88,6 +101,8 @@ service imap {
service imap-hibernate {
unix_listener imap-hibernate {
+ # Match user running imap processes, cf.
+ # https://dovecot.org/pipermail/dovecot/2015-August/101783.html
user = vmail
mode = 0600
}
@@ -98,6 +113,11 @@ service pop3 {
#process_limit = 1024
}
+service submission {
+ # Max. number of SMTP Submission processes (connections)
+ #process_limit = 1024
+}
+
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Users that have
@@ -120,6 +140,7 @@ service auth {
# Postfix smtp-auth
unix_listener /var/spool/postfix-{{ postfix_instance.MSA.name }}/private/dovecot-auth {
user = postfix
+ group = postfix
mode = 0600
}
@@ -143,10 +164,3 @@ service dict {
#group =
}
}
-
-service stats {
- fifo_listener stats-mail {
- user = vmail
- mode = 0600
- }
-}
diff --git a/roles/IMAP/templates/etc/dovecot/conf.d/15-lda.conf b/roles/IMAP/templates/etc/dovecot/conf.d/15-lda.conf
deleted file mode 100644
index e69de29..0000000
--- a/roles/IMAP/templates/etc/dovecot/conf.d/15-lda.conf
+++ /dev/null
diff --git a/roles/IMAP/templates/etc/postfix/main.cf.j2 b/roles/IMAP/templates/etc/postfix/main.cf.j2
index 2105d29..64a2a40 100644
--- a/roles/IMAP/templates/etc/postfix/main.cf.j2
+++ b/roles/IMAP/templates/etc/postfix/main.cf.j2
@@ -21,7 +21,7 @@ append_dot_mydomain = no
mynetworks = 127.0.0.0/8, [::1]/128
{%- if groups.all | length > 1 -%}
{%- for mx in groups.MX | sort -%}
- , {{ ipsec[ hostvars[mx].inventory_hostname_short ] | ipaddr }}
+ , {{ ipsec[ hostvars[mx].inventory_hostname_short ] | ansible.utils.ipaddr }}
{%- endfor %}
{% endif %}
diff --git a/roles/LDAP-provider/tasks/main.yml b/roles/LDAP-provider/tasks/main.yml
index af46c51..9bc227e 100644
--- a/roles/LDAP-provider/tasks/main.yml
+++ b/roles/LDAP-provider/tasks/main.yml
@@ -4,6 +4,9 @@
target=etc/ldap/syncprov.ldif
local=file
+## XXX should be /etc/sasl2/slapd.conf ideally, but it doesn't work with
+## Stretch, cf #211156 and #798462:
+## ldapsearch -LLLx -H ldapi:// -b "" -s base supportedSASLMechanisms
- name: Enable the EXTERNAL SASL mechanism
lineinfile: dest=/usr/lib/sasl2/slapd.conf
regexp='^mech_list{{':'}}'
diff --git a/roles/MSA/files/etc/postfix/anonymize_sender.pcre b/roles/MSA/files/etc/postfix/anonymize_sender.pcre
index 7c11f4e..b91b981 100644
--- a/roles/MSA/files/etc/postfix/anonymize_sender.pcre
+++ b/roles/MSA/files/etc/postfix/anonymize_sender.pcre
@@ -1,5 +1,6 @@
/^Received:\s+from\s+(?:\S+\s+\(\S+\s+\[(?:IPv6:)?[[:xdigit:].:]{3,39}\]\))
- (\s+\(using\s+(?:TLS|SSL)(?:v\S+)?\s+with\s+cipher\s+\S+\s+\(\S+\s+bits\)\)\s+).*
+ (\s+\(using\s+(?:TLS|SSL)(?:v\S+)?\s+with\s+cipher\s+\S+\s+\(\S+\s+bits\)
+ (?:\s+key-exchange\s+\S+\s+(?:\([^)]+\)\s+)?server-signature\s+\S+\s+\(\d+\s+bits\)(?:\s+server-[[:alnum:]]+\s+\S+)*)?\)\s+).*
(\bby\s+(?:\S+\.)?fripost\.org\s+\([^)]+\)
\s+with\s+E?SMTPS?A\s+id\s+[[:xdigit:]]+;?\s.*)/x
REPLACE Received: from [127.0.0.1] (localhost [127.0.0.1])${1}${2}
diff --git a/roles/MSA/files/etc/systemd/system/postfix-sender-login.service b/roles/MSA/files/etc/systemd/system/postfix-sender-login.service
index f5e6d89..d652f75 100644
--- a/roles/MSA/files/etc/systemd/system/postfix-sender-login.service
+++ b/roles/MSA/files/etc/systemd/system/postfix-sender-login.service
@@ -4,8 +4,7 @@ After=mail-transport-agent.target
Requires=postfix-sender-login.socket
[Service]
-User=postfix
-Group=postfix
+User=_postfix-sender-login
StandardInput=null
SyslogFacility=mail
ExecStart=/usr/local/bin/postfix-sender-login.pl
@@ -13,10 +12,9 @@ ExecStart=/usr/local/bin/postfix-sender-login.pl
# Hardening
NoNewPrivileges=yes
PrivateDevices=yes
+PrivateNetwork=yes
ProtectHome=yes
ProtectSystem=strict
-PrivateDevices=yes
-PrivateNetwork=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
diff --git a/roles/MSA/files/usr/local/bin/postfix-sender-login.pl b/roles/MSA/files/usr/local/bin/postfix-sender-login.pl
index 374cc70..a37f872 100755
--- a/roles/MSA/files/usr/local/bin/postfix-sender-login.pl
+++ b/roles/MSA/files/usr/local/bin/postfix-sender-login.pl
@@ -3,7 +3,7 @@
#----------------------------------------------------------------------
# socketmap lookup table returning the SASL login name(s) owning a given
# sender address
-# Copyright © 2017 Guilhem Moulin <guilhem@fripost.org>
+# Copyright © 2017,2020 Guilhem Moulin <guilhem@fripost.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -33,12 +33,13 @@ use Authen::SASL ();
$ENV{PATH} = join ':', qw{/usr/bin /bin};
delete @ENV{qw/IFS CDPATH ENV BASH_ENV/};
-my $nProc = 2; # number of pre-forked servers
-my $POSTMASTER = 'postmaster@fripost.org'; # returned for forbidden envelope sender addresses
+my $nProc = 2; # number of pre-forked servers
+my $maxRequests = 32; # maximum number of requests per worker
+my $POSTMASTER = 'postmaster@fripost.org'; # returned for forbidden envelope sender addresses
-my $BASEDN = 'ou=virtual,dc=fripost,dc=org';
+my $BASEDN = "ou=virtual,dc=fripost,dc=org";
my $BUFSIZE = 65536; # try to read that many bytes at the time
-my $LDAPI = 'ldapi://%2Fvar%2Fspool%2Fpostfix-msa%2Fprivate%2Fldapi/';
+my $LDAPI = "ldapi://";
sub server();
@@ -66,7 +67,7 @@ exit $?;
#############################################################################
sub server() {
- for (my $n = 0; $n < 32; $n++) {
+ for (my $n = 0; $n < $maxRequests; $n++) {
accept(my $conn, $S) or do {
next if $! == EINTR;
die "accept: $!";
diff --git a/roles/MSA/tasks/main.yml b/roles/MSA/tasks/main.yml
index c78139a..bf17702 100644
--- a/roles/MSA/tasks/main.yml
+++ b/roles/MSA/tasks/main.yml
@@ -3,15 +3,32 @@
vars:
packages:
- postfix
+ - postfix-lmdb
- postfix-pcre
- postfix-policyd-spf-python
+- name: Install Net::LDAP and Authen::SASL
+ apt: pkg={{ packages }}
+ vars:
+ packages:
+ - libnet-ldap-perl
+ - libauthen-sasl-perl
+
- name: Copy Postfix sender login socketmap
copy: src=usr/local/bin/postfix-sender-login.pl
dest=/usr/local/bin/postfix-sender-login.pl
owner=root group=staff
mode=0755
+- name: Create '_postfix-sender-login' user
+ user: name=_postfix-sender-login system=yes
+ group=nogroup
+ createhome=no
+ home=/nonexistent
+ shell=/usr/sbin/nologin
+ password=!
+ state=present
+
- name: Copy Postfix sender login socketmap systemd unit files
copy: src=etc/systemd/system/{{ item }}
dest=/etc/systemd/system/{{ item }}
@@ -23,6 +40,19 @@
notify:
- systemctl daemon-reload
+- name: Copy the SMTP TLS policy maps
+ template: src=etc/postfix/smtp_tls_policy.j2
+ dest=/etc/postfix-{{ postfix_instance[inst].name }}/smtp_tls_policy
+ owner=root group=root
+ mode=0644
+
+- name: Compile the SMTP TLS policy maps
+ postmap: cmd=postmap src=/etc/postfix-{{ postfix_instance[inst].name }}/smtp_tls_policy db=lmdb
+ owner=root group=root
+ mode=0644
+ notify:
+ - Reload Postfix
+
- meta: flush_handlers
- name: Enable Postfix sender login socketmap
diff --git a/roles/MSA/templates/etc/postfix/main.cf.j2 b/roles/MSA/templates/etc/postfix/main.cf.j2
index 65a0339..bc98d9e 100644
--- a/roles/MSA/templates/etc/postfix/main.cf.j2
+++ b/roles/MSA/templates/etc/postfix/main.cf.j2
@@ -20,7 +20,7 @@ append_dot_mydomain = no
mynetworks = 127.0.0.0/8, [::1]/128
{%- for h in groups.webmail | difference([inventory_hostname]) | sort -%}
- , {{ ipsec[ hostvars[h].inventory_hostname_short ] | ipaddr }}
+ , {{ ipsec[ hostvars[h].inventory_hostname_short ] | ansible.utils.ipaddr }}
{% endfor %}
queue_directory = /var/spool/postfix-{{ postfix_instance[inst].name }}
@@ -40,7 +40,7 @@ message_size_limit = 67108864
recipient_delimiter = +
# Forward everything to our internal outgoing proxy
-relayhost = [{{ postfix_instance.out.addr | ipaddr }}]:{{ postfix_instance.out.port }}
+relayhost = [{{ postfix_instance.out.addr | ansible.utils.ipaddr }}]:{{ postfix_instance.out.port }}
relay_domains =
@@ -60,14 +60,14 @@ header_checks = pcre:$config_directory/anonymize_sender.pcre
# TLS
smtp_tls_security_level = none
smtpd_tls_security_level = encrypt
-smtpd_tls_ciphers = high
-smtpd_tls_protocols = !SSLv2, !SSLv3
-smtpd_tls_exclude_ciphers = EXPORT, LOW, MEDIUM, aNULL, eNULL, DES, RC4, MD5
+smtpd_tls_mandatory_ciphers = high
+smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_cert_file = $config_directory/ssl/smtp.fripost.org.pem
smtpd_tls_key_file = $config_directory/ssl/smtp.fripost.org.key
smtpd_tls_dh1024_param_file = /etc/ssl/dhparams.pem
smtpd_tls_session_cache_database=
smtpd_tls_received_header = yes
+tls_high_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
# SASL
smtpd_sasl_auth_enable = yes
diff --git a/roles/MSA/templates/etc/postfix/smtp_tls_policy.j2 b/roles/MSA/templates/etc/postfix/smtp_tls_policy.j2
new file mode 120000
index 0000000..b40876f
--- /dev/null
+++ b/roles/MSA/templates/etc/postfix/smtp_tls_policy.j2
@@ -0,0 +1 @@
+../../../../out/templates/etc/postfix/smtp_tls_policy.j2 \ No newline at end of file
diff --git a/roles/MX/files/etc/opendmarc.conf b/roles/MX/files/etc/opendmarc.conf
new file mode 100644
index 0000000..575d02d
--- /dev/null
+++ b/roles/MX/files/etc/opendmarc.conf
@@ -0,0 +1,116 @@
+# This is a basic configuration that can easily be adapted to suit a standard
+# installation. For more advanced options, see openmarc.conf(5) and/or
+# /usr/share/doc/opendmarc/examples/opendmarc.conf.sample.
+
+## AuthservID (string)
+## defaults to MTA name
+##
+## Sets the "authserv-id" to use when generating the Authentication-Results:
+## header field after verifying a message. If the string "HOSTNAME" is
+## provided, the name of the host running the filter (as returned by the
+## gethostname(3) function) will be used.
+#
+# AuthservID name
+
+## FailureReports { true | false }
+## default "false"
+##
+## Enables generation of failure reports when the DMARC test fails and the
+## purported sender of the message has requested such reports. Reports are
+## formatted per RFC6591.
+#
+# FailureReports false
+
+## PublicSuffixList path
+## default (none)
+##
+## Specifies the path to a file that contains top-level domains (TLDs) that
+## will be used to compute the Organizational Domain for a given domain name,
+## as described in the DMARC specification. If not provided, the filter will
+## not be able to determine the Organizational Domain and only the presented
+## domain will be evaluated.
+#
+PublicSuffixList /usr/share/publicsuffix
+
+## RejectFailures { true | false }
+## default "false"
+##
+## If set, messages will be rejected if they fail the DMARC evaluation, or
+## temp-failed if evaluation could not be completed. By default, no message
+## will be rejected or temp-failed regardless of the outcome of the DMARC
+## evaluation of the message. Instead, an Authentication-Results header
+## field will be added.
+#
+RejectFailures false
+
+## Socket socketspec
+## default (none)
+##
+## Specifies the socket that should be established by the filter to receive
+## connections from sendmail(8) in order to provide service. socketspec is
+## in one of two forms: local:path, which creates a UNIX domain socket at
+## the specified path, or inet:port[@host] or inet6:port[@host] which creates
+## a TCP socket on the specified port for the appropriate protocol family.
+## If the host is not given as either a hostname or an IP address, the
+## socket will be listening on all interfaces. This option is mandatory
+## either in the configuration file or on the command line. If an IP
+## address is used, it must be enclosed in square brackets.
+#
+Socket local:/var/run/opendmarc/opendmarc.sock
+
+## Syslog { true | false }
+## default "false"
+##
+## Log via calls to syslog(3) any interesting activity.
+#
+Syslog true
+
+## SyslogFacility facility-name
+## default "mail"
+##
+## Log via calls to syslog(3) using the named facility. The facility names
+## are the same as the ones allowed in syslog.conf(5).
+#
+# SyslogFacility mail
+
+## TrustedAuthservIDs string
+## default HOSTNAME
+##
+## Specifies one or more "authserv-id" values to trust as relaying true
+## upstream DKIM and SPF results. The default is to use the name of
+## the MTA processing the message. To specify a list, separate each entry
+## with a comma. The key word "HOSTNAME" will be replaced by the name of
+## the host running the filter as reported by the gethostname(3) function.
+#
+# TrustedAuthservIDs HOSTNAME
+
+## SPFIgnoreResults { true | false }
+## default "false"
+##
+## Causes the filter to ignore any SPF results in the header of the message.
+## This is useful if you want the filter to perfrom SPF checks itself, or
+## because you don't trust the arriving header.
+#
+SPFIgnoreResults true
+
+## SPFSelfValidate { true | false }
+## default "false"
+##
+## Causes the filter to perform a fallback SPF check itself when it can
+## find no SPF results in the message header. If SPFIgnoreResults is also
+## set, it never looks for SPF results in headers and always performs the
+## SPF check itself when this is set.
+#
+SPFSelfValidate true
+
+## UMask mask
+## default (none)
+##
+## Requests a specific permissions mask to be used for file creation. This
+## only really applies to creation of the socket when Socket specifies a
+## UNIX domain socket, and to the HistoryFile and PidFile (if any); temporary
+## files are normally created by the mkstemp(3) function that enforces a
+## specific file mode on creation regardless of the process umask. See
+## umask(2) for more information.
+#
+UMask 0007
diff --git a/roles/MX/files/etc/systemd/system/opendmarc.service.d/override.conf b/roles/MX/files/etc/systemd/system/opendmarc.service.d/override.conf
new file mode 100644
index 0000000..1fb5567
--- /dev/null
+++ b/roles/MX/files/etc/systemd/system/opendmarc.service.d/override.conf
@@ -0,0 +1,17 @@
+[Service]
+Type=simple
+User=opendmarc
+ExecStart=
+ExecStart=/usr/sbin/opendmarc -f -p fd:3
+StandardOutput=journal
+SyslogFacility=mail
+RuntimeDirectory=opendmarc
+
+# Hardening
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectHome=yes
+ProtectSystem=strict
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
diff --git a/roles/MX/files/etc/systemd/system/opendmarc.socket b/roles/MX/files/etc/systemd/system/opendmarc.socket
new file mode 100644
index 0000000..483ef60
--- /dev/null
+++ b/roles/MX/files/etc/systemd/system/opendmarc.socket
@@ -0,0 +1,10 @@
+[Unit]
+Description=OpenDMARC Milter activation socket
+
+[Socket]
+ListenStream=/var/spool/postfix-mx/public/opendmarc
+SocketUser=postfix
+SocketMode=0666
+
+[Install]
+WantedBy=sockets.target
diff --git a/roles/MX/handlers/main.yml b/roles/MX/handlers/main.yml
index 9edf610..00223a5 100644
--- a/roles/MX/handlers/main.yml
+++ b/roles/MX/handlers/main.yml
@@ -1,6 +1,15 @@
---
+- name: systemctl daemon-reload
+ command: /bin/systemctl daemon-reload
+
- name: Reload Postfix
service: name=postfix state=reloaded
- name: Restart munin-node
service: name=munin-node state=restarted
+
+- name: Stop OpenDMARC
+ service: name=opendmarc.service state=stopped
+
+- name: Restart OpenDMARC
+ service: name=opendmarc.socket state=restarted
diff --git a/roles/MX/tasks/main.yml b/roles/MX/tasks/main.yml
index 507a4f2..57f17f8 100644
--- a/roles/MX/tasks/main.yml
+++ b/roles/MX/tasks/main.yml
@@ -137,3 +137,48 @@
- munin-node
notify:
- Restart munin-node
+
+# XXX we probaly want SPF verification for domains without DMARC
+# policies
+- name: Install OpenDMARC
+ apt: pkg=opendmarc
+
+- name: Copy OpenDMARC configuration
+ copy: src=etc/opendmarc.conf
+ dest=/etc/opendmarc.conf
+ owner=root group=root
+ mode=0644
+ notify:
+ - Stop OpenDMARC
+
+- name: Create directory /etc/systemd/system/opendmarc.service.d
+ file: path=/etc/systemd/system/opendmarc.service.d
+ state=directory
+ owner=root group=root
+ mode=0755
+
+- name: Harden OpenDMARC service unit
+ copy: src=etc/systemd/system/opendmarc.service.d/override.conf
+ dest=/etc/systemd/system/opendmarc.service.d/override.conf
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Stop OpenDMARC
+
+- meta: flush_handlers
+
+- name: Copy OpenDMARC socket unit
+ copy: src=etc/systemd/system/opendmarc.socket
+ dest=/etc/systemd/system/opendmarc.socket
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Restart OpenDMARC
+
+- name: Disable OpenDMARC service
+ service: name=opendmarc.service enabled=false
+
+- name: Start OpenDMARC socket
+ service: name=opendmarc.socket state=started enabled=true
diff --git a/roles/MX/templates/etc/postfix/main.cf.j2 b/roles/MX/templates/etc/postfix/main.cf.j2
index a2cc2a8..d10f901 100644
--- a/roles/MX/templates/etc/postfix/main.cf.j2
+++ b/roles/MX/templates/etc/postfix/main.cf.j2
@@ -37,7 +37,7 @@ message_size_limit = 67108864
recipient_delimiter = +
# Forward everything to our internal outgoing proxy
-relayhost = [{{ postfix_instance.out.addr | ipaddr }}]:{{ postfix_instance.out.port }}
+relayhost = [{{ postfix_instance.out.addr | ansible.utils.ipaddr }}]:{{ postfix_instance.out.port }}
relay_domains =
@@ -123,6 +123,7 @@ postscreen_dnsbl_sites =
postscreen_greet_action = enforce
postscreen_whitelist_interfaces = static:all
+smtpd_milters = { unix:public/opendmarc, protocol=6, default_action=accept }
smtpd_client_restrictions =
permit_mynetworks
@@ -148,10 +149,6 @@ smtpd_recipient_restrictions =
check_recipient_access ldap:$config_directory/reject-unknown-client-hostname.cf
reject_rhsbl_reverse_client dbl.spamhaus.org=127.0.1.[2..99]
reject_rhsbl_sender dbl.spamhaus.org=127.0.1.[2..99]
- # defer if "abused legit": DBL return code in the 127.0.1.100+ range
- defer_if_reject
- reject_rhsbl_reverse_client dbl.spamhaus.org=127.0.1.[100..254]
- reject_rhsbl_sender dbl.spamhaus.org=127.0.1.[100..254]
smtpd_data_restrictions =
reject_unauth_pipelining
diff --git a/roles/MX/templates/etc/postfix/virtual/transport.j2 b/roles/MX/templates/etc/postfix/virtual/transport.j2
index 126cb72..536748a 100644
--- a/roles/MX/templates/etc/postfix/virtual/transport.j2
+++ b/roles/MX/templates/etc/postfix/virtual/transport.j2
@@ -17,5 +17,5 @@
reserved.fripost.org reserved-alias:
discard.fripost.org discard:
-mda.fripost.org smtp:[{{ postfix_instance.IMAP.addr | ipaddr }}]:{{ postfix_instance.IMAP.port }}
-sympa.fripost.org smtp:[{{ postfix_instance.lists.addr | ipaddr }}]:{{ postfix_instance.lists.port }}
+mda.fripost.org smtp:[{{ postfix_instance.IMAP.addr | ansible.utils.ipaddr }}]:{{ postfix_instance.IMAP.port }}
+sympa.fripost.org smtp:[{{ postfix_instance.lists.addr | ansible.utils.ipaddr }}]:{{ postfix_instance.lists.port }}
diff --git a/roles/amavis/tasks/main.yml b/roles/amavis/tasks/main.yml
index 3036c52..79c973c 100644
--- a/roles/amavis/tasks/main.yml
+++ b/roles/amavis/tasks/main.yml
@@ -17,8 +17,6 @@
- unrar-free
- arj
- nomarch
- - zoo
- - ripole
- cabextract
- unar
- tnef
@@ -58,8 +56,6 @@
changed_when: dkim.rc == 0
failed_when: dkim.rc > 1
when: "'out' in group_names"
- notify:
- - Restart Amavis
tags:
- genkey
- dkim
diff --git a/roles/amavis/templates/etc/amavis/conf.d/50-user.j2 b/roles/amavis/templates/etc/amavis/conf.d/50-user.j2
index a09c366..8563a8c 100644
--- a/roles/amavis/templates/etc/amavis/conf.d/50-user.j2
+++ b/roles/amavis/templates/etc/amavis/conf.d/50-user.j2
@@ -33,12 +33,13 @@ $enable_dkim_signing = 1;
# Sign *all* outgoing mails with *our* key (yes, amavis complains, but this is
# safe as we force our domain with the 'd' tag).
{% for x,k in dkim_keys[inventory_hostname_short] | default({}) | dictsort() -%}
-dkim_key({{ (x == "~") | ternary('qr/./', "'"+x+"'") }}, '{{ k.s }}', '/etc/amavis/dkim/{{ k.s }}:{{ k.d }}.pem');
+dkim_key({{ (x == "~") | ternary('qr/./', "'"+(x | regex_replace('^.*@',''))+"'") }}, '{{ k.s }}', '/etc/amavis/dkim/{{ k.s }}:{{ k.d }}.pem');
{% endfor -%}
@dkim_signature_options_bysender_maps = (
{% for x,k in dkim_keys[inventory_hostname_short] | default({}) | dictsort() %}
{ '{{ (x == "~") | ternary('.', x) }}' => {
d => '{{ k.d }}'
+ , s => '{{ k.s }}'
, a => 'rsa-sha256'
, ttl => 21*24*3600
, c => 'relaxed/simple' }
diff --git a/roles/bacula-dir/files/etc/systemd/system/bacula-director.service b/roles/bacula-dir/files/etc/systemd/system/bacula-director.service.d/override.conf
index 4873689..f0d36c4 100644
--- a/roles/bacula-dir/files/etc/systemd/system/bacula-director.service
+++ b/roles/bacula-dir/files/etc/systemd/system/bacula-director.service.d/override.conf
@@ -1,14 +1,4 @@
-[Unit]
-Description=Bacula Director service
-After=network.target
-
[Service]
-Type=simple
-StandardOutput=syslog
-User=bacula
-Group=bacula
-ExecStart=/usr/sbin/bacula-dir -f -c /etc/bacula/bacula-dir.conf
-
# Hardening
NoNewPrivileges=yes
PrivateDevices=yes
@@ -16,12 +6,8 @@ ProtectHome=yes
ProtectSystem=strict
ReadWriteDirectories=-/var/lib/bacula
ReadWriteDirectories=-/var/log/bacula
-ReadWriteDirectories=-/var/run/bacula
PrivateDevices=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
-
-[Install]
-WantedBy=multi-user.target
diff --git a/roles/bacula-dir/tasks/main.yml b/roles/bacula-dir/tasks/main.yml
index 2f7ab25..53d44ee 100644
--- a/roles/bacula-dir/tasks/main.yml
+++ b/roles/bacula-dir/tasks/main.yml
@@ -7,12 +7,12 @@
- bacula-director-mysql
- name: Create a 'bacula' SQL user
- mysql_user2: name=bacula password= auth_plugin=unix_socket
- state=present
+ mysql_user: name=bacula password= plugin=unix_socket
+ state=present
notify:
- Restart bacula-director
-# Create with:
+# Populate with:
# echo bconsole $(pwgen -sn 64 1) | sudo tee -a /etc/bacula/passwords-dir
# echo $sd-sd $(pwgen -sn 64 1) | sudo tee -a /etc/bacula/passwords-dir
# echo $fd-fd $(pwgen -sn 64 1) | sudo tee -a /etc/bacula/passwords-dir
@@ -41,9 +41,15 @@
notify:
- Restart bacula-director
-- name: Copy bacula-director.service
- copy: src=etc/systemd/system/bacula-director.service
- dest=/etc/systemd/system/bacula-director.service
+- name: Create /etc/systemd/system/bacula-director.service.d
+ file: path=/etc/systemd/system/bacula-director.service.d
+ state=directory
+ owner=root group=root
+ mode=0755
+
+- name: Copy bacula-director.service override
+ copy: src=etc/systemd/system/bacula-director.service.d/override.conf
+ dest=/etc/systemd/system/bacula-director.service.d/override.conf
owner=root group=root
mode=0644
notify:
diff --git a/roles/bacula-dir/templates/etc/bacula/bacula-dir.conf.j2 b/roles/bacula-dir/templates/etc/bacula/bacula-dir.conf.j2
index f2ffd17..ab22375 100644
--- a/roles/bacula-dir/templates/etc/bacula/bacula-dir.conf.j2
+++ b/roles/bacula-dir/templates/etc/bacula/bacula-dir.conf.j2
@@ -1,6 +1,6 @@
#
# Default Bacula Director Configuration file
-# For Bacula release 5.2.6 (21 February 2012) -- debian jessie/sid
+# For Bacula release 9.4.2 (04 February 2019) -- debian buster/sid
#
Director { # define myself
@@ -8,7 +8,7 @@ Director { # define myself
@|"sed -n '/^bconsole\\s/ {s//Password = /p; q}' /etc/bacula/passwords-dir"
Messages = Daemon
Working Directory = /var/lib/bacula
- Pid Directory = /var/run/bacula
+ Pid Directory = /run/bacula
QueryFile = "/etc/bacula/scripts/query.sql"
Maximum Concurrent Jobs = 1
DirAddress = 127.0.0.1
@@ -79,7 +79,7 @@ JobDefs {
Runs On Success = yes
Runs On Failure = yes
Runs When = after
- Command = "/usr/bin/find /var/lib/bacula/tmp -type f -name '*.ldif' -delete"
+ Command = "/usr/bin/find /var/lib/bacula/tmp -type f \( -name \"*.ldif\" -o -name \"slapd-*\" \) -delete"
}
Pool = database
Priority = 20
@@ -112,6 +112,21 @@ Job {
}
{% endfor %}
+# Backup the Nextcloud data
+{% for h in groups.nextcloud | sort %}
+Job {
+ Name = {{ hostvars[h].inventory_hostname_short }}-nextcloud
+ Client = {{ hostvars[h].inventory_hostname_short }}-fd
+ JobDefs = DefaultJob
+ FileSet = NextcloudData
+ Pool = nextcloud-inc
+ Full Backup Pool = nextcloud-full
+ Schedule = Nextcloud13WeeksCycle
+ Max Start Delay = 50 min # To avoid too many overlaps
+ Max Full Interval = 15 weeks
+}
+{% endfor %}
+
# Backup each machine
{% for fd in groups.all | sort %}
Job {
@@ -133,7 +148,7 @@ Job {
}
{% endfor %}
-{% for fd in groups['MDA'] | union(groups['MSA']) | union(groups['LDAP-provider']) | union(groups['MX']) | sort %}
+{% for fd in groups['MDA'] | union(groups['MSA']) | union(groups['LDAP_provider']) | union(groups['MX']) | sort %}
Job {
Name = {{ hostvars[fd].inventory_hostname_short }}-slapd
Client = {{ hostvars[fd].inventory_hostname_short }}-fd
@@ -175,33 +190,71 @@ Schedule {
Run = Level=Full Pool=mailboxes-full w17 mon at 02:00
Run = Level=Full Pool=mailboxes-full w30 mon at 02:00
Run = Level=Full Pool=mailboxes-full w43 mon at 02:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 01:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet w05-w16 mon-sun at 02:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet w18-w29 mon-sun at 02:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet w31-w42 mon-sun at 02:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet w44-w03 mon-sun at 02:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 03:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 04:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 05:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 06:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 07:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 08:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 09:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 10:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 11:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 12:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 13:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 14:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 15:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 16:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 17:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 18:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 19:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 20:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 21:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 22:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 23:00
- Run = Level=Incremental Pool=mailboxes-inc FullPool=mailboxes-full Messages=Quiet mon-sun at 00:00
+ Run = Level=Differential Pool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet w05-w16 mon at 02:00
+ Run = Level=Differential Pool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet w18-w29 mon at 02:00
+ Run = Level=Differential Pool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet w31-w42 mon at 02:00
+ Run = Level=Differential Pool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet w44-w03 mon at 02:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 00:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 01:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet tue-sun at 02:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 03:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 04:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 05:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 06:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 07:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 08:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 09:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 10:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 11:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 12:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 13:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 14:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 15:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 16:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 17:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 18:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 19:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 20:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 21:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 22:00
+ Run = Level=Incremental Pool=mailboxes-inc DifferentialPool=mailboxes-diff FullPool=mailboxes-full Messages=Quiet mon-sun at 23:00
+}
+
+# Backup Nextcloud data: full backup every 3 months, hourly incremental backup
+Schedule {
+ Name = Nextcloud13WeeksCycle
+ Run = Level=Full Pool=nextcloud-full w05 mon at 02:30
+ Run = Level=Full Pool=nextcloud-full w18 mon at 02:30
+ Run = Level=Full Pool=nextcloud-full w31 mon at 02:30
+ Run = Level=Full Pool=nextcloud-full w44 mon at 02:30
+ Run = Level=Differential Pool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet w06-w17 mon at 02:30
+ Run = Level=Differential Pool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet w19-w30 mon at 02:30
+ Run = Level=Differential Pool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet w32-w43 mon at 02:30
+ Run = Level=Differential Pool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet w45-w04 mon at 02:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 00:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 01:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet tue-sun at 02:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 03:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 04:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 05:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 06:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 07:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 08:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 09:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 10:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 11:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 12:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 13:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 14:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 15:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 16:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 17:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 18:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 19:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 20:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 21:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 22:30
+ Run = Level=Incremental Pool=nextcloud-inc DifferentialPool=nextcloud-diff FullPool=nextcloud-full Messages=Quiet mon-sun at 23:30
}
# This schedule does the databases. It starts after the WeeklyCycle
@@ -318,6 +371,7 @@ FileSet {
File = /exports
File = /misc
File = /media
+ File = /lost+found
}
}
@@ -353,9 +407,26 @@ FileSet {
FileSet {
Name = Mailboxes
Include {
+ # NOTE: debug FileSet with:
+ # `sudo -u bacula bconsole <<<"estimate job=mistral-mailboxes level=Full listing" | grep -F -e.{log,cache}`
+ # we use RegexFile here since bacula's doesn't set FNM_PATHNAME so the `*' and `?' metacharacters match `/'
Options {
- WildDir = /home/mail/attachments/queue
Exclude = yes
+
+ # cached mailbox data: $mail_location/mailboxes/INBOX/dbox-Mails/dovecot.index.cache
+ RegexFile = "^/home/mail/virtual/[^/]+/[^/]+/mail/mailboxes/([^/]+/)+dbox-Mails/dovecot\\.index\\.cache$"
+ # transaction log file: $mail_location/mailboxes/INBOX/dbox-Mails/dovecot.index.log
+ RegexFile = "^/home/mail/virtual/[^/]+/[^/]+/mail/mailboxes/([^/]+/)+dbox-Mails/dovecot\\.index\\.log(\\.[0-9])?$"
+ RegexFile = "^/home/mail/virtual/[^/]+/[^/]+/mail/storage/dovecot\\.map\\.index\\.log(\\.[0-9])?$"
+ # mailbox list index files: $mail_location/dovecot.list.index.log
+ RegexFile = "^/home/mail/virtual/[^/]+/[^/]+/mail/dovecot\\.list\\.index\\.log(\\.[0-9])?$"
+ # mailbox changelog: $mail_location/dovecot.mailbox.log
+ RegexFile = "^/home/mail/virtual/[^/]+/[^/]+/mail/dovecot\\.mailbox\\.log(\\.[0-9])?$"
+ # sieve logfile: ~/dovecot.sieve
+ RegexFile = "^/home/mail/virtual/[^/]+/[^/]+/dovecot\\.sieve\\.log(\\.[0-9])?$"
+
+ # exclude queued files for SiS deduplication
+ Wild = "/home/mail/attachments/queue/*"
}
Options {
signature = SHA1
@@ -365,6 +436,37 @@ FileSet {
File = /home/mail/attachments
File = /home/mail/spamspool
}
+ Exclude {
+ File = "/home/mail/lost+found"
+ }
+}
+
+FileSet {
+ Name = NextcloudData
+ Include {
+ Options {
+ Exclude = yes
+ RegexFile = "^/mnt/nextcloud-data/nextcloud\\.log(\\.[0-9])?$"
+ RegexFile = "^/mnt/nextcloud-data/updater\\.log(\\.[0-9])?$"
+ RegexDir = "^/mnt/nextcloud-data/[a-z0-9\\-]+/files_trashbin$"
+ RegexDir = "^/mnt/nextcloud-data/[a-z0-9\\-]+/files_versions$"
+ RegexDir = "^/mnt/nextcloud-data/[a-z0-9\\-]+/cache$"
+ RegexDir = "^/mnt/nextcloud-data/[a-z0-9\\-]+/uploads$"
+ RegexDir = "^/mnt/nextcloud-data/__groupfolders/trash$"
+ RegexDir = "^/mnt/nextcloud-data/__groupfolders/versions$"
+ RegexDir = "^/mnt/nextcloud-data/updater-[[:alnum:]]+$"
+ RegexDir = "^/mnt/nextcloud-data/appdata_[[:alnum:]]+/preview$"
+ RegexDir = "^/mnt/nextcloud-data/appdata_[[:alnum:]]+/[^/]+/cache$"
+ }
+ Options {
+ signature = SHA1
+ verify = pins1
+ }
+ File = /mnt/nextcloud-data
+ }
+ Exclude {
+ File = "/mnt/nextcloud-data/lost+found"
+ }
}
@@ -435,9 +537,22 @@ Pool {
Pool Type = Backup
Recycle = yes
AutoPrune = yes
- Volume Retention = 26 weeks
- Maximum Volume Bytes = 5GB
- Label Format = "mailboxes-full-${NumVols:p/4/0/r}"
+ Volume Retention = 26 weeks # >13 weeks cycle
+ Maximum Volume Jobs = 1
+ Label Format = "mailboxes-full-"
+ Maximum Volumes = 3 # >2 volumes used at the end of retention period
+}
+
+# Mailbox pool definition (diff backup)
+Pool {
+ Name = mailboxes-diff
+ Pool Type = Backup
+ Recycle = yes
+ AutoPrune = yes
+ Volume Retention = 15 weeks # >13 weeks cycle
+ Maximum Volume Jobs = 1
+ Label Format = "mailboxes-diff-"
+ Maximum Volumes = 20 # >15 volumes used at the end of retention period
}
# Mailbox pool definition (inc backup)
@@ -446,9 +561,46 @@ Pool {
Pool Type = Backup
Recycle = yes
AutoPrune = yes
- Volume Retention = 26 weeks
- Maximum Volume Bytes = 5GB
- Label Format = "mailboxes-inc-${NumVols:p/4/0/r}"
+ Volume Retention = 8 days # >1 week cycle
+ Maximum Volume Jobs = 24 # group by day
+ Label Format = "mailboxes-inc-"
+ Maximum Volumes = 10 # >8 volumes used at the end of retention period
+}
+
+# Nextcloud pool definition (full backup)
+Pool {
+ Name = nextcloud-full
+ Pool Type = Backup
+ Recycle = yes
+ AutoPrune = yes
+ Volume Retention = 26 weeks # >13 weeks cycle
+ Maximum Volume Jobs = 1
+ Label Format = "nextcloud-full-"
+ Maximum Volumes = 3 # >2 volumes used at the end of retention period
+}
+
+# Nextcloud pool definition (diff backup)
+Pool {
+ Name = nextcloud-diff
+ Pool Type = Backup
+ Recycle = yes
+ AutoPrune = yes
+ Volume Retention = 15 weeks # >13 weeks cycle
+ Maximum Volume Jobs = 1
+ Label Format = "nextcloud-diff-"
+ Maximum Volumes = 20 # >15 volumes used at the end of retention period
+}
+
+# Nextcloud pool definition (inc backup)
+Pool {
+ Name = nextcloud-inc
+ Pool Type = Backup
+ Recycle = yes
+ AutoPrune = yes
+ Volume Retention = 8 days # >1 week cycle
+ Maximum Volume Jobs = 24 # group by day
+ Label Format = "nextcloud-inc-"
+ Maximum Volumes = 10 # >8 volumes used at the end of retention period
}
# Database pool definition
diff --git a/roles/bacula-sd/files/etc/systemd/system/bacula-sd.service b/roles/bacula-sd/files/etc/systemd/system/bacula-sd.service.d/override.conf
index 30fa562..b228078 100644
--- a/roles/bacula-sd/files/etc/systemd/system/bacula-sd.service
+++ b/roles/bacula-sd/files/etc/systemd/system/bacula-sd.service.d/override.conf
@@ -1,27 +1,13 @@
-[Unit]
-Description=Bacula Storage Daemon service
-After=network.target
-
[Service]
-Type=simple
-StandardOutput=syslog
-User=bacula
-Group=tape
-ExecStart=/usr/sbin/bacula-sd -f -c /etc/bacula/bacula-sd.conf
-
# Hardening
NoNewPrivileges=yes
PrivateDevices=yes
ProtectHome=yes
ProtectSystem=strict
ReadWriteDirectories=-/var/lib/bacula
-ReadWriteDirectories=-/var/run/bacula
ReadWriteDirectories=/mnt/backup/bacula
PrivateDevices=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
RestrictAddressFamilies=AF_INET AF_INET6
-
-[Install]
-WantedBy=multi-user.target
diff --git a/roles/bacula-sd/tasks/main.yml b/roles/bacula-sd/tasks/main.yml
index 93958a8..f30fe7f 100644
--- a/roles/bacula-sd/tasks/main.yml
+++ b/roles/bacula-sd/tasks/main.yml
@@ -1,7 +1,7 @@
- name: Install bacula-sd
apt: pkg=bacula-sd
-# Create with:
+# Populate with:
# echo $director-dir $(pwgen -sn 64 1) | sudo tee -a /etc/bacula/passwords-sd
- name: Ensure /etc/bacula/passwords-sd exists
file: path=/etc/bacula/passwords-sd
@@ -17,9 +17,15 @@
notify:
- Restart bacula-sd
-- name: Copy bacula-sd.service
- copy: src=etc/systemd/system/bacula-sd.service
- dest=/etc/systemd/system/bacula-sd.service
+- name: Create /etc/systemd/system/bacula-sd.service.d
+ file: path=/etc/systemd/system/bacula-sd.service.d
+ state=directory
+ owner=root group=root
+ mode=0755
+
+- name: Copy bacula-sd.service override
+ copy: src=etc/systemd/system/bacula-sd.service.d/override.conf
+ dest=/etc/systemd/system/bacula-sd.service.d/override.conf
owner=root group=root
mode=0644
notify:
diff --git a/roles/bacula-sd/templates/etc/bacula/bacula-sd.conf.j2 b/roles/bacula-sd/templates/etc/bacula/bacula-sd.conf.j2
index 3cbf7fe..a898e0d 100644
--- a/roles/bacula-sd/templates/etc/bacula/bacula-sd.conf.j2
+++ b/roles/bacula-sd/templates/etc/bacula/bacula-sd.conf.j2
@@ -1,7 +1,7 @@
#
# Default Bacula Storage Daemon Configuration file
#
-# For Bacula release 5.2.6 (21 February 2012) -- debian jessie/sid
+# For Bacula release 9.4.2 (04 February 2019) -- debian buster/sid
#
# You may need to change the name of your tape drive
# on the "Archive Device" directive in the Device
@@ -13,7 +13,7 @@
Storage { # define myself
Name = {{ inventory_hostname_short }}-sd
Working Directory = /var/lib/bacula
- Pid Directory = /var/run/bacula
+ Pid Directory = /run/bacula
Maximum Concurrent Jobs = 20
SDAddress = {{ ipsec[inventory_hostname_short] }}
SDPort = 9103
diff --git a/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh b/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh
index cd5abd9..db128c9 100755
--- a/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh
+++ b/roles/common-LDAP/files/usr/local/sbin/slapcat-all.sh
@@ -1,20 +1,31 @@
#!/bin/sh
# Usage: slapcat-all.sh DIR
-# Save all LDAP databases in DIR: DIR/0.ldif, DIR/1.ldif, ...
+# Save all LDAP databases in DIR: DIR/SUFFIX0.ldif, DIR/SUFFIX1.ldif, ...
set -ue
-PATH=/usr/sbin:/sbin:/usr/bin:/bin
+PATH="/usr/bin:/bin"
+export PATH
-target="$1"
+TARGET="$1"
umask 0077
-prefix=slapcat-
-slapcat -n0 -l"$target/${prefix}0.ldif"
-n=$(grep -Ec '^dn:\s+olcDatabase={[1-9][0-9]*}' "$target/${prefix}0.ldif")
+ldapsearch() {
+ command ldapsearch -H "ldapi://" -QY EXTERNAL "$@"
+}
-while [ $n -gt 0 ]; do
- # the Monitor backend can't be slapcat(8)'ed
- grep -qE "^dn:\s+olcDatabase=\{$n\}monitor,cn=config$" "$target/${prefix}0.ldif" || slapcat -n$n -l"$target/${prefix}$n.ldif"
- n=$(( $n - 1 ))
-done
+backup_database() {
+ local base="$1"
+ ldapsearch -b "$base" \+ \* >"$TARGET/$base.ldif"
+}
+
+backup_database "cn=config"
+
+SUFFIXES="$TARGET/slapd-suffixes"
+ldapsearch -LLL -oldif-wrap="no" -b "cn=config" "(&(objectClass=olcDatabaseConfig)(objectClass=olcMdbConfig))" "olcSuffix" >"$SUFFIXES"
+sed -n -i "s/^olcSuffix:\\s*//p" "$SUFFIXES"
+
+while IFS= read -r b; do
+ [ "${b%,dc=fripost-test,dc=org}" = "$b" ] || continue
+ backup_database "$b"
+done <"$SUFFIXES"
diff --git a/roles/common-LDAP/tasks/main.yml b/roles/common-LDAP/tasks/main.yml
index 5255cdf..37edb0b 100644
--- a/roles/common-LDAP/tasks/main.yml
+++ b/roles/common-LDAP/tasks/main.yml
@@ -8,7 +8,7 @@
- ldap-utils
- ldapvi
- db-util
- - python-ldap
+ - python3-ldap
# for the 'slapd2' munin plugin
- libnet-ldap-perl
- libauthen-sasl-perl
@@ -48,7 +48,7 @@
changed_when: r2.rc == 0
failed_when: r2.rc > 1
with_items:
- - { group: 'LDAP-provider', name: ldap.fripost.org, ou: }
+ - { group: 'LDAP_provider', name: ldap.fripost.org, ou: }
- { group: 'MX', name: mx, ou: --ou=SyncRepl }
- { group: 'lists', name: lists, ou: --ou=SyncRepl }
when: "item.group in group_names"
@@ -62,7 +62,7 @@
stdin=/etc/ldap/ssl/{{ item.name }}.pem
dest=certs/ldap/{{ item.name }}.pem
with_items:
- - { group: 'LDAP-provider', name: ldap.fripost.org }
+ - { group: 'LDAP_provider', name: ldap.fripost.org }
- { group: 'MX', name: mx }
- { group: 'lists', name: lists }
when: "item.group in group_names"
@@ -74,7 +74,7 @@
dest=/etc/ldap/ssl/ldap.fripost.org.pem
owner=root group=root
mode=0644
- when: "'LDAP-provider' not in group_names"
+ when: "'LDAP_provider' not in group_names"
tags:
- genkey
@@ -83,7 +83,7 @@
dest=/etc/ldap/ssl/clients.pem
owner=root group=root
mode=0644
- when: "'LDAP-provider' in group_names"
+ when: "'LDAP_provider' in group_names"
tags:
- genkey
diff --git a/roles/common-LDAP/templates/etc/default/slapd.j2 b/roles/common-LDAP/templates/etc/default/slapd.j2
index fdd7481..d761775 100644
--- a/roles/common-LDAP/templates/etc/default/slapd.j2
+++ b/roles/common-LDAP/templates/etc/default/slapd.j2
@@ -12,7 +12,7 @@ SLAPD_USER="openldap"
SLAPD_GROUP="openldap"
# Path to the pid file of the slapd server. If not set the init.d script
-# will try to figure it out from $SLAPD_CONF (/etc/ldap/slapd.conf by
+# will try to figure it out from $SLAPD_CONF (/etc/ldap/slapd.d by
# default)
SLAPD_PIDFILE=
@@ -23,7 +23,7 @@ SLAPD_SERVICES="ldapi:///"
{% for i in group_names | intersect(['MX','lists','MSA']) | sort %}
SLAPD_SERVICES="$SLAPD_SERVICES ldapi://%2Fvar%2Fspool%2Fpostfix-{{ postfix_instance[i].name }}%2Fprivate%2Fldapi/"
{% endfor %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
SLAPD_SERVICES="$SLAPD_SERVICES ldaps:///"
{% endif %}
diff --git a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2
index b9f282f..2c0db0b 100644
--- a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2
+++ b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2
@@ -17,8 +17,8 @@
dn: cn=config
objectClass: olcGlobal
cn: config
-olcArgsFile: /var/run/slapd/slapd.args
-olcPidFile: /var/run/slapd/slapd.pid
+olcArgsFile: /run/slapd/slapd.args
+olcPidFile: /run/slapd/slapd.pid
olcLogLevel: none
olcToolThreads: 1
{% if ansible_processor_vcpus > 4 %}
@@ -26,7 +26,7 @@ olcThreads: {{ 2 * ansible_processor_vcpus }}
{% else %}
olcThreads: 8
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
olcTLSCertificateFile: /etc/ldap/ssl/ldap.fripost.org.pem
olcTLSCertificateKeyFile: /etc/ldap/ssl/ldap.fripost.org.key
# If we are being offered a client cert, it has to be trusted (in which
@@ -39,6 +39,7 @@ olcAuthzRegexp: "^(cn=[^,]+,ou=syncRepl),ou=LDAP,ou=SSLcerts,o=Fripost$"
"dn.exact:$1,dc=fripost,dc=org"
olcSaslSecProps: minssf=128,noanonymous,noplain,nodict
olcTLSCipherSuite: PFS:%LATEST_RECORD_VERSION:!CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1
+olcTLSDHParamFile: /etc/ssl/dhparams.pem
{% endif %}
olcLocalSSF: 128
# /!\ This is not portable! But we only use glibc's crypt(3), which
@@ -61,10 +62,10 @@ objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=fripost,dc=org
-{% if 'LDAP-provider' not in group_names and 'MX' in group_names %}
+{% if 'LDAP_provider' not in group_names and 'MX' in group_names %}
olcReadOnly: TRUE
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
olcLastMod: TRUE
olcDbCheckpoint: 512 15
{% else %}
@@ -91,23 +92,23 @@ olcSecurity: simple_bind=128 ssf=128 update_ssf=128
#
olcDbIndex: objectClass eq
# Let us make Postfix's life easier.
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
olcDbIndex: fvd,fvl eq,sub
olcDbIndex: fripostIsStatusActive eq
{% elif 'MX' in group_names or 'MDA' in group_names %}
olcDbIndex: fripostIsStatusActive,fvd,fvl eq
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
olcDbIndex: fripostOptionalMaildrop,fripostMaildrop eq,sub
olcDbIndex: fripostCanAddDomain,fripostCanAddAlias,fripostCanAddList,fripostOwner,fripostPostmaster,fripostListManager eq
{% elif 'MX' in group_names %}
olcDbIndex: fripostOptionalMaildrop pres
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
olcDbIndex: member,cn eq
{% endif %}
-{% if ('LDAP-provider' not in group_names and 'MX' in group_names) or
- ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if ('LDAP_provider' not in group_names and 'MX' in group_names) or
+ ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
# SyncProv/SyncRepl specific indexing.
olcDbIndex: entryCSN,entryUUID eq
{% endif%}
@@ -128,14 +129,14 @@ olcDbIndex: entryCSN,entryUUID eq
# - http://www.openldap.org/doc/admin24/replication.html#Syncrepl
# - http://www.zytrax.com/books/ldap/ch7/#ol-syncrepl-rap
#
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
olcLimits: dn.onelevel="ou=syncRepl,dc=fripost,dc=org"
time.soft=unlimited
time.hard=unlimited
size.soft=unlimited
size.hard=unlimited
{% endif %}
-{% if 'MX' in group_names and 'LDAP-provider' not in group_names %}
+{% if 'MX' in group_names and 'LDAP_provider' not in group_names %}
# Test it:
# LDAPSASL_MECH=external LDAPTLS_CACERT=/etc/ldap/ssl/ldap.fripost.org.pem LDAPTLS_CERT=/etc/ldap/ssl/mx.pem LDAPTLS_KEY=/etc/ldap/ssl/mx.key sudo -u openldap ldapwhoami -H ldaps://ldap.fripost.org/
# LDAPSASL_MECH=external LDAPTLS_CACERT=/etc/ldap/ssl/ldap.fripost.org.pem LDAPTLS_CERT=/etc/ldap/ssl/mx.pem LDAPTLS_KEY=/etc/ldap/ssl/mx.key sudo -u openldap ldapsearch -H ldaps://ldap.fripost.org/ -b ou=virtual,dc=fripost,dc=org
@@ -215,7 +216,7 @@ olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,dc=fripost,dc=org)$"
by group.exact="cn=admin,ou=groups,dc=fripost,dc=org" =w
#
# * Services can authenticate
-{% if 'LDAP-provider' in group_names -%}
+{% if 'LDAP_provider' in group_names -%}
olcAccess: to dn.onelevel="ou=services,dc=fripost,dc=org"
filter=(objectClass=simpleSecurityObject)
attrs=userPassword
@@ -233,7 +234,7 @@ olcAccess: to dn.subtree="dc=fripost,dc=org"
#
# * Only SyncRepl replicates may access operational attributes in the
# subtree, when using a TLS-protected connection.
-{% if 'LDAP-provider' in group_names -%}
+{% if 'LDAP_provider' in group_names -%}
olcAccess: to dn.subtree="ou=virtual,dc=fripost,dc=org"
attrs=entryCSN,structuralObjectClass,hasSubordinates,subschemaSubentry
by dn.onelevel="ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
@@ -256,20 +257,23 @@ olcAccess: to dn.children="ou=virtual,dc=fripost,dc=org"
# * Postfix may use the base as a searchBase on the MX:es, when
# connecting a local ldapi:// socket from the 'private' directory in
# one of the non-default instance's chroot.
-# * So may Dovecot on the MDA (needed for the iterate filter), when
-# SASL-binding using the EXTERNAL mechanism and connecting to a local
-# ldapi:// socket.
+# * So may _dovecot-auth-proxy on the MDA (needed for the iterate
+# logic), when SASL-binding using the EXTERNAL mechanism and
+# connecting to a local ldapi:// socket.
# * So may Nextcloud on the LDAP provider
olcAccess: to dn.exact="ou=virtual,dc=fripost,dc=org"
attrs=entry,objectClass
filter=(objectClass=FripostVirtual)
{% if 'MDA' in group_names -%}
- by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd
+ by dn.exact="username=_dovecot-auth-proxy,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd
{% endif -%}
- {% if 'MX' in group_names or 'MSA' in group_names -%}
+ {% if 'MX' in group_names -%}
by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =sd
{% endif -%}
- {% if 'LDAP-provider' in group_names -%}
+ {% if 'MSA' in group_names -%}
+ by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd
+ {% endif -%}
+ {% if 'LDAP_provider' in group_names -%}
by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =sd
{% endif -%}
by users =0 break
@@ -281,30 +285,35 @@ olcAccess: to dn.exact="ou=virtual,dc=fripost,dc=org"
# using a TLS-protected connection.
# * So has Postfix, when connecting a local ldapi:// socket from the
# 'private' directory in one of the non-default instance's chroot.
-# * So has Dovecot on the MDA (for the iterate filter), when
-# SASL-binding using the EXTERNAL mechanism and connecting to a local
-# ldapi:// socket.
-# * Amavis may use the entry as searchBase (required to look for the
-# per-user preferences) but doesn't have read access to the entry.
# * The 'nobody' UNIX user has read access on the MX:es, when using
# SASL-binding using the EXTERNAL mechanism and connecting to a local
# ldapi:// socket. This is required for the 'reserved-alias.pl'
# script.
+# * Amavis may use the entry as searchBase (required to look for the
+# per-user preferences) but doesn't have read access to the entry.
+# * So has _dovecot-auth-proxy on the MDA (for the iterate logic), when
+# SASL-binding using the EXTERNAL mechanism and connecting to a local
+# ldapi:// socket.
+# * So has _postfix-sender-login on the submission service to verify
+# envelope sender ownership
olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=entry,objectClass,fvd
filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry)))
- {% if 'LDAP-provider' in group_names -%}
- {% if groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
- {% endif -%}
+ {% if 'MX' in group_names -%}
by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd
+ by dn.exact="username=nobody,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ {% endif -%}
{% if 'MDA' in group_names -%}
- by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
by dn.exact="username=amavis,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =sd
{% endif -%}
- {% if 'MX' in group_names -%}
- by dn.exact="username=nobody,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ {% if 'IMAP' in group_names -%}
+ by dn.exact="username=_dovecot-auth-proxy,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ {% endif -%}
+ {% if 'MSA' in group_names -%}
+ by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
{% endif -%}
by users =0 break
#
@@ -314,11 +323,11 @@ olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# * So can Postfix on the MX:es, when connecting a local ldapi:// socket
# from the 'private' directory in one of the non-default instance's
# chroot.
-{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=fripostIsStatusActive,fripostOptionalMaildrop
filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry)))
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names -%}
@@ -334,7 +343,7 @@ olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=fripostOwner,fripostPostmaster
filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry)))
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names %}
@@ -351,11 +360,11 @@ olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# * So has Postfix on the MX:es, when connecting a local ldapi:// socket
# from the 'private' directory in one of the non-default instance's
# chroot.
-{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=entry,fripostMaildrop
filter=(&(objectClass=FripostVirtualAliasDomain)(!(objectClass=FripostPendingEntry)))
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names -%}
@@ -371,7 +380,7 @@ olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# using a TLS-protected connection.
# * So has Postfix, when connecting a local ldapi:// socket from the
# 'private' directory in one of the non-default instance's chroot.
-# * So has Dovecot on the MDA (for the iterate filter), when
+# * So has _dovecot-auth-proxy on the MDA (for the iterate logic), when
# SASL-binding using the EXTERNAL mechanism and connecting to a local
# ldapi:// socket.
# * So has Amavis on the MDA, when SASL-binding using the EXTERNAL
@@ -379,14 +388,21 @@ olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=entry,objectClass,fvl
filter=(objectClass=FripostVirtualUser)
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
+ {% if 'MX' in group_names -%}
by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd
+ {% endif -%}
{% if 'MDA' in group_names -%}
- by dn.exact="username=dovecot,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
by dn.exact="username=amavis,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
{% endif -%}
+ {% if 'IMAP' in group_names -%}
+ by dn.exact="username=_dovecot-auth-proxy,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ {% endif -%}
+ {% if 'MSA' in group_names -%}
+ by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ {% endif -%}
by users =0 break
#
# * The SyncRepl MX replicates can check whether a virtual user is
@@ -394,11 +410,11 @@ olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# * So can Postfix on the MX:es, when connecting a local ldapi:// socket
# from the 'private' directory in one of the non-default instance's
# chroot.
-{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=fripostIsStatusActive,fripostUseContentFilter
filter=(objectClass=FripostVirtualUser)
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names -%}
@@ -437,11 +453,11 @@ olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# * So can Postfix on the MX:es, when connecting a local ldapi:// socket
# from the 'private' directory in one of the non-default instance's
# chroot.
-{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=entry,objectClass,fvl,fripostMaildrop,fripostIsStatusActive
filter=(objectClass=FripostVirtualAlias)
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names -%}
@@ -457,11 +473,11 @@ olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# using a TLS-protected connection.
# * So can Postfix on the MX:es, when connecting a local ldapi:// socket
# from the 'private' directory in one of the non-default instance's chroot.
-{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=entry,objectClass,fvl,fripostListManager
filter=(&(objectClass=FripostVirtualList)(!(objectClass=FripostPendingEntry)))
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names -%}
@@ -475,11 +491,11 @@ olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
# * So can Postfix on the MX:es, when connecting a local ldapi:// socket
# from the 'private' directory in one of the non-default instance's
# chroot.
-{% if 'MX' in group_names or ('LDAP-provider' in group_names and groups.MX | difference([inventory_hostname])) %}
+{% if 'MX' in group_names or ('LDAP_provider' in group_names and groups.MX | difference([inventory_hostname])) %}
olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=fripostIsStatusActive
filter=(&(objectClass=FripostVirtualList)(!(objectClass=FripostPendingEntry)))
- {% if 'LDAP-provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
+ {% if 'LDAP_provider' in group_names and groups.MX | difference([inventory_hostname]) -%}
by dn.exact="cn=mX,ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd
{% endif -%}
{% if 'MX' in group_names -%}
@@ -488,21 +504,21 @@ olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
by users =0 break
{% endif %}
#
-# * The MSA's postfix user can read entry ownership to dermine the SASL
-# login name(s) owning a given sender address
+# * The MSA's _postfix-sender-login user can read entry ownership to
+# dermine the SASL login name(s) owning a given sender address
{% if 'MSA' in group_names %}
olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=fripostOwner,fripostPostmaster
filter=(|(objectClass=FripostVirtualAliasDomain)(objectClass=FripostVirtualDomain))
- by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd
- by users =0 break
+ by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ by users =0 break
olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$"
attrs=entry,objectClass,fvl,fripostOwner
filter=(|(objectClass=FripostVirtualAlias)(objectClass=FripostVirtualList)(objectClass=FripostVirtualUser))
- by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://%2Fvar%2Fspool%2Fpostfix-[-[:alnum:]]+%2Fprivate%2F" =rsd
- by users =0 break
+ by dn.exact="username=_postfix-sender-login,cn=peercred,cn=external,cn=auth" sockurl.regex="^ldapi://" =rsd
+ by users =0 break
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
@@ -524,6 +540,9 @@ olcAccess: to dn.exact="ou=groups,dc=fripost,dc=org"
olcAccess: to dn.exact="cn=medlemmar,ou=groups,dc=fripost,dc=org"
by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd
by users =0 break
+olcAccess: to dn.exact="cn=styrelse,ou=groups,dc=fripost,dc=org"
+ by dn.exact="cn=nextcloud,ou=services,dc=fripost,dc=org" tls_ssf=128 =rsd
+ by users =0 break
#
# TODO: allow users to edit their entry, etc
#
diff --git a/roles/common-SQL/files/etc/mysql/mariadb.conf.d/99-user.cnf b/roles/common-SQL/files/etc/mysql/mariadb.conf.d/99-user.cnf
new file mode 100644
index 0000000..f3323f9
--- /dev/null
+++ b/roles/common-SQL/files/etc/mysql/mariadb.conf.d/99-user.cnf
@@ -0,0 +1,4 @@
+[mysqld]
+skip-networking
+innodb_file_per_table
+innodb_flush_method = O_DIRECT
diff --git a/roles/common-SQL/files/etc/mysql/my.cnf b/roles/common-SQL/files/etc/mysql/my.cnf
deleted file mode 100644
index e1dff58..0000000
--- a/roles/common-SQL/files/etc/mysql/my.cnf
+++ /dev/null
@@ -1,131 +0,0 @@
-#
-# The MySQL database server configuration file.
-#
-# You can copy this to one of:
-# - "/etc/mysql/my.cnf" to set global options,
-# - "~/.my.cnf" to set user-specific options.
-#
-# One can use all long options that the program supports.
-# Run program with --help to get a list of available options and with
-# --print-defaults to see which it would actually understand and use.
-#
-# For explanations see
-# http://dev.mysql.com/doc/mysql/en/server-system-variables.html
-
-# This will be passed to all mysql clients
-# It has been reported that passwords should be enclosed with ticks/quotes
-# escpecially if they contain "#" chars...
-# Remember to edit /etc/mysql/debian.cnf when changing the socket location.
-[client]
-port = 3306
-socket = /var/run/mysqld/mysqld.sock
-
-# Here is entries for some specific programs
-# The following values assume you have at least 32M ram
-
-# This was formally known as [safe_mysqld]. Both versions are currently parsed.
-[mysqld_safe]
-socket = /var/run/mysqld/mysqld.sock
-nice = 0
-
-[mysqld]
-#
-# * Basic Settings
-#
-user = mysql
-pid-file = /var/run/mysqld/mysqld.pid
-socket = /var/run/mysqld/mysqld.sock
-port = 3306
-basedir = /usr
-datadir = /var/lib/mysql
-tmpdir = /tmp
-lc-messages-dir = /usr/share/mysql
-character_set_server = utf8
-collation_server = utf8_unicode_ci
-skip-external-locking
-#
-# Instead of skip-networking the default is now to listen only on
-# localhost which is more compatible and is not less secure.
-#bind-address = 127.0.0.1
-skip-networking
-#
-# * Fine Tuning
-#
-key_buffer_size = 16M
-max_allowed_packet = 16M
-thread_stack = 192K
-thread_cache_size = 8
-# This replaces the startup script and checks MyISAM tables if needed
-# the first time they are touched
-myisam-recover = BACKUP
-#max_connections = 100
-#table_cache = 64
-#thread_concurrency = 10
-#
-# * Query Cache Configuration
-#
-query_cache_limit = 1M
-query_cache_size = 16M
-#
-# * Logging and Replication
-#
-# Both location gets rotated by the cronjob.
-# Be aware that this log type is a performance killer.
-# As of 5.1 you can enable the log at runtime!
-#general_log_file = /var/log/mysql/mysql.log
-#general_log = 1
-#
-# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
-#
-# Here you can see queries with especially long duration
-#log_slow_queries = /var/log/mysql/mysql-slow.log
-#long_query_time = 2
-#log-queries-not-using-indexes
-#
-# The following can be used as easy to replay backup logs or for replication.
-# note: if you are setting up a replication slave, see README.Debian about
-# other settings you may need to change.
-#server-id = 1
-#log_bin = /var/log/mysql/mysql-bin.log
-expire_logs_days = 10
-max_binlog_size = 100M
-#binlog_do_db = include_database_name
-#binlog_ignore_db = include_database_name
-#
-# * InnoDB
-#
-# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
-# Read the manual for more InnoDB related options. There are many!
-
-innodb_flush_method = O_DIRECT
-innodb_file_per_table = 1
-
-# * Security Features
-#
-# Read the manual, too, if you want chroot!
-# chroot = /var/lib/mysql/
-#
-# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
-#
-# ssl-ca=/etc/mysql/cacert.pem
-# ssl-cert=/etc/mysql/server-cert.pem
-# ssl-key=/etc/mysql/server-key.pem
-
-
-
-[mysqldump]
-quick
-quote-names
-max_allowed_packet = 16M
-
-[mysql]
-#no-auto-rehash # faster start of mysql but no tab completition
-
-[isamchk]
-key_buffer_size = 16M
-
-#
-# * IMPORTANT: Additional settings that can override those from this file!
-# The files must end with '.cnf', otherwise they'll be ignored.
-#
-!includedir /etc/mysql/conf.d/
diff --git a/roles/common-SQL/tasks/main.yml b/roles/common-SQL/tasks/main.yml
index 3df2cab..7e59f60 100644
--- a/roles/common-SQL/tasks/main.yml
+++ b/roles/common-SQL/tasks/main.yml
@@ -4,13 +4,13 @@
packages:
- mariadb-common
- mariadb-server
- - python-mysqldb
+ - python3-mysqldb
# for the 'mysql_' munin plugin
- libcache-cache-perl
- name: Copy MySQL/MariaDB configuration
- copy: src=etc/mysql/my.cnf
- dest=/etc/mysql/my.cnf
+ copy: src=etc/mysql/mariadb.conf.d/99-user.cnf
+ dest=/etc/mysql/mariadb.conf.d/99-user.cnf
owner=root group=root
mode=0644
register: r
@@ -23,12 +23,12 @@
# XXX Dirty fix for #742046
- name: Force root to use UNIX permissions
- mysql_user2: name=root password= auth_plugin=unix_socket soname=auth_socket.so
- state=present
+ mysql_user: name=root password="" plugin=unix_socket
+ state=present
- name: Disallow anonymous and TCP/IP root login
- mysql_user2: name={{ item.name|default('') }} host={{ item.host }}
- state=absent
+ mysql_user: name={{ item.name|default('') }} host={{ item.host }}
+ state=absent
with_items:
- { host: '{{ inventory_hostname_short }}' }
- { host: 'localhost' }
diff --git a/roles/common-web/files/etc/nginx/sites-available/default b/roles/common-web/files/etc/nginx/sites-available/default
index 63c7910..cae8fc0 100644
--- a/roles/common-web/files/etc/nginx/sites-available/default
+++ b/roles/common-web/files/etc/nginx/sites-available/default
@@ -1,12 +1,12 @@
server {
- listen 80 default_server;
- listen [::]:80 default_server;
+ listen 80 default_server;
+ listen [::]:80 default_server;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log info;
# serve ACME challenges on all virtual hosts
# /!\ need to be served individually for each explicit virtual host as well!
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
include snippets/headers.conf;
}
diff --git a/roles/common-web/files/etc/nginx/snippets/acme-challenge.conf b/roles/common-web/files/etc/nginx/snippets/acme-challenge.conf
deleted file mode 100644
index b2a856a..0000000
--- a/roles/common-web/files/etc/nginx/snippets/acme-challenge.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-location /.well-known/acme-challenge/ {
- alias /var/www/acme-challenge/;
- default_type application/jose+json;
-}
diff --git a/roles/common-web/files/etc/nginx/snippets/fastcgi-php.conf b/roles/common-web/files/etc/nginx/snippets/fastcgi-php.conf
index 9668bb8..f82bc5d 100644
--- a/roles/common-web/files/etc/nginx/snippets/fastcgi-php.conf
+++ b/roles/common-web/files/etc/nginx/snippets/fastcgi-php.conf
@@ -1,10 +1,13 @@
-# cf. http://wiki.nginx.org/Pitfalls#Passing_Uncontrolled_Requests_to_PHP
+# regex to split $uri to $fastcgi_script_name and $fastcgi_path
+fastcgi_split_path_info ^(.+?\.php)(/.*)$;
+
+# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
-include snippets/fastcgi.conf;
-# required if PHP was built with --enable-force-cgi-redirect
-fastcgi_param REDIRECT_STATUS 200;
+# Bypass the fact that try_files resets $fastcgi_path_info
+# see: http://trac.nginx.org/nginx/ticket/321
+set $path_info $fastcgi_path_info;
+fastcgi_param PATH_INFO $path_info;
-fastcgi_intercept_errors on;
-fastcgi_read_timeout 14400;
-fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
+fastcgi_index index.php;
+include snippets/fastcgi.conf;
diff --git a/roles/common-web/files/etc/nginx/snippets/fastcgi.conf b/roles/common-web/files/etc/nginx/snippets/fastcgi.conf
index ee058da..9a0a029 100644
--- a/roles/common-web/files/etc/nginx/snippets/fastcgi.conf
+++ b/roles/common-web/files/etc/nginx/snippets/fastcgi.conf
@@ -1,16 +1,16 @@
+fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
-fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
-fastcgi_param PATH_INFO $fastcgi_path_info;
-fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
+fastcgi_param REQUEST_SCHEME $scheme;
+fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
@@ -21,4 +21,5 @@ fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
-fastcgi_param HTTPS $https if_not_empty;
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
diff --git a/roles/common-web/files/etc/nginx/snippets/ssl.conf b/roles/common-web/files/etc/nginx/snippets/ssl.conf
index d3ccd9e..0284b0a 100644
--- a/roles/common-web/files/etc/nginx/snippets/ssl.conf
+++ b/roles/common-web/files/etc/nginx/snippets/ssl.conf
@@ -1,8 +1,5 @@
# https://wiki.mozilla.org/Security/Server_Side_TLS
-# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.10.3&openssl=1.1.0j&hsts=yes&profile=intermediate
-
-# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
-# ~$ cat /etc/nginx/ssl/srvcert.pem /usr/share/lacme/lets-encrypt-x3-cross-signed.pem | sudo tee /etc/nginx/ssl/srvcert.chained.pem
+# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.14.2&openssl=1.1.1c&hsts=yes&profile=intermediate
ssl on;
@@ -13,13 +10,12 @@ ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/ssl/dhparams.pem;
-# intermediate configuration. tweak to your needs.
-ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
-ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
-ssl_prefer_server_ciphers on;
+ssl_protocols TLSv1.2 TLSv1.3;
+ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
+ssl_prefer_server_ciphers off;
-# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
-add_header Strict-Transport-Security 'max-age=15768000; includeSubDomains';
+# HSTS (ngx_http_headers_module is required) (31557600 seconds = 1 year)
+add_header Strict-Transport-Security "max-age=31557600; includeSubDomains" always;
# OCSP Stapling: fetch OCSP records from URL in ssl_certificate and cache them
# https://github.com/jsha/ocsp-stapling-examples/blob/master/nginx.conf
@@ -27,4 +23,4 @@ ssl_stapling on;
ssl_stapling_verify on;
# verify chain of trust of OCSP response using Root CA and Intermediate certs
-ssl_trusted_certificate /usr/share/lacme/lets-encrypt-x3-cross-signed.pem;
+ssl_trusted_certificate /usr/share/lacme/ca-certificates.crt;
diff --git a/roles/common-web/tasks/main.yml b/roles/common-web/tasks/main.yml
index e2eb3ee..f059bfc 100644
--- a/roles/common-web/tasks/main.yml
+++ b/roles/common-web/tasks/main.yml
@@ -8,7 +8,7 @@
tags:
- logrotate
-- name: Copy fastcgi parameters, acme-challenge and SSL configuration snippets
+- name: Copy fastcgi parameters and SSL configuration snippets
copy: src=etc/nginx/snippets/{{ item }}
dest=/etc/nginx/snippets/{{ item }}
owner=root group=root
@@ -20,7 +20,6 @@
- fastcgi-php-ssl.conf
- ssl.conf
- headers.conf
- - acme-challenge.conf
notify:
- Restart Nginx
@@ -51,8 +50,19 @@
notify:
- Restart Nginx
+# WARN Bullseye: nginx >=1.15.1 uses font/woff and font/woff2 (cf. https://trac.nginx.org/nginx/ticket/1243)
+# however Bootstrap(?) appears to query resources with "Accept: application/font-woff" resp. application/font-woff2.
+# Unfortunately it also uses "Accept-Encoding: identity" so the resource isn't compressed...
+- name: Fix MIME type for woff
+ lineinfile: dest=/etc/nginx/mime.types
+ insertafter='^\s*\S+\s\s+woff;'
+ line=' application/font-woff2 woff2;'
+ register: r5
+ notify:
+ - Restart Nginx
+
- name: Start Nginx
service: name=nginx state=started
- when: not (r1.changed or r2.changed or r3.changed or r4.changed)
+ when: not (r1.changed or r2.changed or r3.changed or r4.changed or r5.changed)
- meta: flush_handlers
diff --git a/roles/common/files/etc/apt/apt.conf.d/50unattended-upgrades b/roles/common/files/etc/apt/apt.conf.d/50unattended-upgrades
index 86c6508..fd7cf1d 100644
--- a/roles/common/files/etc/apt/apt.conf.d/50unattended-upgrades
+++ b/roles/common/files/etc/apt/apt.conf.d/50unattended-upgrades
@@ -1,7 +1,7 @@
// Unattended-Upgrade::Origins-Pattern controls which packages are
// upgraded.
//
-// Lines below have the format format is "keyword=value,...". A
+// Lines below have the format "keyword=value,...". A
// package will be upgraded only if the values in its metadata match
// all the supplied keywords in a line. (In other words, omitted
// keywords are wild cards.) The keywords originate from the Release
@@ -19,15 +19,18 @@
// Within lines unattended-upgrades allows 2 macros whose values are
// derived from /etc/debian_version:
// ${distro_id} Installed origin.
-// ${distro_codename} Installed codename (eg, "jessie")
+// ${distro_codename} Installed codename (eg, "buster")
Unattended-Upgrade::Origins-Pattern {
// Codename based matching:
// This will follow the migration of a release through different
// archives (e.g. from testing to stable and later oldstable).
-// "o=Debian,n=jessie";
-// "o=Debian,n=jessie-updates";
-// "o=Debian,n=jessie-proposed-updates";
-// "o=Debian,n=jessie,l=Debian-Security";
+ // Software will be the latest available for the named release,
+ // but the Debian release itself will not be automatically upgraded.
+// "origin=Debian,codename=${distro_codename}-updates";
+// "origin=Debian,codename=${distro_codename}-proposed-updates";
+ "origin=Debian,codename=${distro_codename},label=Debian";
+ "origin=Debian,codename=${distro_codename},label=Debian-Security";
+ "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
// Archive or Suite based matching:
// Note that this will silently match a different release after
@@ -36,33 +39,53 @@ Unattended-Upgrade::Origins-Pattern {
// "o=Debian,a=stable";
// "o=Debian,a=stable-updates";
// "o=Debian,a=proposed-updates";
- "origin=Debian,codename=${distro_codename},label=Debian-Security";
+// "o=Debian Backports,a=${distro_codename}-backports,l=Debian Backports";
};
-// List of packages to not update (regexp are supported)
+// Python regular expressions, matching packages to exclude from upgrading
Unattended-Upgrade::Package-Blacklist {
-// "vim";
-// "libc6";
-// "libc6-dev";
-// "libc6-i686";
+ // The following matches all packages starting with linux-
+// "linux-";
+
+ // Use $ to explicitely define the end of a package name. Without
+ // the $, "libc6" would match all of them.
+// "libc6$";
+// "libc6-dev$";
+// "libc6-i686$";
+
+ // Special characters need escaping
+// "libstdc\+\+6$";
+
+ // The following matches packages like xen-system-amd64, xen-utils-4.1,
+ // xenstore-utils and libxenstore3.0
+// "(lib)?xen(store)?";
+
+ // For more information about Python regular expressions, see
+ // https://docs.python.org/3/howto/regex.html
};
// This option allows you to control if on a unclean dpkg exit
// unattended-upgrades will automatically run
// dpkg --force-confold --configure -a
// The default is true, to ensure updates keep getting installed
-//Unattended-Upgrade::AutoFixInterruptedDpkg "false";
+//Unattended-Upgrade::AutoFixInterruptedDpkg "true";
// Split the upgrade into the smallest possible chunks so that
-// they can be interrupted with SIGUSR1. This makes the upgrade
+// they can be interrupted with SIGTERM. This makes the upgrade
// a bit slower but it has the benefit that shutdown while a upgrade
// is running is possible (with a small delay)
//Unattended-Upgrade::MinimalSteps "true";
-// Install all unattended-upgrades when the machine is shuting down
-// instead of doing it in the background while the machine is running
-// This will (obviously) make shutdown slower
-//Unattended-Upgrade::InstallOnShutdown "true";
+// Install all updates when the machine is shutting down
+// instead of doing it in the background while the machine is running.
+// This will (obviously) make shutdown slower.
+// Unattended-upgrades increases logind's InhibitDelayMaxSec to 30s.
+// This allows more time for unattended-upgrades to shut down gracefully
+// or even install a few packages in InstallOnShutdown mode, but is still a
+// big step back from the 30 minutes allowed for InstallOnShutdown previously.
+// Users enabling InstallOnShutdown mode are advised to increase
+// InhibitDelayMaxSec even further, possibly to 30 minutes.
+//Unattended-Upgrade::InstallOnShutdown "false";
// Send email to this address for problems or packages upgrades
// If empty or unset then no email is sent, make sure that you
@@ -70,19 +93,29 @@ Unattended-Upgrade::Package-Blacklist {
// 'mailx' must be installed. E.g. "user@example.com"
Unattended-Upgrade::Mail "admin@fripost.org";
-// Set this value to "true" to get emails only on errors. Default
-// is to always send a mail if Unattended-Upgrade::Mail is set
-//Unattended-Upgrade::MailOnlyOnError "true";
+// Set this value to one of:
+// "always", "only-on-error" or "on-change"
+// If this is not set, then any legacy MailOnlyOnError (boolean) value
+// is used to chose between "only-on-error" and "on-change"
+//Unattended-Upgrade::MailReport "on-change";
+
+// Remove unused automatically installed kernel-related packages
+// (kernel images, kernel headers and kernel version locked tools).
+//Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
-// Do automatic removal of new unused dependencies after the upgrade
+// Do automatic removal of newly unused dependencies after the upgrade
+//Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
+
+// Do automatic removal of unused packages after the upgrade
// (equivalent to apt-get autoremove)
//Unattended-Upgrade::Remove-Unused-Dependencies "false";
// Automatically reboot *WITHOUT CONFIRMATION* if
-// the file /var/run/reboot-required is found after the upgrade
+// the file /var/run/reboot-required is found after the upgrade
//Unattended-Upgrade::Automatic-Reboot "false";
-// Automatically reboot even if there are users currently logged in.
+// Automatically reboot even if there are users currently logged in
+// when Unattended-Upgrade::Automatic-Reboot is set to true
//Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
// If automatic reboot is enabled and needed, reboot at the specific
@@ -99,3 +132,33 @@ Acquire::http::Dl-Limit "256";
// Specify syslog facility. Default is daemon
// Unattended-Upgrade::SyslogFacility "daemon";
+
+// Download and install upgrades only on AC power
+// (i.e. skip or gracefully stop updates on battery)
+// Unattended-Upgrade::OnlyOnACPower "true";
+
+// Download and install upgrades only on non-metered connection
+// (i.e. skip or gracefully stop updates on a metered connection)
+// Unattended-Upgrade::Skip-Updates-On-Metered-Connections "true";
+
+// Verbose logging
+// Unattended-Upgrade::Verbose "false";
+
+// Print debugging information both in unattended-upgrades and
+// in unattended-upgrade-shutdown
+// Unattended-Upgrade::Debug "false";
+
+// Allow package downgrade if Pin-Priority exceeds 1000
+// Unattended-Upgrade::Allow-downgrade "false";
+
+// When APT fails to mark a package to be upgraded or installed try adjusting
+// candidates of related packages to help APT's resolver in finding a solution
+// where the package can be upgraded or installed.
+// This is a workaround until APT's resolver is fixed to always find a
+// solution if it exists. (See Debian bug #711128.)
+// The fallback is enabled by default, except on Debian's sid release because
+// uninstallable packages are frequent there.
+// Disabling the fallback speeds up unattended-upgrades when there are
+// uninstallable packages at the expense of rarely keeping back packages which
+// could be upgraded or installed.
+// Unattended-Upgrade::Allow-APT-Mark-Fallback "true";
diff --git a/roles/common/files/etc/apt/listchanges.conf b/roles/common/files/etc/apt/listchanges.conf
index dc31f5e..cee0648 100644
--- a/roles/common/files/etc/apt/listchanges.conf
+++ b/roles/common/files/etc/apt/listchanges.conf
@@ -4,3 +4,6 @@ email_address=admin@fripost.org
confirm=0
save_seen=/var/lib/apt/listchanges.db
which=news
+email_format=text
+headers=false
+reverse=false
diff --git a/roles/common/files/etc/fail2ban/action.d/nftables-allports.local b/roles/common/files/etc/fail2ban/action.d/nftables-allports.local
index 3c8c030..3b9ebc8 100644
--- a/roles/common/files/etc/fail2ban/action.d/nftables-allports.local
+++ b/roles/common/files/etc/fail2ban/action.d/nftables-allports.local
@@ -9,8 +9,8 @@ actionunban =
[Init]
# With banaction = *-allports there is no need for separate rule names
-set_name = fail2ban
-blocktype = drop
+table = filter
+addr_set = fail2ban
[Init?family=inet6]
-set_name = fail2ban6
+addr_set = fail2ban6
diff --git a/roles/common/files/etc/fail2ban/fail2ban.local b/roles/common/files/etc/fail2ban/fail2ban.local
index 53cba35..23a92e9 100644
--- a/roles/common/files/etc/fail2ban/fail2ban.local
+++ b/roles/common/files/etc/fail2ban/fail2ban.local
@@ -1,16 +1,5 @@
[Definition]
-# Option: logtarget
-# Notes.: Set the log target. This could be a file, SYSLOG, STDERR or STDOUT.
-# Only one log target can be specified.
-# If you change logtarget from the default value and you are
-# using logrotate -- also adjust or disable rotation in the
-# corresponding configuration file
-# (e.g. /etc/logrotate.d/fail2ban on Debian systems)
-# Values: [ STDOUT | STDERR | SYSLOG | SYSOUT | FILE ] Default: STDERR
-#
-logtarget = /var/log/fail2ban/fail2ban.log
-
# Options: dbfile
# Notes.: Set the file for the fail2ban persistent data to be stored.
# A value of ":memory:" means database is only stored in memory
diff --git a/roles/common/files/etc/fail2ban/filter.d/dovecot.conf b/roles/common/files/etc/fail2ban/filter.d/dovecot.conf
index 4d4ea16..c8f5345 100644
--- a/roles/common/files/etc/fail2ban/filter.d/dovecot.conf
+++ b/roles/common/files/etc/fail2ban/filter.d/dovecot.conf
@@ -1,4 +1,6 @@
-# Fail2Ban filter Dovecot authentication and pop3/imap server
+# Fail2Ban filter Dovecot authentication and pop3/imap/managesieve server
+# guilhem 2020-05-19: This is the filter from Buster (fail2ban
+# 0.10.2-2.1) with managesieve to the list of protected services
#
[INCLUDES]
@@ -7,26 +9,39 @@ before = common.conf
[Definition]
-_daemon = (auth|dovecot(-auth)?|auth-worker)
+_auth_worker = (?:dovecot: )?auth(?:-worker)?
+_daemon = (?:dovecot(?:-auth)?|auth)
-# Take the filter from Stretch and add managesieve to the list of protected services
-failregex = ^%(__prefix_line)s(?:%(__pam_auth)s(?:\(dovecot:auth\))?:)?\s+authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(?:\s+user=\S*)?\s*$
- ^%(__prefix_line)s(?:pop3|imap|managesieve)-login: (?:Info: )?(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:auth failed, \d+ attempts( in \d+ secs)?|tried to use (disabled|disallowed) \S+ auth)\):( user=<[^>]+>,)?( method=\S+,)? rip=<HOST>(?:, lip=\S+)?(?:, TLS(?: handshaking(?:: SSL_accept\(\) failed: error:[\dA-F]+:SSL routines:[TLS\d]+_GET_CLIENT_HELLO:unknown protocol)?)?(: Disconnected)?)?(, session=<\S+>)?\s*$
- ^%(__prefix_line)s(?:Info|dovecot: auth\(default\)|auth-worker\(\d+\)): pam\(\S+,<HOST>\): pam_authenticate\(\) failed: (User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\))\s*$
- ^%(__prefix_line)s(?:auth|auth-worker\(\d+\)): (?:pam|passwd-file)\(\S+,<HOST>\): unknown user\s*$
- ^%(__prefix_line)s(?:auth|auth-worker\(\d+\)): Info: ldap\(\S*,<HOST>,\S*\): invalid credentials\s*$
+prefregex = ^%(__prefix_line)s(?:%(_auth_worker)s(?:\([^\)]+\))?: )?(?:%(__pam_auth)s(?:\(dovecot:auth\))?: |(?:pop3|imap|managesieve)-login: )?(?:Info: )?<F-CONTENT>.+</F-CONTENT>$
-ignoreregex =
+failregex = ^authentication failure; logname=\S* uid=\S* euid=\S* tty=dovecot ruser=\S* rhost=<HOST>(?:\s+user=\S*)?\s*$
+ ^(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:auth failed, \d+ attempts(?: in \d+ secs)?|tried to use (?:disabled|disallowed) \S+ auth)\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
+ ^pam\(\S+,<HOST>(?:,\S*)?\): pam_authenticate\(\) failed: (?:User not known to the underlying authentication module: \d+ Time\(s\)|Authentication failure \(password mismatch\?\)|Permission denied)\s*$
+ ^[a-z\-]{3,15}\(\S*,<HOST>(?:,\S*)?\): (?:unknown user|invalid credentials)\s*$
+ <mdre-<mode>>
-[Init]
+mdre-aggressive = ^(?:Aborted login|Disconnected)(?::(?: [^ \(]+)+)? \((?:no auth attempts|disconnected before auth was ready,|client didn't finish \S+ auth,)(?: (?:in|waited) \d+ secs)?\):(?: user=<[^>]*>,)?(?: method=\S+,)? rip=<HOST>(?:[^>]*(?:, session=<\S+>)?)\s*$
+
+mdre-normal =
+
+# Parameter `mode` - `normal` or `aggressive`.
+# Aggressive mode can be used to match log-entries like:
+# 'no auth attempts', 'disconnected before auth was ready', 'client didn't finish SASL auth'.
+# Note it may produce lots of false positives on misconfigured MTAs.
+# Ex.:
+# filter = dovecot[mode=aggressive]
+mode = normal
+
+ignoreregex =
journalmatch = _SYSTEMD_UNIT=dovecot.service
+datepattern = {^LN-BEG}TAI64N
+ {^LN-BEG}
+
# DEV Notes:
# * the first regex is essentially a copy of pam-generic.conf
# * Probably doesn't do dovecot sql/ldap backends properly (resolved in edit 21/03/2016)
-# * Removed the 'no auth attempts' log lines from the matches because produces
-# lots of false positives on misconfigured MTAs making regexp unusable
#
# Author: Martin Waschbuesch
# Daniel Black (rewrote with begin and end anchors)
diff --git a/roles/common/files/etc/logcheck/ignore.d.server/common-local b/roles/common/files/etc/logcheck/ignore.d.server/common-local
index e64ec44..9b0d0fe 100644
--- a/roles/common/files/etc/logcheck/ignore.d.server/common-local
+++ b/roles/common/files/etc/logcheck/ignore.d.server/common-local
@@ -1,43 +1,60 @@
# Ansible Managed
# Do NOT edit this file directly!
#
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: subsystem request for sftp by user [-_.[:alnum:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: User [-_.[:alnum:]]+ not allowed because account is locked$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: fatal: Read from socket failed: (Connection reset by peer|Connection timed out) \[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: (error: )?Received disconnect from [:.[:xdigit:]]+: (3|11|14): .* \[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Disconnecting: Change of username or service not allowed: \(\S+\) -> (\(\S+\) )?\[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Disconnecting: Too many authentication failures for invalid user [-_.[:alnum:]]+ from [:.[:xdigit:]]+ port [[:digit:]]+ ssh2? \[preauth\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: (error: )?Received disconnect from [:.[:xdigit:]]+ port [0-9]+:(3|11|14): .* \[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: (Disconnected from|Connection (closed|reset) by) [[:xdigit:].:]{3,39} port [0-9]+ \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: maximum authentication attempts exceeded for invalid user .* from [:.[:xdigit:]]+ port [0-9]+ ssh2 \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: (Disconnected from|Connection (closed|reset) by)( invalid user .*)? [[:xdigit:].:]{3,39} port [0-9]+( \[preauth\])?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: (Did not receive identification string|Invalid user .*) from [[:xdigit:].:]{3,39} port [0-9]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Invalid user .* from [:.[:xdigit:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: input_userauth_request: invalid user .* \[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: userauth_pubkey: unsupported public key algorithm: [[:alnum:]-]+ \[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: fatal: Write failed: (Connection (timed out|reset by peer)|Broken pipe) \[preauth\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: fatal: (no hostkey alg|Unable to negotiate a key exchange method) \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: (Disconnected from|Connection closed by) (invalid|authenticating) user .* \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Disconnecting (authenticating|invalid) user .* [[:xdigit:].:]{3,39} port [0-9]+: (Change of username or service not allowed: .* -> .*|Too many authentication failures) \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: ssh_dispatch_run_fatal: Connection from (invalid user .* )?[[:xdigit:].:]{3,39} port [0-9]+: (message authentication code incorrect|Connection corrupted|Broken pipe) \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Disconnected from user [a-z0-9]+ [[:xdigit:].:]{3,39} port [0-9]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Unable to negotiate with [:.[:xdigit:]]+ port [[:digit:]]+: no matching (host key type|key exchange method|cipher) found\. Their offer: [@.[:alnum:],-]+ \[preauth\]$
no matching cipher found: client [.@[:alnum:]-]+(,[.@[:alnum:]-]+)* server [.@[:alnum:]-]+(,[.@[:alnum:]-]+)* \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Bad packet length [0-9]+\. \[preauth\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Bad protocol version identification '.*' from [:.[:xdigit:]]+ port [[:digit:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Accepted publickey for [^[:space:]]+ from [^[:space:]]+ port [[:digit:]]+( (ssh|ssh2))?(: (DSA|RSA|ECDSA|ED25519) ([[:xdigit:]]{2}:){15}[[:xdigit:]]{2})?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: WARNING: no suitable primes in /etc/ssh/primes$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: dispatch_protocol_error: type [0-9]+ seq [0-9]+ \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: Bad remote protocol version identification: '.*'$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Corrupted MAC on input\. \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: Protocol major versions differ for [[:xdigit:].:]{3,39} port [0-9]+: .+ vs\. .+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: userauth_pubkey: key type [-[:alnum:]]+ not in PubkeyAcceptedKeyTypes \[preauth\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: kex_exchange_identification: Connection closed by remote host$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: kex_exchange_identification: read: Connection reset by peer$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: kex_exchange_identification: client sent invalid protocol identifier "
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: kex_exchange_identification: banner line contains invalid characters$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: send_error: write: Connection reset by peer$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: banner exchange: Connection from [[:xdigit:].:]{3,39} port [0-9]+: invalid format$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: error: beginning MaxStartups throttling$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: exited MaxStartups throttling after [0-9]{2}:[0-9]{2}:[0-9]{2}, [0-9]+ connections dropped$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sshd\[[[:digit:]]+\]: drop connection #[0-9]+ from \[[[:xdigit:].:]{3,39}\]:[0-9]+ on \[[[:xdigit:].:]{3,39}\]:[0-9]+ past MaxStartups$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Cleanup of Temporary Directories\.(\.\.)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ slapd\[[[:digit:]]+\]: connection_input: conn=[[:digit:]]+ deferring operation: binding$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (slapd\[[[:digit:]]+\]|slap(acl|add|auth|cat|dn|index)|ldap(add|compare|delete|exop|modify|modrdn|passwd|search|url|whoami)): DIGEST-MD5 common mech free$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: (Created|Removed) slice User Slice of [-[:alnum:]]+\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: (Created|Removed) slice User Slice of UID [-[:alnum:]]+\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[[0-9]+\]: (Listening on|Closed) .*\.$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Cleanup of Temporary Directories\.(\.\.)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Starting (Cleanup of Temporary Directories|Daily man-db regeneration)\.\.\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Started (Cleanup of Temporary Directories|Daily man-db regeneration)\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Session [0-9]+ of user [-[:alnum:]]+\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[[0-9]+\]: Startup finished in \S+\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd: pam_unix\(systemd-user:session\): session (opened|closed) for user [-[:alnum:]]+( by \(uid=0\))?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[[0-9]+\]: [-[:alnum:]\\_@]+\.(mount|scope|service|socket): Succeeded\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd(-[a-z]+)?\[[0-9]+\]: \[/[^]]+:[0-9]+\] Line references path below legacy directory /var/run/, updating /var/run/
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: /lib/systemd/system/[-[:alnum:]\\_@]+\.service:[0-9]+: PIDFile= references path below legacy directory /var/run/, updating /var/run/
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ runuser: pam_unix\(runuser:session\): session (opened|closed) for user [-[:alnum:]]+( by \(uid=0\))?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ CRON\[[0-9]+\]: pam_unix\(cron:session\): session (opened|closed) for user _?[-[:alnum:]]+(\(uid=[0-9]+\))?( by \(uid=0\))?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Session [0-9]+ of user [-[:alnum:]]+\.$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[[0-9]+\]: Received SIGRTMIN\+24 from PID [0-9]+ \(kill\)\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd-logind\[[0-9]+\]: New session c?[0-9]+ of user [-[:alnum:]]+\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd-logind\[[0-9]+\]: Removed session c?[0-9]+\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd-logind\[[0-9]+\]: Session [0-9]+ logged out\. Waiting for processes to exit\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: apt-daily(-upgrade)?\.timer: Adding .* random time\.$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sudo:[[:space:]]+[_[:alnum:].-]+ : TTY=(unknown|(pts/|tty|vc/)[[:digit:]]+) ; PWD=[^;]+ ; USER=[._[:alnum:]-]+ (; ENV=([_a-zA-Z]+=\S* )+)?; COMMAND=(/(usr|etc|bin|sbin)/|sudoedit )
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> (bytecode|main|daily)\.(cld|cvd) (is up to date|updated) \(version: [[:digit:]]+, sigs: [[:digit:]]+, f-level: [[:digit:]]+, builder: [._[:alnum:]-]+\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Starting Daily apt-listbugs preferences cleanup\.\.\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Finished (Daily apt-listbugs preferences cleanup|Cleanup of Temporary Directories|Autocommit of changes in /etc directory|Clean php session files|Launch apticron to notify of packages pending an update|Online ext4 Metadata Check for All Filesystems|Autocommit of changes in /etc directory)\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: [[:alnum:]@-]+\.(service|scope): Consumed ([0-9]+min )?[0-9.]+s CPU time\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: rsyslog\.service: Sent signal SIGHUP to main process [0-9]+ \(rsyslogd\) on client request\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sudo:[[:space:]]+[_[:alnum:].-]+ : (TTY=(unknown|(pts/|tty|vc/)[[:digit:]]+) ; )?PWD=[^;]+ ; USER=[._[:alnum:]-]+ (; ENV=([_a-zA-Z]+=\S* )+)?; COMMAND=(/(usr|etc|bin|sbin)/|sudoedit )
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> daily database available for update \(local version: [[:digit:]]+, remote version: [[:digit:]]+\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> (bytecode|main|daily)\.(cld|cvd) (database )?(is up to date|is up-to-date|updated) \(version: [[:digit:]]+, sigs: [[:digit:]]+, f-level: [[:digit:]]+, builder: [._[:alnum:]-]+\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Testing database: '/var/lib/clamav/tmp[^/]*/clamav-.*' \.\.\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Database test passed\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Received signal: wake up$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> ClamAV update process started at \w{3} \w{3} [ :[:digit:]]{16}$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: (\w{3} \w{3} [ :[:digit:]]{16} -> \^|WARNING: )Your ClamAV installation is OUTDATED!$
@@ -45,26 +62,69 @@ no matching cipher found: client [.@[:alnum:]-]+(,[.@[:alnum:]-]+)* server [.@[:
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: WARNING: getfile: [._[:alnum:]-]+ not found on remote server \(IP: [.[:digit:]]+\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: WARNING: Incremental update failed, trying to download daily\.cvd$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: (WARNING|ERROR): (getpatch: )?Can't download [._[:alnum:]-]+ from [.[:alnum:]-]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> \*Can't query [._[:alnum:]-]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Downloading [._[:alnum:]-]+ \[[[:digit:]]+%\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> DON'T PANIC! Read https?://www\.clamav\.net/(support/faq|documents/upgrading-clamav)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> DON'T PANIC! Read https://docs\.clamav\.net/manual/Installing\.html$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[0-9]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Database updated \([0-9]+ signatures\) from .* \(IP: [[:xdigit:].:]{3,39}\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[0-9]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Clamd successfully notified about the update\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ freshclam\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Received signal: re-opening log file$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ clamd\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Reading databases from /var/lib/clamav$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ clamd\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> SelfCheck: Database status OK\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ clamd\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> SelfCheck: (Database status OK|Database modification detected\. Forcing reload)\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ clamd\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Database correctly reloaded \([0-9]+ signatures\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ clamd\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> SIGHUP caught: re-opening log file\.$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ clamd\[[[:digit:]]+\]: \w{3} \w{3} [ :[:digit:]]{16} -> Activating the newly loaded database\.\.\.$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ kernel: \[ *[[:digit:]]+\.[[:digit:]]+ *\] Peer [.[:digit:]]+:[[:digit:]]+/[[:digit:]]+ unexpectedly shrunk window [[:digit:]]+:[[:digit:]]+ \(repaired\)$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ liblogging-stdlog: \[origin software="rsyslogd" swVersion="[0-9.]+" x-pid="[0-9]+" x-info="http://www\.rsyslog\.com"\] rsyslogd was HUPed$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ansible-([_a-z0-9]+|<stdin>): Invoked with
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ rsyslogd: \[origin software="rsyslogd" swVersion="[0-9.]+" x-pid="[0-9]+" x-info="https://www\.rsyslog\.com"\] rsyslogd was HUPed$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ smartd\[[0-9]+\]: Device: /dev/sd[a-z] \[SAT\], CHECK POWER STATUS spins up disk \(0x[0-9a-f]{2} -> 0xff\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ansible-([_a-z0-9.]+|<stdin>): Invoked with
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ python3\[[0-9]+\]: ansible-[_a-z0-9.]+ Invoked with
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ fail2ban-server\[[0-9]+\]: fail2ban\.filter\s*\[[0-9]+\]: INFO\s+\[[._[:alnum:]-]+\] Found [[:xdigit:].:]{3,39} -
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ fail2ban-server\[[0-9]+\]: fail2ban\.actions\s*\[[0-9]+\]: NOTICE\s+\[sshd\] (Ban|Unban) [[:xdigit:].:]{3,39}
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: notice Sympa::Request::Message::
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: info Sympa::Request::Handler::
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: notice Sympa::Bulk::store\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: notice Sympa::(Bulk|Spool)::store\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: info Sympa::Spool::_create\(\) Creating directory /var/spool/sympa/auth
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: (info|notice) Sympa::Spindle::Process(Incoming|Message|Template|Digest)::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: info Sympa::Spindle::Do(Command|Message)::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: info Sympa::Request::Handler::(subscribe|confirm|reject|signoff|distribute)::_twist\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: (info|notice) Sympa::Spindle::To(List|Moderation|Auth|Held)::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: notice Sympa::Spindle::AuthorizeMessage::_twist\(\) Message Sympa::Message .* rejected\(\) because sender not allowed$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: err main::#[0-9]+ > Sympa::Spindle::spin#[0-9]+ >
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ task_manager\[[0-9]+\]: (info|notice) main::
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info main::do_
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[[:digit:]]+\]: notice main:: \([.[:alnum:]-]+\) \[robot [.[:alnum:]-]+\] \[client [[:xdigit:].:]{3,39}\] Does NOT match HTTP_HOST; setting cookie_domain to [.[:alnum:]-]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[[:digit:]]+\]: notice Sympa::(Spindle::ProcessTemplate::_twist|Bulk::store)\(\)
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bulk\[[[:digit:]]+\]: notice Sympa::(Spindle::ProcessOutgoing::_twist|Mailer::store)\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ task_manager\[[0-9]+\]: (info|notice) Sympa::(Spindle::ProcessTask|Spool)::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info main::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info Sympa::Ticket::load\(\) Ticket:
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: notice main:: Redirecting to\s
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: notice main::do_login\(\) Authentication failed$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: err main::#[0-9]+ > main::do_login#[0-9]+ > Sympa::WWW::Auth::check_auth#[0-9]+ > Sympa::WWW::Auth::authentication#[0-9]+ Incorrect password for user\s
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: notice Sympa::WWW::Tools::_get_css_url\(\) Template file /usr/share/sympa/default/web_tt2/css\.tt2 or configuration has changed; updating CSS file /var/lib/sympa/css/[.[:alnum:]-]+/style\.css$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info Sympa::WWW::Session::new\(\) Ignoring unknown session cookie
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: err main::#[0-9]+ > main::get_parameters#[0-9]+ \[robot [.[:alnum:]-]+\] \[client [[:xdigit:].:]{3,39}\] Syntax error for parameter action value "(lists|search_list_request)#[^"]+" not conform to regexp:
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: err main::#[0-9]+ > main::check_action_parameters#[0-9]+ \[robot [.[:alnum:]-]+\] \[session [[:xdigit:]]+\] \[client [[:xdigit:].:]{3,39}\] \[list [.[:alnum:]-]+\] User not logged in$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: err main::#[0-9]+ > main::do_arc#[0-9]+ \[robot [.[:alnum:]-]+\] \[session [[:xdigit:]]+\] \[client [[:xdigit:].:]{3,39}\] \[list [.[:alnum:]-]+\] Empty archive Sympa::Archive <
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: notice Sympa::Spindle::Process(Request|Template)::_twist\(\) Processing Sympa::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info Sympa::Spindle::ToAuth::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: notice Sympa::Bulk::store\(\) Message Sympa::Message::Template <
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (wwsympa|sympa_msg)\[[0-9]+\]: notice Sympa::Spool::Outgoing::store\(\) Message Sympa::Message::Template <
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: notice Sympa::Spool::store\(\) Sympa::Request <
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info Sympa::Request::Handler::(add|signoff|subscribe)::_twist\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: info Sympa::Scenario::authz\(\) Sympa::Scenario <create_list\.[.[:alnum:]-]+;ERROR>: No rule match, reject$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ wwsympa\[[0-9]+\]: err main::#[0-9]+ > main::check_param_in#[0-9]+ > Sympa::Scenario::new#[0-9]+ Unable to find scenario file "
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sympa_msg\[[0-9]+\]: notice Sympa::Spool::Outgoing::store\(\) Message Sympa::Message <[^>]+> is stored into bulk spool as <[^>]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ mysqld\[[0-9]+\]: [0-9: -]{19} [0-9]+ \[Warning\] Aborted connection [0-9]+ to db: '[^']+' user: '[^']+' host: 'localhost' \(Got (timeout|an error) reading communication packets\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bulk\[[0-9]+\]: notice main:: Bulk exited normally due to signal$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bulk\[[0-9]+\]: notice Sympa::Mailer::store\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bulk\[[0-9]+\]: (info|notice) Sympa::Spindle::ProcessOutgoing::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bounced\[[0-9]+\]: notice Sympa::Tracking::store\(\) Sympa::Message <
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bounced\[[0-9]+\]: notice Sympa::Spindle::ProcessBounce::_twist\(\)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bounced\[[0-9]+\]: info Sympa::Spindle::ProcessBounce::_twist\(\) No address found in message Sympa::Message <
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ archived\[[0-9]+\]: notice Sympa::Spindle::ProcessArchive::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ archived\[[0-9]+\]: (info|notice) Sympa::Archive::
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ikiwiki\[[[:digit:]]+\]:
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ikiwiki: \[Fripost wiki\] (invalid page|unknown do) parameter$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ unbound: \[[0-9]+:[0-9]+\] info: generate keytag query _ta-[[:xdigit:]]+\. NULL IN$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ rrdcached\[[[:digit:]]+\]: (flushing old values|rotating journals|started new journal /\S+$|removing old journal /\S+$)
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ rrdcached\[[[:digit:]]+\]: queue_thread_main: rrd_update_r \(([^)]+)\) failed with status -1. \(opening '\1': No such file or directory\)
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ auditd\[[[:digit:]]+\]: Audit daemon rotating log files$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ stunnel(:|4\[[[:digit:]]+\]: [0-9]{4}\.[0-9]{2}\.[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) LOG3\[[[:digit:]]+\]: SSL_accept: (Peer suddenly disconnected|[[:xdigit:]]+: error:[[:xdigit:]]+:SSL routines:SSL2?3_GET_CLIENT_HELLO:(unknown protocol|http request|no shared cipher))$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dhclient\[[[:digit:]]+\]: DHCPREQUEST for [[:digit:].]{3,15} on [[:alnum:]]+ to [[:digit:].]{3,15} port 67$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ bacula-sd\[[[:digit:]]+\]: [._[:alnum:]-]+: askdir\.c:[0-9]+-[0-9]+ Discard: JobMedia Vol=[._[:alnum:]-]+ wrote=[0-9]+ MediaId=[0-9]+ FI=[0-9]+ LI=[0-9]+ StartAddr=[0-9]+ EndAddr=[0-9]+$
diff --git a/roles/common/files/etc/logcheck/ignore.d.server/dovecot-local b/roles/common/files/etc/logcheck/ignore.d.server/dovecot-local
index 66c8101..89c4b9a 100644
--- a/roles/common/files/etc/logcheck/ignore.d.server/dovecot-local
+++ b/roles/common/files/etc/logcheck/ignore.d.server/dovecot-local
@@ -1,17 +1,23 @@
# Ansible Managed
# Do NOT edit this file directly!
#
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap\([-_.@[:alnum:]]+\): (Logged out|Disconnected for inactivity) in=[[:digit:]]+ out=[[:digit:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap\([-_.@[:alnum:]]+\): Connection closed( \(IDLE running for .*\))? in=[[:digit:]]+ out=[[:digit:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap-hibernate\([-_.@[:alnum:]]+\): Connection closed in=[[:digit:]]+ out=[[:digit:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap|managesieve)-login: Login: user=<[^>]*>, method=[[:alnum:]-]+, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+, mpid=[0-9]+, (TLS|secured), session=<[^>]+>$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: (Aborted login|Disconnected) \(auth failed, [[:digit:]]+ attempts in [[:digit:]]+ secs\): user=<[^>]*>, method=[[:alnum:]-]+, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+, (TLS(: Disconnected)?|secured), session=<[^>]+>$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: (Aborted login|Disconnected(: Inactivity)?) \(no auth attempts in [[:digit:]]+ secs\): user=<>, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+, TLS( handshaking|: Disconnected)?(: SSL_(accept|read)\(\) syscall failed: (Connection reset by peer|Success))?, session=<[^>]+>$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: Disconnected(: Inactivity during authentication)? \(client didn't finish SASL auth, waited [[:digit:]]+ secs\): user=<>, method=[[:alnum:]-]+, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+, TLS(: SSL_read\(\) syscall failed: Connection reset by peer)?, session=<[^>]+>$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\): [+/[:alnum:]]{22}: msgid=((\? )?<[^>]*>|unspecified): saved mail to\s
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\): [+/[:alnum:]]{22}(:[0-9]+)?: sieve: msgid=((\? )?<[^>]*>|unspecified): (stored mail into mailbox '|(forwarded|discarded duplicate forward) to <[^[:space:]]+>$|marked message to be discarded if not explicitly delivered \(discard action\)$)
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([^@]+@[^@]+\): [+/[:alnum:]]{22}:[0-9]+: sieve: Execution of script \S+ failed, but implicit keep was successful \(user logfile \S+ may reveal additional details\)$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap|managesieve)-login: Maximum number of connections from user\+IP exceeded \(mail_max_userip_connections=[[:digit:]]+\): user=<[^>]*>, method=[[:alnum:]-]+, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+(, TLS, session=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\): Connect from local$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([0-9]+\): Disconnect from local: Successful quit$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap\([-_.@[:alnum:]]+\)<[0-9]+><[+/[:alnum:]]+>: Connection closed(: read\(size=[0-9]+\) failed: Connection reset by peer)? \((((UID )?[[:alpha:]\-]* finished [0-9.]+ secs ago|IDLE running for [^\)]+, state=wait-input|No commands sent)\))?( in=[0-9]+ out=[0-9]+ deleted=[0-9]+ expunged=[0-9]+ trashed=[0-9]+ hdr_count=[0-9]+ hdr_bytes=[0-9]+ body_count=[0-9]+ body_bytes=[0-9]+)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap\(.+\)<[0-9]+><[+/[:alnum:]]+>(: Disconnected)?(: Logged out| for inactivity( in reading our output)?|: Disconnected in [[:upper:]]+ \([0-9]+ msgs, [0-9]+ secs, [0-9]+/[0-9]+ bytes\)|: Too many invalid IMAP commands\.)?( in=[0-9]+ out=[0-9]+ deleted=[0-9]+ expunged=[0-9]+ trashed=[0-9]+ hdr_count=[0-9]+ hdr_bytes=[0-9]+ body_count=[0-9]+ body_bytes=[0-9]+)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap: Logged out in=[0-9]+ out=[0-9]+ deleted=0 expunged=0 trashed=0 hdr_count=0 hdr_bytes=0 body_count=0 body_bytes=0$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap\([-_.@[:alnum:]]+\)<[0-9]+><[+/[:alnum:]]+>: Connection closed(: (read\(size=[0-9]+\) failed: )?Connection reset by peer)?( \((LOGOUT,)?(UID )?[[:alpha:]\-]+ (running for|finished) [^\)]+\))? in=[0-9]+ out=[0-9]+ deleted=[0-9]+ expunged=[0-9]+ trashed=[0-9]+ hdr_count=[0-9]+ hdr_bytes=[0-9]+ body_count=[0-9]+ body_bytes=[0-9]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap-hibernate\([-_.@[:alnum:]]+\)<[0-9]+><[+/[:alnum:]]+>: Connection closed in=[0-9]+ out=[0-9]+ deleted=[0-9]+ expunged=[0-9]+ trashed=[0-9]+ hdr_count=[0-9]+ hdr_bytes=[0-9]+ body_count=[0-9]+ body_bytes=[0-9]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap|managesieve)-login: Login: user=<[^>]*>, method=[[:alnum:]-]+, rip=[[:xdigit:].:]{3,39}, lip=[[:xdigit:].:]{3,39}, mpid=[0-9]+, (TLS|secured)(: (read\(size=[0-9]+\) failed: )?Connection (closed|reset by peer))?, session=<[^>]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: managesieve\([-_.@[:alnum:]]+\)<[0-9]+><[+/[:alnum:]]+>: Disconnected: Logged out bytes=[[:digit:]]+/[[:digit:]]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (imap|managesieve)-login: (Disconnected(: Inactivity)?|Aborted login) \(auth failed, [[:digit:]]+ attempts in [[:digit:]]+ secs\): user=<[^>]*>, method=[A-Z\-]+, rip=[[:xdigit:].:]{3,39}, lip=[[:xdigit:].:]{3,39}, (TLS|SSL|secured)(: (Disconnected|Connection closed))?, session=<[^>]*>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (imap|managesieve)-login: Disconnected \((no auth attempts in|disconnected before auth was ready, waited) [[:digit:]]+ secs\):( user=<>,)? rip=[[:xdigit:].:]{3,39}, lip=[[:xdigit:].:]{3,39}, (TLS|SSL)( handshaking)?: (SSL_accept\(\)( syscall)? failed:|(read\(size=[0-9]+\) failed: )?Connection (closed|reset by peer), session=<[+/[:alnum:]]+>$|SSL_read failed: error:[[:xdigit:]]+:SSL routines:(ssl3_get_record:decryption failed or bad record mac|ssl3_read_bytes:unexpected record), session=<[+/[:alnum:]]+>$)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (imap|managesieve)-login: (Disconnected(: (Inactivity|Too many invalid commands\.?))?|Aborted login) \(no auth attempts in [[:digit:]]+ secs\):( user=<>,)? rip=[[:xdigit:].:]{3,39}, lip=[[:xdigit:].:]{3,39}(, (TLS|SSL)( handshaking)?)?, session=<[+/[:alnum:]]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: Disconnected(: Inactivity during authentication)? \(client didn't finish SASL auth, waited [[:digit:]]+ secs\): user=<>, method=[[:alnum:]-]+, rip=[[:xdigit:].:]{3,39}, lip=[[:xdigit:].:]{3,39}, TLS(: (read\(size=[0-9]+\) failed: Connection reset by peer|Disconnected|Connection closed))?, session=<[^>]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\)<[0-9]+><[+/[:alnum:]]{22}(:[0-9]+)?>: msgid=(\? )?(<[^>]*>|[^[:blank:]]*|[^,()]+@[.[:alnum:]-]+)( \(added by \S+@[.[:alnum:]-]+\))?: saved mail to\s
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\)<[0-9]+><[+/[:alnum:]]{22}(:[0-9]+)?>: sieve: msgid=(\? )?(<[^>]*>\s*|[^[:blank:]]*|[^,()]+@[.[:alnum:]-]+)( \(added by \S+@[.[:alnum:]-]+\))?: (stored mail into mailbox '|(forwarded|discarded duplicate forward) to <[^[:space:]]+>$|marked message to be discarded if not explicitly delivered \(discard action\)$)
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([^@]+@[^@]+\)<[0-9]+><[+/[:alnum:]]{22}(:[0-9]+)?>: sieve: Execution of script \S+ failed, but implicit keep was successful \(user logfile \S+ may reveal additional details\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap|managesieve)-login: Maximum number of connections from user\+IP exceeded \(mail_max_userip_connections=[[:digit:]]+\): user=<[^>]*>, method=[[:alnum:]-]+, rip=[[:xdigit:].:]{3,39}, lip=[[:xdigit:].:]{3,39}(, TLS, session=<[^>]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([0-9]+\): Disconnect from local: (Client has quit the connection|Remote closed connection) \(state=[[:upper:]]+\)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: auth: Warning: auth client [0-9]+ disconnected with [0-9]+ pending requests: (Connection reset by peer|EOF)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: stats: Warning: UPDATE-CMD: Already expired$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Dovecot authentication proxy\.(\.\.)?$
diff --git a/roles/common/files/etc/logcheck/ignore.d.server/postfix-local b/roles/common/files/etc/logcheck/ignore.d.server/postfix-local
index f539c3a..cffb438 100644
--- a/roles/common/files/etc/logcheck/ignore.d.server/postfix-local
+++ b/roles/common/files/etc/logcheck/ignore.d.server/postfix-local
@@ -13,37 +13,43 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/scache\[[[:digit:]]+\]: statistics: (domain|address) lookup hits=[[:digit:]]+ miss=[[:digit:]]+ success=[[:digit:]]+%$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/scache\[[[:digit:]]+\]: statistics: max simultaneous domains=[[:digit:]]+ addresses=[[:digit:]]+ connection=[[:digit:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: client=[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/cleanup\[[[:digit:]]+\]: [[:xdigit:]]+: (resent-)?message-id=([^[:blank:]]*|(mid:)?[[:alnum:]_/+.$@-]+)( \(added by [^[:space:]]+\))?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/cleanup\[[[:digit:]]+\]: [[:xdigit:]]+: (resent-)?message-id=([^[:blank:]]*|(mid:)?[[:alnum:]_/+.$@-]+|[^,()]+@[.[:alnum:]-]+)( \(added by [^[:space:]]+\))?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/cleanup\[[[:digit:]]+\]: warning: unix_trigger_event: read timeout for service public/qmgr$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/cleanup\[[[:digit:]]+\]: warning: milter unix:public/opendmarc: can't read SMFIC_BODYEOB reply packet header: Success$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/qmgr\[[[:digit:]]+\]: [[:xdigit:]]+: from=<[^>]*>, size=[[:digit:]]+, nrcpt=[[:digit:]]+ \(queue active\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:xdigit:]]+: from=<[^>]*>, status=expired, returned to sender$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:xdigit:]]+: message-id=(<[^>]*>|[[:alnum:]_/+.$@-]+)( \(added by [^[:space:]]+\))?
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:xdigit:]]+: removed$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:xdigit:]]+: skipped, still being delivered$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/verify\[[[:digit:]]+\]: cache [a-z]+:\S+ (partial|full) cleanup: retained=[[:digit:]]+ dropped=[[:digit:]]+ entries$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/smtpd\[[[:digit:]]+\]: disconnect from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]( (ehlo|helo|xforward|starttls|auth|mail|rcpt|data|noop|rset|quit|commands|unknown)=[0-9]+(/[0-9]+)?)+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/smtpd\[[[:digit:]]+\]: disconnect from [._[:alnum:]-]+\[([[:xdigit:].:]{3,39}|unknown)\]( (ehlo|helo|xforward|starttls|auth|mail|rcpt|data|bdat|noop|rset|quit|commands|unknown)=[0-9]+(/[0-9]+)?)+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: warning: non-SMTP command from [-._[:alnum:]]+\[[[:xdigit:].:]{3,39}\]:\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/pickup\[[[:digit:]]+\]: [[:xdigit:]]+: uid=[[:digit:]]+ from=<[^>]*>$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/cleanup\[[[:digit:]]+\]: [[:xdigit:]]+: replace: header\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: client=[^[:space:]]+, sasl_method=[-[:alnum:]]+, sasl_username=[-_.@[:alnum:]]+(, sasl_sender=[-_.@[:alnum:]]+)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: warning: [-._[:alnum:]]+\[[[:xdigit:].:]{3,39}\]: SASL (PLAIN|LOGIN) authentication (failed|aborted)(:[ [:alnum:]]*)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: improper command pipelining after (EHLO|HELO|MAIL|QUIT) from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]:
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: warning: [-._[:alnum:]]+\[[[:xdigit:].:]{3,39}\]: SASL [[:alpha:]]+ authentication (failed|aborted)(:[ [:alnum:]]*)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: improper command pipelining after (EHLO|HELO|AUTH|MAIL|QUIT) from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]:
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: warning: hostname [._[:alnum:]-]+ does not resolve to address [[:xdigit:].:]{3,39}(: Name or service not known)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: warning: Connection concurrency limit exceeded: [0-9]+ from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\] for service (submissions?|smtpd)$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 5[[:digit:]]{2} 5(\.[[:digit:]]){2} <[^>]+>: Helo command rejected: need fully-qualified hostname;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^>]+>)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 4[[:digit:]]{2} 4(\.[[:digit:]]){2} <[^>]+>: Sender address rejected: Domain not found;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^>]+>)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 5[[:digit:]]{2} 5(\.[[:digit:]]){2} Service unavailable; (Unverified Client host|Sender address) \[\S+\] blocked using [._[:alnum:]-]+; https?://[^[:blank:];]+; from=<[^>]*> to=<[^>]+> proto=E?SMTP( helo=<[^>]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 5[[:digit:]]{2} 5(\.[[:digit:]]+){2} <[^>]+>: Helo command rejected: need fully-qualified hostname;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^>]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 4[[:digit:]]{2} 4(\.[[:digit:]]+){2} <[^>]+>: Sender address rejected: (Domain not found|Malformed DNS server reply);( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^>]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 5[[:digit:]]{2} 5(\.[[:digit:]]+){2} <[^>]+>: Sender address rejected: Domain [.[:alnum:]-]+ does not accept mail \(nullMX\);( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^>]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 5[[:digit:]]{2} 5(\.[[:digit:]]+){2} Service unavailable; (Unverified Client host|Sender address) \[\S+\] blocked using [._[:alnum:]-]+(; https?://[^[:blank:];]+)?; from=<[^>]*> to=<[^>]+> proto=E?SMTP( helo=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[([[:xdigit:].:]{3,39})\]: 4[[:digit:]]{2} 4(\.[[:digit:]]+){2} Client host rejected: cannot find your hostname, \[\1\]; from=<[^>]*> to=<[^>]+> proto=E?SMTP( helo=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: timeout after [-[:upper:]]+( \([[:digit:]]+ bytes\))? from [^[:space:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-((msa|mx)/smtpd|out/smtp)\[[[:digit:]]+\]: warning: (tls_text_name: [-._[:alnum:]]+\[[[:xdigit:].:]{3,39}\]: )?peer certificate has no (subject CN|issuer Organization)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 450( 4\.1\.2)? <[^>]*>: Recipient address rejected: Domain not found;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 450( 4\.1\.8)? <[^>]*>: Sender address rejected: Domain not found;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: [[:upper:]]+ from [^[:space:]]+: 554( 5\.7\.1)? <>: Sender address rejected: Null sender not allowed; from=<> to=<[^>]+> proto=E?SMTP( helo=<[^[:space:]]+>)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 5[[:digit:]]{2} 5(\.[[:digit:]]){2} <[^>]*>: (Recipient|Sender) address rejected: need fully-qualified address;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: [[:upper:]]+ from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 550( 5\.7\.23)? <[^>]+>: Sender address rejected: Message rejected due to: (domain owner discourages use of this host|SPF fail - not authorized)\. Please see https?://www\.openspf\.net/Why\?\S+; from=<[^>]+> to=<[^>]+> proto=E?SMTP( helo=<[^[:space:]]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ policyd-spf\[[[:digit:]]+\]: 550( 5\.7\.23)? Message rejected due to: (domain owner discourages use of this host|SPF fail - not authorized)\. Please see https?://www\.openspf\.net/Why\?\S+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 5[[:digit:]]{2} 5(\.[[:digit:]]+){2} <[^>]*>: (Recipient|Sender) address rejected: need fully-qualified address;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 554 5\.7\.1 <[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]>: Client host rejected: Access denied;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mda/lmtp\[[[:digit:]]+\]: [[:xdigit:]]+: to=<[^>]+>,( orig_to=<[^>]+>,)? relay=[._[:alnum:]-]+\[private/dovecot-lmtpd\],( conn_use=[[:digit:]]+,)? delay=[.[:digit:]]+(, delays=([.[:digit:]]+/){3}[.[:digit:]]+)?(, dsn=2(\.[[:digit:]]+){2})?, status=sent \(2[[:digit:]][[:digit:]] .+\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-\w+/(error|n?qmgr|smtp)\[[[:digit:]]+\]: [[:xdigit:]]+: to=<[^>]+>,( orig_to=<[^>]+>,)? relay=(none|[^[:space:]]+\[[[:xdigit:].:]{3,39}\]:[[:digit:]]+),( conn_use=[[:digit:]]+,)? delay=[[:digit:].]+,( delays=[[:digit:]./]+,)?( dsn=[45]\.[[:digit:]]\.[[:digit:]],)? status=(deferred|undeliverable|bounced) \((delivery temporarily suspended: )?((lost connection with [^[:space:]]+|conversation with [^[:space:]]+ timed out) while (sending [[:alnum:]]+( [[:alnum:]]+)?|performing the (HELO|EHLO) handshake|receiving the initial server greeting|sending [[:alnum:]]+( [/[:alnum:]]+)?|sending end of data -- message may be sent more than once)|connect to [^[:space:]]+: (Connection timed out|read timeout|Connection refused|Network is unreachable|No route to host)|host [^[:space:]]+ refused to talk to me: [45][[:digit:]][[:digit:]].*|Host or domain name not found. Name service error for name=[^[:space:]]+ type=(MX|A|AAAA): Host (not found, try again|found but no data record of requested type)|User unknown in virtual alias table|host [^[:space:]]+\[[[:xdigit:].:]{3,39}\] said: [45][[:digit:]][[:digit:]] [45](\.[[:digit:]]+){2} <[^>]+>: (Temporarily rejected\. Try again later\.|Recipient address rejected: ((undeliverable|unverified) address:|Domain not found)) .*)\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: to=<[^>]+>,( orig_to=<[^>]+>,)? relay=[^[:space:]]+\[[[:xdigit:].:]{3,39}\]:[[:digit:]]+,( conn_use=[[:digit:]]+,)? delay=[[:digit:].]+,( delays=[[:digit:]./]+,)?( dsn=[45]\.[[:digit:]]\.[[:digit:]],)? status=undeliverable \(host
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: host [^[:space:]]+\[[[:xdigit:].:]{3,39}\]( said: 45[01] .* \(in reply to RCPT TO command\)| refused to talk to me: 421 )
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: lost connection with [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\] while (receiving the initial server greeting|sending [[:upper:] ]+|performing the HELO handshake|sending end of data -- message may be sent more than once)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: host [^[:space:]]+\[[[:xdigit:].:]{3,39}\]( said: 45[01][ -].* \(in reply to (MAIL FROM|RCPT TO) command\)| refused to talk to me: (421|450) )
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(out|msa)/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: lost connection with [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\] while (receiving the initial server greeting|sending [[:upper:] ]+|performing the HELO handshake|sending end of data -- message may be sent more than once)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: conversation with [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\] timed out while (sending message body|receiving the initial server greeting)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: to=<[^>]+>(, orig_to=<[^>]+>)?, relay=[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\](:[[:digit:]]{1,5})?,( conn_use=[[:digit:]]+,)? delay=[.[:digit:]]+(, delays=([.[:digit:]]+/){3}[.[:digit:]]+)?(, dsn=[45](\.[[:digit:]]+){2})?, status=(deferred|bounced|undeliverable|SOFTBOUNCE) \(host [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\] said: [45][[:digit:]][[:digit:]][- ]+.* \(in reply to (HELO|EHLO|MAIL FROM|RCPT TO|DATA|end of DATA) command\)\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: connect to [^[:space:]]+: (read timeout|Connection (refused|timed out)|Network is unreachable|No route to host)( \(port [[:digit:]]+\))?$
@@ -52,10 +58,11 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mda|out|lists)/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: client=[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\], orig_queue_id=[[:xdigit:]]+, orig_client=[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [A-Z[:digit:]]+: to=<[^>]+>,( orig_to=<[^>]+>,) relay=[^[:space:]]+, delay=[[:digit:]]+, status=deferred \(host [^[:space:]]+ said: [45][[:digit:]]{2} <[^[:space:]]*>: Recipient address rejected: Greylisted for [[:digit:]]+ (seconds|minutes)(\(see http://isg.ee.ethz.ch/tools/postgrey/help/[.[:alnum:]-]+.html\))? \(in reply to (HELO|EHLO|MAIL FROM|RCPT TO|DATA|end of DATA) command\)\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: to=<.*>,( orig_to=<[^>]+>,)? relay=[^[:space:]]+\](:[[:digit:]]+)?,( conn_use=[[:digit:]]+,)? delay=[[:digit:].]+,( delays=[[:digit:]./]+,)?( dsn=4\.[[:digit:]]\.[[:digit:]],)? status=deferred \(host [^[:space:]]+\] said: .*$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mda|out)/smtpd?\[[[:digit:]]+\]: warning: numeric domain name in resource data of MX record for [._[:alnum:]-]+: [[:xdigit:].:]{3,39}$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mda|out|mx)/smtpd?\[[[:digit:]]+\]: warning: valid_hostname: numeric hostname: [0-9]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mda|out|mx)/smtpd?\[[[:digit:]]+\]: warning: (malformed|numeric) domain name in resource data of MX record for [._[:alnum:]-]+:\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: warning: no MX host for [._[:alnum:]-]+ has a valid address record$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/smtpd\[[[:digit:]]+\]: SSL_accept error from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: (lost connection|Connection reset by peer|-?[[:digit:]]+|Connection timed out)$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:SSL3_READ_BYTES:(sslv3|tlsv1) alert (unknown ca|certificate unknown):s3_pkt.c:[0-9]+:SSL alert number [[:digit:]]+:$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/smtpd\[[[:digit:]]+\]: SSL_accept error from [._[:alnum:]-]+\[([[:xdigit:].:]{3,39}|unknown)\]: (lost connection|Connection reset by peer|-?[[:digit:]]+|Connection timed out|Broken pipe)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:SSL3_READ_BYTES:(sslv3|tlsv1) alert (unknown ca|certificate unknown):s3_pkt\.c:[0-9]+:SSL alert number [[:digit:]]+:$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(out|mx)/bounce\[[[:digit:]]+\]: [[:xdigit:]]+: sender (delay|non-delivery|delivery status) notification: [[:xdigit:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: host [^[:space:]]+ refused to talk to me: [45][[:digit:]][[:digit:]].*$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: enabling PIX <CRLF>\.<CRLF> workaround for [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\](:[[:digit:]]{1,5})?$
@@ -64,20 +71,23 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: SSL_connect error to [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]:[[:digit:]]+: -?[[:digit:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:SSL2?3_CHECK_CERT_AND_ALGORITHM:dh key too small:s2?3_clnt\.c:[0-9]+:$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:xdigit:]]+: Cannot start TLS: handshake failure$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/tlsproxy\[[[:digit:]]+\]: TLS handshake failed for service=smtpd peer=\[[[:xdigit:].:]{3,39}\]:[0-9]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: Host offered STARTTLS: \[[._[:alnum:]-]+\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: reject: RCPT from [^[:space:]]+: 4[[:digit:]][[:digit:]]( 4(\.[[:digit:]]){2}) <[^[:space:]]*>: Recipient address rejected: Greylisted( for [[:digit:]]+ (second|minute)s)?, see https?://[-_.:/[:alnum:]]+\.html?; from=<[^>]*> to=<[^>]+> proto=E?SMTP helo=<[^[:space:]]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: reject: RCPT from [^[:space:]]+: 4[[:digit:]][[:digit:]]( 4(\.[[:digit:]]+){2}) <[^[:space:]]*>: Recipient address rejected: Greylisted( for [[:digit:]]+ (second|minute)s)?, see https?://[-_.:/[:alnum:]]+\.html?; from=<[^>]*> to=<[^>]+> proto=E?SMTP helo=<[^[:space:]]+>$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-\w+/smtpd\[[[:digit:]]+\]: lost connection after [[:upper:]]+( \([[:digit:]]+ bytes\))? from [._[:alnum:]-]+\[(unknown|[[:xdigit:].:]{3,39})\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: reject: (CONNECT|RCPT) from [^[:space:]]+: [45][[:digit:]][[:digit:]]( [45](\.[[:digit:]]){2})? Service unavailable; Client host \[([[:xdigit:].:]{3,39}|[-._[:alnum:]]+)\] blocked using [._[:alnum:]-]+;( .+;)? (from=<[^>]*> to=<[^>]+> )?proto=E?SMTP( helo=<[^[:space:]]+>)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: RCPT from [^[:space:]]+: [[:digit:]]{3}( [45](\.[[:digit:]]){2})? <[^[:space:]]*>: Relay access denied; from=<[^>]*> to=<[^>]+> proto=E?SMTP helo=<[^[:space:]]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: reject: (CONNECT|RCPT) from [^[:space:]]+: [45][[:digit:]][[:digit:]]( [45](\.[[:digit:]]+){2})? Service unavailable; Client host \[([[:xdigit:].:]{3,39}|[-._[:alnum:]]+)\] blocked using [._[:alnum:]-]+;( .+;)? (from=<[^>]*> to=<[^>]+> )?proto=E?SMTP( helo=<[^[:space:]]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: RCPT from [^[:space:]]+: [[:digit:]]{3}( [45](\.[[:digit:]]+){2})? <[^[:space:]]*>: Relay access denied; from=<[^>]*> to=<[^>]+> proto=E?SMTP helo=<[^[:space:]]+>$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: warning: ([-._[:alnum:]]+): RBL lookup error: Host or domain name not found\. Name service error for name=\1 type=A(AAA)?: Host not found, try again$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/(smtpd|tlsproxy)\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:SSL2?3_(GET_RECORD:(decryption failed or bad record mac|wrong version number):s3_pkt\.c:[0-9]+:|READ_BYTES:(reason\([[:digit:]]+\)|sslv3 alert (unexpected message|bad certificate)):s3_pkt\.c:[[:digit:]]+:SSL alert number (0|10|42):|GET_CLIENT_HELLO:(unsupported protocol|no shared cipher|unknown protocol|wrong version number):s2?3_srvr\.c:[0-9]+:)$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/tlsproxy\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:tls_post_process_client_hello:no shared cipher:\.\./ssl/statem/statem_srvr\.c:[0-9]+:$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:tls_process_client_hello:(no shared cipher|unknown protocol|version too low):\.\./ssl/statem/statem_srvr\.c:[0-9]+:$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 554( 5\.1\.[01])? <[^[:space:]]*>: Recipient address rejected: User unknown in virtual alias table;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: reject: RCPT from [^[:space:]]+: [45][[:digit:]][[:digit:]]( [45](\.[[:digit:]]){2})? <[^[:space:]]*>: Helo command rejected: .+; from=<[^>]*> to=<[^>]+> proto=E?SMTP helo=<[^[:space:]]+>$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/(smtpd|tlsproxy)\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:(ssl3_get_record:(wrong version number|http request|packet length too long|bad record type)|tls_post_process_client_hello:no shared cipher):
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/(smtpd|tlsproxy)\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:ssl3_read_bytes:(sslv3 alert bad certificate|unexpected record|tlsv1 alert user cancelled):
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/(smtpd|tlsproxy)\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:tls_parse_ctos_key_share:bad key share:
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/(smtpd|tlsproxy)\[[[:digit:]]+\]: warning: TLS library problem: error:[[:xdigit:]]+:SSL routines:tls_((early_)?post_)?process_client_hello:(no shared cipher|unknown protocol|unsupported protocol|version too low):\.\./ssl/statem/statem_srvr\.c:[0-9]+:$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 55[0-9]( 5\.1\.[01])? <[^[:space:]]*>: Recipient address rejected: User unknown in virtual alias table;( from=<[^>]*> to=<[^>]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: [[:xdigit:]]+: reject: RCPT from [^[:space:]]+: [45][[:digit:]][[:digit:]]( [45](\.[[:digit:]]+){2})? <[^[:space:]]*>: Helo command rejected: .+; from=<[^>]*> to=<[^>]+> proto=E?SMTP helo=<[^[:space:]]+>$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: too many errors after ([[:upper:]]{4}|END-OF-MESSAGE|UNKNOWN|DATA \(0 bytes\)) from [._[:alnum:]-]+\[[.[:digit:]]+\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/smtpd\[[[:digit:]]+\]: warning: hostname [^[:space:]]+ does not resolve to address [[:xdigit:].:]{3,39}: (No address associated with hostname|Temporary failure in name resolution)$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: warning: (numeric hostname: [[:xdigit:].:]{3,39}|valid_hostname: misplaced delimiter: \S)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/smtpd\[[[:digit:]]+\]: warning: (numeric hostname: [[:xdigit:].:]{3,39}|valid_hostname: misplaced delimiter: \S)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|lists)/pipe\[[[:digit:]]+\]: [[:xdigit:]]+: to=<[^>]+>,( orig_to=<[^>]+>,)* relay=([-_.[:alnum:]]+), delay=[.[:digit:]]+(, delays=([.[:digit:]]+/){3}[.[:digit:]]+)?(, dsn=2(\.[[:digit:]]+){2})?, status=sent \(delivered via \3 service\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/discard\[[[:digit:]]+\]: [[:xdigit:]]+: to=<[^>]+>,( orig_to=<[^>]+>,)* relay=none, delay=[.[:digit:]]+(, delays=([.[:digit:]]+/){3}[.[:digit:]]+)?(, dsn=2(\.[[:digit:]]+){2})?, status=sent \(discard\.fripost\.org\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mx|msa)/smtpd\[[[:digit:]]+\]: warning: Illegal address syntax from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\] in (MAIL|RCPT) command:\s
@@ -90,10 +100,11 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: NON-SMTP COMMAND from \[[[:xdigit:].:]{3,39}\]:[[:digit:]]+\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: COMMAND PIPELINING from \[[[:xdigit:].:]{3,39}\]:[[:digit:]]+ after\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: DNSBL rank [[:digit:]]+ for \[[[:xdigit:].:]{3,39}\]:[[:digit:]]+$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from (\[[[:xdigit:].:]{3,39}\]):[[:digit:]]+: [45][[:digit:]][[:digit:]]( [45](\.[[:digit:]]){2})? (Service unavailable; client \1 blocked using [._[:alnum:]-]+|Protocol error|Service currently unavailable);( .+;)? (from=<[^>]*>, to=<[^>]+>, )?proto=E?SMTP(, helo=<[^[:space:]]+>)?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: [[:upper:]]+ without valid RCPT from \[[[:xdigit:].:]{3,39}\]:[[:digit:]]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from (\[[[:xdigit:].:]{3,39}\]):[[:digit:]]+: [45][[:digit:]][[:digit:]]( [45](\.[[:digit:]]+){2})? (Service unavailable; client \1 blocked using [._[:alnum:]-]+|Protocol error|Service currently unavailable);( .+;)? (from=<[^>]*>, to=<[^>]+>, )?proto=E?SMTP(, helo=<[^[:space:]]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: NOQUEUE: reject: CONNECT from \[[[:xdigit:].:]{3,39}\]:[[:digit:]]+: too many connections$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: COMMAND (COUNT|TIME) LIMIT from \[[[:xdigit:].:]{3,39}\]:[[:digit:]]+( after [[:upper:]]+)?$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: cache [a-z]+:\S+ full cleanup: retained=[[:digit:]]+ dropped=[[:digit:]]+ entries$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: cache [a-z]+:\S+ (partial|full) cleanup: retained=[[:digit:]]+ dropped=[[:digit:]]+ entries$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: warning: getpeername: Transport endpoint is not connected -- dropping this connection$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: warning: psc_cache_update: lmdb:/var/lib/postfix-mx/postscreen_cache update average delay is [[:digit:]]+ ms$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/postscreen\[[[:digit:]]+\]: warning: dnsblog reply timeout [[:digit:]]+s for [._[:alnum:]-]+$
@@ -104,8 +115,19 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Postfix sender login socketmap\.(\.\.)?$
#
# Amavis
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) Passed (CLEAN|SPAM|UNCHECKED(-[A-Z]+)?|BAD-HEADER-[28]) {Relayed(Tagged)?(Internal|Inbound|Outbound)}, (INCOMING|OUTGOING)( LOCAL)? \[(IPv6:)?[[:xdigit:].:]{3,39}\](:[[:digit:]]+)?( \[[[:xdigit:].:]{3,39}\])? <[^>]*> -> (<[^>]*>,)+( Queue-ID: [[:xdigit:]]+,)?( Message-ID: <[^>]+>,)?( Resent-Message-ID: <[^>]+>,)? mail_id: [_-+[:alnum:]]+, Hits: -?[[:digit:].]*, size: [[:digit:]]+, queued_as: [[:xdigit:]]+(, dkim_(new|sd)?=[-_.,:[:alnum:]]+)?, [[:digit:]]+ ms$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) Passed (CLEAN|SPAM|UNCHECKED(-[A-Z]+)?|BAD-HEADER-[28]) {Relayed(Tagged)?(Internal|Inbound|Outbound)}, (INCOMING|OUTGOING)( LOCAL)? \[(IPv6:)?[[:xdigit:].:]{3,39}\](:[[:digit:]]+)?( \[[[:xdigit:].:]{3,39}\])? <[^>]*> -> (<[^>]*>,)+( Queue-ID: [[:xdigit:]]+,)?( Message-ID: <[^>]+>,)?( Resent-Message-ID: <[^>]+>,)? mail_id: [-_+[:alnum:]]+, Hits: -?[[:digit:].]*, size: [[:digit:]]+, queued_as: [[:xdigit:]]+(, dkim_(new|sd)?=[-_.,:[:alnum:]]+)?, [[:digit:]]+ ms$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) Passed (CLEAN|SPAM|UNCHECKED(-[A-Z]+)?|BAD-HEADER-[28]) {Relayed(Tagged)?(Internal|Inbound|Outbound)}, (INCOMING|OUTGOING)( LOCAL)? \[(IPv6:)?[[:xdigit:].:]{3,39}\](:[[:digit:]]+)?( \[[[:xdigit:].:]{3,39}\])? <[^>]*> -> (<[^>]*>,)*(<[^>]*)?\.\.\.$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) \.\.\.([^>]*>)?(,<[^>]*>)*,( Queue-ID: [[:xdigit:]]+,)?( Message-ID: <[^>]+>,)?( Resent-Message-ID: <[^>]+>,)? mail_id: [_-+[:alnum:]]+, Hits: -?[[:digit:].]*, size: [[:digit:]]+, queued_as: [[:xdigit:]]+(, dkim_(new|sd)?=[-_.,:[:alnum:]]+)?, [[:digit:]]+ ms$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) \.\.\.([^>]*>)?(,<[^>]*>)*,( Queue-ID: [[:xdigit:]]+,)?( Message-ID: <[^>]+>,)?( Resent-Message-ID: <[^>]+>,)? mail_id: [-_+[:alnum:]]+, Hits: -?[[:digit:].]*, size: [[:digit:]]+, queued_as: [[:xdigit:]]+(, dkim_(new|sd)?=[-_.,:[:alnum:]]+)?, [[:digit:]]+ ms$
# SMTP client connection caching was introduced in 2.6.0; the SMTP session is held for the next task, and is terminated by Postfix if the next mail comes soon enough
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtpd\[[[:digit:]]+\]: timeout after END-OF-MESSAGE from [._[:alnum:]-]+\[(127.0.0.1|::1)\]$
+#
+# OpenDMARC
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: implicit authentication service: [-_.[:alnum:]]+\.fripost\.org$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+: RFC5322 requirement error: (not exactly one Date field|multiple Message-Id fields)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+: unable to parse From header field$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+:? ignoring Authentication-Results at\s
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+: ignoring invalid ARC-Authentication-Results header ".*"
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+: SPF\((mailfrom|helo)\): \S+ (none|tempfail|fail|pass)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+: [-_.[:alnum:]]+ (none|fail|pass)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: [[:xdigit:]]+: opendmarc_policy_query_dmarc\([._[:alnum:]-]+\) returned status 4$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ opendmarc\[[0-9]+\]: opendmarc: opendmarc-arcares\.c:[0-9]+: opendmarc_arcares_strip_whitespace: Assertion `.*' failed\.$
diff --git a/roles/common/files/etc/logcheck/ignore.d.server/strongswan-local b/roles/common/files/etc/logcheck/ignore.d.server/strongswan-local
index cebfaba..e78e9f0 100644
--- a/roles/common/files/etc/logcheck/ignore.d.server/strongswan-local
+++ b/roles/common/files/etc/logcheck/ignore.d.server/strongswan-local
@@ -3,17 +3,22 @@
#
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[KNL\] creating acquire job for policy [[:xdigit:].:]{3,39}/[[:digit:]]+(\[\w+(/[[:alnum:]-]+)?\])? === [[:xdigit:].:]{3,39}/[[:digit:]]+(\[\w+(/[[:alnum:]-]+)?\])? with reqid \{[[:digit:]]+\}$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[KNL\] creating rekey job for CHILD_SA ESP/0x[[:xdigit:]]{8}/[[:xdigit:].:]{3,39}$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[KNL\] creating delete job for CHILD_SA ESP/0x[[:xdigit:]]{8}/[[:xdigit:].:]{3,39}$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[JOB\] CHILD_SA ESP/0x[[:xdigit:]]{8}/[[:xdigit:].:]{3,39} not found for delete$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[KNL\] creating delete job for CHILD_SA ESP/0x[[:xdigit:]]{8}/([[:xdigit:].:]{3,39}|%any)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[JOB\] CHILD_SA ESP/0x[[:xdigit:]]{8}/([[:xdigit:].:]{3,39}|%any) not found for delete$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ipsec\[[[:digit:]]+\]: [[:digit:]]+\[JOB\] spawning [0-9]+ worker threads$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] initiating IKE_SA [[:alnum:]._-]+\[[[:digit:]]+\] to [[:xdigit:].:]{3,39}$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] [[:xdigit:].:]{3,39} is initiating an IKE_SA$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] establishing CHILD_SA [[:alnum:]._-]+(\{[[:digit:]]+\})?$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] establishing CHILD_SA [[:alnum:]._-]+(\{[[:digit:]]+\}( reqid [0-9]+)?)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] IKE_SA [[:alnum:]._-]+\[[[:digit:]]+\] established between [[:xdigit:].:]{3,39}\[[^]\"]+\]\.\.\.[[:xdigit:].:]{3,39}\[[^]]+\]$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] CHILD_SA [[:alnum:]._-]+\{[[:digit:]]+\} established with SPIs [[:xdigit:]]{8}_i [[:xdigit:]]{8}_o and TS [[:xdigit:].:]{3,39}/[[:digit:]]+ === [[:xdigit:].:]{3,39}/[[:digit:]]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] ((in|out)bound )?CHILD_SA [[:alnum:]._-]+\{[[:digit:]]+\} established with SPIs [[:xdigit:]]{8}_i [[:xdigit:]]{8}_o and TS [[:xdigit:].:]{3,39}/[[:digit:]]+ === [[:xdigit:].:]{3,39}/[[:digit:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] closing CHILD_SA [[:alnum:]._-]+\{[[:digit:]]+\} with SPIs [[:xdigit:]]{8}_i \([[:digit:]]+ bytes\) [[:xdigit:]]{8}_o \([[:digit:]]+ bytes\) and TS [[:xdigit:].:]{3,39}/[[:digit:]]+ === [[:xdigit:].:]{3,39}/[[:digit:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] reauthenticating IKE_SA [[:alnum:]._-]+\[[[:digit:]]+\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[JOB\] deleting IKE_SA after [[:digit:]]+ seconds of CHILD_SA inactivity$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] deleting IKE_SA [[:alnum:]._-]+\[[[:digit:]]+\] between [[:xdigit:].:]{3,39}\[[^]\"]+\]\.\.\.[[:xdigit:].:]{3,39}\[[^]]+\]$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[JOB\] deleting CHILD_SA after [[:digit:]]+ seconds of inactivity$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[JOB\] CHILD_SA \{[[:digit:]]+\} not found for delete$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[JOB\] deleting half open IKE_SA with [[:xdigit:].:]{3,39} after timeout$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[IKE\] IKE_SA deleted$
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ vpn: [+-] .* [[:xdigit:].:]{3,39}/[[:digit:]]+ == [[:xdigit:].:]{3,39} -- [[:xdigit:].:]{3,39} == [[:xdigit:].:]{3,39}/[[:digit:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[MGR\] ignoring request with ID [[:digit:]]+, already processing$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[KNL\] flags changed for [[:xdigit:].:]{3,39} on e[nt][[:alnum:]]+
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ (charon|ipsec\[[[:digit:]]+\]): [[:digit:]]+\[KNL\] [[:xdigit:].:]{3,39} appeared on e[nt][[:alnum:]]+$
diff --git a/roles/common/files/etc/logcheck/logcheck.conf b/roles/common/files/etc/logcheck/logcheck.conf
index 9a7e7c6..4c7ff10 100644
--- a/roles/common/files/etc/logcheck/logcheck.conf
+++ b/roles/common/files/etc/logcheck/logcheck.conf
@@ -69,7 +69,18 @@ FQDN=1
# Controls [logcheck] prefix on Subject: lines
-#ADDTAG="no"
+#ADDTAG="no"
+
+# Previous versions of logcheck always sent messages in 7bit encoding,
+# even if that resulted in RFC-violating messages. For example, really
+# long syslog lines would generate too-long SMTP lines, which are
+# rejected at least by Debian's default exim configuration. The new
+# default is to let mime-construct pick an appropriate encoding, but you
+# can override it by setting the below (to any of the encodings
+# supported by mime-construct). You may need to do this if you have
+# tools handling logcheck emails that don't understand MIME encoding.
+
+#MIMEENCODING=
# Set a different location for temporary files than /tmp
# this is useful if your /tmp is small and you are getting
diff --git a/roles/common/files/etc/logcheck/violations.ignore.d/logcheck-sudo b/roles/common/files/etc/logcheck/violations.ignore.d/logcheck-sudo
index e474019..70673ae 100644
--- a/roles/common/files/etc/logcheck/violations.ignore.d/logcheck-sudo
+++ b/roles/common/files/etc/logcheck/violations.ignore.d/logcheck-sudo
@@ -1,7 +1,5 @@
-^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sudo: pam_krb5\(sudo:auth\): user [[:alnum:]-]+ authenticated as [[:alnum:]-]+@[.A-Z]+$
-# ignore sudo with custom ENV
-#^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo:[[:space:]]+[_[:alnum:].-]+ : TTY=(unknown|console|(pts/|tty|vc/)[[:digit:]]+) ; PWD=[^;]+ ; USER=[._[:alnum:]-]+ ; COMMAND=((/(usr|etc|bin|sbin)/|sudoedit ).*|list)$
-^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo:[[:space:]]+[_[:alnum:].-]+ : TTY=(unknown|console|(pts/|tty|vc/)[[:digit:]]+) ; PWD=[^;]+ ; USER=[._[:alnum:]-]+ (; ENV=([_a-zA-Z]+=\S* )+)?; COMMAND=((/(usr|etc|bin|sbin)/|sudoedit ).*|list)$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ sudo: pam_krb5\(sudo:auth\): user [._[:alnum:]-]+ authenticated as [._[:alnum:]-]+@[.A-Z]+$
+^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo:[[:space:]]+[_[:alnum:].-]+ : (TTY=(unknown|console|(pts/|tty|vc/)[[:digit:]]+) ; )?PWD=[^;]+ ; USER=[._[:alnum:]-]+( ; GROUP=[._[:alnum:]-]+)? ; COMMAND=((/(usr|etc|bin|sbin)/|sudoedit ).*|list)$
^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo:[[:space:]]+[_[:alnum:].-]+ : \(command continued\).*$
-^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo: pam_[[:alnum:]]+\(sudo:session\): session opened for user [[:alnum:]-]+ by ([[:alnum:]-]+)?\(uid=[0-9]+\)$
-^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo: pam_[[:alnum:]]+\(sudo:session\): session closed for user [[:alnum:]-]+$
+^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo: pam_[[:alnum:]]+\(sudo:session\): session opened for user [._[:alnum:]-]+\(uid=[0-9]+\) by ([[:alnum:]-]+)?\(uid=[0-9]+\)$
+^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sudo: pam_[[:alnum:]]+\(sudo:session\): session closed for user [._[:alnum:]-]+$
diff --git a/roles/common/files/etc/logrotate.d/fripost-mail b/roles/common/files/etc/logrotate.d/fripost-mail
index 4fc1a85..7f7ffc2 100644
--- a/roles/common/files/etc/logrotate.d/fripost-mail
+++ b/roles/common/files/etc/logrotate.d/fripost-mail
@@ -12,7 +12,7 @@
delaycompress
sharedscripts
postrotate
- invoke-rc.d rsyslog rotate > /dev/null
+ /usr/lib/rsyslog/rsyslog-rotate
endscript
}
@@ -29,6 +29,6 @@
delaycompress
sharedscripts
postrotate
- invoke-rc.d rsyslog rotate > /dev/null
+ /usr/lib/rsyslog/rsyslog-rotate
endscript
}
diff --git a/roles/common/files/etc/rkhunter.conf b/roles/common/files/etc/rkhunter.conf
index b6a7d06..c7358d0 100644
--- a/roles/common/files/etc/rkhunter.conf
+++ b/roles/common/files/etc/rkhunter.conf
@@ -14,8 +14,8 @@
# been made.
#
# Please review the documentation before posting bug reports or questions.
-# To report bugs, obtain updates, or provide patches or comments, please go
-# to: http://rkhunter.sourceforge.net
+# To report bugs, provide patches or comments, please go to:
+# http://rkhunter.sourceforge.net
#
# To ask questions about rkhunter, please use the 'rkhunter-users' mailing list.
# Note that this is a moderated list, so please subscribe before posting.
@@ -50,7 +50,8 @@
# should be configured with one entry per line as in the first example.
#
# If wildcard characters (globbing) are allowed for an option, then the
-# text describing the option will say so.
+# text describing the option will say so. Any globbing character explicitly
+# required in a pathname should be escaped.
#
# Space-separated lists may be enclosed by quotes, although they are not
# required. If they are used, then they must only appear at the start and
@@ -69,7 +70,9 @@
# If a configuration option is never set, then the program will assume a
# default value. The text describing the option will state the default value.
# If there is no default, then rkhunter will calculate a value or pathname
-# to use.
+# to use. If a value is set for a configuration option, then the default
+# value is ignored. If it is wished to keep the default value, as well as
+# any other set value, then the default must be explicitly set.
#
@@ -259,12 +262,12 @@ LOGFILE=/var/log/rkhunter.log
#
# USE_SYSLOG=authpriv.warning
#
-# Setting the value to 'none', or just leaving the option commented out,
+# Setting the value to 'NONE', or just leaving the option commented out,
# disables the use of syslog.
#
# The default value is not to use syslog.
#
-#USE_SYSLOG=authpriv.notice
+USE_SYSLOG=authpriv.warning
#
# Set the following option to '1' if the second colour set is to be used. This
@@ -317,12 +320,12 @@ AUTO_X_DETECT=1
#
# The default value is '0'.
#
-#ALLOW_SSH_PROT_V1=0
+ALLOW_SSH_PROT_V1=2
#
# This setting tells rkhunter the directory containing the SSH configuration
-# file. This setting will be worked out by rkhunter, and so should not
-# usually need to be set.
+# file. If unset, this setting will be worked out by rkhunter, and so should
+# not usually need to be set.
#
# This option has no default value.
#
@@ -330,8 +333,8 @@ AUTO_X_DETECT=1
#
# These two options determine which tests are to be performed. The ENABLE_TESTS
-# option can use the word 'all' to refer to all of the available tests. The
-# DISABLE_TESTS option can use the word 'none' to mean that no tests are
+# option can use the word 'ALL' to refer to all of the available tests. The
+# DISABLE_TESTS option can use the word 'NONE' to mean that no tests are
# disabled. The list of disabled tests is applied to the list of enabled tests.
#
# Both options are space-separated lists of test names, and both options may
@@ -349,15 +352,8 @@ AUTO_X_DETECT=1
# either of the options below are specified, then they will override the
# program defaults.
#
-# hidden_procs test requires the unhide and/or unhide.rb commands which are
-# part of the unhide respectively unhide.rb packages in Debian.
-#
-# apps test is disabled by default as it triggers warnings about outdated
-# applications (and warns about possible security risk: we better trust
-# the Debian Security Team).
-#
-ENABLE_TESTS=all
-DISABLE_TESTS=suspscan hidden_procs deleted_files packet_cap_apps apps
+ENABLE_TESTS=ALL
+DISABLE_TESTS=suspscan hidden_ports hidden_procs deleted_files packet_cap_apps apps
#
# The HASH_CMD option can be used to specify the command to use for the file
@@ -384,11 +380,13 @@ DISABLE_TESTS=suspscan hidden_procs deleted_files packet_cap_apps apps
#
# NOTE: Whenever this option is changed 'rkhunter --propupd' must be run.
#
-# The default value is the SHA1 function, or MD5 if SHA1 cannot be found.
+# The default value is the SHA256 function, unless prelinking is used in
+# which case it defaults to the SHA1 function.
#
-# Also see the HASH_FLD_IDX option.
+# Also see the HASH_FLD_IDX option. In addition, note the comments under
+# the PKGMGR option relating to the use of HASH_CMD.
#
-HASH_CMD=sha512sum
+HASH_CMD=SHA512
#
# The HASH_FLD_IDX option specifies which field from the HASH_CMD command
@@ -407,20 +405,28 @@ HASH_CMD=sha512sum
# properties file ('rkhunter.dat'), and when running the file properties check.
# For RedHat/RPM-based systems, 'RPM' can be used to get information from the
# RPM database. For Debian-based systems 'DPKG' can be used, for *BSD systems
-# 'BSD' can be used, and for Solaris systems 'SOLARIS' can be used. No value,
-# or a value of 'NONE', indicates that no package manager is to be used.
+# 'BSD' can be used, or for *BSD systems with the 'pkg' command 'BSDng' can be
+# used, and for Solaris systems 'SOLARIS' can be used. No value, or a value of
+# 'NONE', indicates that no package manager is to be used.
#
-# The current package managers, except 'SOLARIS', store the file hash values
-# using an MD5 hash function. The Solaris package manager includes a checksum
-# value, but this is not used by default (see USE_SUNSUM below).
+# The package managers obtain each file hash value using a hash function. The
+# Solaris package manager includes a 16-bit checksum value, but this is not
+# used by default (see USE_SUNSUM below). The 'RPM' and 'BSDng' package managers
+# currently use a SHA256 hash function. Other package managers will, typically,
+# use an MD5 hash function.
#
-# The 'DPKG' and 'BSD' package managers only provide MD5 hash values.
-# The 'RPM' package manager additionally provides values for the inode,
-# file permissions, uid, gid and other values. The 'SOLARIS' also provides
-# most of the values, similar to 'RPM', but not the inode number.
+# The 'DPKG', 'BSD' and 'BSDng' package managers only provide a file hash value.
+# The 'RPM' package manager additionally provides values for the inode, file
+# permissions, uid, gid and other values. The 'SOLARIS' package manager also
+# provides most of the values, similar to 'RPM', but not the inode number.
#
# For any file not part of a package, rkhunter will revert to using the
-# HASH_CMD hash function instead.
+# HASH_CMD hash function instead. This means that if the HASH_CMD option
+# is set, and PKGMGR is set, then the HASH_CMD hash function is only used,
+# and stored, for non-packaged files. All packaged files will use, and store,
+# whatever hash function the relevant package manager uses. So, for example,
+# with the 'RPM' package manager, packaged files will be stored with their
+# SHA256 value regardless of the value of the HASH_CMD option.
#
# NOTE: Whenever this option is changed 'rkhunter --propupd' must be run.
#
@@ -499,6 +505,9 @@ HASH_CMD=sha512sum
# simple command names.
# For example, 'top*' cannot be given, but '/usr/bin/top*' is allowed.
#
+# To extend the use of wildcards to include recursive checking of directories,
+# see the GLOBSTAR configuration option.
+#
# Specific files may be excluded by using the EXCLUDE_USER_FILEPROP_FILES_DIRS
# option. Wildcards may be used with this option.
#
@@ -528,11 +537,8 @@ HASH_CMD=sha512sum
#USER_FILEPROP_FILES_DIRS=/usr/local/sbin
#USER_FILEPROP_FILES_DIRS=/etc/rkhunter.conf
#USER_FILEPROP_FILES_DIRS=/etc/rkhunter.conf.local
-#USER_FILEPROP_FILES_DIRS=/var/lib/rkhunter/db/*
-#USER_FILEPROP_FILES_DIRS=/var/lib/rkhunter/db/i18n/*
+#USER_FILEPROP_FILES_DIRS=/etc/rkhunter.d/*
#EXCLUDE_USER_FILEPROP_FILES_DIRS=/opt/ps*
-#EXCLUDE_USER_FILEPROP_FILES_DIRS=/var/lib/rkhunter/db/mirrors.dat
-#EXCLUDE_USER_FILEPROP_FILES_DIRS=/var/lib/rkhunter/db/rkhunter*
#
# This option whitelists files and directories from existing, or not existing,
@@ -549,13 +555,17 @@ HASH_CMD=sha512sum
# NOTE: The user must take into consideration how often the file will appear
# and disappear from the system in relation to how often rkhunter is run. If
# the file appears, and disappears, too often then rkhunter may not notice
-# this. All it will see is that the file has changed. The inode-number and DTM
+# this. All it will see is that the file has changed. The inode number and DTM
# will certainly be different for each new file, and rkhunter will report this.
#
# The default value is the null string.
#
#EXISTWHITELIST=""
+# work around for usr-merge, cf. https://bugs.debian.org/932594
+EXISTWHITELIST=/usr/bin/egrep
+EXISTWHITELIST=/usr/bin/fgrep
+
#
# Whitelist various attributes of the specified file. The attributes are those
# of the 'attributes' test. Specifying a file name here does not include it
@@ -586,7 +596,9 @@ HASH_CMD=sha512sum
#
SCRIPTWHITELIST=/bin/egrep
SCRIPTWHITELIST=/bin/fgrep
-SCRIPTWHITELIST=/bin/which
+SCRIPTWHITELIST=/usr/bin/egrep
+SCRIPTWHITELIST=/usr/bin/fgrep
+SCRIPTWHITELIST=/usr/bin/which
SCRIPTWHITELIST=/usr/bin/ldd
SCRIPTWHITELIST=/usr/bin/lwp-request
SCRIPTWHITELIST=/usr/sbin/adduser
@@ -612,6 +624,18 @@ SCRIPTWHITELIST=/usr/sbin/adduser
#IMMUTABLE_SET=0
#
+# If this option is set to '1', then any changed inode value is ignored in
+# the file properties check. The inode test itself still runs, but it will
+# always return that no inodes have changed.
+#
+# This option may be useful for filesystems such as Btrfs, which handle inodes
+# slightly differently than other filesystems.
+#
+# The default value is '0'.
+#
+#SKIP_INODE_CHECK=0
+
+#
# Allow the specified hidden directory to be whitelisted.
#
# This option may be specified more than once, and may use wildcard characters.
@@ -644,13 +668,21 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#
# Allow the specified process to use deleted files. The process name may be
-# followed by a colon-separated list of full pathnames. The process will then
-# only be whitelisted if it is using one of the given files. For example:
+# followed by a colon-separated list of full pathnames (which have been
+# deleted). The process will then only be whitelisted if it is using one of
+# the given pathnames. For example:
#
# ALLOWPROCDELFILE=/usr/libexec/gconfd-2:/tmp/abc:/var/tmp/xyz
#
# This option may be specified more than once. It may also use wildcards, but
-# only in the file names.
+# only in the deleted file pathnames, not in the process name. The use of
+# extended pattern matching in pathname expansion (for example, '**') is not
+# supported for this option. However, the option itself extends globbing when
+# the '*' character is used by matching zero or more characters in the
+# pathname, including those in sub-directories. For example, the pathname
+# '/tmp/abc/def/xyz' would not be matched by shell globbing using '/tmp/*/xyz'
+# but is matched when used in this option. Similarly, using '/tmp/*' will
+# match any file found in the '/tmp' directory or any sub-directories.
#
# The default value is the null string.
#
@@ -707,6 +739,46 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#ALLOWDEVFILE=/dev/shm/sem.ADBE_*
#
+# Allow the specified process pathnames to use shared memory segments.
+#
+# This option may be specified more than once, and may use wildcard characters.
+#
+# The default value is the null string.
+#
+#ALLOWIPCPROC=/usr/bin/firefox
+#ALLOWIPCPROC=/usr/bin/vlc
+
+#
+# Allow the specified memory segment creator PIDs to use shared memory segments.
+#
+# This is a space-separated list of PID numbers (as given by the
+# 'ipcs -p' command). This option may be specified more than once.
+#
+# The default value is the null string.
+#
+#ALLOWIPCPID=12345 6789
+
+#
+# Allow the specified account names to use shared memory segments.
+#
+# This is a space-separated list of account names. The option may be specified
+# more than once.
+#
+# The default value is the null string.
+#
+#ALLOWIPCUSER=usera userb
+
+#
+# This option can be used to set the maximum shared memory segment size
+# (in bytes) that is not considered suspicious. Any segment above this size,
+# and with 600 or 666 permissions, will be considered suspicious during the
+# shared memory check.
+#
+# The default is 1048576 (1M) bytes.
+#
+#IPC_SEG_SIZE=1048576
+
+#
# This option is used to indicate if the Phalanx2 test is to perform a basic
# check, or a more thorough check. If the option is set to '0', then a basic
# check is performed. If it is set to '1', then all the directories in the
@@ -776,9 +848,9 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#
# This option tells rkhunter the local system startup file pathnames. The
-# directories will be searched for files. By default rkhunter will try and
-# determine were the startup files are located. If the option is set to 'NONE',
-# then certain tests will be skipped.
+# directories will be searched for files. If unset, then rkhunter will try
+# and determine were the startup files are located. If the option is set to
+# 'NONE' then certain tests will be skipped.
#
# This is a space-separated list of file and directory pathnames. The option
# may be specified more than once, and may use wildcard characters.
@@ -789,9 +861,9 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#
# This option tells rkhunter the pathname to the file containing the user
-# account passwords. This setting will be worked out by rkhunter, and so
-# should not usually need to be set. Users of TCB shadow files should not
-# set this option.
+# account passwords. If unset, this setting will be worked out by rkhunter,
+# and so should not usually need to be set. Users of TCB shadow files should
+# not set this option.
#
# This option has no default value.
#
@@ -825,9 +897,10 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#
# This option tells rkhunter the pathname to the syslog configuration file.
-# This setting will be worked out by rkhunter, and so should not usually need
-# to be set. A value of 'NONE' can be used to indicate that there is no
-# configuration file, but that the syslog daemon process may be running.
+# If unset, this setting will be worked out by rkhunter, and so should not
+# usually need to be set. A value of 'NONE' can be used to indicate that
+# there is no configuration file, but that the syslog daemon process may
+# be running.
#
# This is a space-separated list of pathnames. The option may be specified
# more than once.
@@ -896,7 +969,7 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#
# The default value is '1024000'.
#
-#SUSPSCAN_MAXSIZE=10240000
+#SUSPSCAN_MAXSIZE=1024000
#
# This option specifies the 'suspscan' test score threshold. Below this value
@@ -907,6 +980,18 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#SUSPSCAN_THRESH=200
#
+# This option may be used to whitelist file pathnames from the suspscan test.
+#
+# Shell globbing may be used in the pathname. Also see the GLOBSTAR configuration
+# option.
+#
+# This option may be specified more than once.
+#
+# The default value is the null string.
+#
+#SUSPSCAN_WHITELIST=""
+
+#
# The following options can be used to whitelist network ports which are known
# to have been used by malware.
#
@@ -1076,8 +1161,8 @@ ALLOWHIDDENFILE=/etc/.etckeeper
#
# This setting tells rkhunter the directory containing the available Linux
-# kernel modules. This setting will be worked out by rkhunter, and so should
-# not usually need to be set.
+# kernel modules. If unset, this setting will be worked out by rkhunter, and
+# so should not usually need to be set.
#
# This option has no default value.
#
@@ -1114,18 +1199,33 @@ WEB_CMD="/bin/false"
# The lock is set just before logging starts, and is removed when the program
# ends. It is used to prevent items such as the log file, and the file
# properties file, from becoming corrupted if rkhunter is running more than
-# once. The mechanism used is to simply create a lock file in the TMPDIR
+# once. The mechanism used is to simply create a lock file in the LOCKDIR
# directory. If the lock file already exists, because rkhunter is already
# running, then the current process simply loops around sleeping for 10 seconds
# and then retrying the lock. A value of '0' means not to use locking.
#
# The default value is '0'.
#
-# Also see the LOCK_TIMEOUT and SHOW_LOCK_MSGS options.
+# Also see the LOCKDIR, LOCK_TIMEOUT and SHOW_LOCK_MSGS options.
#
#USE_LOCKING=0
#
+# This option specifies the directory to be used when locking is enabled.
+# If the option is unset, then the directory to be used will be worked out
+# by rkhunter. In that instance the directories '/run/lock', '/var/lock',
+# '/var/run/lock', '/run' and '/var/run' will be checked in turn. If none
+# of those can be found, or are not read/writeable, then the TMPDIR directory
+# will be used.
+#
+# To avoid the lock file persisting across a server reboot, the directory
+# used should be memory-resident.
+#
+# This option has no default value.
+#
+#LOCKDIR=""
+
+#
# If locking is used, then rkhunter may have to wait to get the lock file.
# This option sets the total amount of time, in seconds, that rkhunter should
# wait. It will retry the lock every 10 seconds, until either it obtains the
@@ -1191,22 +1291,6 @@ WEB_CMD="/bin/false"
#UNHIDETCP_OPTS=""
#
-# If both the C 'unhide', and Ruby 'unhide.rb', programs exist on the system,
-# then it is possible to disable the execution of one of the programs if
-# desired. By default rkhunter will look for both programs, and execute each
-# of them as they are found. If the value of this option is '0', then both
-# programs will be executed if they are present. A value of '1' will disable
-# execution of the C 'unhide' program, and a value of '2' will disable the Ruby
-# 'unhide.rb' program. To disable both programs, then disable the
-# 'hidden_procs' test.
-#
-# The default value is '0'.
-#
-DISABLE_UNHIDE=1
-
-INSTALLDIR=/usr
-
-#
# This option can be set to either '0' or '1'. If set to '1' then the summary,
# shown after rkhunter has run, will display the actual number of warnings
# found. If it is set to '0', then the summary will simply indicate that
@@ -1249,3 +1333,25 @@ INSTALLDIR=/usr
#EMPTY_LOGFILES=""
#MISSING_LOGFILES=""
+#
+# This option can be set to either '0' or '1'. If set to '1' then the globbing
+# characters '**' can be used to allow the recursive checking of directories.
+# This can be useful, for example, with the USER_FILEPROP_FILES_DIRS option.
+# For example:
+#
+# USER_FILEPROP_FILES_DIRS=/etc/**/*.conf
+#
+# This will check all '.conf' files within the '/etc' directory, and any
+# sub-directories (at any level). If GLOBSTAR is not set, then the shell will
+# interpret '**' as '*' and only one level of sub-directories will be checked.
+#
+# NOTE: This option is only valid for those shells which support the 'globstar'
+# option. Typically this will be 'bash' (version 4 and above) via the 'shopt' command,
+# and 'ksh' via the 'set' command.
+#
+# The default value is '0'.
+#
+#GLOBSTAR=0
+
+INSTALLDIR=/usr
+
diff --git a/roles/common/files/etc/rsyslog.conf b/roles/common/files/etc/rsyslog.conf
index 70e8a77..42b01c5 100644
--- a/roles/common/files/etc/rsyslog.conf
+++ b/roles/common/files/etc/rsyslog.conf
@@ -1,7 +1,7 @@
-# /etc/rsyslog.conf Configuration file for rsyslog.
+# /etc/rsyslog.conf configuration file for rsyslog
#
-# For more information see
-# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html
+# For more information install rsyslog-doc and see
+# /usr/share/doc/rsyslog-doc/html/configuration/index.html
#################
@@ -75,7 +75,7 @@ if $programname == 'amavis' and $syslogfacility-text == 'mail' and $syslogseveri
if ($programname startswith 'postfix-' or $programname == 'dovecot') and $syslogfacility-text == 'mail' and $syslogseverity >= 6 then ~
#
-# Some standard log files. Log by facility.
+# First some standard log files. Log by facility.
#
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
@@ -90,11 +90,11 @@ user.* -/var/log/user.log
#
*.=debug;\
auth,authpriv.none;\
- news.none;mail.none -/var/log/debug
+ mail.none -/var/log/debug
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
- mail,news.none -/var/log/messages
+ mail.none -/var/log/messages
#
# Emergencies are sent to everybody logged in.
diff --git a/roles/common/files/etc/strongswan.d/charon.conf b/roles/common/files/etc/strongswan.d/charon.conf
index 5ed6452..7cbe7db 100644
--- a/roles/common/files/etc/strongswan.d/charon.conf
+++ b/roles/common/files/etc/strongswan.d/charon.conf
@@ -1,15 +1,19 @@
# Options for the charon IKE daemon.
charon {
+ # Deliberately violate the IKE standard's requirement and allow the use of
+ # private algorithm identifiers, even if the peer implementation is unknown.
+ # accept_private_algs = no
+
# Accept unencrypted ID and HASH payloads in IKEv1 Main Mode.
# accept_unencrypted_mainmode_messages = no
# Maximum number of half-open IKE_SAs for a single peer IP.
# block_threshold = 5
- # Whether Certicate Revocation Lists (CRLs) fetched via HTTP or LDAP should
- # be saved under a unique file name derived from the public key of the
- # Certification Authority (CA) to /etc/ipsec.d/crls (stroke) or
+ # Whether Certificate Revocation Lists (CRLs) fetched via HTTP or LDAP
+ # should be saved under a unique file name derived from the public key of
+ # the Certification Authority (CA) to /etc/ipsec.d/crls (stroke) or
# /etc/swanctl/x509crl (vici), respectively.
# cache_crls = no
@@ -17,6 +21,13 @@ charon {
# memory.
# cert_cache = yes
+ # Whether to use DPD to check if the current path still works after any
+ # changes to interfaces/addresses.
+ # check_current_path = no
+
+ # Send the Cisco FlexVPN vendor ID payload (IKEv2 only).
+ # cisco_flexvpn = no
+
# Send Cisco Unity vendor ID payload (IKEv1 only).
# cisco_unity = no
@@ -29,6 +40,10 @@ charon {
# Delete CHILD_SAs right after they got successfully rekeyed (IKEv1 only).
# delete_rekeyed = no
+ # Delay in seconds until inbound IPsec SAs are deleted after rekeyings
+ # (IKEv2 only).
+ # delete_rekeyed_delay = 5
+
# Use ANSI X9.42 DH exponent size or optimum size matched to cryptographic
# strength.
# dh_exponent_ansi_x9_42 = yes
@@ -56,6 +71,10 @@ charon {
# Whether to follow IKEv2 redirects (RFC 5685).
# follow_redirects = yes
+ # Violate RFC 5998 and use EAP-only authentication even if the peer did not
+ # send an EAP_ONLY_AUTHENTICATION notify during IKE_AUTH.
+ # force_eap_only_authentication = no
+
# Maximum size (complete IP datagram size in bytes) of a sent IKE fragment
# when using proprietary IKEv1 or standardized IKEv2 fragmentation, defaults
# to 1280 (use 0 for address family specific default values, which uses a
@@ -131,6 +150,11 @@ charon {
# NAT keep alive interval.
# keep_alive = 20s
+ # Number of seconds the keep alive interval may be exceeded before a DPD is
+ # sent instead of a NAT keep alive (0 to disable). This is only useful if a
+ # clock is used that includes time spent suspended (e.g. CLOCK_BOOTTIME).
+ # keep_alive_dpd_margin = 0s
+
# Plugins to load in the IKE daemon charon.
# load =
@@ -164,19 +188,25 @@ charon {
# will be allocated.
# port_nat_t = 4500
+ # Whether to prefer updating SAs to the path with the best route.
+ # prefer_best_path = no
+
# Prefer locally configured proposals for IKE/IPsec over supplied ones as
# responder (disabling this can avoid keying retries due to
# INVALID_KE_PAYLOAD notifies).
# prefer_configured_proposals = yes
- # By default public IPv6 addresses are preferred over temporary ones (RFC
- # 4941), to make connections more stable. Enable this option to reverse
- # this.
+ # Controls whether permanent or temporary IPv6 addresses are used as source,
+ # or announced as additional addresses if MOBIKE is used.
# prefer_temporary_addrs = no
# Process RTM_NEWROUTE and RTM_DELROUTE events.
# process_route = yes
+ # How RDNs in subject DNs of certificates are matched against configured
+ # identities (strict, reordered, or relaxed).
+ # rdn_matching = strict
+
# Delay in ms for receiving packets, to simulate larger RTT.
# receive_delay = 0
@@ -196,6 +226,14 @@ charon {
# in strongswan.conf(5).
# retransmit_base = 1.8
+ # Maximum jitter in percent to apply randomly to calculated retransmission
+ # timeout (0 to disable).
+ # retransmit_jitter = 0
+
+ # Upper limit in seconds for calculated retransmission timeout (0 to
+ # disable).
+ # retransmit_limit = 0
+
# Timeout in seconds before sending first retransmit.
# retransmit_timeout = 4.0
@@ -215,6 +253,9 @@ charon {
# Priority of the routing table.
# routing_table_prio =
+ # Whether to use RSA with PSS padding instead of PKCS#1 padding by default.
+ # rsa_pss = no
+
# Delay in ms for sending packets, to simulate larger RTT.
# send_delay = 0
@@ -236,6 +277,19 @@ charon {
# Whether to enable constraints against IKEv2 signature schemes.
# signature_authentication_constraints = yes
+ # Value mixed into the local IKE SPIs after applying spi_mask.
+ # spi_label = 0x0000000000000000
+
+ # Mask applied to local IKE SPIs before mixing in spi_label (bits set will
+ # be replaced with spi_label).
+ # spi_mask = 0x0000000000000000
+
+ # The upper limit for SPIs requested from the kernel for IPsec SAs.
+ # spi_max = 0xcfffffff
+
+ # The lower limit for SPIs requested from the kernel for IPsec SAs.
+ # spi_min = 0xc0000000
+
# Number of worker threads in charon.
# threads = 16
@@ -250,7 +304,7 @@ charon {
# Buffer size used for crypto benchmark.
# bench_size = 1024
- # Number of iterations to test each algorithm.
+ # Time in ms during which crypto algorithm performance is measured.
# bench_time = 50
# Test crypto algorithms during registration (requires test vectors
diff --git a/roles/common/files/etc/strongswan.d/charon/socket-default.conf b/roles/common/files/etc/strongswan.d/charon/socket-default.conf
index 6d4b73d..abf4650 100644
--- a/roles/common/files/etc/strongswan.d/charon/socket-default.conf
+++ b/roles/common/files/etc/strongswan.d/charon/socket-default.conf
@@ -10,6 +10,9 @@ socket-default {
# Set source address on outbound packets, if possible.
# set_source = yes
+ # Force sending interface on outbound packets, if possible.
+ # set_sourceif = no
+
# Listen on IPv4, if possible.
# use_ipv4 = yes
diff --git a/roles/common/files/etc/systemd/system/bacula-fd.service b/roles/common/files/etc/systemd/system/bacula-fd.service
deleted file mode 100644
index 792d964..0000000
--- a/roles/common/files/etc/systemd/system/bacula-fd.service
+++ /dev/null
@@ -1,25 +0,0 @@
-[Unit]
-Description=Bacula File Daemon service
-After=network.target
-
-[Service]
-Type=simple
-StandardOutput=syslog
-ExecStart=/usr/sbin/bacula-fd -f -c /etc/bacula/bacula-fd.conf
-
-# Hardening
-NoNewPrivileges=yes
-PrivateDevices=yes
-ProtectHome=read-only
-ProtectSystem=strict
-PrivateTmp=yes
-ReadWriteDirectories=-/var/lib
-ReadWriteDirectories=-/var/run/bacula
-PrivateDevices=yes
-ProtectControlGroups=yes
-ProtectKernelModules=yes
-ProtectKernelTunables=yes
-RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
-
-[Install]
-WantedBy=multi-user.target
diff --git a/roles/common/files/etc/systemd/system/bacula-fd.service.d/override.conf b/roles/common/files/etc/systemd/system/bacula-fd.service.d/override.conf
new file mode 100644
index 0000000..537bf1e
--- /dev/null
+++ b/roles/common/files/etc/systemd/system/bacula-fd.service.d/override.conf
@@ -0,0 +1,13 @@
+[Service]
+# Hardening
+NoNewPrivileges=yes
+ProtectHome=read-only
+ProtectSystem=strict
+ReadWriteDirectories=/var/lib/bacula
+PrivateTmp=yes
+PrivateDevices=yes
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+CapabilityBoundingSet=CAP_DAC_READ_SEARCH
diff --git a/roles/common/files/etc/systemd/system/fail2ban.service.d/override.conf b/roles/common/files/etc/systemd/system/fail2ban.service.d/override.conf
index e3e651f..b34d130 100644
--- a/roles/common/files/etc/systemd/system/fail2ban.service.d/override.conf
+++ b/roles/common/files/etc/systemd/system/fail2ban.service.d/override.conf
@@ -2,13 +2,16 @@
After=nftables.service
[Service]
+ExecStartPre=
+ExecStart=
+ExecStart=/usr/bin/fail2ban-server -xf --logtarget=sysout start
+
# Need explicit rights to read logs as we don't grant CAP_DAC_READ_SEARCH
SupplementaryGroups=adm
# Hardening
NoNewPrivileges=yes
ProtectSystem=strict
-ReadWriteDirectories=/var/log/fail2ban
RuntimeDirectory=fail2ban
PrivateDevices=yes
ProtectControlGroups=yes
diff --git a/roles/common/files/etc/systemd/system/munin-node.service.d/override.conf b/roles/common/files/etc/systemd/system/munin-node.service.d/override.conf
new file mode 100644
index 0000000..fee16b3
--- /dev/null
+++ b/roles/common/files/etc/systemd/system/munin-node.service.d/override.conf
@@ -0,0 +1,14 @@
+[Service]
+ExecStartPre=
+
+# Hardening
+NoNewPrivileges=yes
+ProtectSystem=strict
+ReadWriteDirectories=/var/lib/munin-node/plugin-state
+ReadWriteDirectories=/var/log/munin
+RuntimeDirectory=munin
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
+CapabilityBoundingSet=CAP_SETUID CAP_SETGID
diff --git a/roles/common/files/etc/systemd/system/stunnel4@.service b/roles/common/files/etc/systemd/system/stunnel4@.service
index 1a30599..4d69702 100644
--- a/roles/common/files/etc/systemd/system/stunnel4@.service
+++ b/roles/common/files/etc/systemd/system/stunnel4@.service
@@ -1,10 +1,15 @@
[Unit]
Description=SSL tunnel for network daemons (instance %i)
+Documentation=man:stunnel4(8)
After=network.target nss-lookup.target
PartOf=stunnel4.service
ReloadPropagatedFrom=stunnel4.service
[Service]
+DynamicUser=yes
+; force dynamic user/group allocation (stunnel4 user exists already)
+User=_stunnel4-%i
+Group=_stunnel4-%i
ExecStart=/usr/bin/stunnel4 /etc/stunnel/%i.conf
ExecReload=/bin/kill -HUP ${MAINPID}
KillSignal=SIGINT
diff --git a/roles/common/files/usr/local/sbin/update-firewall b/roles/common/files/usr/local/sbin/update-firewall
index 957bdc1..e11e8a9 100755
--- a/roles/common/files/usr/local/sbin/update-firewall
+++ b/roles/common/files/usr/local/sbin/update-firewall
@@ -22,13 +22,6 @@ cat <"$NFTABLES" >>"$script"
ip netns add "nft-dryrun"
netns="nft-dryrun"
-# clear sets in the old rules before diff'ing with the new ones
-nft list ruleset -sn >"$oldrules"
-ip netns exec "$netns" nft -f - <"$oldrules"
-ip netns exec "$netns" nft flush set inet filter fail2ban
-ip netns exec "$netns" nft flush set inet filter fail2ban6
-ip netns exec "$netns" nft list ruleset -sn >"$oldrules"
-
declare -a INTERFACES=()
for iface in /sys/class/net/*; do
idx="$(< "$iface/ifindex")"
@@ -42,8 +35,15 @@ for idx in "${!INTERFACES[@]}"; do
ip netns exec "$netns" ip link add "${INTERFACES[idx]}" index "$idx" type dummy
done
+# clear sets in the old rules before diff'ing with the new ones
+nft -sn list ruleset >"$oldrules"
+ip netns exec "$netns" nft -f - <"$oldrules"
+ip netns exec "$netns" nft flush set inet filter fail2ban || true
+ip netns exec "$netns" nft flush set inet filter fail2ban6 || true
+ip netns exec "$netns" nft -sn list ruleset >"$oldrules"
+
ip netns exec "$netns" nft -f - <"$script"
-ip netns exec "$netns" nft list ruleset -sn >"$newrules"
+ip netns exec "$netns" nft -sn list ruleset >"$newrules"
ip netns del "$netns"
netns=
diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml
index 394c67a..bbaaef5 100644
--- a/roles/common/handlers/main.yml
+++ b/roles/common/handlers/main.yml
@@ -18,7 +18,7 @@
command: /usr/bin/rkhunter --propupd
- name: Update firewall
- command: /usr/local/sbin/update-firewall.sh -c
+ command: /usr/local/sbin/update-firewall -c
- name: Restart fail2ban
service: name=fail2ban state=restarted
@@ -26,15 +26,12 @@
- name: Restart IPsec
service: name=ipsec state=restarted
-- name: Reload networking
- # /etc/init.d/networking doesn't answer the status command; but since
- # it should be "up" whenever ansible has access to the machine, we use
- # pattern=init as a dummy assumption.
- service: name=networking pattern=init state=reloaded
-
- name: Restart rsyslog
service: name=rsyslog state=restarted
+- name: Restart systemd-timesyncd
+ service: name=systemd-timesyncd state=restarted
+
- name: Restart ntp
service: name=ntp state=restarted
diff --git a/roles/common/tasks/bacula.yml b/roles/common/tasks/bacula.yml
index 73a2fa1..308e358 100644
--- a/roles/common/tasks/bacula.yml
+++ b/roles/common/tasks/bacula.yml
@@ -10,7 +10,7 @@
- name: Delete /etc/bacula/common_default_passwords
file: path=/etc/bacula/common_default_passwords state=absent
-# Create with:
+# Populate with:
# echo $director-dir $(pwgen -sn 64 1) | sudo tee -a /etc/bacula/passwords-fd
- name: Ensure /etc/bacula/passwords-fd exists
file: path=/etc/bacula/passwords-fd
@@ -54,9 +54,15 @@
tags:
- genkey
-- name: Copy bacula-fd.service
- copy: src=etc/systemd/system/bacula-fd.service
- dest=/etc/systemd/system/bacula-fd.service
+- name: Create /etc/systemd/system/bacula-fd.service.d
+ file: path=/etc/systemd/system/bacula-fd.service.d
+ state=directory
+ owner=root group=root
+ mode=0755
+
+- name: Copy bacula-fd.service override
+ copy: src=etc/systemd/system/bacula-fd.service.d/override.conf
+ dest=/etc/systemd/system/bacula-fd.service.d/override.conf
owner=root group=root
mode=0644
notify:
diff --git a/roles/common/tasks/fail2ban.yml b/roles/common/tasks/fail2ban.yml
index 89427ea..563075f 100644
--- a/roles/common/tasks/fail2ban.yml
+++ b/roles/common/tasks/fail2ban.yml
@@ -1,22 +1,6 @@
- name: Install fail2ban
apt: pkg=fail2ban
-# Log into a dedicate directory so we can use ReadWriteDirectories in
-# the .service file
-- name: Create directory /var/log/fail2ban
- file: path=/var/log/fail2ban
- state=directory
- owner=root group=adm
- mode=0750
-
-- name: Fix fail2ban logrotate snippet
- lineinfile: dest=/etc/logrotate.d/fail2ban
- state=present
- line="/var/log/fail2ban/*.log"
- insertbefore="^[^#]*\\s{$"
- tags:
- - logrotate
-
- name: Configure fail2ban (fail2ban.local)
copy: src=etc/fail2ban/fail2ban.local
dest=/etc/fail2ban/fail2ban.local
@@ -53,11 +37,11 @@
notify:
- Restart fail2ban
-- name: Create directory /etc/systemd/system/fail2ban.service.d/override.conf
+- name: Create directory /etc/systemd/system/fail2ban.service.d
file: path=/etc/systemd/system/fail2ban.service.d
state=directory
owner=root group=root
- mode=0750
+ mode=0755
- name: Harden fail2ban.service
copy: src=etc/systemd/system/fail2ban.service.d/override.conf
diff --git a/roles/common/tasks/ipsec.yml b/roles/common/tasks/ipsec.yml
index 989541b..917c687 100644
--- a/roles/common/tasks/ipsec.yml
+++ b/roles/common/tasks/ipsec.yml
@@ -3,6 +3,7 @@
vars:
packages:
- strongswan-charon
+ - strongswan-starter
# for the GCM and openssl plugins
- libstrongswan-standard-plugins
notify:
@@ -14,16 +15,12 @@
dest=/etc/network/if-up.d/ipsec
owner=root group=root
mode=0755
- notify:
- - Reload networking
- name: Auto-deactivate the dedicated virtual subnet for IPsec
file: src=../if-up.d/ipsec
dest=/etc/network/if-down.d/ipsec
owner=root group=root state=link force=yes
-- meta: flush_handlers
-
- name: Configure IPsec
template: src=etc/ipsec.conf.j2
diff --git a/roles/common/tasks/logging.yml b/roles/common/tasks/logging.yml
index b602a49..2b4a42a 100644
--- a/roles/common/tasks/logging.yml
+++ b/roles/common/tasks/logging.yml
@@ -3,7 +3,6 @@
vars:
packages:
- rsyslog
- - syslog-summary
- logcheck
- logcheck-database
- logrotate
diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml
index 02a745c..a6795ba 100644
--- a/roles/common/tasks/main.yml
+++ b/roles/common/tasks/main.yml
@@ -16,7 +16,7 @@
- import_tasks: stunnel.yml
tags: stunnel
- when: "'webmail' in group_names and 'LDAP-provider' not in group_names"
+ when: "'webmail' in group_names and 'LDAP_provider' not in group_names"
- import_tasks: auditd.yml
tags: auditd
- import_tasks: unbound.yml
@@ -82,7 +82,7 @@
- molly-guard
- rsync
- screen
- - telnet-ssl
+ - bind9-dnsutils
- name: Disable resume device
# Cf. initramfs-tools(7) and initramfs.conf(5).
diff --git a/roles/common/tasks/munin-node.yml b/roles/common/tasks/munin-node.yml
index f43094a..2411b59 100644
--- a/roles/common/tasks/munin-node.yml
+++ b/roles/common/tasks/munin-node.yml
@@ -62,10 +62,6 @@
- load
- memory
- netstat
- - ntp_kernel_err
- - ntp_kernel_pll_freq
- - ntp_kernel_pll_off
- - ntp_offset
- open_files
- open_inodes
- processes
@@ -78,6 +74,20 @@
notify:
- Restart munin-node
+- name: Install Munin plugins
+ file: src=/usr/share/munin/plugins/{{ item }}
+ dest=/etc/munin/plugins/{{ item }}
+ owner=root group=root
+ state=link force=yes
+ with_items:
+ - ntp_kernel_err
+ - ntp_kernel_pll_freq
+ - ntp_kernel_pll_off
+ - ntp_offset
+ when: "'NTP_master' in group_names"
+ notify:
+ - Restart munin-node
+
- name: Delete unnecessary Munin plugins
file: path=/etc/munin/plugins/{{ item }}
state=absent
@@ -90,6 +100,18 @@
notify:
- Restart munin-node
+- name: Delete unnecessary Munin plugins
+ file: path=/etc/munin/plugins/{{ item }}
+ state=absent
+ with_items:
+ - ntp_kernel_err
+ - ntp_kernel_pll_freq
+ - ntp_kernel_pll_off
+ - ntp_offset
+ when: "'NTP_master' not in group_names"
+ notify:
+ - Restart munin-node
+
- name: Install 'if_' Munin wildcard plugin
file: src=/usr/share/munin/plugins/{{ item.0 }}_
dest=/etc/munin/plugins/{{ item.0 }}_{{ item.1 }}
@@ -133,8 +155,32 @@
notify:
- Restart munin-node
+- name: Create directory /etc/systemd/system/munin-node.service.d
+ file: path=/etc/systemd/system/munin-node.service.d
+ state=directory
+ owner=root group=root
+ mode=0755
+
+- name: Copy munin-node.service override
+ copy: src=etc/systemd/system/munin-node.service.d/override.conf
+ dest=/etc/systemd/system/munin-node.service.d/override.conf
+ owner=root group=root
+ mode=0644
+ register: r8
+ notify:
+ - systemctl daemon-reload
+ - Restart munin-node
+
+# We use RuntimeDirectory in our overrride unit to avoid permission
+# issues caused by the restrictive Capability Bounding Set
+- name: Mask /usr/lib/tmpfiles.d/munin-common.conf
+ file: src=/dev/null
+ dest=/etc/tmpfiles.d/munin-common.conf
+ owner=root group=root
+ state=link
+
- name: Start munin-node
service: name=munin-node state=started
- when: not (r1.changed or r2.changed or r3.changed or r4.changed or r5.changed or r6.changed or r7.changed)
+ when: not (r1.changed or r2.changed or r3.changed or r4.changed or r5.changed or r6.changed or r7.changed or r8.changed)
- meta: flush_handlers
diff --git a/roles/common/tasks/ntp.yml b/roles/common/tasks/ntp.yml
index f9a01c8..2ff9e49 100644
--- a/roles/common/tasks/ntp.yml
+++ b/roles/common/tasks/ntp.yml
@@ -1,15 +1,33 @@
-- name: Install ntp
- apt: pkg=ntp
+- name: Remove ntp package
+ apt: pkg=ntp state={{ state }} purge=yes
+ vars:
+ state: "{{ ('NTP_master' in group_names) | ternary('present', 'absent') }}"
+
+- name: Install systemd-timesyncd package
+ apt: pkg=systemd-timesyncd state=present purge=yes
+ when: "'NTP_master' not in group_names"
+
+- name: Create /etc/systemd/timesyncd.conf.d
+ file: path=/etc/systemd/timesyncd.conf.d
+ state=directory
+ owner=root group=root
+ mode=0755
+ when: "'NTP_master' not in group_names"
- name: Configure ntp
- template: src=etc/ntp.conf.j2
- dest=/etc/ntp.conf
+ template: src=etc/{{ conf }}.j2
+ dest=/etc/{{ conf }}
owner=root group=root
mode=0644
+ vars:
+ conf: "{{ ('NTP_master' in group_names) | ternary('ntp.conf', 'systemd/timesyncd.conf.d/fripost.conf') }}"
+ service: "{{ ('NTP_master' in group_names) | ternary('ntp', 'systemd-timesyncd') }}"
notify:
- - Restart ntp
+ - Restart {{ service }}
- meta: flush_handlers
-- name: Start ntp
- service: name=ntp state=started
+- name: Start and enable ntp
+ service: name={{ service }}.service state=started enabled=true
+ vars:
+ service: "{{ ('NTP_master' in group_names) | ternary('ntp', 'systemd-timesyncd') }}"
diff --git a/roles/common/tasks/smart.yml b/roles/common/tasks/smart.yml
index 8d35d9f..68e507f 100644
--- a/roles/common/tasks/smart.yml
+++ b/roles/common/tasks/smart.yml
@@ -1,12 +1,5 @@
- name: Install smartmontools
apt: pkg=smartmontools
-- name: Auto-enable smartmontools
- lineinfile: dest=/etc/default/smartmontools
- regexp='^(\s*#)?\s*start_smartd='
- line='start_smartd=yes'
- owner=root group=root
- mode=0644
-
- name: Start smartd
service: name=smartmontools state=started
diff --git a/roles/common/tasks/stunnel.yml b/roles/common/tasks/stunnel.yml
index 7cb8823..1522f1f 100644
--- a/roles/common/tasks/stunnel.yml
+++ b/roles/common/tasks/stunnel.yml
@@ -1,14 +1,7 @@
- name: Install stunnel4
apt: pkg=stunnel4
-- name: Set 'ENABLED=0' in /etc/default/stunnel4
- lineinfile: dest=/etc/default/stunnel4
- regexp='^(\s*#)?\s*ENABLED='
- line='ENABLED=0'
- owner=root group=root
- mode=0644
-
-- name: Copy stunnel4 service file
+- name: Copy stunnel4 service files
copy: src=etc/systemd/system/{{ item }}
dest=/etc/systemd/system/{{ item }}
owner=root group=root
@@ -18,3 +11,6 @@
with_items:
- stunnel4.service
- stunnel4@.service
+
+- name: Disable stunnel4 service
+ service: name=stunnel4.service enabled=false
diff --git a/roles/common/tasks/sysctl.yml b/roles/common/tasks/sysctl.yml
index 3bf3b4f..08a1b13 100644
--- a/roles/common/tasks/sysctl.yml
+++ b/roles/common/tasks/sysctl.yml
@@ -11,10 +11,14 @@
- { name: 'net.ipv4.conf.default.rp_filter', value: 1 }
- { name: 'net.ipv4.conf.all.rp_filter', value: 1 }
- # Enable TCP/IP SYN cookies to avoid TCP SYN flood attacks. We
- # rate-limit not only the default ICMP types 3, 4, 11 and 12
+ # Disable SYN cookies and improve SYN backlog handling, see tcp(7) and
+ # https://levelup.gitconnected.com/linux-kernel-tuning-for-high-performance-networking-high-volume-incoming-connections-196e863d458a
+ - { name: 'net.ipv4.tcp_syncookies', value: 0 }
+ - { name: 'net.ipv4.tcp_synack_retries', value: 1 }
+ - { name: 'net.ipv4.tcp_max_syn_backlog', value: 32768 }
+
+ # We rate-limit not only the default ICMP types 3, 4, 11 and 12
# (0x1818), but also types 0 and 8. See icmp(7).
- - { name: 'net.ipv4.tcp_syncookies', value: 1 }
- { name: 'net.ipv4.icmp_ratemask', value: 6425 }
- { name: 'net.ipv4.icmp_ratelimit', value: 1000 }
diff --git a/roles/common/templates/etc/apt/sources.list.j2 b/roles/common/templates/etc/apt/sources.list.j2
index 4ae1cb5..c8f5dfc 100644
--- a/roles/common/templates/etc/apt/sources.list.j2
+++ b/roles/common/templates/etc/apt/sources.list.j2
@@ -2,12 +2,12 @@
# Do NOT edit this file directly!
# vim: set filetype=debsources :
-deb http://deb.debian.org/debian {{ ansible_lsb.codename }} main{% if inventory_hostname_short in non_free_packages.keys() or (ansible_processor[1] is search("^(Genuine)?Intel.*") and not ansible_virtualization_role == 'guest') %} contrib non-free{% endif %}
+deb https://deb.debian.org/debian {{ ansible_lsb.codename }} main{% if inventory_hostname_short in non_free_packages.keys() or (ansible_processor[1] is search("^(Genuine)?Intel.*") and not ansible_virtualization_role == 'guest') %} contrib non-free{% endif %}
-deb http://deb.debian.org/debian-security {{ ansible_lsb.codename }}/updates main{% if inventory_hostname_short in non_free_packages.keys() or (ansible_processor[1] is search("^(Genuine)?Intel.*") and not ansible_virtualization_role == 'guest') %} contrib non-free{% endif %}
+deb https://deb.debian.org/debian-security {{ ansible_lsb.codename }}{% if ansible_lsb.major_release | int < 11 %}/updates{% else %}-security{% endif %} main{% if inventory_hostname_short in non_free_packages.keys() or (ansible_processor[1] is search("^(Genuine)?Intel.*") and not ansible_virtualization_role == 'guest') %} contrib non-free{% endif %}
-deb http://deb.debian.org/debian {{ ansible_lsb.codename }}-updates main
+deb https://deb.debian.org/debian {{ ansible_lsb.codename }}-updates main
{% if 'backports' in group_names -%}
-deb http://deb.debian.org/debian {{ ansible_lsb.codename }}-backports main
+deb https://deb.debian.org/debian {{ ansible_lsb.codename }}-backports main
{% endif %}
diff --git a/roles/common/templates/etc/bacula/bacula-fd.conf.j2 b/roles/common/templates/etc/bacula/bacula-fd.conf.j2
index e06911f..d0af395 100644
--- a/roles/common/templates/etc/bacula/bacula-fd.conf.j2
+++ b/roles/common/templates/etc/bacula/bacula-fd.conf.j2
@@ -1,7 +1,15 @@
#
# Default Bacula File Daemon Configuration file
#
-# For Bacula release 5.2.6 (21 February 2012) -- debian jessie/sid
+# For Bacula release 9.6.7 (10 December 2020) -- debian bullseye/sid
+#
+# There is not much to change here except perhaps the
+# File daemon Name to
+#
+#
+# Copyright (C) 2000-2020 Kern Sibbald
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
#
# List Directors who are permitted to contact this File daemon
@@ -25,7 +33,7 @@ Messages {
FileDaemon { # define myself
Name = {{ inventory_hostname_short }}-fd
Working Directory = /var/lib/bacula
- Pid Directory = /var/run/bacula
+ Pid Directory = /run/bacula
Maximum Concurrent Jobs = 20
FDAddress = {{ ipsec[inventory_hostname_short] }}
FDPort = 9102
diff --git a/roles/common/templates/etc/clamav/freshclam.conf.j2 b/roles/common/templates/etc/clamav/freshclam.conf.j2
index 06cebd1..650a2b3 100644
--- a/roles/common/templates/etc/clamav/freshclam.conf.j2
+++ b/roles/common/templates/etc/clamav/freshclam.conf.j2
@@ -19,7 +19,6 @@ ReceiveTimeout 30
TestDatabases yes
ScriptedUpdates yes
CompressLocalDatabase no
-SafeBrowsing false
Bytecode true
NotifyClamd /etc/clamav/clamd.conf
# Check for new database 24 times a day
diff --git a/roles/common/templates/etc/fail2ban/jail.local.j2 b/roles/common/templates/etc/fail2ban/jail.local.j2
index 29b004c..3cd19cc 100644
--- a/roles/common/templates/etc/fail2ban/jail.local.j2
+++ b/roles/common/templates/etc/fail2ban/jail.local.j2
@@ -13,7 +13,9 @@ destemail = admin@fripost.org
ignoreip = 127.0.0.0/8, ::1, {{ ipsec_subnet }}
banaction = nftables-allports
-logpath = /var/log/fail2ban/fail2ban.log
+
+# must match nftables.conf's blackholes timeouts
+bantime = 10m
#
# JAILS
@@ -33,7 +35,7 @@ enabled = {{ 'MSA' in group_names }}
[roundcube-auth]
enabled = {{ 'webmail' in group_names }}
-# XXX Bullseye: logpath = /var/log/roundcube/errors.log
+logpath = /var/log/roundcube/errors.log
[nextcloud]
enabled = {{ 'nextcloud' in group_names }}
diff --git a/roles/common/templates/etc/ipsec.conf.j2 b/roles/common/templates/etc/ipsec.conf.j2
index 6b3840f..e7505b4 100644
--- a/roles/common/templates/etc/ipsec.conf.j2
+++ b/roles/common/templates/etc/ipsec.conf.j2
@@ -17,7 +17,7 @@ conn %default
{% endif %}
leftauth = pubkey
left = %defaultroute
- leftsubnet = {{ ipsec[inventory_hostname_short] | ipv4 }}/32
+ leftsubnet = {{ ipsec[inventory_hostname_short] | ansible.utils.ipv4 }}/32
leftid = {{ inventory_hostname }}
leftsigkey = {{ inventory_hostname_short }}.pem
leftfirewall = no
@@ -36,7 +36,7 @@ conn {{ hostvars[host].inventory_hostname_short }}
rightallowany = yes
{% endif %}
rightsigkey = {{ hostvars[host].inventory_hostname_short }}.pem
- rightsubnet = {{ ipsec[ hostvars[host].inventory_hostname_short ] | ipv4 }}/32
+ rightsubnet = {{ ipsec[ hostvars[host].inventory_hostname_short ] | ansible.utils.ipv4 }}/32
{% if 'NATed' not in group_names and 'NATed' in hostvars[host].group_names %}
mobike = yes
{% endif %}
diff --git a/roles/common/templates/etc/munin/munin-node.conf.j2 b/roles/common/templates/etc/munin/munin-node.conf.j2
index 1563526..1aba053 100644
--- a/roles/common/templates/etc/munin/munin-node.conf.j2
+++ b/roles/common/templates/etc/munin/munin-node.conf.j2
@@ -4,7 +4,7 @@
log_level 4
log_file /var/log/munin/munin-node.log
-pid_file /var/run/munin/munin-node.pid
+pid_file /run/munin/munin-node.pid
background 1
setsid 1
@@ -42,7 +42,7 @@ host_name {{ inventory_hostname_short }}
# may repeat the allow line as many times as you'd like
{% for host in groups['munin_master'] %}
-allow ^{{ ipsec[ hostvars[host].inventory_hostname_short ] | ipv4 | replace(".","\.") }}$
+allow ^{{ ipsec[ hostvars[host].inventory_hostname_short ] | ansible.utils.ipv4 | replace(".","\.") }}$
{% endfor %}
# Which address to bind to;
diff --git a/roles/common/templates/etc/munin/plugin-conf.d/munin-node.j2 b/roles/common/templates/etc/munin/plugin-conf.d/munin-node.j2
index 2d434bc..ec471eb 100644
--- a/roles/common/templates/etc/munin/plugin-conf.d/munin-node.j2
+++ b/roles/common/templates/etc/munin/plugin-conf.d/munin-node.j2
@@ -73,7 +73,7 @@ user root
[mysql*]
user root
env.mysqlopts --defaults-file=/etc/mysql/debian.cnf
-env.mysqluser debian-sys-maint
+env.mysqluser root
env.mysqlconnection DBI:mysql:mysql;mysql_read_default_file=/etc/mysql/debian.cnf
[postfix_mailqueue_*]
diff --git a/roles/common/templates/etc/nftables.conf.j2 b/roles/common/templates/etc/nftables.conf.j2
index 1e1fde2..805d1a8 100755
--- a/roles/common/templates/etc/nftables.conf.j2
+++ b/roles/common/templates/etc/nftables.conf.j2
@@ -5,7 +5,7 @@ define in-tcp-ports = {
{% if 'MX' in group_names %}
, 25 # SMTP
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
, 636 # ldaps
{% endif %}
{% if 'IMAP' in group_names %}
@@ -29,7 +29,7 @@ define out-tcp-ports = {
{% if 'out' in group_names or 'MSA' in group_names %}
, 25 # SMTP
{% endif %}
-{% if 'LDAP-provider' in group_names %}
+{% if 'LDAP_provider' in group_names %}
, 11371 # OpenPGP HTTP Keyserver
, 43 # whois
{% elif 'MX' in group_names or 'lists' in group_names or 'nextcloud' in group_names %}
@@ -45,8 +45,107 @@ define out-tcp-ports = {
flush ruleset
+table netdev filter {
+{% for if in ansible_interfaces %}
+{% if if != "lo" and ansible_facts[if].active %}
+{% set addr = (ansible_facts[if].ipv4 | default({'address': '0.0.0.0'})).address %}
+ chain INGRESS-{{ if }} {
+ type filter hook ingress device {{ if }} priority -499
+ policy accept
+
+ # IPsec traffic (refined later in the filter rule)
+ ip saddr {{ ipsec_subnet }} ip daddr {{ ipsec[inventory_hostname_short] }} meta secpath exists accept
+
+ # rate-limiting is done directly by the kernel (net.ipv4.icmp_{ratelimit,ratemask} runtime options)
+ icmp type { echo-reply, echo-request, destination-unreachable, time-exceeded } counter accept
+ icmpv6 type { echo-reply, echo-request, destination-unreachable,
+ packet-too-big, time-exceeded, parameter-problem } counter accept
+
+ # accept neighbour discovery for autoconfiguration, RFC 4890 sec. 4.4.1
+ ip6 hoplimit 255 icmpv6 type { 133,134,135,136,141,142 } counter accept
+
+ # accept link-local multicast receiver notification messages
+ ip6 saddr fe80::/10 ip6 daddr ff02::/16 ip6 hoplimit 1 icmpv6 type { 130,131,132,143 } counter accept
+
+ # drop all remaining ICMP/ICMPv6 traffic
+ meta l4proto { icmp, icmpv6 } counter drop
+
+ # bogon filter (cf. RFC 6890 for non-global ip addresses)
+ define bogon = {
+ 0.0.0.0/8 # this host, on this network (RFC 1122 sec. 3.2.1.3)
+{% if not addr | ansible.utils.ipaddr('10.0.0.0/8') %}
+ , 10.0.0.0/8 # private-use (RFC 1918)
+{% endif %}
+ , 100.64.0.0/10 # shared address space (RFC 6598)
+ , 127.0.0.0/8 # loopback (RFC 1122, sec. 3.2.1.3)
+ , 169.254.0.0/16 # link local (RFC 3927)
+ , 172.16.0.0/12 # private-use (RFC 1918)
+ , 192.0.0.0/24 # IETF protocol assignments (RFC 6890 sec. 2.1)
+ , 192.0.2.0/24 # documentation (RFC 5737)
+{% if not addr | ansible.utils.ipaddr('192.168.0.0/16') %}
+ , 192.168.0.0/16 # private-use (RFC 1918)
+{% endif %}
+ , 198.18.0.0/15 # benchmarking (RFC 2544)
+ , 198.51.100.0/24 # documentation (RFC 5737)
+ , 203.0.113.0/24 # documentation (RFC 5737)
+ , 224.0.0.0/3 # multicast - class D 224.0.0.0/4 + class E 240.0.0.0/4 (RFC 1112 sec. 4)
+ , 255.255.255.255/32 # limited broadcast (RFC 0919 sec. 7)
+ }
+
+ ip saddr $bogon counter drop
+ ip daddr $bogon counter drop
+
+ # See also https://www.team-cymru.org/Services/Bogons/fullbogons-ipv6.txt
+ define bogon6 = {
+ ::1/128 # loopback address (RFC 4291)
+ , ::/128 # unspecified (RFC 4291)
+ , ::ffff:0:0/96 # IPv4-mapped address (RFC 4291)
+ , 100::/64 # discard-only address block (RFC 6666)
+ , 2001::/23 # IETF protocol assignments (RFC 2928)
+ , 2001::/32 # TEREDO (RFC 4380)
+ , 2001:2::/48 # benchmarking (RFC 5180)
+ , 2001:db8::/32 # documentation (RFC 3849)
+ , 2001:10::/28 # ORCHID (RFC 4843)
+ , 2002::/16 # 6to4 (RFC 3056)
+ , fc00::/7 # unique-local (RFC 4193)
+ , fe80::/10 # linked-scoped unicast (RFC 4291)
+ }
+
+ ip6 saddr $bogon6 counter drop
+ ip6 saddr $bogon6 counter drop
+ }
+{% endif %}
+{% endfor %}
+}
+
+table inet raw {
+ chain PREROUTING-stateless {
+ # XXX can't add that to the ingress hook as that happens before IP defragmentation
+ # so we don't have the TCP header in later fragments (we don't want to drop IP
+ # fragments, see https://blog.cloudflare.com/ip-fragmentation-is-broken/ )
+ type filter hook prerouting priority -399 # > NF_IP_PRI_CONNTRACK_DEFRAG (-400)
+ policy accept
+
+ # stateless filter for bogus TCP packets
+ tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop # null packet
+ tcp flags & (fin|psh|urg) == fin|psh|urg counter drop # XMAS packet
+ tcp flags & (syn|rst) == syn|rst counter drop
+ tcp flags & (fin|rst) == fin|rst counter drop
+ tcp flags & (fin|syn) == fin|syn counter drop
+ tcp flags & (fin|psh|ack) == fin|psh counter drop
+ }
+
+ chain PREROUTING {
+ type filter hook prerouting priority -199 # > NF_IP_PRI_CONNTRACK (-200)
+ policy accept
+
+ # stateful filter
+ ct state invalid counter drop
+ }
+}
+
table inet filter {
- # blackholes
+ # blackholes (timeout must match /etc/fail2ban/jail.local)
set fail2ban { type ipv4_addr; timeout 10m; }
set fail2ban6 { type ipv6_addr; timeout 10m; }
@@ -56,26 +155,23 @@ table inet filter {
iif lo accept
- # XXX Bullseye: this is a hack for the lack of reqid matches in
- # nftables: we mark the esp packet and accept after decapsulation
+ # XXX Bullseye: this is a rather crude match as nftables 0.9.0 lacks support for ipsec expressions
+ # to match match inbound resp. outbound policies and source resp. destination tunnel addresses.
# https://serverfault.com/questions/971735/how-to-match-reqid-in-nftables
# https://blog.fraggod.net/2016/09/25/nftables-re-injected-ipsec-matching-without-xt_policy.html
- define IPsec.mark = 0x220
- meta l4proto esp mark set mark | $IPsec.mark accept
- ip saddr 172.16.0.0/24 ip daddr 172.16.0.7 mark & $IPsec.mark == $IPsec.mark accept
+ # (We can't use marks to match post-ESP decapsulation here because that doesn't work well with UDP
+ # encapsulation.) We'll also pin the reqid to the lowest address byte in ipsec.conf(5); that way
+ # peers can't impersonate each other.
+ meta l4proto esp accept
+ # ip saddr {{ ipsec_subnet }} ip daddr {{ ipsec[inventory_hostname_short] }} ipsec in reqid $i accept
+ ip saddr {{ ipsec_subnet }} ip daddr {{ ipsec[inventory_hostname_short] }} meta secpath exists accept
- # rate-limiting is done directly by the kernel (net.ipv4.icmp_{ratelimit,ratemask} runtime options)
- icmp type { echo-reply, echo-request, destination-unreachable } counter accept
- icmpv6 type { echo-reply, echo-request, destination-unreachable,
- packet-too-big, time-exceeded, parameter-problem } counter accept
+ # incoming ICMP/ICMPv6 traffic was filtered in the ingress chain already
+ meta l4proto { icmp, icmpv6 } counter accept
- # accept neighbour discovery for autoconfiguration, RFC 4890 sec. 4.4.1
- icmpv6 type { 133,134,135,136,141,142 } ip6 hoplimit 255 counter accept
+ # NTP (ntpd uses sport 123 but systemd-timesyncd does not)
+ udp sport 123 ct state related,established accept
- jump martian
- jump invalid
-
- udp sport 123 udp dport 123 ct state related,established accept
{% if groups.all | length > 1 %}
udp sport 500 udp dport 500 ct state new,related,established accept
{% if groups.NATed | length > 0 %}
@@ -86,11 +182,12 @@ table inet filter {
udp sport 53 ct state related,established accept
tcp sport 53 ct state related,established accept
{% if 'dhclient' in group_names %}
- udp sport 67 ct state related,established accept
+ ip version 4 udp sport 67 udp dport 68 ct state related,established accept
+ ip6 version 6 udp sport 547 udp dport 546 ct state related,established accept
{% endif %}
- meta l4proto tcp ip saddr @fail2ban counter drop
- meta l4proto tcp ip6 saddr @fail2ban6 counter drop
+ ip saddr @fail2ban counter drop
+ ip6 saddr @fail2ban6 counter drop
tcp dport $in-tcp-ports ct state related,established accept
tcp dport $in-tcp-ports ct state new counter accept
@@ -103,25 +200,30 @@ table inet filter {
oif lo accept
- # XXX Bullseye: unlike for input we can't use marks here,
- # because by the time we see a packet to 172.16.0.0/24 we don't
- # know if it'll be encapsulated
+ # XXX Bullseye: unlike for input we can't use marks or test for
+ # secpath existence here, because by the time we see a packet to
+ # 172.16.0.0/24 we don't know if it'll be encapsulated
meta l4proto esp accept
- ip saddr 172.16.0.7 ip daddr 172.16.0.0/24 accept
+ # ip saddr {{ ipsec[inventory_hostname_short] }} ip daddr {{ ipsec_subnet }} ipsec out reqid $i accept
+ ip saddr {{ ipsec[inventory_hostname_short] }} ip daddr {{ ipsec_subnet }} accept
- meta l4proto { icmp, icmpv6 } accept
+ meta l4proto { icmp, icmpv6 } counter accept
- jump martian
- jump invalid
+ # NTP (ntpd uses sport 123 but systemd-timesyncd does not)
+ udp dport 123 ct state new,related,established accept
- udp sport 123 udp dport 123 ct state new,related,established accept
+{% if groups.all | length > 1 %}
udp sport 500 udp dport 500 ct state new,related,established accept
+{% if groups.NATed | length > 0 %}
udp sport 4500 udp dport 4500 ct state new,related,established accept
+{% endif %}
+{% endif %}
udp dport 53 ct state new,related,established accept
tcp dport 53 ct state new,related,established accept
{% if 'dhclient' in group_names %}
- udp dport 67 ct state new,related,established accept
+ ip version 4 udp sport 68 udp dport 67 ct state new,related,established accept
+ ip6 version 6 udp sport 546 udp dport 547 ct state new,related,established accept
{% endif %}
tcp sport $in-tcp-ports ct state related,established accept
@@ -132,62 +234,4 @@ table inet filter {
meta l4proto udp counter reject
counter reject
}
-
- chain martian {
- # bogon filter (cf. RFC 6890 for non-global ip addresses)
- define invalid-ip = {
- 0.0.0.0/8 # this host, on this network (RFC 1122 sec. 3.2.1.3)
-{% if not ansible_default_ipv4.address | ipaddr('10.0.0.0/8') %}
- , 10.0.0.0/8 # private-use (RFC 1918)
-{% endif %}
- , 100.64.0.0/10 # shared address space (RFC 6598)
- , 127.0.0.0/8 # loopback (RFC 1122, sec. 3.2.1.3)
- , 169.254.0.0/16 # link local (RFC 3927)
-{% if not ansible_default_ipv4.address | ipaddr('172.16.0.0/12') %}
- , 172.16.0.0/12 # private-use (RFC 1918)
-{% endif %}
- , 192.0.0.0/24 # IETF protocol assignments (RFC 6890 sec. 2.1)
- , 192.0.2.0/24 # documentation (RFC 5737)
-{% if not ansible_default_ipv4.address | ipaddr('192.168.0.0/16') %}
- , 192.168.0.0/16 # private-use (RFC 1918)
-{% endif %}
- , 198.18.0.0/15 # benchmarking (RFC 2544)
- , 198.51.100.0/24 # documentation (RFC 5737)
- , 203.0.113.0/24 # documentation (RFC 5737)
- , 240.0.0.0/4 # reserved (RFC 1112, sec. 4)
- , 255.255.255.255/32 # limited broadcast (RFC 0919, section 7)
- }
-
- define invalid-ip6 = {
- ::1/128 # loopback address (RFC 4291)
- , ::/128 # unspecified (RFC 4291)
- , ::ffff:0:0/96 # IPv4-mapped address (RFC 4291)
- , 100::/64 # discard-only address block (RFC 6666)
- , 2001::/23 # IETF protocol assignments (RFC 2928)
- , 2001::/32 # TEREDO (RFC 4380)
- , 2001:2::/48 # benchmarking (RFC 5180)
- , 2001:db8::/32 # documentation (RFC 3849)
- , 2001:10::/28 # ORCHID (RFC 4843)
- , 2002::/16 # 6to4 (RFC 3056)
- , fc00::/7 # unique-local (RFC 4193)
- , fe80::/10 # linked-scoped unicast (RFC 4291)
- }
-
- ip saddr $invalid-ip counter drop
- ip daddr $invalid-ip counter drop
-
- ip6 saddr $invalid-ip6 counter drop
- ip6 daddr $invalid-ip6 counter drop
- }
-
- chain invalid {
- ct state invalid counter reject
-
- # drop bogus TCP packets
- tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop # null packets
- tcp flags != syn ct state new counter drop # SYN-flood attacks
- tcp flags & (fin|syn|rst|psh|ack|urg) == fin|psh|urg counter drop # XMAS packets
- tcp flags & (fin|syn) == fin|syn counter drop # bogus
- tcp flags & (syn|rst) == syn|rst counter drop # bogus
- }
}
diff --git a/roles/common/templates/etc/ntp.conf.j2 b/roles/common/templates/etc/ntp.conf.j2
index 18c03cf..b76f0dd 100644
--- a/roles/common/templates/etc/ntp.conf.j2
+++ b/roles/common/templates/etc/ntp.conf.j2
@@ -2,6 +2,8 @@
driftfile /var/lib/ntp/ntp.drift
+# Leap seconds definition provided by tzdata
+leapfile /usr/share/zoneinfo/leap-seconds.list
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
@@ -13,7 +15,6 @@ filegen clockstats file clockstats type day enable
# You do need to talk to an NTP server or two (or three).
-{% if 'NTP_master' in group_names %}
# Use Stratum One Time Servers:
# http://support.ntp.org/bin/view/Servers/StratumOneTimeServers
server sth1.ntp.se iburst
@@ -22,17 +23,6 @@ server gbg1.ntp.se iburst
server gbg2.ntp.se iburst
server ntp1.sp.se iburst
server ntp2.sp.se iburst
-{% else %}
-# Sychronize to our (stratum 2) NTP server, to ensure our network has a
-# consistent time.
-{% for host in groups['NTP_master'] | sort %}
-server {{ ipsec[ hostvars[host].inventory_hostname_short ] }} prefer iburst
-{% endfor %}
-pool 0.{{ geoip | default('debian') }}.pool.ntp.org iburst
-pool 1.{{ geoip | default('debian') }}.pool.ntp.org iburst
-pool 2.{{ geoip | default('debian') }}.pool.ntp.org iburst
-pool 3.{{ geoip | default('debian') }}.pool.ntp.org iburst
-{% endif %}
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
diff --git a/roles/common/templates/etc/postfix/main.cf.j2 b/roles/common/templates/etc/postfix/main.cf.j2
index b369d43..5ac7920 100644
--- a/roles/common/templates/etc/postfix/main.cf.j2
+++ b/roles/common/templates/etc/postfix/main.cf.j2
@@ -31,7 +31,7 @@ virtual_alias_maps = lmdb:/etc/aliases
alias_database = $virtual_alias_maps
# Forward everything to our internal outgoing proxy
-relayhost = [{{ postfix_instance.out.addr | ipaddr }}]:{{ postfix_instance.out.port }}
+relayhost = [{{ postfix_instance.out.addr | ansible.utils.ipaddr }}]:{{ postfix_instance.out.port }}
relay_domains =
smtp_tls_security_level = none
diff --git a/roles/common/templates/etc/postfix/master.cf.j2 b/roles/common/templates/etc/postfix/master.cf.j2
index a9c73f7..3954085 100644
--- a/roles/common/templates/etc/postfix/master.cf.j2
+++ b/roles/common/templates/etc/postfix/master.cf.j2
@@ -19,10 +19,8 @@ tlsproxy unix - - y - 0 tlsproxy
dnsblog unix - - y - 0 dnsblog
{% elif inst == 'MSA' %}
submission inet n - y - - smtpd
- -o tls_high_cipherlist=EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
submissions inet n - y - - smtpd
-o smtpd_tls_wrappermode=yes
- -o tls_high_cipherlist=EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
{% if groups.webmail | difference([inventory_hostname]) | length > 0 %}
[{{ postfix_instance.MSA.addr }}]:{{ postfix_instance.MSA.port }} inet n - y - - smtpd
-o broken_sasl_auth_clients=no
@@ -59,6 +57,8 @@ smtp_verify unix - - y - - smtp
-o smtp_tls_protocols=!SSLv2,!SSLv3
-o smtp_tls_note_starttls_offer=yes
-o smtp_tls_session_cache_database=lmdb:$data_directory/smtp_tls_session_cache
+ -o smtp_tls_fingerprint_digest=sha256
+ -o smtp_tls_policy_maps=lmdb:$config_directory/smtp_tls_policy
{% endif %}
relay unix - - y - - smtp
showq unix n - y - - showq
@@ -85,7 +85,7 @@ sympa unix - n n - - pipe
{% if inst is defined and inst == 'out' %}
# Client part (lmtp) - amavis
-amavisfeed unix - - n - 5 lmtp
+amavisfeed unix - - y - 5 lmtp
-o lmtp_destination_recipient_limit=1000
-o lmtp_send_xforward_command=yes
-o lmtp_data_done_timeout=1200s
diff --git a/roles/common/templates/etc/systemd/timesyncd.conf.d/fripost.conf.j2 b/roles/common/templates/etc/systemd/timesyncd.conf.d/fripost.conf.j2
new file mode 100644
index 0000000..f578cd9
--- /dev/null
+++ b/roles/common/templates/etc/systemd/timesyncd.conf.d/fripost.conf.j2
@@ -0,0 +1,9 @@
+[Time]
+# Sychronize to our (stratum 2) NTP server, to ensure our network has a
+# consistent time.
+{%- set ntp = [] -%}
+{%- for host in groups['NTP_master'] -%}
+{%- set _ = ntp.append(ipsec[ hostvars[host].inventory_hostname_short ]) -%}
+{%- endfor %}
+
+NTP={{ ntp | join(' ') }}
diff --git a/roles/common/templates/etc/unbound/unbound.conf.j2 b/roles/common/templates/etc/unbound/unbound.conf.j2
index 64f32bf..e75e66f 100644
--- a/roles/common/templates/etc/unbound/unbound.conf.j2
+++ b/roles/common/templates/etc/unbound/unbound.conf.j2
@@ -29,4 +29,4 @@ server:
#
# The following line includes additional configuration files from the
# /etc/unbound/unbound.conf.d directory.
-include: "/etc/unbound/unbound.conf.d/*.conf"
+include-toplevel: "/etc/unbound/unbound.conf.d/*.conf"
diff --git a/roles/git/files/etc/nginx/sites-available/git b/roles/git/files/etc/nginx/sites-available/git
index 73ac1e6..9e9d16e 100644
--- a/roles/git/files/etc/nginx/sites-available/git
+++ b/roles/git/files/etc/nginx/sites-available/git
@@ -4,7 +4,7 @@ server {
server_name git.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
access_log /var/log/nginx/git.access.log;
error_log /var/log/nginx/git.error.log info;
@@ -25,44 +25,54 @@ server {
error_log /var/log/nginx/git.error.log info;
include snippets/headers.conf;
+ add_header Content-Security-Policy
+ "default-src 'none'; img-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self'; frame-ancestors 'none'; form-action 'self'";
include snippets/ssl.conf;
ssl_certificate ssl/git.fripost.org.pem;
ssl_certificate_key ssl/git.fripost.org.key;
include snippets/git.fripost.org.hpkp-hdr;
- location ^~ /static/ {
- alias /usr/share/cgit/;
- expires 30d;
- }
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 256;
+ gzip_types application/javascript application/json application/xml image/svg+xml image/x-icon text/css text/plain;
- # Bypass the CGI to return static files stored on disk. Try first repo with
- # a trailing '.git', then without.
- location ~* "^/((?U)[^/]+)(?:\.git)?/objects/(?:[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(?:pack|idx))$" {
- root /var/lib/gitolite/repositories;
- try_files /$1.git/objects/$2 /$1/objects/$2 =404;
+ location ^~ /static/ {
expires 30d;
- gzip off;
- # TODO honor git-daemon-export-ok
+ alias /usr/share/cgit/;
}
# disallow push over HTTP/HTTPS
- location ~* "^/[^/]+/git-receive-pack$" { return 403; }
+ location ~ "^/.+/git-receive-pack$" { return 403; }
- location ~* "^/[^/]+/(?:HEAD|info/refs|objects/info/[^/]+|git-upload-pack)$" {
+ location ~ "^/.+/(?:info/refs|git-upload-pack)$" {
+ limit_except GET POST { deny all; }
+ fastcgi_buffering off;
gzip off;
- include uwsgi_params;
- uwsgi_modifier1 9;
- uwsgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories;
- uwsgi_pass unix:/run/uwsgi/app/git-http-backend/socket;
+
+ fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
+ fastcgi_param NO_BUFFERING "";
+
+ # cf. git-http-backend(1)
+ fastcgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories;
+ fastcgi_param PATH_INFO $uri;
+ fastcgi_param CONTENT_TYPE $content_type;
+ fastcgi_param QUERY_STRING $query_string;
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_pass unix:/run/git-http-backend.socket;
}
+ location = /robots.txt { root /usr/share/cgit; }
+ location = /favicon.ico { root /usr/share/cgit; }
# send all other URLs to cgit
location / {
- gzip off;
- include uwsgi_params;
- uwsgi_modifier1 9;
- uwsgi_pass unix:/run/uwsgi/app/cgit/socket;
+ fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
+ fastcgi_param PATH_INFO $uri;
+ fastcgi_param CONTENT_TYPE $content_type;
+ fastcgi_param QUERY_STRING $query_string;
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_pass unix:/run/cgit.socket;
}
}
diff --git a/roles/git/files/etc/systemd/system/cgit.service b/roles/git/files/etc/systemd/system/cgit.service
new file mode 100644
index 0000000..08037ac
--- /dev/null
+++ b/roles/git/files/etc/systemd/system/cgit.service
@@ -0,0 +1,23 @@
+[Unit]
+Description=hyperfast web frontend for git repositories written in C
+Documentation=https://git.zx2c4.com/cgit/
+
+[Service]
+User=_cgit
+Group=nogroup
+SupplementaryGroups=gitolite
+ExecStart=/usr/sbin/fcgiwrap
+SyslogIdentifier=cgit
+#
+# Hardening
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectHome=yes
+ProtectSystem=strict
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ReadWriteDirectories=/var/cache/cgit
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/git/files/etc/systemd/system/cgit.socket b/roles/git/files/etc/systemd/system/cgit.socket
new file mode 100644
index 0000000..bba4bef
--- /dev/null
+++ b/roles/git/files/etc/systemd/system/cgit.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=hyperfast web frontend for git repositories written in C
+Documentation=https://git.zx2c4.com/cgit/
+
+[Socket]
+ListenStream=%t/cgit.socket
+SocketUser=www-data
+SocketMode=0600
+
+[Install]
+WantedBy=sockets.target
diff --git a/roles/git/files/etc/systemd/system/git-http-backend.service b/roles/git/files/etc/systemd/system/git-http-backend.service
new file mode 100644
index 0000000..f973370
--- /dev/null
+++ b/roles/git/files/etc/systemd/system/git-http-backend.service
@@ -0,0 +1,21 @@
+[Unit]
+Description=Git HTTP backend
+Documentation=man:git-http-backend(1)
+
+[Service]
+DynamicUser=yes
+SupplementaryGroups=gitolite
+ExecStart=/usr/sbin/fcgiwrap
+SyslogIdentifier=git-http-backend
+#
+# Hardening
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectHome=yes
+ProtectSystem=strict
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/git/files/etc/systemd/system/git-http-backend.socket b/roles/git/files/etc/systemd/system/git-http-backend.socket
new file mode 100644
index 0000000..c2820d4
--- /dev/null
+++ b/roles/git/files/etc/systemd/system/git-http-backend.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=Git HTTP backend
+Documentation=man:git-http-backend(1)
+
+[Socket]
+ListenStream=%t/git-http-backend.socket
+SocketUser=www-data
+SocketMode=0600
+
+[Install]
+WantedBy=sockets.target
diff --git a/roles/git/files/etc/uwsgi/apps-available/cgit.ini b/roles/git/files/etc/uwsgi/apps-available/cgit.ini
deleted file mode 100644
index 2fb5b25..0000000
--- a/roles/git/files/etc/uwsgi/apps-available/cgit.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[uwsgi]
-plugins = cgi
-procname-master = uwsgi %(deb-confname)
-cgi = /usr/lib/cgit/cgit.cgi
-uid = cgit
-gid = www-data
diff --git a/roles/git/files/etc/uwsgi/apps-available/git-http-backend.ini b/roles/git/files/etc/uwsgi/apps-available/git-http-backend.ini
deleted file mode 100644
index 326d6d6..0000000
--- a/roles/git/files/etc/uwsgi/apps-available/git-http-backend.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[uwsgi]
-plugins = cgi
-procname-master = uwsgi %(deb-confname)
-cgi = /usr/lib/git-core/git-http-backend
-
-# XXX regression in git-http-backend
-# https://www.mail-archive.com/git@vger.kernel.org/msg119603.html
-cgi-close-stdin-on-eof = true
diff --git a/roles/git/handlers/main.yml b/roles/git/handlers/main.yml
index d52c9cc..6212d91 100644
--- a/roles/git/handlers/main.yml
+++ b/roles/git/handlers/main.yml
@@ -2,8 +2,17 @@
- name: systemctl daemon-reload
command: /bin/systemctl daemon-reload
-- name: Restart uWSGI
- service: name=uwsgi state=restarted
+- name: Stop cgit
+ service: name=cgit.service state=stopped
+
+- name: Restart cgit
+ service: name=cgit.socket state=restarted
+
+- name: Stop git-http-backend
+ service: name=git-http-backend.service state=stopped
+
+- name: Restart git-http-backend
+ service: name=git-http-backend.socket state=restarted
- name: Restart Nginx
service: name=nginx state=restarted
diff --git a/roles/git/tasks/cgit.yml b/roles/git/tasks/cgit.yml
index 160ede6..120f204 100644
--- a/roles/git/tasks/cgit.yml
+++ b/roles/git/tasks/cgit.yml
@@ -4,79 +4,99 @@
packages:
- cgit
- highlight
- - uwsgi
+ - fcgiwrap
+
+- name: Stop and disable fcgiwrap socket
+ service: name=fcgiwrap.socket state=stopped enabled=false
+
+- name: Stop fcgiwrap service
+ service: name=fcgiwrap.service state=stopped
- name: Configure cgit
copy: src=etc/cgitrc
dest=/etc/cgitrc
owner=root group=root
mode=0644
- register: r1
notify:
- - Restart uWSGI
+ - Stop cgit
- name: Copy /usr/lib/cgit/filters/syntax-highlighting2.sh
copy: src=usr/lib/cgit/filters/syntax-highlighting2.sh
dest=/usr/lib/cgit/filters/syntax-highlighting2.sh
owner=root group=root
mode=0755
- register: r2
notify:
- - Restart uWSGI
+ - Stop cgit
-- name: Create a user 'cgit'
- user: name=cgit system=yes
- home=/var/www
+- name: Create '_cgit' user
+ user: name=_cgit system=yes
+ group=nogroup
+ home=/nonexistent
shell=/usr/sbin/nologin
password=!
state=present
- register: r3
notify:
- - Restart uWSGI
+ - Stop cgit
+# Make it sticky: `dpkg-statoverride --add _cgit nogroup 0700 /var/cache/cgit`
- name: Create cache directory /var/cache/cgit
file: path=/var/cache/cgit
state=directory
- owner=cgit group=cgit
+ owner=_cgit group=nogroup
mode=0700
-- name: Create /etc/uwsgi/apps-available/{cgit,git-http-backend}.ini
- copy: src=etc/uwsgi/apps-available/{{ item }}.ini
- dest=/etc/uwsgi/apps-available/{{ item }}.ini
+- name: Copy cgit service unit
+ copy: src=etc/systemd/system/cgit.service
+ dest=/etc/systemd/system/cgit.service
owner=root group=root
mode=0644
- register: r4
- with_items:
- - cgit
- - git-http-backend
notify:
- - Restart uWSGI
+ - systemctl daemon-reload
+ - Stop cgit
-- name: Create /etc/uwsgi/apps-enabled/{cgit,git-http-backend}.ini
- file: src=../apps-available/{{ item }}.ini
- dest=/etc/uwsgi/apps-enabled/{{ item }}.ini
+- name: Copy cgit socket unit
+ copy: src=etc/systemd/system/cgit.socket
+ dest=/etc/systemd/system/cgit.socket
owner=root group=root
- state=link force=yes
- register: r5
- with_items:
- - cgit
- - git-http-backend
+ mode=0644
notify:
- - Restart uWSGI
+ - systemctl daemon-reload
+ - Restart cgit
-- name: Start uWSGI
- service: name=nginx state=started
- when: not (r1.changed or r2.changed or r3.changed or r4.changed or r5.changed)
+- name: Disable cgit service
+ service: name=cgit.service enabled=false
+
+- name: Start cgit socket
+ service: name=cgit.socket state=started enabled=true
- meta: flush_handlers
-- name: Add 'cgit' & 'www-data' to the group 'gitolite'
- user: name={{ item }} groups=gitolite append=yes
- with_items:
- # for the cgit interface
- - cgit
- # for pulls over HTTP/HTTPS
- - www-data
+
+- name: Copy git-http-backend service unit
+ copy: src=etc/systemd/system/git-http-backend.service
+ dest=/etc/systemd/system/git-http-backend.service
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Stop git-http-backend
+
+- name: Copy git-http-backend socket unit
+ copy: src=etc/systemd/system/git-http-backend.socket
+ dest=/etc/systemd/system/git-http-backend.socket
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Restart git-http-backend
+
+- name: Disable git-http-backend service
+ service: name=git-http-backend.service enabled=false
+
+- name: Start git-http-backend socket
+ service: name=git-http-backend.socket state=started enabled=true
+
+- meta: flush_handlers
- name: Copy /etc/nginx/sites-available/git
diff --git a/roles/git/tasks/gitolite.yml b/roles/git/tasks/gitolite.yml
index 38000a7..e7d1fe3 100644
--- a/roles/git/tasks/gitolite.yml
+++ b/roles/git/tasks/gitolite.yml
@@ -31,6 +31,6 @@
owner=root group=root
mode=0644
with_items:
- # See /usr/share/doc/gitolite3/README.txt.gz
+ # See /usr/share/doc/gitolite3/README.markdown.gz
- { var: UMASK, value: "0027" }
- { var: GIT_CONFIG_KEYS, value: "'gitweb\\..* gc\\..* hook\\..*'" }
diff --git a/roles/lacme/files/etc/lacme/lacme.conf b/roles/lacme/files/etc/lacme/lacme.conf
index 5aa1252..28633b6 100644
--- a/roles/lacme/files/etc/lacme/lacme.conf
+++ b/roles/lacme/files/etc/lacme/lacme.conf
@@ -1,86 +1,123 @@
-# For certificate issuance (new-cert command), specify the certificate
-# configuration file to use
+# For certificate issuance (newOrder command), specify a space-separated
+# certificate configuration files or directories to use
#
-#config-certs = config/lacme-certs.conf
+#config-certs = lacme-certs.conf lacme-certs.conf.d/
+
[client]
-# The value of "socket" specifies the lacme-accountd(1) UNIX-domain
-# socket to connect to for signature requests from the ACME client.
-# lacme(1) aborts if the socket is readable or writable by other users,
-# or if its parent directory is writable by other users.
-# Default: "$XDG_RUNTIME_DIR/S.lacme" if the XDG_RUNTIME_DIR environment
-# variable is set.
+
+# The value of "socket" specifies the path to the lacme-accountd(1)
+# UNIX-domain socket to connect to for signature requests from the ACME
+# client. lacme(8) aborts if the socket is readable or writable by
+# other users, or if its parent directory is writable by other users.
+# This setting is ignored when lacme-accountd(1) is spawned by lacme(8),
+# since the two processes communicate through a socket pair. See the
+# "accountd" section below for details.
#
-#socket = /run/user/1000/S.lacme
+#socket = %t/S.lacme
# username to drop privileges to (setting both effective and real uid).
-# Preserve root privileges if the value is empty (not recommended).
-# Default: "nobody".
+# Skip privilege drop if the value is empty (not recommended).
#
-user = lacme
+#user = _lacme-client
# groupname to drop privileges to (setting both effective and real gid,
# and also setting the list of supplementary gids to that single group).
-# Preserve root privileges if the value is empty (not recommended).
+# Skip privilege drop if the value is empty (not recommended).
#
-group = nogroup
+#group = nogroup
-# Path to the ACME client executable.
-#command = /usr/lib/lacme/client
+# ACME client command.
+#
+#command = /usr/libexec/lacme/client
-# Root URI of the ACME server. NOTE: Use the staging server for testing
-# as it has relaxed ratelimit.
+# URI of the ACME server's directory. NOTE: Use the staging server
+# <https://acme-staging-v02.api.letsencrypt.org/directory> for testing
+# as it has relaxed rate-limiting.
#
-#server = https://acme-v01.api.letsencrypt.org/
-#server = https://acme-staging.api.letsencrypt.org/
+#server = https://acme-v02.api.letsencrypt.org/directory
# Timeout in seconds after which the client stops polling the ACME
# server and considers the request failed.
#
-#timeout = 10
+#timeout = 30
# Whether to verify the server certificate chain.
-SSL_verify = yes
+#
+#SSL_verify = yes
# Specify the version of the SSL protocol used to transmit data.
-SSL_version = SSLv23:!TLSv1_1:!TLSv1:!SSLv3:!SSLv2
+#
+#SSL_version = SSLv23:!TLSv1_1:!TLSv1:!SSLv3:!SSLv2
# Specify the cipher list for the connection.
-SSL_cipher_list = EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
+#
+#SSL_cipher_list = EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
[webserver]
-# Specify the local address to listen on, in the form ADDRESS[:PORT].
+# Comma- or space-separated list of addresses to listen on, for instance
+# "0.0.0.0:80 [::]:80".
#
-#listen = 0.0.0.0:80
-listen = [::]:80
+#listen = /run/lacme-www.socket
-# If a webserver is already running, specify a non-existent directory
-# under which the webserver is configured to serve GET requests for
-# challenge files under "/.well-known/acme-challenge/" (for each virtual
-# hosts requiring authorization) as static files.
+# Directory under which an external HTTP daemon is configured to serve
+# GET requests for challenge files under "/.well-known/acme-challenge/"
+# (for each virtual host requiring authorization) as static files.
+# NOTE: the directory must exist and be writable by the lacme client
+# user.
#
-challenge-directory = /var/www/acme-challenge
+#challenge-directory =
# username to drop privileges to (setting both effective and real uid).
-# Preserve root privileges if the value is empty (not recommended).
+# Skip privilege drop if the value is empty (not recommended).
#
-user = www-data
+#user = _lacme-www
# groupname to drop privileges to (setting both effective and real gid,
# and also setting the list of supplementary gids to that single group).
-# Preserve root privileges if the value is empty (not recommended).
+# Skip privilege drop if the value is empty (not recommended).
#
-user = www-data
+#group = nogroup
-# Path to the ACME webserver executable.
-#command = /usr/lib/lacme/webserver
+# ACME webserver command.
+#
+#command = /usr/libexec/lacme/webserver
# Whether to automatically install iptables(8) rules to open the
# ADDRESS[:PORT] specified with listen. Theses rules are automatically
-# removed once lacme(1) exits.
+# removed once lacme(8) exits.
+#
+#iptables = No
+
+
+[accountd]
+# lacme-accound(1) section. Comment out this section (including its
+# header), or use the --socket= CLI option, to make lacme(8) connect to
+# an existing lacme-accountd(1) process via a UNIX-domain socket.
+
+# username to drop privileges to (setting both effective and real uid).
+# Skip privilege drop if the value is empty.
+#
+#user =
+
+# groupname to drop privileges to (setting both effective and real gid,
+# and also setting the list of supplementary gids to that single group).
+# Skip privilege drop if the value is empty.
+#
+#group =
+
+# lacme-accountd(1) command.
+#
+#command = /usr/bin/lacme-accountd
+
+# Path to the lacme-accountd(1) configuration file.
+#
+#config =
+
+# Be quiet.
#
-#iptables = Yes
+#quiet = Yes
; vim:ft=dosini
diff --git a/roles/lacme/tasks/main.yml b/roles/lacme/tasks/main.yml
index 9ff88c2..b031b25 100644
--- a/roles/lacme/tasks/main.yml
+++ b/roles/lacme/tasks/main.yml
@@ -5,15 +5,6 @@
- liblwp-protocol-https-perl
- lacme
-- name: Create a user 'lacme'
- user: name=lacme system=yes
- group=nogroup
- createhome=no
- home=/nonexistent
- shell=/usr/sbin/nologin
- password=!
- state=present
-
- name: Copy lacme/lacme-certs.conf
copy: src=etc/lacme/lacme.conf
dest=/etc/lacme/lacme.conf
diff --git a/roles/lists/files/etc/nginx/sites-available/sympa b/roles/lists/files/etc/nginx/sites-available/sympa
index f5a67bf..8ac6bb4 100644
--- a/roles/lists/files/etc/nginx/sites-available/sympa
+++ b/roles/lists/files/etc/nginx/sites-available/sympa
@@ -4,7 +4,7 @@ server {
server_name lists.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
access_log /var/log/nginx/lists.access.log;
error_log /var/log/nginx/lists.error.log info;
@@ -26,13 +26,18 @@ server {
include snippets/headers.conf;
add_header Content-Security-Policy
- "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; referrer no-referrer-when-downgrade; frame-ancestors 'none'; form-action 'self'; base-uri lists.fripost.org";
+ "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri lists.fripost.org";
include snippets/ssl.conf;
ssl_certificate ssl/lists.fripost.org.pem;
ssl_certificate_key ssl/lists.fripost.org.key;
include snippets/lists.fripost.org.hpkp-hdr;
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 256;
+ gzip_types application/font-woff application/font-woff2 application/javascript application/json application/xml image/x-icon text/css text/plain;
+
location = /robots.txt {
allow all;
log_not_found off;
@@ -44,36 +49,34 @@ server {
return 302 /sympa$args;
}
- location ^~ /static-sympa/ {
- alias /var/lib/sympa/static_content/;
- expires 30d;
- }
+ location ^~ /static-sympa/ { expires 30d; try_files $uri =404; alias /usr/share/sympa/static_content/; }
+ location ^~ /css-sympa/ { expires 30d; try_files $uri =404; alias /var/lib/sympa/css/; }
+ location ^~ /pictures-sympa/ { expires 30d; try_files $uri =404; alias /var/lib/sympa/pictures; }
+
+ location ~* ^/sympa(?:/|$) {
+ gzip off; # protect against BREACH
- location ^~ /sympa {
fastcgi_split_path_info ^(/sympa)(.*)$;
include snippets/fastcgi.conf;
-
+ fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass unix:/run/wwsympa.socket;
- gzip off;
}
location ~* ^/([^/]+)/?$ {
return 302 /$1/sympa$args;
}
- location ~* ^/([^/]+)/sympa(/.*)?$ {
- set $vhost $1;
+ location ~* ^/(?<vhost>[^/]+)/sympa(?:/|$) {
+ gzip off; # protect against BREACH
if (!-f /etc/sympa/$vhost/robot.conf) {
return 404;
}
- fastcgi_split_path_info ^(/[^/]+/sympa)(.*)$;
+ fastcgi_split_path_info ^/[^/]+(/sympa)(.*)$;
include snippets/fastcgi.conf;
-
- fastcgi_pass unix:/run/wwsympa.socket;
- gzip off;
-
+ fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SERVER_NAME $vhost;
+ fastcgi_pass unix:/run/wwsympa.socket;
}
location / {
diff --git a/roles/lists/files/etc/sympa/sympa/sympa.conf b/roles/lists/files/etc/sympa/sympa/sympa.conf
index 0e88baf..7d29173 100644
--- a/roles/lists/files/etc/sympa/sympa/sympa.conf
+++ b/roles/lists/files/etc/sympa/sympa/sympa.conf
@@ -12,21 +12,25 @@ email sympa
listmaster listmaster@fripost.org
## URL of main Web page
-wwsympa_url http://lists.fripost.org/sympa
+wwsympa_url https://lists.fripost.org/sympa
max_wrong_password 19
## Directory for storing static contents (CSS, members pictures, documentation) directly delivered by Apache
-static_content_path /var/lib/sympa/static_content
+static_content_path /usr/share/sympa/static_content
## URL mapped with the static_content_path directory defined above
static_content_url /static-sympa
-css_url /static-sympa/css
+css_url /css-sympa
+css_path /var/lib/sympa/css
+
+pictures_path /var/lib/sympa/pictures
+pictures_url /pictures-sympa
## Secret used by Sympa to make MD5 fingerprint in web cookies secure
## Should not be changed ! May invalid all user password
-cookie `head -n1 /etc/sympa/cookie`
+cookie `/usr/bin/head -n1 /etc/sympa/cookie`
## Who is able to create lists
## This parameter is a scenario, check sympa documentation about scenarios if you want to define one
@@ -44,7 +48,7 @@ etc /etc/sympa
## Syslog facility for sympa
## Do not forget to edit syslog.conf
-syslog `cat /etc/sympa/facility`
+syslog LOCAL1
## Log verbosity
## 0: normal, 2,3,4: for debug
@@ -63,8 +67,6 @@ umask 027
sendmail /usr/sbin/sendmail
sendmail_aliases none
-distribution_mode fork
-
## Max. number of Sendmail processes (launched by Sympa) running simultaneously
## Proposed value is quite low, you can rise it up to 100, 200 or even 300 with powerfull systems.
maxsmtp 128
@@ -131,8 +133,6 @@ spool /var/spool/sympa
## Directory for incoming spool
queue /var/spool/sympa/msg
-queuedistribute /var/spool/sympa/distribute
-
## Directory for moderation spool
queuemod /var/spool/sympa/moderation
@@ -261,18 +261,12 @@ default_sql_fetch_timeout 300
###\\\\ S/MIME configuration ////###
-## Path to OpenSSL
-## Sympa recognizes S/MIME if OpenSSL is installed
-openssl /usr/bin/openssl
-
## Directory containing trusted CA certificates
#capath /etc/sympa/ssl.crt
## File containing bundled trusted CA certificates
#cafile /usr/local/apache/conf/ssl.crt/ca-bundle.crt
-crl_dir /var/lib/sympa/list_data/crl
-
## Directory containing user certificates
ssl_cert_dir /var/lib/sympa/list_data/X509-user-certs
@@ -370,7 +364,7 @@ ldap_force_canonical_email 1
## Syslog facility for wwsympa, archived and bounced
## Default is to use previously defined sympa log facility.
-log_facility `cat /etc/sympa/facility`
+log_facility LOCAL1
#log_module
@@ -399,3 +393,5 @@ use_fast_cgi 1
## Default number of lines of the array displaying the log entries in the logs
## page
viewlogs_page_size 25
+
+shared_feature on
diff --git a/roles/lists/files/etc/systemd/system/wwsympa.service b/roles/lists/files/etc/systemd/system/wwsympa.service
index 3f76aca..cff2db7 100644
--- a/roles/lists/files/etc/systemd/system/wwsympa.service
+++ b/roles/lists/files/etc/systemd/system/wwsympa.service
@@ -12,16 +12,15 @@ ExecStart=/usr/lib/cgi-bin/sympa/wwsympa.fcgi
# Hardening
NoNewPrivileges=yes
-PrivateDevices=yes
-ProtectHome=yes
-ProtectSystem=strict
-PrivateTmp=yes
ReadWriteDirectories=/etc/sympa
ReadWriteDirectories=/var/lib/sympa
-ReadWriteDirectories=-/var/run/sympa
ReadWriteDirectories=/var/spool/sympa
+ReadWriteDirectories=/run/sympa
PrivateDevices=yes
PrivateNetwork=yes
+ProtectHome=yes
+ProtectSystem=strict
+PrivateTmp=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
diff --git a/roles/lists/tasks/mail.yml b/roles/lists/tasks/mail.yml
index 44b96e5..2821b02 100644
--- a/roles/lists/tasks/mail.yml
+++ b/roles/lists/tasks/mail.yml
@@ -3,7 +3,7 @@
vars:
packages:
- postfix
- - postfix-ldap
+ - postfix-lmdb
- name: Configure Postfix
template: src=etc/postfix/{{ item }}.j2
diff --git a/roles/lists/tasks/sympa.yml b/roles/lists/tasks/sympa.yml
index 0d5eac1..27a5823 100644
--- a/roles/lists/tasks/sympa.yml
+++ b/roles/lists/tasks/sympa.yml
@@ -10,8 +10,8 @@
- libcrypt-openssl-x509-perl
- name: Make the 'sympa' MySQL user use unix_socket
- mysql_user2: name=sympa password= auth_plugin=unix_socket
- state=present
+ mysql_user: name=sympa password= plugin=unix_socket
+ state=present
- name: Configure Sympa
copy: src=etc/sympa/{{ item }}
diff --git a/roles/lists/templates/etc/postfix/main.cf.j2 b/roles/lists/templates/etc/postfix/main.cf.j2
index 1bf02eb..2be1b41 100644
--- a/roles/lists/templates/etc/postfix/main.cf.j2
+++ b/roles/lists/templates/etc/postfix/main.cf.j2
@@ -21,7 +21,7 @@ append_dot_mydomain = no
mynetworks = 127.0.0.0/8, [::1]/128
{%- if groups.all | length > 1 -%}
{%- for mx in groups.MX | sort -%}
- , {{ ipsec[ hostvars[mx].inventory_hostname_short ] | ipaddr }}
+ , {{ ipsec[ hostvars[mx].inventory_hostname_short ] | ansible.utils.ipaddr }}
{%- endfor %}
{% endif %}
diff --git a/roles/lists/templates/etc/sympa/robot.conf.j2 b/roles/lists/templates/etc/sympa/robot.conf.j2
index 75687d8..28998e3 100644
--- a/roles/lists/templates/etc/sympa/robot.conf.j2
+++ b/roles/lists/templates/etc/sympa/robot.conf.j2
@@ -1,3 +1,2 @@
-http_host {{ item }}
+#wwsympa_url_local https://{{ item }}/sympa
wwsympa_url https://{{ item }}/sympa
-# wwsympa_url https://lists.fripost.org/{{ item }}/sympa
diff --git a/roles/munin-master/files/etc/nginx/sites-available/munin b/roles/munin-master/files/etc/nginx/sites-available/munin
index 7b0b789..2f681fb 100644
--- a/roles/munin-master/files/etc/nginx/sites-available/munin
+++ b/roles/munin-master/files/etc/nginx/sites-available/munin
@@ -23,15 +23,15 @@ server {
location /munin-cgi/munin-cgi-graph/ {
fastcgi_split_path_info ^(/munin-cgi/munin-cgi-graph)(.*);
+ fastcgi_param PATH_INFO $fastcgi_path_info;
include snippets/fastcgi.conf;
fastcgi_pass unix:/run/munin/cgi-graph.socket;
- gzip off;
}
location /munin/ {
fastcgi_split_path_info ^(/munin)(.*);
+ fastcgi_param PATH_INFO $fastcgi_path_info;
include snippets/fastcgi.conf;
fastcgi_pass unix:/run/munin/cgi-html.socket;
- gzip off;
}
}
diff --git a/roles/munin-master/tasks/main.yml b/roles/munin-master/tasks/main.yml
index 9ed3577..6dad93b 100644
--- a/roles/munin-master/tasks/main.yml
+++ b/roles/munin-master/tasks/main.yml
@@ -8,8 +8,15 @@
- name: Configure rrdcached
lineinfile: "dest=/etc/default/rrdcached
- regexp='^#?OPTS='
- line='OPTS=\"-s munin -m 660 -l unix:/var/run/rrdcached.sock -w 1800 -z 1800 -f 3600 -j /var/lib/rrdcached/journal -F -b /var/lib/munin -B\"'"
+ regexp='^#?{{ item.name }}='
+ line='{{ item.name }}=\"{{ item.value }}\"'"
+ with_items:
+ - { name: 'BASE_OPTIONS', value: '-B -F' }
+ - { name: 'BASE_PATH', value: '/var/lib/munin' }
+ - { name: 'SOCKFILE', value: '/run/rrdcached.sock' }
+ - { name: 'SOCKGROUP', value: 'munin' }
+ - { name: 'SOCKMODE', value: '0660' }
+ - { name: 'WRITE_TIMEOUT', value: '1800' }
register: r
notify:
- Restart rrdcached
diff --git a/roles/munin-master/templates/etc/munin/munin.conf.j2 b/roles/munin-master/templates/etc/munin/munin.conf.j2
index 401094a..b53ef0e 100644
--- a/roles/munin-master/templates/etc/munin/munin.conf.j2
+++ b/roles/munin-master/templates/etc/munin/munin.conf.j2
@@ -8,7 +8,7 @@
#dbdir /var/lib/munin
#htmldir /var/cache/munin/www
#logdir /var/log/munin
-#rundir /var/run/munin
+#rundir /run/munin
# Where to look for the HTML templates
#
@@ -83,7 +83,7 @@ html_strategy cgi
# To reduce IO and enable the use of the rrdcached, uncomment it and set it to
# the location of the socket that rrdcached uses.
#
-rrdcached_socket /var/run/rrdcached.sock
+rrdcached_socket /run/rrdcached.sock
# Drop somejuser@fnord.comm and anotheruser@blibb.comm an email everytime
# something changes (OK -> WARNING, CRITICAL -> OK, etc)
diff --git a/roles/nextcloud/files/etc/cron.d/nextcloud b/roles/nextcloud/files/etc/cron.d/nextcloud
index 8bd7d86..3c4aac0 100644
--- a/roles/nextcloud/files/etc/cron.d/nextcloud
+++ b/roles/nextcloud/files/etc/cron.d/nextcloud
@@ -1,2 +1,2 @@
MAILTO=root
-*/15 * * * * www-data php -f /var/www/nextcloud/cron.php
+*/5 * * * * _nextcloud php -f /usr/local/share/nextcloud/cron.php
diff --git a/roles/nextcloud/files/etc/logrotate.d/nextcloud b/roles/nextcloud/files/etc/logrotate.d/nextcloud
new file mode 100644
index 0000000..b8dd232
--- /dev/null
+++ b/roles/nextcloud/files/etc/logrotate.d/nextcloud
@@ -0,0 +1,9 @@
+/var/log/nextcloud/*.log {
+ daily
+ missingok
+ rotate 7
+ compress
+ delaycompress
+ notifempty
+ create 0640 _nextcloud adm
+}
diff --git a/roles/nextcloud/files/etc/nginx/sites-available/nextcloud b/roles/nextcloud/files/etc/nginx/sites-available/nextcloud
index 9e5e9b0..e8820ca 100644
--- a/roles/nextcloud/files/etc/nginx/sites-available/nextcloud
+++ b/roles/nextcloud/files/etc/nginx/sites-available/nextcloud
@@ -4,10 +4,10 @@ server {
server_name cloud.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
- access_log /var/log/nginx/cloud.access.log;
- error_log /var/log/nginx/cloud.error.log info;
+ access_log /var/log/nginx/cloud.access.log;
+ error_log /var/log/nginx/cloud.error.log info;
location / {
return 301 https://$host$request_uri;
@@ -20,10 +20,10 @@ server {
server_name cloud.fripost.org;
- root /var/www/nextcloud/;
+ root /usr/local/share/nextcloud;
include snippets/headers.conf;
- add_header X-Robots-Tag none;
+ add_header X-Robots-Tag "noindex, nofollow";
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
@@ -38,11 +38,12 @@ server {
access_log off;
}
- access_log /var/log/nginx/cloud.access.log;
- error_log /var/log/nginx/cloud.error.log info;
+ access_log /var/log/nginx/cloud.access.log;
+ error_log /var/log/nginx/cloud.error.log info;
- location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; }
- location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; }
+ location = /.well-known/carddav { return 301 /remote.php/dav; }
+ location = /.well-known/caldav { return 301 /remote.php/dav; }
+ location ^~ /.well-known/ { return 301 /index.php$request_uri; }
# set max upload size
client_max_body_size 512M;
@@ -57,40 +58,38 @@ server {
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
- error_page 403 /core/templates/403.php;
- error_page 404 /core/templates/404.php;
-
- location = / { return 303 $scheme://$host/apps/files/; }
+ location = / { return 303 /apps/files/; }
location / { rewrite ^ /index.php$uri last; }
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ { internal; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { internal; }
- location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
- fastcgi_split_path_info ^(.+\.php)(/.*)$;
+ location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|oc[ms]-provider/.+|core/templates/40[34])\.php(?:$|/) {
include snippets/fastcgi-php.conf;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
+ fastcgi_intercept_errors on;
fastcgi_request_buffering off;
- fastcgi_param PHP_VALUE "upload_max_filesize=512M
- post_max_size=512M
- memory_limit=512M";
- fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root:/mnt/nextcloud-data:/etc/nextcloud:/usr/share/php:/tmp:/dev";
+ fastcgi_pass unix:/run/php/php7.3-fpm@nextcloud.sock;
}
- location ~ ^/(?:updater|ocs-provider)(?:$|/) {
+ location ~ ^/(?:updater|oc[ms]-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}
- location ~* \.(?:css|js|woff|svg|gif)$ {
- try_files $uri /index.php$uri$is_args$args;
+ location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
expires 30d;
+ try_files $uri /index.php$uri$is_args$args;
}
- location ~* \.(?:png|html|ttf|ico|jpg|jpeg)$ {
+ location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
try_files $uri /index.php$uri$is_args$args;
}
+
+ location = /core/img/favicon.ico {
+ alias /var/www/nextcloud/fripost.ico;
+ }
}
server {
@@ -101,10 +100,10 @@ server {
server_name www.cloud.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
- access_log /var/log/nginx/cloud.access.log;
- error_log /var/log/nginx/cloud.error.log info;
+ access_log /var/log/nginx/cloud.access.log;
+ error_log /var/log/nginx/cloud.error.log info;
location / {
return 301 https://cloud.fripost.org$request_uri;
diff --git a/roles/nextcloud/files/etc/php/fpm/pool.d/nextcloud.conf b/roles/nextcloud/files/etc/php/fpm/pool.d/nextcloud.conf
new file mode 100644
index 0000000..eb92adb
--- /dev/null
+++ b/roles/nextcloud/files/etc/php/fpm/pool.d/nextcloud.conf
@@ -0,0 +1,24 @@
+[nextcloud]
+user = _nextcloud
+group = nogroup
+listen = /run/php/php7.3-fpm@nextcloud.sock
+listen.owner = www-data
+listen.group = www-data
+listen.mode = 0600
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+
+php_value[upload_max_filesize] = 512M
+php_value[post_max_size] = 512M
+php_value[memory_limit] = 512M
+
+php_admin_value[open_basedir] = /usr/local/share/nextcloud:/var/www/nextcloud:/mnt/nextcloud-data:/etc/nextcloud:/var/cache/nextcloud:/var/log/nextcloud:/usr/share/php:/tmp:/dev
+
+env[HOSTNAME] = $HOSTNAME
+env[PATH] = /usr/bin:/bin
+env[TMP] = /tmp
+env[TMPDIR] = /tmp
+env[TEMP] = /tmp
diff --git a/roles/nextcloud/handlers/main.yml b/roles/nextcloud/handlers/main.yml
index 6552940..1e48524 100644
--- a/roles/nextcloud/handlers/main.yml
+++ b/roles/nextcloud/handlers/main.yml
@@ -1,6 +1,9 @@
---
-- name: Restart php7.0-fpm
- service: name=php7.0-fpm state=restarted
+- name: Restart php7.3-fpm
+ service: name=php7.3-fpm state=restarted
+
+- name: Restart Redis
+ service: name=redis-server state=restarted
- name: Restart Nginx
service: name=nginx state=restarted
diff --git a/roles/nextcloud/tasks/main.yml b/roles/nextcloud/tasks/main.yml
index 09554e0..4c049a9 100644
--- a/roles/nextcloud/tasks/main.yml
+++ b/roles/nextcloud/tasks/main.yml
@@ -3,12 +3,13 @@
vars:
packages:
- php-cli
+ - php-bcmath
- php-fpm
- php-apcu
- php-gd
+ - php-gmp
- php-imagick
- php-mbstring
- - php-mcrypt
- php-xml
- php-curl
- php-intl
@@ -16,41 +17,44 @@
- php-mysql
- php-zip
- php-json
+ - php-gmp
-- name: Configure PHP 7.0 Zend opcache
- lineinfile: dest=/etc/php/7.0/fpm/php.ini
+- name: Configure PHP 7.3 Zend opcache
+ lineinfile: dest=/etc/php/7.3/fpm/php.ini
regexp='^;?{{ item.var }}\\s*='
line="{{ item.var }} = {{ item.value }}"
owner=root group=root
mode=0644
with_items:
- - { var: opcache.enable, value: 1 }
- - { var: opcache.enable_cli, value: 1 }
- - { var: opcache.memory_consumption, value: 128 }
- - { var: opcache.interned_strings_buffer, value: 8 }
- - { var: opcache.max_accelerated_files, value: 10000 }
- - { var: opcache.revalidate_freq, value: 1 }
- - { var: opcache.fast_shutdown, value: 1 }
+ - { var: opcache.memory_consumption, value: 512 }
+ - { var: opcache.revalidate_freq, value: 180 }
notify:
- - Restart php7.0-fpm
+ - Restart php7.3-fpm
-- name: Configure PHP 7.0 pool environment
- lineinfile: dest=/etc/php/7.0/fpm/pool.d/www.conf
- regexp='^;?env\[{{ item.var }}\]\\s*='
- line="env[{{ item.var }}] = {{ item.value }}"
- owner=root group=root
- mode=0644
- with_items:
- - { var: HOSTNAME, value: "$HOSTNAME" }
- - { var: PATH, value: "/usr/bin:/bin" }
- - { var: TMP, value: "/tmp" }
- - { var: TMPDIR, value: "/tmp" }
- - { var: TEMP, value: "/tmp" }
+- name: Create '_nextcloud' user
+ user: name=_nextcloud system=yes
+ group=nogroup
+ createhome=no
+ home=/nonexistent
+ shell=/usr/sbin/nologin
+ password=!
+ state=present
+
+- name: Delete PHP 7.3 FPM's www pool
+ file: path=/etc/php/7.3/fpm/pool.d/www.conf state=absent
+ notify:
+ - Restart php7.3-fpm
+
+- name: Configure PHP 7.3 FPM's nextcloud pool
+ copy: src=etc/php/fpm/pool.d/nextcloud.conf
+ dest=/etc/php/7.3/fpm/pool.d/nextcloud.conf
+ owner=root group=root
+ mode=0644
notify:
- - Restart php7.0-fpm
+ - Restart php7.3-fpm
-- name: Start php7.0-fpm
- service: name=php7.0-fpm state=started
+- name: Start php7.3-fpm
+ service: name=php7.3-fpm state=started
- name: Copy /etc/cron.d/nextcloud
copy: src=etc/cron.d/nextcloud
@@ -103,6 +107,84 @@
- genkey
- import_tasks: ldap.yml
- when: "'LDAP-provider' not in group_names"
+ when: "'LDAP_provider' not in group_names"
tags:
- ldap
+
+# Note: intentionally don't set an owner/group as we don't want to set
+# ownership unless the path is a mountpoint. The service will fail
+# unless the data directory is mounted and accessible, and that's what
+# we want.
+- name: Create directory /mnt/nextcloud-data
+ file: path=/mnt/nextcloud-data
+ state=directory
+ mode=0700
+
+- name: Create directory /var/www/nextcloud
+ file: path=/var/www/nextcloud
+ state=directory
+ owner=root group=root
+ mode=0755
+
+# Note: Nextcloud doesn't like symlinked apps
+# * https://github.com/nextcloud/server/issues/10437
+# * https://github.com/nextcloud/server/issues/13556
+- name: Create directory /var/www/nextcloud/apps
+ file: path=/var/www/nextcloud/apps
+ state=directory
+ owner=_nextcloud group=nogroup
+ mode=0755
+
+- name: Create directory /var/log/nextcloud
+ file: path=/var/log/nextcloud
+ state=directory
+ owner=_nextcloud group=adm
+ mode=0750
+
+- name: Create directory /var/cache/nextcloud
+ file: path=/var/cache/nextcloud
+ state=directory
+ owner=_nextcloud group=nogroup
+ mode=0700
+
+- name: Copy Nextcloud logrotate snippet
+ copy: src=etc/logrotate.d/nextcloud
+ dest=/etc/logrotate.d/nextcloud
+ owner=root group=root
+ mode=0644
+ tags:
+ - logrotate
+
+- name: Install redis-server
+ apt: pkg={{ packages }}
+ vars:
+ packages:
+ - php-redis
+ - redis-server
+
+- name: Configure Redis
+ lineinfile: dest=/etc/redis/redis.conf
+ regexp='^#?\\s*{{ item.var }}\\s+'
+ line="{{ item.var }} {{ item.value }}"
+ owner=redis group=redis
+ mode=0640
+ with_items:
+ - { var: port, value: 0 }
+ - { var: unixsocket, value: /run/redis/redis-server.sock }
+ - { var: unixsocketperm, value: 660 }
+ notify:
+ - Restart Redis
+
+- name: Start redis-server
+ service: name=redis-server state=started
+
+- name: Add '_nextcloud' user to 'redis' group
+ user: name=_nextcloud groups=redis append=yes
+ notify:
+ - Restart php7.3-fpm
+
+- name: Install other Nextcloud dependencies
+ apt: pkg={{ packages }}
+ vars:
+ packages:
+ - libmagickcore-6.q16-6-extra
diff --git a/roles/out/tasks/main.yml b/roles/out/tasks/main.yml
index 0e64443..7a297f1 100644
--- a/roles/out/tasks/main.yml
+++ b/roles/out/tasks/main.yml
@@ -1,5 +1,9 @@
- name: Install Postfix
- apt: pkg=postfix
+ apt: pkg={{ packages }}
+ vars:
+ packages:
+ - postfix
+ - postfix-lmdb
- name: Configure Postfix
template: src=etc/postfix/{{ item }}.j2
@@ -24,6 +28,19 @@
owner=root group=root
mode=0644
+- name: Copy the SMTP TLS policy maps
+ template: src=etc/postfix/smtp_tls_policy.j2
+ dest=/etc/postfix-{{ postfix_instance[inst].name }}/smtp_tls_policy
+ owner=root group=root
+ mode=0644
+
+- name: Compile the SMTP TLS policy maps
+ postmap: cmd=postmap src=/etc/postfix-{{ postfix_instance[inst].name }}/smtp_tls_policy db=lmdb
+ owner=root group=root
+ mode=0644
+ notify:
+ - Reload Postfix
+
- meta: flush_handlers
- name: Start Postfix
diff --git a/roles/out/templates/etc/postfix/main.cf.j2 b/roles/out/templates/etc/postfix/main.cf.j2
index c05d9a5..f8aa55a 100644
--- a/roles/out/templates/etc/postfix/main.cf.j2
+++ b/roles/out/templates/etc/postfix/main.cf.j2
@@ -56,7 +56,10 @@ smtp_tls_protocols = !SSLv2, !SSLv3
smtp_tls_note_starttls_offer = yes
smtp_tls_session_cache_database = lmdb:$data_directory/smtp_tls_session_cache
-smtpd_tls_security_level = none
+smtp_tls_fingerprint_digest = sha256
+smtp_tls_policy_maps = lmdb:$config_directory/smtp_tls_policy
+
+smtpd_tls_security_level = none
strict_rfc821_envelopes = yes
smtpd_delay_reject = yes
diff --git a/roles/out/templates/etc/postfix/smtp_tls_policy.j2 b/roles/out/templates/etc/postfix/smtp_tls_policy.j2
new file mode 100644
index 0000000..7722dc8
--- /dev/null
+++ b/roles/out/templates/etc/postfix/smtp_tls_policy.j2
@@ -0,0 +1,12 @@
+# Lookup table matching next-hop destinations to TLS security policies;
+# this allows pining the key material for chosen recipient domains.
+#
+# {{ ansible_managed }}
+# Do NOT edit this file directly!
+{% for nexthop in ['fripost.org','.fripost.org'] %}
+
+{{ nexthop }} fingerprint ciphers=high protocols=!SSLv2:!SSLv3:!TLSv1:!TLSv1.1
+{% for h in groups.MX | sort %}
+ match={{ lookup('pipe', 'openssl pkey -pubin -outform DER <"certs/public/mx'+(hostvars[h].mxno | default('') | string)+'.fripost.org.pub" | openssl dgst -sha256 -c | sed "s/[^=]*=\s*//"') }}
+{% endfor %}
+{% endfor %}
diff --git a/roles/webmail/files/etc/cron.d/roundcube-core b/roles/webmail/files/etc/cron.d/roundcube-core
new file mode 100644
index 0000000..6d9e7af
--- /dev/null
+++ b/roles/webmail/files/etc/cron.d/roundcube-core
@@ -0,0 +1,7 @@
+#
+# Roundcube database cleaning: finally removes all records that are
+# marked as deleted.
+MAILTO=root
+
+# m h dom mon dow user command
+0 5 * * * _roundcube /usr/share/roundcube/bin/cleandb.sh >/dev/null
diff --git a/roles/webmail/files/etc/nginx/sites-available/roundcube b/roles/webmail/files/etc/nginx/sites-available/roundcube
index ae73562..46012f8 100644
--- a/roles/webmail/files/etc/nginx/sites-available/roundcube
+++ b/roles/webmail/files/etc/nginx/sites-available/roundcube
@@ -6,10 +6,10 @@ server {
server_name mail.fripost.org;
server_name webmail.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
- access_log /var/log/nginx/roundcube.access.log;
- error_log /var/log/nginx/roundcube.error.log info;
+ access_log /var/log/nginx/roundcube.access.log;
+ error_log /var/log/nginx/roundcube.error.log info;
location / {
return 301 https://$host$request_uri;
@@ -24,22 +24,27 @@ server {
server_name mail.fripost.org;
server_name webmail.fripost.org;
- root /var/lib/roundcube;
+ root /var/lib/roundcube/public_html;
include snippets/headers.conf;
add_header Content-Security-Policy
- "default-src 'none'; child-src 'self'; frame-src 'self'; connect-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data:; font-src 'self'; reflected-xss block; referrer no-referrer-when-downgrade; frame-ancestors 'self'; form-action 'self'; base-uri mail.fripost.org webmail.fripost.org";
+ "default-src 'none'; frame-src 'self'; connect-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri mail.fripost.org webmail.fripost.org";
include snippets/ssl.conf;
ssl_certificate ssl/mail.fripost.org.pem;
ssl_certificate_key ssl/mail.fripost.org.key;
include snippets/mail.fripost.org.hpkp-hdr;
+ gzip on;
+ gzip_static on;
+ gzip_vary on;
+ gzip_min_length 256;
+ gzip_types application/font-woff application/font-woff2 application/javascript application/json application/xml image/svg+xml image/x-icon text/css text/plain text/vcard;
+
location = /favicon.ico {
- root /usr/share/roundcube/skins/default/images;
+ root /usr/share/roundcube/skins/elastic/images;
log_not_found off;
access_log off;
- expires max;
}
location = /robots.txt {
@@ -48,28 +53,24 @@ server {
access_log off;
}
- access_log /var/log/nginx/roundcube.access.log;
- error_log /var/log/nginx/roundcube.error.log info;
+ access_log /var/log/nginx/roundcube.access.log;
+ error_log /var/log/nginx/roundcube.error.log info;
client_max_body_size 64m;
location = / { index index.php; }
location = /index.php {
+ # TODO enable gzip for Roundcube >=1.5: it's immune to BREACH attacks once
+ # $config['session_samesite'] is set to 'Strict', see
+ # https://github.com/roundcube/roundcubemail/pull/6772
+ # https://www.sjoerdlangkemper.nl/2016/11/07/current-state-of-breach-attack/#same-site-cookies
+ gzip off;
include snippets/fastcgi-php-ssl.conf;
-
- # From /var/lib/roundcube/.htaccess
- fastcgi_param PHP_VALUE "upload_max_filesize=25M
- post_max_size=30M
- memory_limit=64M
- session.gc_maxlifetime=21600
- session.gc_divisor=500
- session.gc_probability=1";
- fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root:/usr/share/roundcube:/etc/roundcube:/var/log/roundcube:/usr/share/php:/usr/share/javascript:/usr/share/tinymce:/usr/share/misc/magic:/dev
- upload_tmp_dir=$document_root/temp";
+ fastcgi_pass unix:/var/run/php/php7.3-fpm@roundcube.sock;
}
- location ~ "^/(?:plugins|program/js|program/resources|skins)/.*[^./]\.(?:css|eot|gif|html|ico|jpg|js|pdf|png|svg|tif|ttf|woff)$" {
- try_files $uri =404;
+ location ~ "^/(?:plugins|program/js|program/resources|skins)(?:/[[:alnum:]][[:alnum:]\-\._]*)+\.(?:css|eot|gif|html|ico|jpg|js|pdf|png|svg|tiff?|ttf|webp|woff2?)$" {
expires 30d;
+ try_files $uri =404;
}
location / { internal; }
}
diff --git a/roles/webmail/files/etc/php/fpm/pool.d/roundcube.conf b/roles/webmail/files/etc/php/fpm/pool.d/roundcube.conf
new file mode 100644
index 0000000..1512d66
--- /dev/null
+++ b/roles/webmail/files/etc/php/fpm/pool.d/roundcube.conf
@@ -0,0 +1,22 @@
+[roundcube]
+user = _roundcube
+group = nogroup
+listen = /run/php/php7.3-fpm@roundcube.sock
+listen.owner = www-data
+listen.group = www-data
+listen.mode = 0600
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+
+php_value[upload_max_filesize] = 25M
+php_value[post_max_size] = 30M
+php_value[memory_limit] = 64M
+php_value[session.gc_maxlifetime] = 21600
+php_value[session.gc_divisor] = 500
+php_value[session.gc_probability] = 1
+
+php_admin_value[upload_tmp_dir] = /var/lib/roundcube/temp
+php_admin_value[open_basedir] = /var/lib/roundcube:/usr/share/roundcube:/etc/roundcube:/var/log/roundcube:/usr/share/php:/usr/share/javascript:/usr/lib/nodejs:/usr/share/tinymce:/usr/share/misc/magic:/dev
diff --git a/roles/webmail/files/etc/roundcube/plugins/authres_status/config.inc.php b/roles/webmail/files/etc/roundcube/plugins/authres_status/config.inc.php
new file mode 100644
index 0000000..6d41d4f
--- /dev/null
+++ b/roles/webmail/files/etc/roundcube/plugins/authres_status/config.inc.php
@@ -0,0 +1,6 @@
+<?php
+
+$config['use_fallback_verifier'] = false;
+$config['trusted_mtas'] = array('mx1.fripost.org', 'mx2.fripost.org');
+
+?>
diff --git a/roles/webmail/files/etc/roundcube/plugins/html5_notifier/config.inc.php b/roles/webmail/files/etc/roundcube/plugins/html5_notifier/config.inc.php
new file mode 100644
index 0000000..1ec7922
--- /dev/null
+++ b/roles/webmail/files/etc/roundcube/plugins/html5_notifier/config.inc.php
@@ -0,0 +1,6 @@
+<?php
+
+$config['html5_notifier_duration'] = '3';
+$config['html5_notifier_smbox'] = '1';
+
+?>
diff --git a/roles/webmail/files/etc/roundcube/plugins/jqueryui/config.inc.php b/roles/webmail/files/etc/roundcube/plugins/jqueryui/config.inc.php
deleted file mode 100644
index bb9720b..0000000
--- a/roles/webmail/files/etc/roundcube/plugins/jqueryui/config.inc.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-// if you want to load localization strings for specific sub-libraries of jquery-ui, configure them here
-$config['jquery_ui_i18n'] = array('datepicker');
-
-// map Roundcube skins with jquery-ui themes here
-$config['jquery_ui_skin_map'] = array(
- 'default' => 'smoothness',
-);
-
-?>
diff --git a/roles/webmail/files/etc/roundcube/plugins/password/config.inc.php b/roles/webmail/files/etc/roundcube/plugins/password/config.inc.php
index c32f58e..e53b753 100644
--- a/roles/webmail/files/etc/roundcube/plugins/password/config.inc.php
+++ b/roles/webmail/files/etc/roundcube/plugins/password/config.inc.php
@@ -122,7 +122,7 @@ $config['password_saslpasswd_args'] = '';
// You can provide one or several hosts in an array in which case the hosts are tried from left to right.
// Exemple: array('ldap1.exemple.com', 'ldap2.exemple.com');
// Default: 'localhost'
-$config['password_ldap_host'] = 'localhost';
+$config['password_ldap_host'] = '127.0.0.1';
// LDAP server port to connect to
// Default: '389'
diff --git a/roles/webmail/files/etc/roundcube/plugins/thunderbird_labels/config.inc.php b/roles/webmail/files/etc/roundcube/plugins/thunderbird_labels/config.inc.php
new file mode 100644
index 0000000..2abb423
--- /dev/null
+++ b/roles/webmail/files/etc/roundcube/plugins/thunderbird_labels/config.inc.php
@@ -0,0 +1,5 @@
+<?php
+
+$rcmail_config['tb_label_enable'] = true;
+
+?>
diff --git a/roles/webmail/files/etc/systemd/system/stunnel4@ldap.socket b/roles/webmail/files/etc/systemd/system/stunnel4@ldap.socket
new file mode 100644
index 0000000..72aa82c
--- /dev/null
+++ b/roles/webmail/files/etc/systemd/system/stunnel4@ldap.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=SSL tunnel for network daemons (instance %i)
+Documentation=man:stunnel4(8)
+
+[Socket]
+BindToDevice=lo
+ListenStream=127.0.0.1:389
+NoDelay=yes
+
+[Install]
+WantedBy=sockets.target
diff --git a/roles/webmail/files/usr/share/roundcube/skins/classic/images/fripost_logo.png b/roles/webmail/files/usr/share/roundcube/program/resources/fripost_logo_black.png
index 7af586a..7af586a 100644
--- a/roles/webmail/files/usr/share/roundcube/skins/classic/images/fripost_logo.png
+++ b/roles/webmail/files/usr/share/roundcube/program/resources/fripost_logo_black.png
Binary files differ
diff --git a/roles/webmail/files/usr/share/roundcube/skins/larry/images/fripost_logo.png b/roles/webmail/files/usr/share/roundcube/program/resources/fripost_logo_white.png
index c581a30..c581a30 100644
--- a/roles/webmail/files/usr/share/roundcube/skins/larry/images/fripost_logo.png
+++ b/roles/webmail/files/usr/share/roundcube/program/resources/fripost_logo_white.png
Binary files differ
diff --git a/roles/webmail/handlers/main.yml b/roles/webmail/handlers/main.yml
index 446c771..5b730d7 100644
--- a/roles/webmail/handlers/main.yml
+++ b/roles/webmail/handlers/main.yml
@@ -2,8 +2,14 @@
- name: Restart stunnel@ldap
service: name=stunnel4@ldap state=restarted
-- name: Restart php7.0-fpm
- service: name=php7.0-fpm state=restarted
+- name: Restart php7.3-fpm
+ service: name=php7.3-fpm state=restarted
- name: Restart Nginx
service: name=nginx state=restarted
+
+- name: Stop stunnel4@ldap.service
+ service: name=stunnel4@ldap.service state=stopped
+
+- name: Restart stunnel4@ldap.socket
+ service: name=stunnel4@ldap.socket state=restarted
diff --git a/roles/webmail/tasks/ldap.yml b/roles/webmail/tasks/ldap.yml
index b24860c..f0b461c 100644
--- a/roles/webmail/tasks/ldap.yml
+++ b/roles/webmail/tasks/ldap.yml
@@ -1,3 +1,12 @@
+- name: Copy stunnel4@ldap.socket
+ copy: src=etc/systemd/system/stunnel4@ldap.socket
+ dest=/etc/systemd/system/stunnel4@ldap.socket
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Restart stunnel4@ldap.socket
+
- name: Create /etc/stunnel/certs
file: path=/etc/stunnel/certs
state=directory
@@ -9,24 +18,19 @@
dest=/etc/stunnel/certs/ldap.pem
owner=root group=root
mode=0644
- register: r1
notify:
- - Restart stunnel@ldap
+ - Stop stunnel4@ldap.service
- name: Configure stunnel
- copy: src=etc/stunnel/ldap.conf
- dest=/etc/stunnel/ldap.conf
- owner=root group=root
- mode=0644
- register: r2
+ template: src=etc/stunnel/ldap.conf.j2
+ dest=/etc/stunnel/ldap.conf
+ owner=root group=root
+ mode=0644
notify:
- - Restart stunnel@ldap
-
-- name: Enable stunnel@ldap
- service: name=stunnel4@ldap enabled=yes
+ - Stop stunnel4@ldap.service
-- name: Start stunnel@ldap
- service: name=stunnel4@ldap state=started
- when: not (r1.changed or r2.changed)
+- name: Disable stunnel4@ldap.service
+ service: name=stunnel4@ldap.service enabled=false
-- meta: flush_handlers
+- name: Start stunnel4@ldap.socket socket
+ service: name=stunnel4@ldap.socket state=started enabled=true
diff --git a/roles/webmail/tasks/main.yml b/roles/webmail/tasks/main.yml
index 210d277..146c36f 100644
--- a/roles/webmail/tasks/main.yml
+++ b/roles/webmail/tasks/main.yml
@@ -1,5 +1,5 @@
- import_tasks: ldap.yml
- when: "'LDAP-provider' not in group_names"
+ when: "'LDAP_provider' not in group_names"
tags:
- ldap
- stunnel
diff --git a/roles/webmail/tasks/roundcube.yml b/roles/webmail/tasks/roundcube.yml
index b7678a3..dc73358 100644
--- a/roles/webmail/tasks/roundcube.yml
+++ b/roles/webmail/tasks/roundcube.yml
@@ -9,22 +9,57 @@
# spell-checking
- php-enchant
-- name: Configure PHP 7.0 Zend opcache
- lineinfile: dest=/etc/php/7.0/fpm/php.ini
+## TODO: run php as a dedicated system user
+- name: Configure PHP 7.3 Zend opcache
+ lineinfile: dest=/etc/php/7.3/fpm/php.ini
regexp='^;?{{ item.var }}\\s*='
line="{{ item.var }} = {{ item.value }}"
owner=root group=root
mode=0644
with_items:
- - { var: opcache.enable, value: 1 }
- - { var: opcache.enable_cli, value: 1 }
- { var: opcache.memory_consumption, value: 128 }
- - { var: opcache.interned_strings_buffer, value: 8 }
- - { var: opcache.max_accelerated_files, value: 2048 }
- { var: opcache.revalidate_freq, value: 60 }
- - { var: opcache.fast_shutdown, value: 1 }
notify:
- - Restart php7.0-fpm
+ - Restart php7.3-fpm
+
+- name: Create '_roundcube' user
+ user: name=_roundcube system=yes
+ group=nogroup
+ createhome=no
+ home=/nonexistent
+ shell=/usr/sbin/nologin
+ password=!
+ state=present
+
+- name: Delete PHP 7.3 FPM's www pool
+ file: path=/etc/php/7.3/fpm/pool.d/www.conf state=absent
+ notify:
+ - Restart php7.3-fpm
+
+- name: Configure PHP 7.3 FPM's roundcube pool
+ copy: src=etc/php/fpm/pool.d/roundcube.conf
+ dest=/etc/php/7.3/fpm/pool.d/roundcube.conf
+ owner=root group=root
+ mode=0644
+ notify:
+ - Restart php7.3-fpm
+
+- name: Start php7.3-fpm
+ service: name=php7.3-fpm state=started
+
+# Make it sticky: `dpkg-statoverride --add _roundcube nogroup 0700 /var/lib/roundcube/temp`
+- name: Create cache directory /var/lib/roundcube/temp
+ file: path=/var/lib/roundcube/temp
+ state=directory
+ owner=_roundcube group=nogroup
+ mode=0700
+
+# Make it sticky: `dpkg-statoverride --add _roundcube adm 0750 /var/log/roundcube`
+- name: Create cache directory /var/log/roundcube
+ file: path=/var/log/roundcube
+ state=directory
+ owner=_roundcube group=adm
+ mode=0750
- name: Install GNU Aspell and some dictionaries
apt: pkg={{ packages }}
@@ -40,64 +75,69 @@
- aspell-sv
- name: Install Roundcube
- apt: pkg={{ packages }}
+ apt: pkg={{ packages }} default_release={{ ansible_lsb.codename }}-backports
vars:
packages:
- roundcube-core
- roundcube-mysql
- roundcube-plugins
+ - roundcube-plugins-extra
+
+- name: Install plugin dependencies
+ apt: pkg={{ packages }}
+ vars:
+ packages:
- php-net-sieve
- - php-net-ldap3
- name: Copy fripost's logo
- copy: src=usr/share/roundcube/skins/{{ item }}/images/fripost_logo.png
- dest=/usr/share/roundcube/skins/{{ item }}/images/fripost_logo.png
+ copy: src=usr/share/roundcube/program/resources/{{ item }}
+ dest=/usr/share/roundcube/program/resources/{{ item }}
owner=root group=root
mode=0644
with_items:
- - classic
- - larry
+ - fripost_logo_black.png
+ - fripost_logo_white.png
- name: Configure Roundcube
lineinfile: dest=/etc/roundcube/config.inc.php
regexp='^\\s*\\$config\\[\'{{ item.var }}\'\\]\\s*='
line='$config[\'{{ item.var }}\'] = {{ item.value }};'
- owner=root group=www-data
- mode=0640
+ owner=_roundcube group=nogroup
+ mode=0600
with_items:
# Logging/Debugging
- { var: smtp_log, value: "false" }
# IMAP
# WARNING: After hostname change update of mail_host column in users
# table is required to match old user data records with the new host.
- - { var: default_host, value: "'{{ imapsvr_addr | ipaddr }}'" }
- - { var: default_port, value: "143" }
- - { var: imap_auth_type, value: "'PLAIN'" }
- - { var: imap_cache, value: "null" }
- - { var: imap_timeout, value: "180" }
- - { var: imap_force_ns, value: "true" }
- - { var: messages_cache, value: "false" }
+ - { var: default_host, value: "'{{ imapsvr_addr | ansible.utils.ipaddr }}'" }
+ - { var: default_port, value: "143" }
+ - { var: imap_auth_type, value: "'PLAIN'" }
+ - { var: imap_cache, value: "null" }
+ - { var: imap_timeout, value: "180" }
+ - { var: imap_force_ns, value: "true" }
+ - { var: messages_cache, value: "false" }
# SMTP
- - { var: smtp_server, value: "'{{ postfix_instance.MSA.addr | ipaddr }}'" }
- - { var: smtp_port, value: "{{ postfix_instance.MSA.port }}" }
- - { var: smtp_auth_type, value: "'PLAIN'" }
- - { var: smtp_user, value: "'%u'" }
- - { var: smtp_pass, value: "'%p'" }
+ - { var: smtp_server, value: "'{{ postfix_instance.MSA.addr | ansible.utils.ipaddr }}'" }
+ - { var: smtp_port, value: "{{ postfix_instance.MSA.port }}" }
+ - { var: smtp_auth_type, value: "'PLAIN'" }
+ - { var: smtp_user, value: "'%u'" }
+ - { var: smtp_pass, value: "'%p'" }
# System
- - { var: force_https, value: "true" }
- - { var: login_autocomplete, value: "2" }
- - { var: skin_logo, value: "'/images/fripost_logo.png'" }
- - { var: username_domain, value: "'fripost.org'" }
- - { var: product_name, value: "'Fripost Webmail'" }
- - { var: password_charset, value: "'UTF-8'" }
+ - { var: force_https, value: "true" }
+ - { var: login_autocomplete, value: "2" }
+ - { var: username_domain, value: "'fripost.org'" }
+ - { var: product_name, value: "'Fripost Webmail'" }
+ - { var: password_charset, value: "'UTF-8'" }
+ - { var: skin_logo, value: 'array("classic:*" => "program/resources/fripost_logo_black.png", "larry:*" => "program/resources/fripost_logo_white.png", "elastic:login[favicon]" => "", "elastic:login" => "program/resources/fripost_logo_black.png")' }
# Plugins
- - { var: plugins, value: "array('archive','additional_message_headers','managesieve','password')" }
+ - { var: plugins, value: "array('archive','additional_message_headers','attachment_reminder','authres_status','emoticons','hide_blockquote','html5_notifier','managesieve','password','thunderbird_labels','vcard_attachments')" }
# Spell Checking
- { var: enable_spellcheck, value: "'true'" }
- { var: spellcheck_engine, value: "'enchant'" }
- { var: spellcheck_languages, value: "array('da','de','en','es','fr','no','sv')" }
# User Interface
- - { var: skin, value: "'larry'" }
+ - { var: skin, value: "'elastic'" }
- { var: language, value: "'sv_SE'" }
- { var: create_default_folders, value: "true" }
- { var: support_url, value: "'https://fripost.org/kontakt/'" }
@@ -105,17 +145,22 @@
- { var: htmleditor, value: "3" }
- { var: skip_deleted, value: "true" }
- { var: check_all_folders, value: "false" }
+ - { var: hide_blockquote_limit, value: "8" }
+ - { var: attachment_reminder, value: "true" }
+ # Don't allow overriding these settings
+ - { var: dont_override, value: "array('use_fallback_verifier', 'trusted_mtas')" }
- name: Make the logo a hyperlink to the website
lineinfile: dest=/usr/share/roundcube/skins/{{ item }}/templates/login.html
- regexp='^(<roundcube:object name="logo" src="/images/roundcube_logo.png"[^>]* />)$'
- line='<a href="https://fripost.org">\1</a>'
+ regexp='^(\s*)(<roundcube:object name="logo" src="[^"]*"[^>]* />)'
+ line='\1<a href="https://fripost.org">\2</a>'
backrefs=yes
owner=root group=root
mode=0644
with_items:
- classic
- larry
+ - elastic
- name: Configure Roundcube plugins
copy: src=etc/roundcube/plugins/{{ item }}/config.inc.php
@@ -124,8 +169,10 @@
mode=0644
with_items:
- additional_message_headers
- - jqueryui
+ - authres_status
- password
+ - html5_notifier
+ - thunderbird_labels
- name: Configure Roundcube plugins (2)
template: src=etc/roundcube/plugins/{{ item }}/config.inc.php.j2
@@ -135,8 +182,22 @@
with_items:
- managesieve
-- name: Start php7.0-fpm
- service: name=php7.0-fpm state=started
+- name: Start php7.3-fpm
+ service: name=php7.3-fpm state=started
+
+- name: Copy /etc/cron.d/roundcube-core
+ copy: src=etc/cron.d/roundcube-core
+ dest=/etc/cron.d/roundcube-core
+ owner=root group=root
+ mode=0644
+
+- name: Tweak /etc/logrotate.d/roundcube-core
+ lineinfile: dest=/etc/logrotate.d/roundcube-core
+ regexp='^(\s*)create\s+[0-9]+\s+\S+\s+adm$'
+ backrefs=yes
+ line='\1create 0640 _roundcube adm'
+ owner=root group=root
+ mode=0644
- name: Copy /etc/nginx/sites-available/roundcube
copy: src=etc/nginx/sites-available/roundcube
diff --git a/roles/webmail/templates/etc/roundcube/plugins/managesieve/config.inc.php.j2 b/roles/webmail/templates/etc/roundcube/plugins/managesieve/config.inc.php.j2
index 66af466..7b424e4 100644
--- a/roles/webmail/templates/etc/roundcube/plugins/managesieve/config.inc.php.j2
+++ b/roles/webmail/templates/etc/roundcube/plugins/managesieve/config.inc.php.j2
@@ -10,7 +10,7 @@ $config['managesieve_port'] = 4190;
// %n - http hostname ($_SERVER['SERVER_NAME'])
// %d - domain (http hostname without the first part)
// For example %n = mail.domain.tld, %d = domain.tld
-$config['managesieve_host'] = '{{ imapsvr_addr | ipaddr }}';
+$config['managesieve_host'] = '{{ imapsvr_addr | ansible.utils.ipaddr }}';
// authentication method. Can be CRAM-MD5, DIGEST-MD5, PLAIN, LOGIN, EXTERNAL
// or none. Optional, defaults to best method supported by server.
@@ -60,7 +60,7 @@ $config['managesieve_replace_delimiter'] = '';
// mailbox, mboxmetadata, regex, reject, relational, servermetadata,
// spamtest, spamtestplus, subaddress, vacation, variables, virustest, etc.
// Note: not all extensions are implemented
-$config['managesieve_disabled_extensions'] = array();
+$config['managesieve_disabled_extensions'] = array('reject','ereject');
// Enables debugging of conversation with sieve server. Logs it into <log_dir>/sieve
$config['managesieve_debug'] = false;
diff --git a/roles/webmail/files/etc/stunnel/ldap.conf b/roles/webmail/templates/etc/stunnel/ldap.conf.j2
index b8c7787..6fce2bc 100644
--- a/roles/webmail/files/etc/stunnel/ldap.conf
+++ b/roles/webmail/templates/etc/stunnel/ldap.conf.j2
@@ -2,11 +2,6 @@
; * Global options *
; **************************************************************************
-; setuid()/setgid() to the specified user/group in daemon mode
-setuid = stunnel4
-setgid = stunnel4
-
-; PID is created inside the chroot jail
pid =
foreground = yes
@@ -17,41 +12,30 @@ debug = 4
; * Service defaults may also be specified in individual service sections *
; **************************************************************************
-; Certificate/key is needed in server mode and optional in client mode
-;cert = /etc/stunnel/mail.pem
-;key = /etc/stunnel/mail.pem
client = yes
-socket = a:SO_BINDTODEVICE=lo
; Some performance tunings
-socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
; Prevent MITM attacks
-verify = 4
+verifyPeer = yes
; Disable support for insecure protocols
-;options = NO_SSLv2
-options = NO_SSLv3
-options = NO_TLSv1
-options = NO_TLSv1.1
-
+sslVersionMin = TLSv1.2
options = NO_COMPRESSION
-; These options provide additional security at some performance degradation
-;options = SINGLE_ECDH_USE
-;options = SINGLE_DH_USE
-
; Select permitted SSL ciphers
-ciphers = EECDH+AESGCM:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL
+ciphers = EECDH+AESGCM:EECDH+CHACHA20!MEDIUM!LOW!EXP!aNULL!eNULL
; **************************************************************************
; * Service definitions (remove all services for inetd mode) *
; **************************************************************************
[ldaps]
-accept = localhost:389
-connect = ldap.fripost.org:636
-CAfile = /etc/stunnel/certs/ldap.pem
+; dummy address (socket-activated)
+accept = 127.0.0.1:0
+connect = {{ ipsec[ hostvars[groups.LDAP_provider[0]].inventory_hostname_short ] }}:636
+checkHost = ldap.fripost.org
+CAfile = /etc/stunnel/certs/ldap.pem
; vim:ft=dosini
diff --git a/roles/wiki/files/etc/nginx/sites-available/website b/roles/wiki/files/etc/nginx/sites-available/website
index ba227e5..4aeb3db 100644
--- a/roles/wiki/files/etc/nginx/sites-available/website
+++ b/roles/wiki/files/etc/nginx/sites-available/website
@@ -5,7 +5,7 @@ server {
server_name fripost.org;
server_name www.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
access_log /var/log/nginx/www.access.log;
error_log /var/log/nginx/www.error.log info;
@@ -28,35 +28,36 @@ server {
include snippets/headers.conf;
add_header Content-Security-Policy
- "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; referrer no-referrer-when-downgrade; frame-ancestors 'none'; form-action https://www.paypal.com/; base-uri fripost.org www.fripost.org";
+ "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; form-action https://www.paypal.com/; base-uri fripost.org www.fripost.org";
include snippets/ssl.conf;
ssl_certificate ssl/www.fripost.org.pem;
ssl_certificate_key ssl/www.fripost.org.key;
include snippets/fripost.org.hpkp-hdr;
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 256;
+ gzip_types application/font-woff application/font-woff2 application/javascript application/json application/xml image/svg+xml image/x-icon text/css text/plain;
+
location / {
try_files $uri $uri/ =404;
index index.html;
root /var/lib/ikiwiki/public_html/fripost-wiki/website;
}
+ location = /ikiwiki.cgi { internal; }
location /static/ {
- alias /var/lib/ikiwiki/public_html/fripost-wiki/static/;
expires 30d;
+ try_files $uri =404;
+ alias /var/lib/ikiwiki/public_html/fripost-wiki/static/;
}
location /material/ {
alias /var/www/fripost.org/material/;
- expires 30d;
}
location /minutes/ {
alias /var/www/fripost.org/minutes/;
- expires 30d;
}
location /.well-known/autoconfig/ {
alias /var/www/fripost.org/autoconfig/;
}
-
- location = /ikiwiki.cgi {
- return 403;
- }
}
diff --git a/roles/wiki/files/etc/nginx/sites-available/wiki b/roles/wiki/files/etc/nginx/sites-available/wiki
index 7759fa5..b201ef5 100644
--- a/roles/wiki/files/etc/nginx/sites-available/wiki
+++ b/roles/wiki/files/etc/nginx/sites-available/wiki
@@ -4,7 +4,7 @@ server {
server_name wiki.fripost.org;
- include snippets/acme-challenge.conf;
+ include /etc/lacme/nginx.conf;
access_log /var/log/nginx/wiki.access.log;
error_log /var/log/nginx/wiki.error.log info;
@@ -27,26 +27,33 @@ server {
include snippets/headers.conf;
add_header Content-Security-Policy
- "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; referrer no-referrer-when-downgrade; frame-ancestors 'none'; form-action 'self'; base-uri wiki.fripost.org";
+ "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri wiki.fripost.org";
include snippets/ssl.conf;
ssl_certificate ssl/www.fripost.org.pem;
ssl_certificate_key ssl/www.fripost.org.key;
include snippets/fripost.org.hpkp-hdr;
+ gzip on;
+ gzip_vary on;
+ gzip_min_length 256;
+ gzip_types application/font-woff application/font-woff2 application/javascript application/json application/xml image/svg+xml image/x-icon text/css text/plain;
+
+ root /var/lib/ikiwiki/public_html/fripost-wiki;
+
+ location /static/ { expires 30d; try_files $uri =404; }
location / {
location ~ ^/website(/.*)?$ { return 302 $scheme://fripost.org$1; }
- try_files $uri $uri/ =404;
index index.html;
- root /var/lib/ikiwiki/public_html/fripost-wiki;
+ try_files $uri $uri/ =404;
}
location = /ikiwiki.cgi {
- fastcgi_param DOCUMENT_ROOT /var/lib/ikiwiki/public_html/fripost-wiki;
+ fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_FILENAME /var/lib/ikiwiki/public_html/ikiwiki.cgi;
fastcgi_index ikiwiki.cgi;
include snippets/fastcgi.conf;
- fastcgi_pass unix:/var/run/fcgiwrap.socket;
- gzip off;
+ fastcgi_pass unix:/run/ikiwiki.socket;
+ gzip off; # protect against BREACH
}
}
diff --git a/roles/wiki/files/etc/systemd/system/ikiwiki.service b/roles/wiki/files/etc/systemd/system/ikiwiki.service
new file mode 100644
index 0000000..3ee7d66
--- /dev/null
+++ b/roles/wiki/files/etc/systemd/system/ikiwiki.service
@@ -0,0 +1,23 @@
+[Unit]
+Description=wiki compiler (CGI script)
+Documentation=https://ikiwiki.info/
+
+[Service]
+User=ikiwiki
+Group=ikiwiki
+ExecStart=/usr/sbin/fcgiwrap
+SyslogIdentifier=ikiwiki
+#
+# Hardening
+NoNewPrivileges=yes
+ReadWriteDirectories=/var/lib/ikiwiki/fripost-wiki
+ReadWriteDirectories=/var/lib/ikiwiki/public_html/fripost-wiki
+PrivateDevices=yes
+ProtectHome=yes
+ProtectSystem=strict
+ProtectControlGroups=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/wiki/files/etc/systemd/system/ikiwiki.socket b/roles/wiki/files/etc/systemd/system/ikiwiki.socket
new file mode 100644
index 0000000..8dc1a0e
--- /dev/null
+++ b/roles/wiki/files/etc/systemd/system/ikiwiki.socket
@@ -0,0 +1,11 @@
+[Unit]
+Description=wiki compiler (CGI script)
+Documentation=https://ikiwiki.info/
+
+[Socket]
+ListenStream=%t/ikiwiki.socket
+SocketUser=www-data
+SocketMode=0600
+
+[Install]
+WantedBy=sockets.target
diff --git a/roles/wiki/files/var/lib/ikiwiki/IkiWiki/Plugin/pandoc.pm b/roles/wiki/files/var/lib/ikiwiki/IkiWiki/Plugin/pandoc.pm
index 25081ef..34bdd89 100644
--- a/roles/wiki/files/var/lib/ikiwiki/IkiWiki/Plugin/pandoc.pm
+++ b/roles/wiki/files/var/lib/ikiwiki/IkiWiki/Plugin/pandoc.pm
@@ -21,6 +21,23 @@ my %extra_formats = (
latex => { ext=>'tex', label=>'LaTeX', format=>'latex', extra=>['--standalone'], order=>7 },
);
+my @scalar_meta_keys = qw/
+ title date bibliography csl subtitle abstract summary description
+ version lang locale titlesort tag fripost_debug_inner
+ /;
+
+my @list_meta_keys = qw/
+ author
+ /;
+
+my @hash_meta_keys = qw/
+ experiment
+ /;
+
+my @list_hash_meta_keys = qw/
+ references
+ /;
+
sub import {
my $markdown_ext = $config{pandoc_markdown_ext} || "mdwn";
@@ -96,6 +113,13 @@ sub getsetup () {
safe => 1,
rebuild => 1,
},
+ pandoc_markdown_fmt => {
+ type => "string",
+ example => "markdown",
+ description => "Format string to use when processing files handled by Pandoc.",
+ safe => 1,
+ rebuild => 1,
+ },
pandoc_latex => {
type => "boolean",
example => 0,
@@ -409,10 +433,11 @@ sub htmlize ($@) {
# can be parsed out
# We must omit the 'bibliography' parameter here, otherwise the list of
# references will be doubled.
+ my $markdown_fmt = $config{pandoc_markdown_fmt} || 'markdown';
my $to_json_pid = open2(*JSON_OUT, *PANDOC_OUT, $command,
- '-f', $format,
+ '-f', $markdown_fmt,
'-t', 'json',
- @args, '--normalize');
+ @args);
error("Unable to open $command") unless $to_json_pid;
# Workaround for perl bug (#376329)
@@ -435,8 +460,8 @@ sub htmlize ($@) {
my $meta = undef;
my $decoded_json = decode_json($json_content);
# The representation of the meta block changed in pandoc version 1.18
- if (ref $decoded_json eq 'HASH' && $decoded_json->{'Meta'}) {
- $meta = $decoded_json->{'Meta'} || {}; # post-1.18 version
+ if (ref $decoded_json eq 'HASH' && $decoded_json->{'meta'}) {
+ $meta = $decoded_json->{'meta'} || {}; # post-1.18 version
} elsif (ref $decoded_json eq 'ARRAY') {
$meta = $decoded_json->[0]->{'unMeta'} || {}; # pre-1.18 version
}
@@ -450,12 +475,11 @@ sub htmlize ($@) {
# as well as some configuration options (generate_*, *_extra_options, *_template).
my @format_keys = grep { $_ ne 'pdf' } keys %extra_formats;
- my %scalar_meta = map { ($_=>undef) } qw(
- title date bibliography csl subtitle abstract summary
- description version lang locale);
+ my %scalar_meta = map { ($_=>undef) } @scalar_meta_keys;
$scalar_meta{$_.'_template'} = undef for @format_keys;
my %bool_meta = map { ("generate_$_"=>0) } keys %extra_formats;
- my %list_meta = map { ($_=>[]) } qw/author references/;
+ my %list_meta = map { ($_=>[]) } (
+ @list_meta_keys, @list_hash_meta_keys, @hash_meta_keys);
$list_meta{$_.'_extra_options'} = [] for @format_keys;
my $have_bibl = 0;
foreach my $k (keys %scalar_meta) {
@@ -484,6 +508,7 @@ sub htmlize ($@) {
$list_meta{$k} = unwrap_c($meta->{$k});
$list_meta{$k} = [$list_meta{$k}] unless ref $list_meta{$k} eq 'ARRAY';
$have_bibl = 1 if $k eq 'references';
+ $pagestate{$page}{meta}{$k} = $list_meta{$k};
$pagestate{$page}{meta}{"pandoc_$k"} = $list_meta{$k};
}
# Try to add other keys as scalars, with pandoc_ prefix only.
@@ -596,9 +621,14 @@ sub pagetemplate (@) {
my $page = $params{page};
my $template = $params{template};
foreach my $k (keys %{$pagestate{$page}{meta}}) {
- next unless $k =~ /^pandoc_/;
+ next unless
+ (grep {/^$k$/} (
+ @scalar_meta_keys, @list_meta_keys,
+ @hash_meta_keys, @list_hash_meta_keys)) ||
+ ($k =~ /^(pandoc_)/);
$template->param($k => $pagestate{$page}{meta}{$k});
}
+ return $template;
}
sub pageactions {
@@ -757,6 +787,11 @@ sub unwrap_c {
# Finds the deepest-level scalar value for 'c' in the data structure.
# Lists with one element are replaced with the scalar, lists with more
# than one element are returned as an arrayref containing scalars.
+ #
+ # Elements containing hash as keys are unwrapped. That is to
+ # support *MetaList* containing *MetaMap* with keys pointing to
+ # *MetaInlines*. Reference are examples of that structure. (hash unwrap)
+ #
my $container = shift;
if (ref $container eq 'ARRAY' && @$container > 1) {
if (ref $container->[0] eq 'HASH' && $container->[0]->{t} =~ /^(?:Str|Space)$/) {
@@ -771,6 +806,8 @@ sub unwrap_c {
return;
} elsif (ref $container eq 'HASH' && $container->{c}) {
return unwrap_c($container->{c});
+ } elsif (ref $container eq 'HASH' && keys $container->%*) { # (hash unwrap)
+ return {map { $_ => unwrap_c($container->{$_}) } keys $container->%*};
} elsif (ref $container) {
return;
} else {
diff --git a/roles/wiki/files/var/lib/ikiwiki/fripost-wiki.setup b/roles/wiki/files/var/lib/ikiwiki/fripost-wiki.setup
index 4353965..4af3d59 100644
--- a/roles/wiki/files/var/lib/ikiwiki/fripost-wiki.setup
+++ b/roles/wiki/files/var/lib/ikiwiki/fripost-wiki.setup
@@ -31,7 +31,7 @@ reverse_proxy: 0
# filename of cgi wrapper to generate
cgi_wrapper: /var/lib/ikiwiki/public_html/ikiwiki.cgi
# mode for cgi_wrapper (can safely be made suid)
-cgi_wrappermode: 06755
+cgi_wrappermode: 0755
# number of seconds to delay CGI requests when overloaded
cgi_overload_delay: ''
# message to display when overloaded (may contain html)
@@ -416,5 +416,4 @@ getsource_mimetype: text/plain; charset=utf-8
#tag_autocreate_commit: 1
# pandoc plugin
-pandoc_smart: 1
pandoc_html5: 1
diff --git a/roles/wiki/handlers/main.yml b/roles/wiki/handlers/main.yml
index 109c63d..ac9ad2b 100644
--- a/roles/wiki/handlers/main.yml
+++ b/roles/wiki/handlers/main.yml
@@ -5,3 +5,12 @@
- name: Refresh ikiwiki
become_user: ikiwiki
command: ikiwiki --setup /var/lib/ikiwiki/fripost-wiki.setup --refresh --wrappers
+
+- name: systemctl daemon-reload
+ command: /bin/systemctl daemon-reload
+
+- name: Stop ikiwiki
+ service: name=ikiwiki.service state=stopped
+
+- name: Restart ikiwiki
+ service: name=ikiwiki.socket state=restarted
diff --git a/roles/wiki/tasks/main.yml b/roles/wiki/tasks/main.yml
index 718b433..74c11f8 100644
--- a/roles/wiki/tasks/main.yml
+++ b/roles/wiki/tasks/main.yml
@@ -11,6 +11,16 @@
- libnet-dns-sec-perl
- fcgiwrap
- pandoc
+ ###
+ - fonts-font-awesome
+ - libjs-bootstrap4
+ - libjs-jquery
+
+- name: Stop and disable fcgiwrap socket
+ service: name=fcgiwrap.socket state=stopped enabled=false
+
+- name: Stop fcgiwrap service
+ service: name=fcgiwrap.service state=stopped
- name: Create a user 'ikiwiki'
user: name=ikiwiki system=yes
@@ -21,9 +31,6 @@
generate_ssh_key=yes
ssh_key_comment=ikiwiki@{{ ansible_fqdn }}
-- name: Add 'www-data' to the group 'ikiwiki'
- user: name=www-data groups=ikiwiki append=yes
-
- name: Create directory ~ikiwiki/IkiWiki/Plugin
file: path=/var/lib/ikiwiki/IkiWiki/Plugin
state=directory
@@ -70,6 +77,32 @@
- meta: flush_handlers
+- name: Copy ikiwiki service unit
+ copy: src=etc/systemd/system/ikiwiki.service
+ dest=/etc/systemd/system/ikiwiki.service
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Stop ikiwiki
+
+- name: Copy ikiwiki socket unit
+ copy: src=etc/systemd/system/ikiwiki.socket
+ dest=/etc/systemd/system/ikiwiki.socket
+ owner=root group=root
+ mode=0644
+ notify:
+ - systemctl daemon-reload
+ - Restart ikiwiki
+
+- name: Disable ikiwiki service
+ service: name=ikiwiki.service enabled=false
+
+- name: Start ikiwiki socket
+ service: name=ikiwiki.socket state=started enabled=true
+
+- meta: flush_handlers
+
- name: Copy /etc/nginx/sites-available/{wiki,website}
copy: src=etc/nginx/sites-available/{{ item }}
dest=/etc/nginx/sites-available/{{ item }}