diff options
author | Guilhem Moulin <guilhem@fripost.org> | 2017-05-31 21:42:32 +0200 |
---|---|---|
committer | Guilhem Moulin <guilhem@fripost.org> | 2017-06-01 01:09:00 +0200 |
commit | 6e39bad3fbe75b88fca4c2e2aad8eb51af14b1be (patch) | |
tree | 87898c1653a36f1b23efbef55d6f876d8bc83444 | |
parent | e136d3edbdb6749d4559939dc9fcbc11d166e34c (diff) |
Don't let authenticated client use arbitrary sender addresses.
The following policy is now implemented:
* users can use their SASL login name as sender address;
* alias and/or list owners can use the address as envelope sender;
* domain postmasters can use arbitrary sender addresses under their
domains;
* domain owners can use arbitrary sender addresses under their domains,
unless it is also an existing account name;
* for known domains without owner or postmasters, other sender addresses
are not allowed; and
* arbitrary sender addresses under unknown domains are allowed.
-rw-r--r-- | group_vars/all.yml | 3 | ||||
-rw-r--r-- | roles/MSA/etc/systemd/system/postfix-sender-login.service | 23 | ||||
-rw-r--r-- | roles/MSA/etc/systemd/system/postfix-sender-login.socket | 8 | ||||
-rwxr-xr-x | roles/MSA/files/usr/local/bin/postfix-sender-login.pl | 171 | ||||
-rw-r--r-- | roles/MSA/handlers/main.yml | 3 | ||||
-rw-r--r-- | roles/MSA/tasks/main.yml | 22 | ||||
-rw-r--r-- | roles/MSA/templates/etc/postfix/main.cf.j2 | 2 | ||||
-rw-r--r-- | roles/common-LDAP/templates/etc/default/slapd.j2 | 2 | ||||
-rw-r--r-- | roles/common-LDAP/templates/etc/ldap/database.ldif.j2 | 17 | ||||
-rw-r--r-- | roles/common/templates/etc/postfix/master.cf.j2 | 7 | ||||
-rw-r--r-- | roles/webmail/tasks/roundcube.yml | 7 |
11 files changed, 259 insertions, 6 deletions
diff --git a/group_vars/all.yml b/group_vars/all.yml index 97e2024..236527e 100644 --- a/group_vars/all.yml +++ b/group_vars/all.yml @@ -23,26 +23,27 @@ ipsec: postfix_instance: # The keys are the group names associated with a Postfix role, and the # values are the name and group (optional) of the instance dedicated # to that role. # For internal services, we also specify its (non-routable) IP address # and port. # XXX it's unfortunate that we can only specify a single address, and # therefore have to limit the number of outgoing SMTP proxy and # IMAP server to one. Since hosts(5) files cannot map and IP # address to multiple hostnames, a workaround would be to use # round-robin DNS, but we can't rely on DNS as long as our zone is # unsigned. IMAP: { name: mda , addr: "{{ (groups.all | length > 1) | ternary( ipsec[ hostvars[groups.IMAP[0]].inventory_hostname_short ], '127.0.0.1') }}" , port: 2526 } MX: { name: mx, group: mta, backup: mx3.fripost.org } out: { name: out, group: mta , addr: "{{ (groups.all | length > 1) | ternary( ipsec[ hostvars[groups.out[0]].inventory_hostname_short ], '127.0.0.1') }}" , port: 2525 } MSA: { name: msa - , port: 587 } + , addr: "{{ (groups.all | length > 1) | ternary( ipsec[ hostvars[groups.MSA[0]].inventory_hostname_short ], '127.0.0.1') }}" + , port: 2587 } lists: { name: lists , 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 }}" diff --git a/roles/MSA/etc/systemd/system/postfix-sender-login.service b/roles/MSA/etc/systemd/system/postfix-sender-login.service new file mode 100644 index 0000000..3ceb310 --- /dev/null +++ b/roles/MSA/etc/systemd/system/postfix-sender-login.service @@ -0,0 +1,23 @@ +[Unit] +Description=Postfix sender login socketmap +After=mail-transport-agent.target +Requires=postfix-sender-login.socket + +[Service] +User=postfix +Group=postfix +StandardInput=null +SyslogFacility=mail +ExecStart=/usr/local/bin/postfix-sender-login.pl + +# Hardening +NoNewPrivileges=yes +PrivateDevices=yes +ProtectHome=yes +ProtectSystem=full +ReadOnlyDirectories=/ +RestrictAddressFamilies=AF_UNIX + +[Install] +WantedBy=multi-user.target +Also=postfix-sender-login.socket diff --git a/roles/MSA/etc/systemd/system/postfix-sender-login.socket b/roles/MSA/etc/systemd/system/postfix-sender-login.socket new file mode 100644 index 0000000..c883dc1 --- /dev/null +++ b/roles/MSA/etc/systemd/system/postfix-sender-login.socket @@ -0,0 +1,8 @@ +[Socket] +SocketUser=postfix +SocketGroup=postfix +SocketMode=0666 +ListenStream=/var/spool/postfix-msa/private/sender-login + +[Install] +WantedBy=sockets.target diff --git a/roles/MSA/files/usr/local/bin/postfix-sender-login.pl b/roles/MSA/files/usr/local/bin/postfix-sender-login.pl new file mode 100755 index 0000000..58cbf9d --- /dev/null +++ b/roles/MSA/files/usr/local/bin/postfix-sender-login.pl @@ -0,0 +1,171 @@ +#!/usr/bin/perl -T + +#---------------------------------------------------------------------- +# 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'; +use Socket qw/PF_UNIX SOCK_STREAM SHUT_RDWR/; + +use Net::LDAPI (); +use Net::LDAP::Util qw/ldap_explode_dn escape_dn_value escape_filter_value/; +use Net::LDAP::Constant qw/LDAP_NO_SUCH_OBJECT/; +use Authen::SASL (); + +# clean up PATH +$ENV{PATH} = join ':', qw{/usr/bin /bin}; +delete @ENV{qw/IFS CDPATH ENV BASH_ENV/}; + +# returned for forbidden envelope sender addresses +my $POSTMASTER = 'postmaster@fripost.org'; + +# 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: $!"; + +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/'; +sub process_request($); + +while(1) { + accept(my $conn, $S) or do { + # try again if accept(2) was interrupted by a signal + next if $! == EINTR; + die "accept: $!"; + }; + my $reply = process_request($conn); + + # encode the reply as a netstring and send it back + # https://cr.yp.to/proto/netstrings.txt + $reply = length($reply).':'.$reply.','; + my $len = length($reply); + + for (my $i = 0; $i < $len;) { + my $n = syswrite($conn, $reply, $len-$i, $i) // do { + warn "Can't write: $!"; + last; + }; + $i += $n; + } + close $conn or warn "Can't close: $!"; +} + +############################################################################# + +sub process_request($) { + my $conn = shift; + my ($buf, $offset) = (undef, 0); + + # keep reading until the request length is determined + do { + my $n = sysread($conn, $buf, $BUFSIZE, $offset) // return "TEMP can't read: $!"; + return "TEMP EOF" if $n == 0; + $offset += $n; + } until ($buf =~ /\A(0|[1-9][0-9]*):/); + + # keep reading until the whole request is buffered + my $strlen = length("$1") + 1; # [len]":" + my $len = $strlen + $1 + 1; # [len]":"[string]"," + while ($offset < $len) { + my $n = sysread($conn, $buf, $BUFSIZE, $offset) // return "TEMP can't read: $!"; + return "TEMP EOF" if $n == 0; + $offset += $n; + } + + # requests are of the form $name <space> $key, cf. socketmap_table(5) + my $i = index($buf, ' ', $strlen); + return "TEMP invalid input: $buf" unless $i > $strlen and substr($buf,-1) eq ','; + my $name = substr($buf, $strlen, $i-$strlen); + my $key = substr($buf, $i, -1); + return "TEMP invalid name: $name" unless $name eq 'sender_login'; + + $key =~ /\A(.+)@([^\@]+)\z/ or return "NOTFOUND "; # invalid sender address + my ($localpart, $domainpart) = ($1, $2); + + my $ldap = Net::LDAPI::->new( $LDAPI ) + // return "TEMP couldn't create Net::LDAPI object"; + $ldap->bind( undef, sasl => Authen::SASL::->new(mechanism => 'EXTERNAL') ) + or return "TEMP LDAP: couldn't bind"; + + my $reply = lookup_sender($ldap, $localpart, $domainpart); + $ldap->unbind(); + return $reply; +} + +sub lookup_sender($$$) { + my ($ldap, $l, $d) = @_; + + my $filter = '(&(objectClass=FripostVirtualDomain)(fvd='.escape_filter_value($d).'))'; + my $mesg = $ldap->search( base => $BASEDN, scope => 'one', deref => 'never' + , filter => $filter + , attrs => [qw/objectClass fripostOwner fripostPostmaster/] + ); + return "TEMP LDAP error: ".$mesg->error() if $mesg->code; + my $entry = $mesg->pop_entry() // return "NOTFOUND "; # not a domain we know + return "TEMP LDAP error: multiple entry founds" if defined $mesg->pop_entry(); # sanity check + + # domain postmasters are allowed to use any sender address + my @logins = $entry->get_value('fripostPostmaster', asref => 0); + my @owners = $entry->get_value('fripostOwner', asref => 0); + + if (grep { $_ eq 'FripostVirtualAliasDomain' } $entry->get_value('objectClass', asref => 0)) { + # so are alias domain owners + push @logins, @owners; + } else { + my $dn = 'fvd='.escape_dn_value($d).','.$BASEDN; + my $filter = '(&(|(objectClass=FripostVirtualAlias)(objectClass=FripostVirtualList)(objectClass=FripostVirtualUser))(fvl='.escape_filter_value($l).'))'; + my $mesg = $ldap->search( base => $dn, scope => 'one', deref => 'never' + , filter => $filter + , attrs => [qw/objectClass fripostOwner/] + ); + unless ($mesg->code == 0 and defined ($entry = $mesg->pop_entry())) { + # domains owners are allowed to use any unkwown localpart as sender address + push @logins, @owners; + } else { + return "TEMP LDAP error: multiple entry founds" if defined $mesg->pop_entry(); # sanity check + if (grep { $_ eq 'FripostVirtualUser' } $entry->get_value('objectClass', asref => 0)) { + push @logins, $entry->dn(); + } else { + # alias/list owners can use the address as sender, and so are the domains owners + push @logins, @owners, $entry->get_value('fripostOwner', asref => 0); + } + } + } + + # convert DNs to SASL login names + my %logins; + foreach my $dn (@logins) { + next unless defined $dn; + $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; + $logins{$l.'@'.$d} = 1; + } + + # if the entry is found in LDAP but doesn't have an owner, only + # $POSTMASTER is allowed to use it as sender address + my $reply = %logins ? join(',', keys %logins) : $POSTMASTER; + return "OK $reply"; +} diff --git a/roles/MSA/handlers/main.yml b/roles/MSA/handlers/main.yml index 9edf610..a3db0f8 100644 --- a/roles/MSA/handlers/main.yml +++ b/roles/MSA/handlers/main.yml @@ -1,6 +1,9 @@ --- +- 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 diff --git a/roles/MSA/tasks/main.yml b/roles/MSA/tasks/main.yml index 6eff2cf..00c205d 100644 --- a/roles/MSA/tasks/main.yml +++ b/roles/MSA/tasks/main.yml @@ -1,26 +1,48 @@ - name: Install Postfix apt: pkg={{ item }} with_items: - postfix - postfix-pcre +- 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: Copy Postfix sender login socketmap systemd unit files + copy: src=etc/systemd/system/{{ item }} + dest=/etc/systemd/system/{{ item }} + owner=root group=root + mode=0644 + with_items: + - postfix-sender-login.service + - postfix-sender-login.socket + notify: + - systemctl daemon-reload + +- meta: flush_handlers + +- name: Enable Postfix sender login socketmap + service: name=postfix-sender-login.socket state=started enabled=yes + - name: Configure Postfix template: src=etc/postfix/{{ item }}.j2 dest=/etc/postfix-{{ postfix_instance[inst].name }}/{{ item }} owner=root group=root mode=0644 with_items: - main.cf - master.cf notify: - Reload Postfix - name: Copy the Regex to anonymize senders # no need to reload upon change, as cleanup(8) is short-running copy: src=etc/postfix/anonymize_sender.pcre dest=/etc/postfix-{{ postfix_instance[inst].name }}/anonymize_sender.pcre owner=root group=root mode=0644 - name: Copy the check_sender_access map copy: src=etc/postfix/check_sender_access diff --git a/roles/MSA/templates/etc/postfix/main.cf.j2 b/roles/MSA/templates/etc/postfix/main.cf.j2 index f5f0834..ec6b242 100644 --- a/roles/MSA/templates/etc/postfix/main.cf.j2 +++ b/roles/MSA/templates/etc/postfix/main.cf.j2 @@ -76,36 +76,38 @@ smtpd_sasl_type = dovecot smtpd_sasl_path = unix:private/dovecot-auth strict_rfc821_envelopes = yes smtpd_delay_reject = yes disable_vrfy_command = yes address_verify_sender = $double_bounce_sender@noreply.$mydomain address_verify_sender_ttl = 24h unverified_recipient_defer_code = 250 unverified_recipient_reject_code = 550 smtpd_client_restrictions = permit_sasl_authenticated reject smtpd_helo_required = yes smtpd_helo_restrictions = reject_invalid_helo_hostname +smtpd_sender_login_maps = socketmap:unix:private/sender-login:sender_login smtpd_sender_restrictions = reject_non_fqdn_sender reject_unknown_sender_domain check_sender_access cdb:$config_directory/check_sender_access + reject_known_sender_login_mismatch smtpd_relay_restrictions = reject_non_fqdn_recipient reject_unknown_recipient_domain reject_unverified_recipient permit_sasl_authenticated reject smtpd_data_restrictions = reject_unauth_pipelining # vim: set filetype=pfmain : diff --git a/roles/common-LDAP/templates/etc/default/slapd.j2 b/roles/common-LDAP/templates/etc/default/slapd.j2 index 80c1be1..fdd7481 100644 --- a/roles/common-LDAP/templates/etc/default/slapd.j2 +++ b/roles/common-LDAP/templates/etc/default/slapd.j2 @@ -3,41 +3,41 @@ # /etc/ldap/slapd.conf). SLAPD_CONF= # System account to run the slapd server under. If empty the server # will run as root. SLAPD_USER="openldap" # System group to run the slapd server under. If empty the server will # run in the primary group of its user. 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 # default) SLAPD_PIDFILE= # slapd normally serves ldap only on all TCP-ports 389. slapd can also # service requests on TCP-port 636 (ldaps) and requests via unix # sockets. SLAPD_SERVICES="ldapi:///" -{% for i in group_names | intersect(['MX','lists']) | sort %} +{% 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 %} SLAPD_SERVICES="$SLAPD_SERVICES ldaps:///" {% endif %} # If SLAPD_NO_START is set, the init script will not start or restart # slapd (but stop will still work). Uncomment this if you are # starting slapd via some other means or if you don't want slapd normally # started at boot. #SLAPD_NO_START=1 # If SLAPD_SENTINEL_FILE is set to path to a file and that file exists, # the init script will not start or restart slapd (but stop will still # work). Use this for temporarily disabling startup of slapd (when doing # maintenance, for example, or through a configuration management system) # when you don't want to edit a configuration file. SLAPD_SENTINEL_FILE=/etc/ldap/noslapd # For Kerberos authentication (via SASL), slapd by default uses the system diff --git a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 index 8310818..494888e 100644 --- a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 +++ b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 @@ -241,41 +241,41 @@ olcAccess: to dn.subtree="ou=virtual,dc=fripost,dc=org" by dn.onelevel="ou=syncRepl,dc=fripost,dc=org" tls_ssf=128 =rsd by group.exact="cn=admin,ou=groups,dc=fripost,dc=org" =wrsd by users =0 break olcAccess: to dn.children="ou=virtual,dc=fripost,dc=org" by group.exact="cn=admin,ou=groups,dc=fripost,dc=org" =wrsd by users =0 break {% endif -%} # # * 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. 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 {% endif -%} - {% if 'MX' in group_names -%} + {% if 'MX' in group_names or 'MSA' 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 -%} by users =0 break # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Domain entries # # * The SyncRepl replicates have read access to the entry itself, when # 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. @@ -459,37 +459,52 @@ olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,dc=fripost,dc=org$" by users =0 break {% endif %} # # * The SyncRepl MX replicates can check whether a virtual list is # active when 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])) %} 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]) -%} 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 -%} 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 +{% 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 +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 +{% endif %} {% if 'LDAP-provider' in group_names %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # TODO: allow users to edit their entry, etc # {% endif %} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Catch-all # # * Catch all the breaks above. # * Deny any access to everyone else. olcAccess: to dn.subtree="dc=fripost,dc=org" by dn.children="ou=virtual,dc=fripost,dc=org" +0 by * =0 # vim: set filetype=ldif : diff --git a/roles/common/templates/etc/postfix/master.cf.j2 b/roles/common/templates/etc/postfix/master.cf.j2 index c2ee395..52b2ec4 100644 --- a/roles/common/templates/etc/postfix/master.cf.j2 +++ b/roles/common/templates/etc/postfix/master.cf.j2 @@ -3,42 +3,47 @@ # of the file, see the master(5) manual page (command: "man 5 master"). # # {{ ansible_managed }} # Do NOT edit this file directly! # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== {% if inst is not defined %} [127.0.0.1]:16132 inet n - - - - smtpd {% elif inst == 'MX' %} smtpd pass - - n - - smtpd -o cleanup_service_name=cleanup_nochroot smtp inet n - n - 1 postscreen tlsproxy unix - - n - 0 tlsproxy dnsblog unix - - n - 0 dnsblog cleanup_nochroot unix n - n - 0 cleanup {% elif inst == 'MSA' %} -{{ postfix_instance.MSA.port }} inet n - - - - smtpd +submission inet n - - - - smtpd -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 - - - - smtpd + -o smtpd_tls_security_level=none + -o smtpd_sasl_security_options=noanonymous +{% endif %} {% elif inst in ['IMAP', 'out', 'lists'] %} [{{ postfix_instance[inst].addr }}]:{{ postfix_instance[inst].port }} inet n - - - - smtpd {% endif %} pickup fifo n - - 60 1 pickup cleanup unix n - - - 0 cleanup qmgr fifo n - n 300 1 qmgr tlsmgr unix - - - 1000? 1 tlsmgr rewrite unix - - - - - trivial-rewrite bounce unix - - - - 0 bounce defer unix - - - - 0 bounce trace unix - - - - 0 bounce verify unix - - - - 1 verify flush unix n - - 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - - - - smtp relay unix - - - - - smtp # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 showq unix n - - - - showq error unix - - - - - error diff --git a/roles/webmail/tasks/roundcube.yml b/roles/webmail/tasks/roundcube.yml index 4c7ac8d..5f41ba0 100644 --- a/roles/webmail/tasks/roundcube.yml +++ b/roles/webmail/tasks/roundcube.yml @@ -66,42 +66,45 @@ - 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 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" } # SMTP - - { var: smtp_server, value: "'{{ postfix_instance.out.addr | ipaddr }}'" } - - { var: smtp_port, value: "{{ postfix_instance.out.port }}" } + - { 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'" } # 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'" } # Plugins - { var: plugins, value: "array('archive','additional_message_headers','managesieve','password')" } # 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: language, value: "'sv_SE'" } - { var: create_default_folders, value: "true" } - { var: support_url, value: "'https://fripost.org/kontakt/'" } # User Preferences - { var: htmleditor, value: "3" } - { var: skip_deleted, value: "true" } |