summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2017-05-31 21:42:32 +0200
committerGuilhem Moulin <guilhem@fripost.org>2017-06-01 01:09:00 +0200
commit6e39bad3fbe75b88fca4c2e2aad8eb51af14b1be (patch)
tree87898c1653a36f1b23efbef55d6f876d8bc83444
parente136d3edbdb6749d4559939dc9fcbc11d166e34c (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.yml3
-rw-r--r--roles/MSA/etc/systemd/system/postfix-sender-login.service23
-rw-r--r--roles/MSA/etc/systemd/system/postfix-sender-login.socket8
-rwxr-xr-xroles/MSA/files/usr/local/bin/postfix-sender-login.pl171
-rw-r--r--roles/MSA/handlers/main.yml3
-rw-r--r--roles/MSA/tasks/main.yml22
-rw-r--r--roles/MSA/templates/etc/postfix/main.cf.j22
-rw-r--r--roles/common-LDAP/templates/etc/default/slapd.j22
-rw-r--r--roles/common-LDAP/templates/etc/ldap/database.ldif.j217
-rw-r--r--roles/common/templates/etc/postfix/master.cf.j27
-rw-r--r--roles/webmail/tasks/roundcube.yml7
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" }