From fa82a617a0c50b7478cd2b7189aa5f7d14449954 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sat, 30 May 2015 13:23:19 +0200 Subject: Upgrade the MX configuration from Wheezy to Jessie. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular, since Postfix is now able to perform LDAP lookups using SASL, previous hacks with simble binds on cn=postfix,ou=services,… can now be removed. --- roles/MX/files/etc/postfix/virtual/alias.cf | 5 +- .../MX/files/etc/postfix/virtual/alias_domains.cf | 5 +- roles/MX/files/etc/postfix/virtual/catchall.cf | 5 +- roles/MX/files/etc/postfix/virtual/domains.cf | 5 +- roles/MX/files/etc/postfix/virtual/list.cf | 5 +- roles/MX/files/etc/postfix/virtual/mailbox.cf | 5 +- roles/MX/files/usr/local/bin/reserved-alias.pl | 111 +++++++++++++++++++++ roles/MX/files/usr/local/sbin/reserved-alias.pl | 111 --------------------- roles/MX/tasks/main.yml | 18 +++- roles/MX/templates/etc/postfix/main.cf.j2 | 19 ++-- 10 files changed, 148 insertions(+), 141 deletions(-) create mode 100755 roles/MX/files/usr/local/bin/reserved-alias.pl delete mode 100755 roles/MX/files/usr/local/sbin/reserved-alias.pl (limited to 'roles/MX') diff --git a/roles/MX/files/etc/postfix/virtual/alias.cf b/roles/MX/files/etc/postfix/virtual/alias.cf index 1710376..1c104a9 100644 --- a/roles/MX/files/etc/postfix/virtual/alias.cf +++ b/roles/MX/files/etc/postfix/virtual/alias.cf @@ -3,8 +3,7 @@ version = 3 search_base = fvd=%d,ou=virtual,dc=fripost,dc=org domain = static:all scope = one -bind = yes -bind_dn = cn=postfix,ou=services,dc=fripost,dc=org -bind_pw = FIXME +bind = sasl +sasl_mechs = EXTERNAL query_filter = (&(objectClass=FripostVirtualAlias)(fvl=%u)(fripostIsStatusActive=TRUE)) result_attribute = fripostMaildrop diff --git a/roles/MX/files/etc/postfix/virtual/alias_domains.cf b/roles/MX/files/etc/postfix/virtual/alias_domains.cf index 119b8b2..907166f 100644 --- a/roles/MX/files/etc/postfix/virtual/alias_domains.cf +++ b/roles/MX/files/etc/postfix/virtual/alias_domains.cf @@ -3,9 +3,8 @@ version = 3 search_base = ou=virtual,dc=fripost,dc=org domain = static:all scope = one -bind = yes -bind_dn = cn=postfix,ou=services,dc=fripost,dc=org -bind_pw = FIXME +bind = sasl +sasl_mechs = EXTERNAL # The domain has already been validated (it's active and not pending) query_filter = (&(objectClass=FripostVirtualAliasDomain)(fvd=%d)) result_attribute = fripostMaildrop diff --git a/roles/MX/files/etc/postfix/virtual/catchall.cf b/roles/MX/files/etc/postfix/virtual/catchall.cf index 66053c8..e0e6350 100644 --- a/roles/MX/files/etc/postfix/virtual/catchall.cf +++ b/roles/MX/files/etc/postfix/virtual/catchall.cf @@ -3,9 +3,8 @@ version = 3 search_base = ou=virtual,dc=fripost,dc=org domain = static:all scope = one -bind = yes -bind_dn = cn=postfix,ou=services,dc=fripost,dc=org -bind_pw = FIXME +bind = sasl +sasl_mechs = EXTERNAL # The domain has already been validated (it's active and not pending) query_filter = (&(objectClass=FripostVirtualDomain)(!(objectClass=FripostVirtualAliasDomain))(fvd=%d)(fripostOptionalMaildrop=*)) result_attribute = fripostOptionalMaildrop diff --git a/roles/MX/files/etc/postfix/virtual/domains.cf b/roles/MX/files/etc/postfix/virtual/domains.cf index 4ec247d..f5a7f25 100644 --- a/roles/MX/files/etc/postfix/virtual/domains.cf +++ b/roles/MX/files/etc/postfix/virtual/domains.cf @@ -4,9 +4,8 @@ server_host = ldapi://%2Fprivate%2Fldapi/ version = 3 search_base = ou=virtual,dc=fripost,dc=org scope = one -bind = yes -bind_dn = cn=postfix,ou=services,dc=fripost,dc=org -bind_pw = FIXME +bind = sasl +sasl_mechs = EXTERNAL query_filter = (&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))(fvd=%s)(fripostIsStatusActive=TRUE)) result_attribute = fvd result_format = OK diff --git a/roles/MX/files/etc/postfix/virtual/list.cf b/roles/MX/files/etc/postfix/virtual/list.cf index 3b364c0..99e2147 100644 --- a/roles/MX/files/etc/postfix/virtual/list.cf +++ b/roles/MX/files/etc/postfix/virtual/list.cf @@ -3,9 +3,8 @@ version = 3 search_base = fvd=%d,ou=virtual,dc=fripost,dc=org domain = static:all scope = one -bind = yes -bind_dn = cn=postfix,ou=services,dc=fripost,dc=org -bind_pw = FIXME +bind = sasl +sasl_mechs = EXTERNAL query_filter = (&(objectClass=FripostVirtualList)(!(objectClass=FripostPendingEntry))(fvl=%u)(fripostIsStatusActive=TRUE)) result_attribute = fripostListManager # Use a dedicated "virtual" domain to decongestion potential bottlenecks diff --git a/roles/MX/files/etc/postfix/virtual/mailbox.cf b/roles/MX/files/etc/postfix/virtual/mailbox.cf index 4654607..7289670 100644 --- a/roles/MX/files/etc/postfix/virtual/mailbox.cf +++ b/roles/MX/files/etc/postfix/virtual/mailbox.cf @@ -3,9 +3,8 @@ version = 3 search_base = fvd=%d,ou=virtual,dc=fripost,dc=org domain = static:all scope = one -bind = yes -bind_dn = cn=postfix,ou=services,dc=fripost,dc=org -bind_pw = FIXME +bind = sasl +sasl_mechs = EXTERNAL query_filter = (&(objectClass=FripostVirtualUser)(fvl=%u)(fripostIsStatusActive=TRUE)) result_attribute = fvl # Use a dedicated "virtual" domain to decongestion potential bottlenecks diff --git a/roles/MX/files/usr/local/bin/reserved-alias.pl b/roles/MX/files/usr/local/bin/reserved-alias.pl new file mode 100755 index 0000000..e19492e --- /dev/null +++ b/roles/MX/files/usr/local/bin/reserved-alias.pl @@ -0,0 +1,111 @@ +#!/usr/bin/perl + +# Copyright © 2013 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 Net::LDAPI; +use Net::LDAP::Util qw/escape_filter_value ldap_explode_dn escape_dn_value/; +use Authen::SASL; +use Net::SMTP; + +if (!@ARGV or grep { $_ eq '-h' or $_ eq '--help' } @ARGV) { + # Help + print STDERR "Usage: $0 {original sender} {original recipient} [additional recipient ...]\n"; + print STDERR "\n"; + print STDERR "The message read from the standard input is redirected to 'additional recipient',\n"; + print STDERR "and also forwarded to the domain owner if any. If the 'additional recipient' begins\n"; + print STDERR "with '\@', the localpart of 'original recipient' is prepended.\n"; + print STDERR "\n"; + print STDERR "This is mostly useful to comply to RFC 822 section 6.3 and RFC 2142 section\n"; + print STDERR "4 (to forward mails to 'postmaster\@' and 'abuse\@' to the site admin in\n"; + print STDERR "addition to the virtual domain manager).\n"; + exit; +} + +# The original sender +my $sender = shift; + +# The original recipient +my $orig = shift; +$orig =~ /^(.+)\@([^@]+)$/ + or warn "Warning: Non fully qualified: $orig"; +my ($local,$domain) = ($1,$2); + +# The new recipient (typically, the admin site) +my @recipients = grep { $_ and $orig ne $_ } + # add localparts to domain + map { my $x = $_; + if ($x =~ /^\@/) { + $x = (defined $local and $local ne '') ? $local.$x : undef; + } + $x + } + @ARGV; +# Die if we can't deliver to site admins +die "Error: Aborted delivery to '$orig' in attempt to break an alias expansion loop.\n" + unless @recipients; + +if (defined $domain) { + # Look for the domain owner or postmaster + my $ldap = Net::LDAPI::->new(); + $ldap->bind( undef, sasl => Authen::SASL::->new(mechanism => 'EXTERNAL') ) + or die "Error: Couldn't bind"; + + my @attrs = ( 'fripostPostmaster', 'fripostOwner' ); + my $mesg = $ldap->search( base => 'fvd='.escape_dn_value($domain).',' + .'ou=virtual,dc=fripost,dc=org' + , scope => 'base' + , deref => 'never' + , filter => '(&(objectClass=FripostVirtualDomain)' + .'(fvd='.escape_filter_value($domain).')'. + ')' + , attrs => \@attrs + ); + if ($mesg->code) { + warn "Warning: ".$mesg->error; + } + elsif ($mesg->count != 1) { + # Note: this may happen for "$mydestination", but these mails + # are unlikely. We'll get a harmless warning at worst. + warn "Warning: Something weird happened when looking up domain '".$domain. + "'. Check your ACL."; + } + else { + my $entry = $mesg->pop_entry() // die "Error: Cannot pop entry."; + foreach (@attrs) { + my $v = $entry->get_value($_, asref => 1) or next; + foreach my $dn (@$v) { + my $dn2 = ldap_explode_dn($dn, casefold => 'lower'); + my $l = $dn2->[0]->{fvl}; + my $d = $dn2->[1]->{fvd}; + if ($l ne '' and $d ne '') { + push @recipients, $l.'@'.$d; + } + else { + warn "Warning: Invalid DN: $dn" + } + } + } + } + $ldap->unbind; +} + +my $smtp = Net::SMTP::->new( 'localhost:25', Timeout => 1200 ); +$smtp->mail($sender); +$smtp->to(@recipients, { Notify => ['FAILURE','DELAY'], SkipBad => 1 }); +$smtp->data(); +$smtp->quit; diff --git a/roles/MX/files/usr/local/sbin/reserved-alias.pl b/roles/MX/files/usr/local/sbin/reserved-alias.pl deleted file mode 100755 index e19492e..0000000 --- a/roles/MX/files/usr/local/sbin/reserved-alias.pl +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/perl - -# Copyright © 2013 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 Net::LDAPI; -use Net::LDAP::Util qw/escape_filter_value ldap_explode_dn escape_dn_value/; -use Authen::SASL; -use Net::SMTP; - -if (!@ARGV or grep { $_ eq '-h' or $_ eq '--help' } @ARGV) { - # Help - print STDERR "Usage: $0 {original sender} {original recipient} [additional recipient ...]\n"; - print STDERR "\n"; - print STDERR "The message read from the standard input is redirected to 'additional recipient',\n"; - print STDERR "and also forwarded to the domain owner if any. If the 'additional recipient' begins\n"; - print STDERR "with '\@', the localpart of 'original recipient' is prepended.\n"; - print STDERR "\n"; - print STDERR "This is mostly useful to comply to RFC 822 section 6.3 and RFC 2142 section\n"; - print STDERR "4 (to forward mails to 'postmaster\@' and 'abuse\@' to the site admin in\n"; - print STDERR "addition to the virtual domain manager).\n"; - exit; -} - -# The original sender -my $sender = shift; - -# The original recipient -my $orig = shift; -$orig =~ /^(.+)\@([^@]+)$/ - or warn "Warning: Non fully qualified: $orig"; -my ($local,$domain) = ($1,$2); - -# The new recipient (typically, the admin site) -my @recipients = grep { $_ and $orig ne $_ } - # add localparts to domain - map { my $x = $_; - if ($x =~ /^\@/) { - $x = (defined $local and $local ne '') ? $local.$x : undef; - } - $x - } - @ARGV; -# Die if we can't deliver to site admins -die "Error: Aborted delivery to '$orig' in attempt to break an alias expansion loop.\n" - unless @recipients; - -if (defined $domain) { - # Look for the domain owner or postmaster - my $ldap = Net::LDAPI::->new(); - $ldap->bind( undef, sasl => Authen::SASL::->new(mechanism => 'EXTERNAL') ) - or die "Error: Couldn't bind"; - - my @attrs = ( 'fripostPostmaster', 'fripostOwner' ); - my $mesg = $ldap->search( base => 'fvd='.escape_dn_value($domain).',' - .'ou=virtual,dc=fripost,dc=org' - , scope => 'base' - , deref => 'never' - , filter => '(&(objectClass=FripostVirtualDomain)' - .'(fvd='.escape_filter_value($domain).')'. - ')' - , attrs => \@attrs - ); - if ($mesg->code) { - warn "Warning: ".$mesg->error; - } - elsif ($mesg->count != 1) { - # Note: this may happen for "$mydestination", but these mails - # are unlikely. We'll get a harmless warning at worst. - warn "Warning: Something weird happened when looking up domain '".$domain. - "'. Check your ACL."; - } - else { - my $entry = $mesg->pop_entry() // die "Error: Cannot pop entry."; - foreach (@attrs) { - my $v = $entry->get_value($_, asref => 1) or next; - foreach my $dn (@$v) { - my $dn2 = ldap_explode_dn($dn, casefold => 'lower'); - my $l = $dn2->[0]->{fvl}; - my $d = $dn2->[1]->{fvd}; - if ($l ne '' and $d ne '') { - push @recipients, $l.'@'.$d; - } - else { - warn "Warning: Invalid DN: $dn" - } - } - } - } - $ldap->unbind; -} - -my $smtp = Net::SMTP::->new( 'localhost:25', Timeout => 1200 ); -$smtp->mail($sender); -$smtp->to(@recipients, { Notify => ['FAILURE','DELAY'], SkipBad => 1 }); -$smtp->data(); -$smtp->quit; diff --git a/roles/MX/tasks/main.yml b/roles/MX/tasks/main.yml index 361e379..3c96fad 100644 --- a/roles/MX/tasks/main.yml +++ b/roles/MX/tasks/main.yml @@ -23,6 +23,20 @@ owner=root group=root mode=0755 +# trivial-rewrite(8) runs in a chroot. We create an empty +# /usr/lib/sasl2 to avoid "No such file or directory" warnings. +# Cf. also #738989. +- name: Create directory /usr/lib/sasl2 + file: path=/var/spool/postfix-{{ postfix_instance[inst].name }}/{{ item }} + state=directory + owner=root group=root + mode=0755 + with_items: + - /usr/lib/sasl2 + - /usr/lib/{{ ansible_architecture }}-linux-gnu/sasl2 + notify: + - Reload Postfix + - name: Copy lookup tables (1) copy: src=etc/postfix/virtual/{{ item }} dest=/etc/postfix-{{ postfix_instance[inst].name }}/virtual/{{ item }} @@ -54,8 +68,8 @@ - Reload Postfix - name: Copy reserved-alias.pl - copy: src=usr/local/sbin/reserved-alias.pl - dest=/usr/local/sbin/reserved-alias.pl + copy: src=usr/local/bin/reserved-alias.pl + dest=/usr/local/bin/reserved-alias.pl owner=root group=root mode=0755 diff --git a/roles/MX/templates/etc/postfix/main.cf.j2 b/roles/MX/templates/etc/postfix/main.cf.j2 index 09a5ce7..11c8199 100644 --- a/roles/MX/templates/etc/postfix/main.cf.j2 +++ b/roles/MX/templates/etc/postfix/main.cf.j2 @@ -69,12 +69,12 @@ transport_maps = cdb:$config_directory/virtual/transport # Don't rewrite remote headers -local_header_rewrite_clients = +local_header_rewrite_clients = # Pass the client information along to the content filter -smtp_send_xforward_command = yes +smtp_send_xforward_command = yes # Avoid splitting the envelope and scanning messages multiple times -smtp_destination_recipient_limit = 1000 -reserved-alias_recipient_limit = 1 +smtp_destination_recipient_limit = 1000 +reserved-alias_destination_recipient_limit = 1 # Tolerate occasional high latency smtp_data_done_timeout = 1200s @@ -90,7 +90,6 @@ smtp_tls_session_cache_database = btree:$data_directory/smtp_tls_session_cache smtp_tls_policy_maps = cdb:/etc/postfix/tls_policy smtp_tls_fingerprint_digest = sha256 {% endif %} -smtpd_tls_security_level = none smtpd_tls_security_level = may smtpd_tls_exclude_ciphers = EXPORT, LOW, MEDIUM, aNULL, eNULL, DES, RC4, MD5 @@ -140,7 +139,7 @@ postscreen_dnsbl_sites = list.dnswl.org=127.[0..255].[0..255].[2..255]*-4 postscreen_greet_action = enforce -postscreen_whitelist_interfaces = !88.80.11.28 static:all +postscreen_whitelist_interfaces = !88.80.11.28 ![2a00:16b0:242:13::de30] static:all smtpd_client_restrictions = permit_mynetworks @@ -154,13 +153,13 @@ smtpd_helo_restrictions = smtpd_sender_restrictions = reject_non_fqdn_sender -smtpd_recipient_restrictions = - # RFC requirements - reject_non_fqdn_recipient +smtpd_relay_restrictions = permit_mynetworks reject_unauth_destination reject_unlisted_recipient - permit_dnswl_client list.dnswl.org + +smtpd_recipient_restrictions = + reject_non_fqdn_recipient smtpd_data_restrictions = reject_unauth_pipelining -- cgit v1.2.3