summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--roles/IMAP/files/etc/cron.d/doveadm1
-rw-r--r--roles/IMAP/files/etc/dovecot/conf.d/auth-ldap.conf.ext8
-rw-r--r--roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext12
-rw-r--r--roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service23
-rw-r--r--roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.socket8
-rwxr-xr-xroles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl130
-rw-r--r--roles/IMAP/handlers/main.yml3
-rw-r--r--roles/IMAP/tasks/imap.yml40
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/dovecot-local1
9 files changed, 208 insertions, 18 deletions
diff --git a/roles/IMAP/files/etc/cron.d/doveadm b/roles/IMAP/files/etc/cron.d/doveadm
index 1cb0ed8..b0551e4 100644
--- a/roles/IMAP/files/etc/cron.d/doveadm
+++ b/roles/IMAP/files/etc/cron.d/doveadm
@@ -1,3 +1,4 @@
MAILTO=root
59 * * * * vmail test -x /usr/bin/doveadm && nice -n 19 /usr/bin/doveadm sis deduplicate /home/mail/attachments /home/mail/attachments/queue
+37 5 * * * vmail test -x /usr/bin/doveadm && nice -n 19 /usr/bin/doveadm purge -A
diff --git a/roles/IMAP/files/etc/dovecot/conf.d/auth-ldap.conf.ext b/roles/IMAP/files/etc/dovecot/conf.d/auth-ldap.conf.ext
index 360727e..9917753 100644
--- a/roles/IMAP/files/etc/dovecot/conf.d/auth-ldap.conf.ext
+++ b/roles/IMAP/files/etc/dovecot/conf.d/auth-ldap.conf.ext
@@ -20,20 +20,28 @@ passdb {
# driver = ldap
# # This should be a different file from the passdb's, in order to perform
# # asynchronous requests.
#
# args = /etc/dovecot/dovecot-ldap-userdb.conf.ext
#
# # Default fields can be used to specify defaults that LDAP may override
# default_fields = home=/home/mail/virtual/%d/%n
#}
# If you don't have any user-specific settings, you can avoid the userdb LDAP
# lookup by using userdb static instead of userdb ldap, for example:
# <doc/wiki/UserDatabase.Static.txt>
userdb {
driver = static
# The MTA has already verified the existence of users when doing alias resolution,
# so we can skip the passdb lookup here.
args = home=/home/mail/virtual/%d/%n allow_all_users=yes
}
+
+# Used only for iteration as the static userdb above always succeeds
+userdb {
+ driver = dict
+ skip = found
+ result_internalfail = return-fail
+ args = /etc/dovecot/dovecot-dict-auth.conf.ext
+}
diff --git a/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext b/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext
new file mode 100644
index 0000000..ecd7134
--- /dev/null
+++ b/roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext
@@ -0,0 +1,12 @@
+# This file is commonly accessed via passdb {} or userdb {} section in
+# conf.d/auth-dict.conf.ext
+
+# Dictionary URI
+uri = proxy:/var/run/dovecot/auth-proxy:
+
+# Username iteration prefix. Keys under this are assumed to contain usernames.
+iterate_prefix = userdb/
+
+# Should iteration be disabled for this userdb? If this userdb acts only as a
+# cache there's no reason to try to iterate the (partial & duplicate) users.
+iterate_disable = 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
new file mode 100644
index 0000000..ea5895c
--- /dev/null
+++ b/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service
@@ -0,0 +1,23 @@
+[Unit]
+Description=Dovecot authentication proxy
+After=dovecot.target
+Requires=dovecot-auth-proxy.socket
+
+[Service]
+User=vmail
+Group=vmail
+StandardInput=null
+SyslogFacility=mail
+ExecStart=/usr/local/bin/dovecot-auth-proxy.pl
+
+# Hardening
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectSystem=full
+ProtectHome=read-only
+ReadOnlyDirectories=/
+RestrictAddressFamilies=
+
+[Install]
+WantedBy=multi-user.target
+Also=postfix-sender-login.socket
diff --git a/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.socket b/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.socket
new file mode 100644
index 0000000..6dee91a
--- /dev/null
+++ b/roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.socket
@@ -0,0 +1,8 @@
+[Socket]
+SocketUser=dovecot
+SocketGroup=dovecot
+SocketMode=0600
+ListenStream=/run/dovecot/auth-proxy
+
+[Install]
+WantedBy=sockets.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
new file mode 100755
index 0000000..cc52dbf
--- /dev/null
+++ b/roles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+
+#----------------------------------------------------------------------
+# socketmap lookup table returning the SASL login name(s) owning a given
+# sender address
+# 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 Errno 'EINTR';
+
+# clean up PATH
+$ENV{PATH} = join ':', qw{/usr/bin /bin};
+delete @ENV{qw/IFS CDPATH ENV BASH_ENV/};
+
+# number of pre-forked servers
+my $nProc = 1;
+sub server();
+
+# fdopen(3) the file descriptor FD
+die "This service must be socket-activated.\n"
+ unless defined $ENV{LISTEN_PID} and $ENV{LISTEN_PID} == $$
+ 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: $!";
+ if ($pid) {
+ push @CHILDREN, $pid;
+ } else {
+ server(); # child, never return
+ exit;
+ }
+}
+server();
+waitpid $_ => 0 foreach @CHILDREN;
+exit $?;
+
+
+#############################################################################
+
+sub server() {
+ for (my $n = 0; $n < 1; $n++) {
+ accept(my $conn, $S) or do {
+ next if $! == EINTR;
+ die "accept: $!";
+ };
+
+ my $hello = $conn->getline() // '';
+ unless ($hello =~ /\AH(\d+)\t(\d+)\t(\d+)(?:\t.*)?\n\z/) {
+ warn "Invalid greeting line: $hello\n";
+ close $conn or warn "Can't close: $!";
+ next;
+ }
+ # <major-version> <minor-version> <value type>
+ unless ($1 == 2 and $2 == 0 and $3 == 0) {
+ warn "Unsupported protocol version $1.$2 (or value type $3)\n";
+ close $conn or warn "Can't close: $!";
+ next;
+ }
+
+ my $cmd = $conn->getline() // '';
+ if ($cmd =~ /\AI(\d+)\t(.*)\n\z/) {
+ iterate($conn, $1, $2);
+ }
+ else {
+ fail($conn => "Unknown command line: $cmd");
+ }
+ close $conn or warn "Can't close: $!";
+ }
+}
+
+sub fail($;$) {
+ my ($fh, $msg) = @_;
+ $fh->printflush("F\n");
+ warn "$msg\n" if defined $msg;
+}
+
+# list all users, even the inactive ones
+sub iterate($$$) {
+ my ($fh, $flags, $prefix) = @_;
+ unless ($flags == 0) {
+ fail($fh => "Unsupported iterate flags $flags");
+ return;
+ }
+
+ opendir my $dh, '.' or do {
+ fail($fh => "opendir: $!");
+ return;
+ };
+ 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)) {
+ next if $l eq '.' or $l eq '..';
+ my $user = $l.'@'.$d;
+ next unless $user =~ /\A[a-zA-Z0-9\.\-_@]+\z/; # skip invalid user names
+ $fh->printf("O%s%s\t\n", $prefix, $user);
+ }
+ closedir $dh or warn "closedir: $!";
+ }
+ closedir $dh or warn "closedir: $!";
+
+ $fh->printflush("\n");
+}
diff --git a/roles/IMAP/handlers/main.yml b/roles/IMAP/handlers/main.yml
index 2c49611..d8bbdc9 100644
--- a/roles/IMAP/handlers/main.yml
+++ b/roles/IMAP/handlers/main.yml
@@ -1,21 +1,24 @@
---
+- name: systemctl daemon-reload
+ command: /bin/systemctl daemon-reload
+
- name: Restart Dovecot
service: name=dovecot state=restarted
- name: Reload Postfix
service: name=postfix state=reloaded
- name: Compile Spamassassin rules
become_user: debian-spamd
# it might take a while...
command: /usr/bin/sa-compile --quiet
chdir=/var/lib/spamassassin/
- name: Restart Amavis
service: name=amavis state=restarted
- name: Copy SQL tables for spamassassin
copy: src=tmp/spamassassin.sql
dest=/tmp/spamassassin.sql
owner=root group=root
mode=0600
diff --git a/roles/IMAP/tasks/imap.yml b/roles/IMAP/tasks/imap.yml
index 3fcb31f..75b250d 100644
--- a/roles/IMAP/tasks/imap.yml
+++ b/roles/IMAP/tasks/imap.yml
@@ -1,76 +1,80 @@
- name: Install Dovecot
apt: pkg={{ item }}
with_items:
- dovecot-core
- dovecot-ldap
- dovecot-imapd
- dovecot-lmtpd
- dovecot-antispam
- dovecot-managesieved
- dovecot-sieve
-# 7 5 * * * root users=$(mktemp --tmpdir) && sudo -u dovecot /usr/local/bin/list-members.pl && sudo -u vmail nice -n 19 /usr/bin/doveadm purge -F"$users"
-- name: Copy list-users.pl
- copy: src=usr/local/bin/list-users.pl
- dest=/usr/local/bin/list-users.pl
- owner=root group=staff
- mode=0755
-
- name: Create a user 'vmail'
user: name=vmail system=yes
createhome=no
home=/home/mail
shell=/usr/sbin/nologin
password=!
state=present
-## TODO: make a LDAP query listing all users using iterate_attrs and
-## iterate_filter. (Alternatively, use a dict, see
-## https://www.opensource.apple.com/source/dovecot/dovecot-293/dovecot.Config/dovecot-dict-auth.conf.ext)
-## Required for dbox, see
-## http://wiki2.dovecot.org/MailboxFormat/dbox#Multi-dbox
-#- name: Create a nightly cron job to purge expunged messages
-# cron: name="Purge expunged messages"
-# minute=7 hour=5
-# user=vmail cron_file=doveadm-purge
-# job="/usr/bin/doveadm purge -A"
+- name: Copy dovecot auth proxy
+ copy: src=usr/local/bin/dovecot-auth-proxy.pl
+ dest=/usr/local/bin/dovecot-auth-proxy.pl
+ owner=root group=staff
+ mode=0755
+
+- name: Copy dovecot auth proxy systemd unit files
+ copy: src=etc/systemd/system/{{ item }}
+ dest=/etc/systemd/system/{{ item }}
+ owner=root group=root
+ mode=0644
+ with_items:
+ - dovecot-auth-proxy.service
+ - dovecot-auth-proxy.socket
+ notify:
+ - systemctl daemon-reload
+
+- meta: flush_handlers
+
+- name: Enable dovecot auth proxy
+ service: name=dovecot-auth-proxy.socket state=started enabled=yes
# The ownership and permissions ensure that dovecot won't try to
# deliver mails under an umounted mountpoint.
- name: Create a home directory for user 'vmail'
file: path=/home/mail
state=directory
owner=root group=root
mode=0755
- name: Create /home/mail/{virtual,attachments,spamspool}
file: path=/home/mail/{{ item }}
state=directory
owner=vmail group=vmail
mode=0700
with_items:
- virtual
- attachments
- spamspool
-- name: Create a cronjob for SIS deduplication
+- name: Create a cronjob for purging and SIS deduplication
copy: src=etc/cron.d/doveadm
dest=/etc/cron.d/doveadm
owner=root group=root
mode=0644
- name: Create virtual mailbox directories
file: path=/etc/dovecot/virtual/{{ item }}
state=directory
owner=root group=root
mode=0755
with_items:
- all
- flagged
- recent
- unseen
- name: Create virtual mailboxes
copy: src=etc/dovecot/virtual/{{ item }}/dovecot-virtual
dest=/etc/dovecot/virtual/{{ item }}/dovecot-virtual
owner=root group=root
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 e11cd41..c70249b 100644
--- a/roles/common/files/etc/logcheck/ignore.d.server/dovecot-local
+++ b/roles/common/files/etc/logcheck/ignore.d.server/dovecot-local
@@ -8,20 +8,21 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: Aborted login( \(auth failed, [[:digit:]]+ attempts in [[:digit:]]+ secs\))?: (user=<[^>]*>, method=[[:alnum:]-]+, )?rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+, TLS, session=<[^>]+>$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap|managesieve)-login: (Disconnected|Aborted login)(: Inactivity)? (\(no auth attempts in [[:digit:]]+ secs\):( user=<>,)?|\(auth failed, [[:digit:]]+ attempts in [[:digit:]]+ secs\): user=<[^>]*>, method=PLAIN,|\(aborted authentication\): method=PLAIN,) rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+(, (TLS|SSL)( handshaking)?(: SSL_(accept|read)\(\) (syscall failed: Connection reset by peer|failed: error:140940E5:SSL routines:SSL3_READ_BYTES:ssl handshake failure: SSL alert number 10|failed: error:14094412:SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate: SSL alert number 42|failed: error:14094416:SSL routines:SSL3_READ_BYTES:sslv3 alert certificate unknown: SSL alert number 46|failed: error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca: SSL alert number 48|failed: error:[[:xdigit:]]+:SSL routines:SSL2?3_GET_CLIENT_HELLO:(unknown protocol|unsupported protocol|http request|no shared cipher|wrong version number)|failed: error:[[:xdigit:]]+:SSL routines:SSL_BYTES_TO_CIPHER_LIST:inappropriate fallback)|: Disconnected)?|, secured)?, session=<[^>]+>$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: auth: Warning: auth client [[:digit:]]+ disconnected with [[:digit:]]+ pending requests: EOF$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: Warning: Auth connection closed with [[:digit:]]+ pending requests \(max [[:digit:]]+ secs, pid=[[:digit:]]+, EOF\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: Disconnected \((auth process communication failure|client didn't finish SASL auth, waited [[:digit:]]+ secs)\): user=<>, method=PLAIN, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+(, (TLS|secured)(: SSL_read\(\) syscall failed: Connection reset by peer|: Disconnected)?, session=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap)-login: Error: SSL: Stacked error: error:140943F2:SSL routines:SSL3_READ_BYTES:sslv3 alert unexpected message: SSL alert number 10$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: imap-login: (Disconnected: Inactivity during authentication|Aborted login) \(client didn't finish SASL auth, waited [[:digit:]]+ secs\): user=<[^>]*>, method=[[:alnum:]-]+, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+(, (TLS|secured)(: Disconnected)?, session=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: (pop3|imap|managesieve)-login: Disconnected \(tried to use unsupported auth mechanism\): user=<>, method=[[:alnum:]-]+, rip=[.:[:xdigit:]]+, lip=[.:[:xdigit:]]+(, (TLS|secured), session=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([0-9]+, [-_.@[:alnum:]]+\): [^:]{22}: msgid=<?.*>?( \((added by [^[:space:]]+|sfid-[_[:xdigit:]]+)\)?)?[[:space:]]*: (saved mail to [-_.[:alnum:]]+|(forwarded|discarded duplicate forward) to <[^[:space:]]+>)$
^\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|secured), session=<[^>]+>)?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\): Connect from local$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+\): Disconnect from local: (Client quit|Connection closed) \(in reset\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+, [^@]+@[^@]+\): [+/[:alnum:]]{22}: sieve: msgid=(\S+ )?|<[^>]*>( \(added by \S+\))?: (stored mail into mailbox '|marked message to be discarded if not explicitly delivered \(discard action\)$|forwarded to )
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([-_.@[:alnum:]]+, [^@]+@[^@]+\): [+/[:alnum:]]{22}: sieve: execution of script \S+ script failed, but implicit keep was successful \(user logfile \S+ may reveal additional details\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: lmtp\([0-9]+\): Disconnect from local: Successful quit$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: managesieve\([-_.@[:alnum:]]+\): Disconnected: Logged out bytes=[[:digit:]]+/[[:digit:]]+?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: ssl-params: (Generating SSL parameters|SSL parameters regeneration completed)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: stats: Warning: UPDATE-CMD: Already expired$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: stats: Warning: Couldn't find session GUID: [[:xdigit:]]{32}$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: stats: Error: Mail server input error: UPDATE-SESSION [-_.@[:alnum:]]+ imap: stats shrank: (mcache|mlpath|mrbytes|mrcount) [[:digit:]]+ < [[:digit:]]+$
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ systemd\[1\]: Start(ing|ed) Dovecot authentication proxy\.(\.\.)?$