From b7a7ceb88ed5b44959920cde170bc6aaa83026bb Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Fri, 2 Jun 2017 14:25:21 +0200 Subject: dovecot: enable user iteration and add a cronjob for `doveadm purge -A` --- roles/IMAP/files/etc/cron.d/doveadm | 1 + .../files/etc/dovecot/conf.d/auth-ldap.conf.ext | 8 ++ .../files/etc/dovecot/dovecot-dict-auth.conf.ext | 12 ++ .../etc/systemd/system/dovecot-auth-proxy.service | 23 ++++ .../etc/systemd/system/dovecot-auth-proxy.socket | 8 ++ .../IMAP/files/usr/local/bin/dovecot-auth-proxy.pl | 130 +++++++++++++++++++++ roles/IMAP/handlers/main.yml | 3 + roles/IMAP/tasks/imap.yml | 40 ++++--- .../etc/logcheck/ignore.d.server/dovecot-local | 1 + 9 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 roles/IMAP/files/etc/dovecot/dovecot-dict-auth.conf.ext create mode 100644 roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.service create mode 100644 roles/IMAP/files/etc/systemd/system/dovecot-auth-proxy.socket create mode 100755 roles/IMAP/files/usr/local/bin/dovecot-auth-proxy.pl (limited to 'roles') 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 @@ -37,3 +37,11 @@ userdb { # 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 +# +# 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 . +#---------------------------------------------------------------------- + +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; + } + # + 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,4 +1,7 @@ --- +- name: systemctl daemon-reload + command: /bin/systemctl daemon-reload + - name: Restart Dovecot service: name=dovecot state=restarted 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 @@ -9,13 +9,6 @@ - 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 @@ -24,16 +17,27 @@ 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. @@ -53,7 +57,7 @@ - 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 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 @@ -25,3 +25,4 @@ ^\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\.(\.\.)?$ -- cgit v1.2.3