# HG changeset patch # User IBBoard # Date 1442088055 -3600 # Node ID f192048f9b7e09ba494e5e45b3ab0ee6bafbc4e4 # Parent c2f66d0f2a431f22d8cbec50030100d528c6a303 Add CentOS 7 config for postfix to take advantage of Postfix 2.10 and Postscreen Also, move files to the module diff -r c2f66d0f2a43 -r f192048f9b7e common/postfix/master.cf --- a/common/postfix/master.cf Sat Sep 12 16:42:27 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -# -# Postfix master process configuration file. For details on the format -# of the file, see the master(5) manual page (command: "man 5 master"). -# -# Do not forget to execute "postfix reload" after editing this file. -# -# ========================================================================== -# service type private unpriv chroot wakeup maxproc command + args -# (yes) (yes) (yes) (never) (100) -# ========================================================================== -smtp inet n - n - - smtpd -#submission inet n - n - - smtpd -# -o smtpd_tls_security_level=encrypt -# -o smtpd_sasl_auth_enable=yes -# -o smtpd_client_restrictions=permit_sasl_authenticated,reject -# -o milter_macro_daemon_name=ORIGINATING -smtps inet n - n - - smtpd - -o smtpd_tls_wrappermode=yes - -o smtpd_sasl_auth_enable=yes - -o smtpd_client_restrictions=permit_sasl_authenticated,reject - -o milter_macro_daemon_name=ORIGINATING -#628 inet n - n - - qmqpd -pickup fifo n - n 60 1 pickup -cleanup unix n - n - 0 cleanup -qmgr fifo n - n 300 1 qmgr -#qmgr fifo n - n 300 1 oqmgr -tlsmgr unix - - n 1000? 1 tlsmgr -rewrite unix - - n - - trivial-rewrite -bounce unix - - n - 0 bounce -defer unix - - n - 0 bounce -trace unix - - n - 0 bounce -verify unix - - n - 1 verify -flush unix n - n 1000? 0 flush -proxymap unix - - n - - proxymap -proxywrite unix - - n - 1 proxymap -smtp unix - - n - - smtp -# When relaying mail as backup MX, disable fallback_relay to avoid MX loops -relay unix - - n - - smtp - -o smtp_fallback_relay= -# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 -showq unix n - n - - showq -error unix - - n - - error -retry unix - - n - - error -discard unix - - n - - discard -local unix - n n - - local -virtual unix - n n - - virtual -lmtp unix - - n - - lmtp -anvil unix - - n - 1 anvil -scache unix - - n - 1 scache -# -# ==================================================================== -# Interfaces to non-Postfix software. Be sure to examine the manual -# pages of the non-Postfix software to find out what options it wants. -# -# Many of the following services use the Postfix pipe(8) delivery -# agent. See the pipe(8) man page for information about ${recipient} -# and other message envelope options. -# ==================================================================== -# -# maildrop. See the Postfix MAILDROP_README file for details. -# Also specify in main.cf: maildrop_destination_recipient_limit=1 -# -#maildrop unix - n n - - pipe -# flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient} -# -# ==================================================================== -# -# The Cyrus deliver program has changed incompatibly, multiple times. -# -#old-cyrus unix - n n - - pipe -# flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m ${extension} ${user} -# -# ==================================================================== -# -# Cyrus 2.1.5 (Amos Gouaux) -# Also specify in main.cf: cyrus_destination_recipient_limit=1 -# -#cyrus unix - n n - - pipe -# user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r ${sender} -m ${extension} ${user} -# -# ==================================================================== -# -# See the Postfix UUCP_README file for configuration details. -# -#uucp unix - n n - - pipe -# flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) -# -# ==================================================================== -# -# Other external delivery methods. -# -#ifmail unix - n n - - pipe -# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) -# -#bsmtp unix - n n - - pipe -# flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient -# -#scalemail-backend unix - n n - 2 pipe -# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store -# ${nexthop} ${user} ${extension} -# -#mailman unix - n n - - pipe -# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py -# ${nexthop} ${user} - -policy unix - n n - 0 spawn - user=nobody argv=/usr/bin/perl /usr/local/lib/postfix-policyd-spf-perl/postfix-policyd-spf-perl diff -r c2f66d0f2a43 -r f192048f9b7e common/postfix/postfix-policyd-spf-perl --- a/common/postfix/postfix-policyd-spf-perl Sat Sep 12 16:42:27 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,396 +0,0 @@ -#!/usr/bin/perl - -# postfix-policyd-spf-perl -# http://www.openspf.org/Software -# version 2.010 -# -# (C) 2007-2008,2012 Scott Kitterman -# (C) 2012 Allison Randal -# (C) 2007 Julian Mehnle -# (C) 2003-2004 Meng Weng Wong -# -# 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 2 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, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -use version; our $VERSION = qv('2.010'); - -use strict; - -use IO::Handle; -use Sys::Syslog qw(:DEFAULT setlogsock); -use NetAddr::IP; -use Mail::SPF; -use Sys::Hostname::Long 'hostname_long'; - -# ---------------------------------------------------------- -# configuration -# ---------------------------------------------------------- - -my $resolver = Net::DNS::Resolver->new( - retrans => 5, # Net::DNS::Resolver default: 5 - retry => 2, # Net::DNS::Resolver default: 4 - # Makes for a total timeout for UDP queries of 5s * 2 = 10s. -); - -# query_rr_type_all will query both type TXT and type SPF. This upstream -# default is changed due to there being essentiall no type SPF deployment. -my $spf_server = Mail::SPF::Server->new( - dns_resolver => $resolver, - query_rr_types => Mail::SPF::Server->query_rr_type_txt, - default_authority_explanation => - 'Please see http://www.openspf.net/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' -); - -# Adding more handlers is easy: -my @HANDLERS = ( - { - name => 'exempt_localhost', - code => \&exempt_localhost - }, - { - name => 'exempt_relay', - code => \&exempt_relay - }, - { - name => 'sender_policy_framework', - code => \&sender_policy_framework - } -); - -my $VERBOSE = 0; - -my $DEFAULT_RESPONSE = 'DUNNO'; - -# -# Syslogging options for verbose mode and for fatal errors. -# NOTE: comment out the $syslog_socktype line if syslogging does not -# work on your system. -# - -my $syslog_socktype = 'unix'; # inet, unix, stream, console -my $syslog_facility = 'mail'; -my $syslog_options = 'pid'; -my $syslog_ident = 'postfix/policy-spf'; - -use constant localhost_addresses => map( - NetAddr::IP->new($_), - qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) -); # Does Postfix ever say "client_address=::ffff:"? - -use constant relay_addresses => map( - NetAddr::IP->new($_), - qw( ) -); # add addresses to qw ( ) above separated by spaces using CIDR notation. - -# Fully qualified hostname, if available, for use in authentication results -# headers now provided by the localhost and whitelist checks. -my $host = hostname_long; - -my %results_cache; # by message instance - -# ---------------------------------------------------------- -# initialization -# ---------------------------------------------------------- - -# -# Log an error and abort. -# -sub fatal_exit { - syslog(err => "fatal_exit: @_"); - syslog(warning => "fatal_exit: @_"); - syslog(info => "fatal_exit: @_"); - die("fatal: @_"); -} - -# -# Unbuffer standard output. -# -STDOUT->autoflush(1); - -# -# This process runs as a daemon, so it can't log to a terminal. Use -# syslog so that people can actually see our messages. -# -setlogsock($syslog_socktype); -openlog($syslog_ident, $syslog_options, $syslog_facility); - -# ---------------------------------------------------------- -# main -# ---------------------------------------------------------- - -# -# Receive a bunch of attributes, evaluate the policy, send the result. -# -my %attr; -while () { - chomp; - - if (/=/) { - my ($key, $value) =split (/=/, $_, 2); - $attr{$key} = $value; - next; - } - elsif (length) { - syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); - next; - } - - if ($VERBOSE) { - for (sort keys %attr) { - syslog(debug => "Attribute: %s=%s", $_ || '', $attr{$_} || ''); - } - }; - - my $message_instance = $attr{instance}; - my $cache = defined($message_instance) ? $results_cache{$message_instance} ||= {} : {}; - - my $action = $DEFAULT_RESPONSE; - - foreach my $handler (@HANDLERS) { - my $handler_name = $handler->{name}; - my $handler_code = $handler->{code}; - - my $response = $handler_code->(attr => \%attr, cache => $cache); - - if ($VERBOSE) { - syslog(debug => "handler %s: %s", $handler_name || '', $response || ''); - }; - - # Pick whatever response is not 'DUNNO' - if ($response and $response !~ /^DUNNO/i) { - if ($VERBOSE) { - syslog(info => "handler %s: is decisive.", $handler_name || ''); - } - $action = $response; - last; - } - } - - syslog(info => "Policy action=%s", $action || ''); - - STDOUT->print("action=$action\n\n"); - %attr = (); -} - -# ---------------------------------------------------------- -# handler: localhost exemption -# ---------------------------------------------------------- - -sub exempt_localhost { - my %options = @_; - my $attr = $options{attr}; - if ($attr->{client_address} ne '') { - my $client_address = NetAddr::IP->new($attr->{client_address}); - return "PREPEND Authentication-Results: $host; none (SPF not checked for localhost)" - if grep($_->contains($client_address), localhost_addresses); - }; - return 'DUNNO'; -} - -# ---------------------------------------------------------- -# handler: relay exemption -# ---------------------------------------------------------- - -sub exempt_relay { - my %options = @_; - my $attr = $options{attr}; - if ($attr->{client_address} ne '') { - my $client_address = NetAddr::IP->new($attr->{client_address}); - return "PREPEND Authentication-Results: $host; none (SPF not checked for whitelisted relay)" - if grep($_->contains($client_address), relay_addresses); - }; - return 'DUNNO'; -} - -# ---------------------------------------------------------- -# handler: SPF -# ---------------------------------------------------------- - -sub sender_policy_framework { - my %options = @_; - my $attr = $options{attr}; - my $cache = $options{cache}; - - # ------------------------------------------------------------------------- - # Always do HELO check first. If no HELO policy, it's only one lookup. - # This avoids the need to do any MAIL FROM processing for null sender. - # ------------------------------------------------------------------------- - - my $helo_result = $cache->{helo_result}; - - if (not defined($helo_result)) { - # No HELO result has been cached from earlier checks on this message. - - my $helo_request = eval { - Mail::SPF::Request->new( - scope => 'helo', - identity => $attr->{helo_name}, - ip_address => $attr->{client_address} - ); - }; - - if ($@) { - # An unexpected error occurred during request creation, - # probably due to invalid input data! - my $errmsg = $@; - $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); - if ($VERBOSE) { - syslog( - info => "HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{client_address} || '', - $attr->{sender} || '', $attr->{helo_name} || '', - $errmsg || '' - ); - }; - return; - } - - $helo_result = $cache->{helo_result} = $spf_server->process($helo_request); - } - - my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. - my $helo_local_exp = nullchomp($helo_result->local_explanation); - my $helo_authority_exp = nullchomp($helo_result->authority_explanation) - if $helo_result->is_code('fail'); - my $helo_spf_header = $helo_result->received_spf_header; - - if ($VERBOSE) { - syslog( - info => "SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $helo_result || '', - $attr->{helo_name} || '', $attr->{client_address} || '', - $attr->{recipient} || '' - ); - }; - - # Reject on HELO fail. Defer on HELO temperror if message would otherwise - # be accepted. Use the HELO result and return for null sender. - if ($helo_result->is_code('fail')) { - if ($VERBOSE) { - syslog( - info => "SPF %s: HELO/EHLO: %s", - $helo_result || '', - $attr->{helo_name} || '' - ); - }; - return "550 $helo_authority_exp"; - } - elsif ($helo_result->is_code('temperror')) { - if ($VERBOSE) { - syslog( - info => "SPF %s: HELO/EHLO: %s", - $helo_result || '', - $attr->{helo_name} || '' - ); - }; - return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; - } - elsif ($attr->{sender} eq '') { - if ($VERBOSE) { - syslog( - info => "SPF %s: HELO/EHLO (Null Sender): %s", - $helo_result || '', - $attr->{helo_name} || '' - ); - }; - return "PREPEND $helo_spf_header" - unless $cache->{added_spf_header}++; - } - - # ------------------------------------------------------------------------- - # Do MAIL FROM check (as HELO did not give a definitive result) - # ------------------------------------------------------------------------- - - my $mfrom_result = $cache->{mfrom_result}; - - if (not defined($mfrom_result)) { - # No MAIL FROM result has been cached from earlier checks on this message. - - my $mfrom_request = eval { - Mail::SPF::Request->new( - scope => 'mfrom', - identity => $attr->{sender}, - ip_address => $attr->{client_address}, - helo_identity => $attr->{helo_name} # for %{h} macro expansion - ); - }; - - if ($@) { - # An unexpected error occurred during request creation, - # probably due to invalid input data! - my $errmsg = $@; - $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); - if ($VERBOSE) { - syslog( - info => "Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{client_address} || '', - $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' - ); - }; - return; - } - - $mfrom_result = $cache->{mfrom_result} = $spf_server->process($mfrom_request); - } - - my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. - my $mfrom_local_exp = nullchomp($mfrom_result->local_explanation); - my $mfrom_authority_exp = nullchomp($mfrom_result->authority_explanation) - if $mfrom_result->is_code('fail'); - my $mfrom_spf_header = $mfrom_result->received_spf_header; - - if ($VERBOSE) { - syslog( - info => "SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $mfrom_result || '', - $attr->{sender} || '', $attr->{client_address} || '', - $attr->{recipient} || '' - ); - }; - - # Same approach as HELO.... - if ($VERBOSE) { - syslog( - info => "SPF %s: Envelope-from: %s", - $mfrom_result || '', - $attr->{sender} || '' - ); - }; - if ($mfrom_result->is_code('fail')) { - return "550 $mfrom_authority_exp"; - } - elsif ($mfrom_result->is_code('temperror')) { - return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; - } - else { - return "PREPEND $mfrom_spf_header" - unless $cache->{added_spf_header}++; - } - - return; -} - -# ---------------------------------------------------------- -# utility, string cleaning -# ---------------------------------------------------------- - -sub nullchomp { - my $value = shift; - - # Remove one or more null characters from the - # end of the input. - $value =~ s/\0+$//; - return $value; -} - diff -r c2f66d0f2a43 -r f192048f9b7e modules/postfix/files/master.CentOS7.cf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/postfix/files/master.CentOS7.cf Sat Sep 12 21:00:55 2015 +0100 @@ -0,0 +1,111 @@ +# CentOS 7 +# Postfix master process configuration file. For details on the format +# of the file, see the master(5) manual page (command: "man 5 master"). +# +# Do not forget to execute "postfix reload" after editing this file. +# +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (yes) (never) (100) +# ========================================================================== +#smtp inet n - n - - smtpd +smtpd pass - - n - - smtpd +smtp inet n - n - 1 postscreen +tlsproxy unix - - n - 0 tlsproxy +dnsblog unix - - n - 0 dnsblog +#submission inet n - n - - smtpd +# -o smtpd_tls_security_level=encrypt +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +# -o milter_macro_daemon_name=ORIGINATING +smtps inet n - n - - smtpd + -o smtpd_tls_wrappermode=yes + -o smtpd_sasl_auth_enable=yes + -o smtpd_client_restrictions=permit_sasl_authenticated,reject + -o milter_macro_daemon_name=ORIGINATING +#628 inet n - n - - qmqpd +pickup fifo n - n 60 1 pickup +cleanup unix n - n - 0 cleanup +qmgr fifo n - n 300 1 qmgr +#qmgr fifo n - n 300 1 oqmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - n - - smtp +# When relaying mail as backup MX, disable fallback_relay to avoid MX loops +relay unix - - n - - smtp + -o smtp_fallback_relay= +# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 +showq unix n - n - - showq +error unix - - n - - error +retry unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache +# +# ==================================================================== +# Interfaces to non-Postfix software. Be sure to examine the manual +# pages of the non-Postfix software to find out what options it wants. +# +# Many of the following services use the Postfix pipe(8) delivery +# agent. See the pipe(8) man page for information about ${recipient} +# and other message envelope options. +# ==================================================================== +# +# maildrop. See the Postfix MAILDROP_README file for details. +# Also specify in main.cf: maildrop_destination_recipient_limit=1 +# +#maildrop unix - n n - - pipe +# flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient} +# +# ==================================================================== +# +# The Cyrus deliver program has changed incompatibly, multiple times. +# +#old-cyrus unix - n n - - pipe +# flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m ${extension} ${user} +# +# ==================================================================== +# +# Cyrus 2.1.5 (Amos Gouaux) +# Also specify in main.cf: cyrus_destination_recipient_limit=1 +# +#cyrus unix - n n - - pipe +# user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r ${sender} -m ${extension} ${user} +# +# ==================================================================== +# +# See the Postfix UUCP_README file for configuration details. +# +#uucp unix - n n - - pipe +# flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) +# +# ==================================================================== +# +# Other external delivery methods. +# +#ifmail unix - n n - - pipe +# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) +# +#bsmtp unix - n n - - pipe +# flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient +# +#scalemail-backend unix - n n - 2 pipe +# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store +# ${nexthop} ${user} ${extension} +# +#mailman unix - n n - - pipe +# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py +# ${nexthop} ${user} + +policy unix - n n - 0 spawn + user=nobody argv=/usr/bin/perl /usr/local/lib/postfix-policyd-spf-perl/postfix-policyd-spf-perl diff -r c2f66d0f2a43 -r f192048f9b7e modules/postfix/files/master.cf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/postfix/files/master.cf Sat Sep 12 21:00:55 2015 +0100 @@ -0,0 +1,107 @@ +# +# Postfix master process configuration file. For details on the format +# of the file, see the master(5) manual page (command: "man 5 master"). +# +# Do not forget to execute "postfix reload" after editing this file. +# +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (yes) (never) (100) +# ========================================================================== +smtp inet n - n - - smtpd +#submission inet n - n - - smtpd +# -o smtpd_tls_security_level=encrypt +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +# -o milter_macro_daemon_name=ORIGINATING +smtps inet n - n - - smtpd + -o smtpd_tls_wrappermode=yes + -o smtpd_sasl_auth_enable=yes + -o smtpd_client_restrictions=permit_sasl_authenticated,reject + -o milter_macro_daemon_name=ORIGINATING +#628 inet n - n - - qmqpd +pickup fifo n - n 60 1 pickup +cleanup unix n - n - 0 cleanup +qmgr fifo n - n 300 1 qmgr +#qmgr fifo n - n 300 1 oqmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - n - - smtp +# When relaying mail as backup MX, disable fallback_relay to avoid MX loops +relay unix - - n - - smtp + -o smtp_fallback_relay= +# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 +showq unix n - n - - showq +error unix - - n - - error +retry unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache +# +# ==================================================================== +# Interfaces to non-Postfix software. Be sure to examine the manual +# pages of the non-Postfix software to find out what options it wants. +# +# Many of the following services use the Postfix pipe(8) delivery +# agent. See the pipe(8) man page for information about ${recipient} +# and other message envelope options. +# ==================================================================== +# +# maildrop. See the Postfix MAILDROP_README file for details. +# Also specify in main.cf: maildrop_destination_recipient_limit=1 +# +#maildrop unix - n n - - pipe +# flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient} +# +# ==================================================================== +# +# The Cyrus deliver program has changed incompatibly, multiple times. +# +#old-cyrus unix - n n - - pipe +# flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m ${extension} ${user} +# +# ==================================================================== +# +# Cyrus 2.1.5 (Amos Gouaux) +# Also specify in main.cf: cyrus_destination_recipient_limit=1 +# +#cyrus unix - n n - - pipe +# user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r ${sender} -m ${extension} ${user} +# +# ==================================================================== +# +# See the Postfix UUCP_README file for configuration details. +# +#uucp unix - n n - - pipe +# flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) +# +# ==================================================================== +# +# Other external delivery methods. +# +#ifmail unix - n n - - pipe +# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) +# +#bsmtp unix - n n - - pipe +# flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient +# +#scalemail-backend unix - n n - 2 pipe +# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store +# ${nexthop} ${user} ${extension} +# +#mailman unix - n n - - pipe +# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py +# ${nexthop} ${user} + +policy unix - n n - 0 spawn + user=nobody argv=/usr/bin/perl /usr/local/lib/postfix-policyd-spf-perl/postfix-policyd-spf-perl diff -r c2f66d0f2a43 -r f192048f9b7e modules/postfix/files/postfix-policyd-spf-perl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/postfix/files/postfix-policyd-spf-perl Sat Sep 12 21:00:55 2015 +0100 @@ -0,0 +1,396 @@ +#!/usr/bin/perl + +# postfix-policyd-spf-perl +# http://www.openspf.org/Software +# version 2.010 +# +# (C) 2007-2008,2012 Scott Kitterman +# (C) 2012 Allison Randal +# (C) 2007 Julian Mehnle +# (C) 2003-2004 Meng Weng Wong +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use version; our $VERSION = qv('2.010'); + +use strict; + +use IO::Handle; +use Sys::Syslog qw(:DEFAULT setlogsock); +use NetAddr::IP; +use Mail::SPF; +use Sys::Hostname::Long 'hostname_long'; + +# ---------------------------------------------------------- +# configuration +# ---------------------------------------------------------- + +my $resolver = Net::DNS::Resolver->new( + retrans => 5, # Net::DNS::Resolver default: 5 + retry => 2, # Net::DNS::Resolver default: 4 + # Makes for a total timeout for UDP queries of 5s * 2 = 10s. +); + +# query_rr_type_all will query both type TXT and type SPF. This upstream +# default is changed due to there being essentiall no type SPF deployment. +my $spf_server = Mail::SPF::Server->new( + dns_resolver => $resolver, + query_rr_types => Mail::SPF::Server->query_rr_type_txt, + default_authority_explanation => + 'Please see http://www.openspf.net/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' +); + +# Adding more handlers is easy: +my @HANDLERS = ( + { + name => 'exempt_localhost', + code => \&exempt_localhost + }, + { + name => 'exempt_relay', + code => \&exempt_relay + }, + { + name => 'sender_policy_framework', + code => \&sender_policy_framework + } +); + +my $VERBOSE = 0; + +my $DEFAULT_RESPONSE = 'DUNNO'; + +# +# Syslogging options for verbose mode and for fatal errors. +# NOTE: comment out the $syslog_socktype line if syslogging does not +# work on your system. +# + +my $syslog_socktype = 'unix'; # inet, unix, stream, console +my $syslog_facility = 'mail'; +my $syslog_options = 'pid'; +my $syslog_ident = 'postfix/policy-spf'; + +use constant localhost_addresses => map( + NetAddr::IP->new($_), + qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) +); # Does Postfix ever say "client_address=::ffff:"? + +use constant relay_addresses => map( + NetAddr::IP->new($_), + qw( ) +); # add addresses to qw ( ) above separated by spaces using CIDR notation. + +# Fully qualified hostname, if available, for use in authentication results +# headers now provided by the localhost and whitelist checks. +my $host = hostname_long; + +my %results_cache; # by message instance + +# ---------------------------------------------------------- +# initialization +# ---------------------------------------------------------- + +# +# Log an error and abort. +# +sub fatal_exit { + syslog(err => "fatal_exit: @_"); + syslog(warning => "fatal_exit: @_"); + syslog(info => "fatal_exit: @_"); + die("fatal: @_"); +} + +# +# Unbuffer standard output. +# +STDOUT->autoflush(1); + +# +# This process runs as a daemon, so it can't log to a terminal. Use +# syslog so that people can actually see our messages. +# +setlogsock($syslog_socktype); +openlog($syslog_ident, $syslog_options, $syslog_facility); + +# ---------------------------------------------------------- +# main +# ---------------------------------------------------------- + +# +# Receive a bunch of attributes, evaluate the policy, send the result. +# +my %attr; +while () { + chomp; + + if (/=/) { + my ($key, $value) =split (/=/, $_, 2); + $attr{$key} = $value; + next; + } + elsif (length) { + syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); + next; + } + + if ($VERBOSE) { + for (sort keys %attr) { + syslog(debug => "Attribute: %s=%s", $_ || '', $attr{$_} || ''); + } + }; + + my $message_instance = $attr{instance}; + my $cache = defined($message_instance) ? $results_cache{$message_instance} ||= {} : {}; + + my $action = $DEFAULT_RESPONSE; + + foreach my $handler (@HANDLERS) { + my $handler_name = $handler->{name}; + my $handler_code = $handler->{code}; + + my $response = $handler_code->(attr => \%attr, cache => $cache); + + if ($VERBOSE) { + syslog(debug => "handler %s: %s", $handler_name || '', $response || ''); + }; + + # Pick whatever response is not 'DUNNO' + if ($response and $response !~ /^DUNNO/i) { + if ($VERBOSE) { + syslog(info => "handler %s: is decisive.", $handler_name || ''); + } + $action = $response; + last; + } + } + + syslog(info => "Policy action=%s", $action || ''); + + STDOUT->print("action=$action\n\n"); + %attr = (); +} + +# ---------------------------------------------------------- +# handler: localhost exemption +# ---------------------------------------------------------- + +sub exempt_localhost { + my %options = @_; + my $attr = $options{attr}; + if ($attr->{client_address} ne '') { + my $client_address = NetAddr::IP->new($attr->{client_address}); + return "PREPEND Authentication-Results: $host; none (SPF not checked for localhost)" + if grep($_->contains($client_address), localhost_addresses); + }; + return 'DUNNO'; +} + +# ---------------------------------------------------------- +# handler: relay exemption +# ---------------------------------------------------------- + +sub exempt_relay { + my %options = @_; + my $attr = $options{attr}; + if ($attr->{client_address} ne '') { + my $client_address = NetAddr::IP->new($attr->{client_address}); + return "PREPEND Authentication-Results: $host; none (SPF not checked for whitelisted relay)" + if grep($_->contains($client_address), relay_addresses); + }; + return 'DUNNO'; +} + +# ---------------------------------------------------------- +# handler: SPF +# ---------------------------------------------------------- + +sub sender_policy_framework { + my %options = @_; + my $attr = $options{attr}; + my $cache = $options{cache}; + + # ------------------------------------------------------------------------- + # Always do HELO check first. If no HELO policy, it's only one lookup. + # This avoids the need to do any MAIL FROM processing for null sender. + # ------------------------------------------------------------------------- + + my $helo_result = $cache->{helo_result}; + + if (not defined($helo_result)) { + # No HELO result has been cached from earlier checks on this message. + + my $helo_request = eval { + Mail::SPF::Request->new( + scope => 'helo', + identity => $attr->{helo_name}, + ip_address => $attr->{client_address} + ); + }; + + if ($@) { + # An unexpected error occurred during request creation, + # probably due to invalid input data! + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + if ($VERBOSE) { + syslog( + info => "HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', + $errmsg || '' + ); + }; + return; + } + + $helo_result = $cache->{helo_result} = $spf_server->process($helo_request); + } + + my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. + my $helo_local_exp = nullchomp($helo_result->local_explanation); + my $helo_authority_exp = nullchomp($helo_result->authority_explanation) + if $helo_result->is_code('fail'); + my $helo_spf_header = $helo_result->received_spf_header; + + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $helo_result || '', + $attr->{helo_name} || '', $attr->{client_address} || '', + $attr->{recipient} || '' + ); + }; + + # Reject on HELO fail. Defer on HELO temperror if message would otherwise + # be accepted. Use the HELO result and return for null sender. + if ($helo_result->is_code('fail')) { + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; + return "550 $helo_authority_exp"; + } + elsif ($helo_result->is_code('temperror')) { + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; + return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; + } + elsif ($attr->{sender} eq '') { + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO (Null Sender): %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; + } + + # ------------------------------------------------------------------------- + # Do MAIL FROM check (as HELO did not give a definitive result) + # ------------------------------------------------------------------------- + + my $mfrom_result = $cache->{mfrom_result}; + + if (not defined($mfrom_result)) { + # No MAIL FROM result has been cached from earlier checks on this message. + + my $mfrom_request = eval { + Mail::SPF::Request->new( + scope => 'mfrom', + identity => $attr->{sender}, + ip_address => $attr->{client_address}, + helo_identity => $attr->{helo_name} # for %{h} macro expansion + ); + }; + + if ($@) { + # An unexpected error occurred during request creation, + # probably due to invalid input data! + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + if ($VERBOSE) { + syslog( + info => "Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' + ); + }; + return; + } + + $mfrom_result = $cache->{mfrom_result} = $spf_server->process($mfrom_request); + } + + my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. + my $mfrom_local_exp = nullchomp($mfrom_result->local_explanation); + my $mfrom_authority_exp = nullchomp($mfrom_result->authority_explanation) + if $mfrom_result->is_code('fail'); + my $mfrom_spf_header = $mfrom_result->received_spf_header; + + if ($VERBOSE) { + syslog( + info => "SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $mfrom_result || '', + $attr->{sender} || '', $attr->{client_address} || '', + $attr->{recipient} || '' + ); + }; + + # Same approach as HELO.... + if ($VERBOSE) { + syslog( + info => "SPF %s: Envelope-from: %s", + $mfrom_result || '', + $attr->{sender} || '' + ); + }; + if ($mfrom_result->is_code('fail')) { + return "550 $mfrom_authority_exp"; + } + elsif ($mfrom_result->is_code('temperror')) { + return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; + } + else { + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; + } + + return; +} + +# ---------------------------------------------------------- +# utility, string cleaning +# ---------------------------------------------------------- + +sub nullchomp { + my $value = shift; + + # Remove one or more null characters from the + # end of the input. + $value =~ s/\0+$//; + return $value; +} + diff -r c2f66d0f2a43 -r f192048f9b7e modules/postfix/manifests/init.pp --- a/modules/postfix/manifests/init.pp Sat Sep 12 16:42:27 2015 +0100 +++ b/modules/postfix/manifests/init.pp Sat Sep 12 21:00:55 2015 +0100 @@ -30,7 +30,10 @@ content => template('postfix/main.cf.erb'), } file { '/etc/postfix/master.cf': - source => 'puppet:///common/postfix/master.cf' + source => [ + "puppet:///modules/postfix/master.${operatingsystem}${operatingsystemmajrelease}.cf", + 'puppet:///modules/postfix/master.cf' + ] } #Hosted domains file { '/etc/postfix/vdomains': @@ -99,7 +102,7 @@ ensure => directory } file { '/usr/local/lib/postfix-policyd-spf-perl/postfix-policyd-spf-perl': - source => 'puppet:///common/postfix/postfix-policyd-spf-perl', + source => 'puppet:///modules/postfix/postfix-policyd-spf-perl', } $perl_pkgs = [ 'perl', 'perl-NetAddr-IP', 'perl-Mail-SPF', 'perl-version', 'perl-Sys-Hostname-Long'] package { $perl_pkgs: diff -r c2f66d0f2a43 -r f192048f9b7e modules/postfix/templates/main.cf.erb --- a/modules/postfix/templates/main.cf.erb Sat Sep 12 16:42:27 2015 +0100 +++ b/modules/postfix/templates/main.cf.erb Sat Sep 12 21:00:55 2015 +0100 @@ -65,3 +65,9 @@ transport_maps = hash:/etc/postfix/transport message_size_limit = 15000000 header_checks = regexp:/etc/postfix/header_checks + +# The following may not be used by all versions of Postfix +postscreen_dnsbl_threshold = 2 +postscreen_dnsbl_sites = zen.spamhaus.org*2 bl.spamcop.net*1 b.barracudacentral.org*1 +#postscreen_greet_action = enforce +#postscreen_dnsbl_action = enforce \ No newline at end of file