summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common.yml7
-rw-r--r--roles/IMAP/files/etc/amavis/conf.d/05-domain_id20
-rw-r--r--roles/IMAP/handlers/main.yml3
-rw-r--r--roles/IMAP/tasks/main.yml1
-rw-r--r--roles/IMAP/tasks/spam.yml3
-rw-r--r--roles/IMAP/templates/etc/amavis/conf.d/15-content_filter_mode.j229
-rw-r--r--roles/IMAP/templates/etc/amavis/conf.d/50-user.j2135
-rw-r--r--roles/amavis/handlers/main.yml10
-rw-r--r--roles/amavis/tasks/main.yml (renamed from roles/IMAP/tasks/amavis.yml)34
-rw-r--r--roles/amavis/templates/etc/amavis/conf.d/50-user.j2182
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/postfix-local2
-rw-r--r--roles/common/files/etc/postfix/master.cf27
-rwxr-xr-xroles/common/files/usr/local/bin/genkeypair.sh4
-rw-r--r--roles/out/templates/etc/postfix/main.cf.j24
14 files changed, 255 insertions, 206 deletions
diff --git a/common.yml b/common.yml
index 99628a3..d069ecc 100644
--- a/common.yml
+++ b/common.yml
@@ -22,20 +22,27 @@
- name: Common LDAP tasks
hosts: MDA:MSA:lists:LDAP-provider:MX
gather_facts: False
tags: slapd,ldap
roles:
- common-LDAP
- name: Configure the LDAP provider
hosts: LDAP-provider
gather_facts: False
tags: slapd,ldap
roles:
- LDAP-provider
- name: Configure the Web servers
hosts: webmail:lists
gather_facts: False
tags: nginx,www,web
roles:
- common-web
+
+- name: Configure amavis
+ hosts: MDA:out
+ gather_facts: False
+ tags: amavis
+ roles:
+ - amavis
diff --git a/roles/IMAP/files/etc/amavis/conf.d/05-domain_id b/roles/IMAP/files/etc/amavis/conf.d/05-domain_id
deleted file mode 100644
index 19f10ed..0000000
--- a/roles/IMAP/files/etc/amavis/conf.d/05-domain_id
+++ /dev/null
@@ -1,20 +0,0 @@
-use strict;
-
-# $mydomain is used just for convenience in the config files and it is not
-# used internally by amavisd-new except in the default X_HEADER_LINE (which
-# Debian overrides by default anyway).
-
-$mydomain = "fripost.org";
-
-# amavisd-new needs to know which email domains are to be considered local
-# to the administrative domain. Only emails to "local" domains are subject
-# to certain functionality, such as the addition of spam tags.
-#
-# Default local domains to $mydomain and all subdomains. Remember to
-# override or redefine this if $mydomain is changed later in the config
-# sequence.
-
-@local_domains_acl = ( ".$mydomain" );
-@local_domains_maps = ( ".$mydomain" );
-
-1; # ensure a defined return
diff --git a/roles/IMAP/handlers/main.yml b/roles/IMAP/handlers/main.yml
index bda2ab9..c14468a 100644
--- a/roles/IMAP/handlers/main.yml
+++ b/roles/IMAP/handlers/main.yml
@@ -1,33 +1,30 @@
---
- name: Restart Dovecot
service: name=dovecot state=restarted
- name: Restart Postfix
service: name=postfix state=restarted
- name: Reload Postfix
service: name=postfix state=reloaded
-- name: Restart ClamAV
- service: name=clamav-daemon state=restarted
-
- name: Compile Spamassassin rules
sudo_user: debian-spamd
# it might take a while...
command: /usr/bin/sa-compile --quiet
chdir=/var/lib/spamassassin/
- name: Restart Amavis
service: name=amavis state=restarted
- name: Copy SQL tables for spamassassin
copy: src=tmp/spamassassin.sql
dest=/tmp/spamassassin.sql
owner=root group=root
mode=0600
- name: Create SQL tables for spamassassin
# see https://svn.apache.org/repos/asf/spamassassin/trunk/sql/
# for the original
mysql_db: name=spamassassin state=import
target=/tmp/spamassassin.sql
diff --git a/roles/IMAP/tasks/main.yml b/roles/IMAP/tasks/main.yml
index b43f9fb..c6fbbd9 100644
--- a/roles/IMAP/tasks/main.yml
+++ b/roles/IMAP/tasks/main.yml
@@ -1,5 +1,4 @@
---
- include: imap.yml tags=imap,dovecot
- include: mda.yml tags=mda,mail,postfix
-- include: amavis.yml tags=amavis
- include: spam.yml tags=spam,spamassassin
diff --git a/roles/IMAP/tasks/spam.yml b/roles/IMAP/tasks/spam.yml
index 51fde4b..a8fbe71 100644
--- a/roles/IMAP/tasks/spam.yml
+++ b/roles/IMAP/tasks/spam.yml
@@ -1,23 +1,26 @@
- name: Install spamassassin
apt: pkg={{ item }}
with_items:
+ # The following two lines are for the policy lookup (made by amavis)
+ - libnet-ldap-perl
+ - libauthen-sasl-perl
- razor
- spamassassin
- spamc
- libdbi-perl
- re2c
- libc6-dev
- gcc
- make
notify:
- Compile Spamassassin rules
- Restart Amavis
- name: Create a 'spamassassin' database
mysql_db: name=spamassassin state=present
encoding=latin1 collation=latin1_general_ci
notify:
- Copy SQL tables for spamassassin
- Create SQL tables for spamassassin
- meta: flush_handlers
diff --git a/roles/IMAP/templates/etc/amavis/conf.d/15-content_filter_mode.j2 b/roles/IMAP/templates/etc/amavis/conf.d/15-content_filter_mode.j2
deleted file mode 100644
index cde0452..0000000
--- a/roles/IMAP/templates/etc/amavis/conf.d/15-content_filter_mode.j2
+++ /dev/null
@@ -1,29 +0,0 @@
-use strict;
-
-# You can modify this file to re-enable SPAM checking through spamassassin
-# and to re-enable antivirus checking.
-
-#
-# Default antivirus checking mode
-# Please note, that anti-virus checking is DISABLED by
-# default.
-# If You wish to enable it, please uncomment the following lines:
-
-
-@bypass_virus_checks_maps = (
- \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
-
-
-#
-# Default SPAM checking mode
-# Please note, that anti-spam checking is DISABLED by
-# default.
-# If You wish to enable it, please uncomment the following lines:
-
-
-{% if 'MDA' in group_names -%}
-@bypass_spam_checks_maps = (
- \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
-{% endif %}
-
-1; # ensure a defined return
diff --git a/roles/IMAP/templates/etc/amavis/conf.d/50-user.j2 b/roles/IMAP/templates/etc/amavis/conf.d/50-user.j2
deleted file mode 100644
index b3ae7a9..0000000
--- a/roles/IMAP/templates/etc/amavis/conf.d/50-user.j2
+++ /dev/null
@@ -1,135 +0,0 @@
-use strict;
-
-#
-# Place your configuration directives here. They will override those in
-# earlier files.
-#
-# See /usr/share/doc/amavisd-new/ for documentation and examples of
-# the directives you can use in this file
-#
-
-# $max_servers: num of pre-forked children (2..30 is common). It *must*
-# match the number set in /etc/postfix/master.cf "maxproc" column for
-# the amavisfeed service.
-$max_servers = 2;
-
-# list your internal networks
-@mynetworks = qw( 127.0.0.0/8 172.16.0.1/32 );
-
-
-# Always deliver messages (force *_lovers_maps to [1])
-$final_virus_destiny = D_PASS;
-$final_banned_destiny = D_PASS;
-$final_unchecked_destiny = D_PASS;
-$final_spam_destiny = D_PASS;
-$final_bad_header_destiny = D_PASS;
-$final_destiny_by_ccat{&CC_OVERSIZED} = D_PASS;
-
-%lovers_maps_by_ccat = (
- CC_CATCHALL, 1,
-);
-
-
-# Disable quarantine (force *_quarantine_to_maps to [1]; don't forget to
-# disable setting amavisSpamQuarantineCutoffLevel and amavisVirusQuarantine*To,
-# also)
-$QUARANTINEDIR = undef;
-%quarantine_method_by_ccat = (
- CC_CATCHALL, undef,
-);
-%admin_maps_by_ccat = (
- CC_CATCHALL, undef,
-);
-
-undef $undecipherable_subject_tag;
-
-# Defang virus only
-%defang_maps_by_ccat = (
- CC_VIRUS, 1,
- CC_CATCHALL, undef,
-);
-
-# Never BCC / DSN; don't forget to disallow setting amavisSpamDsnCutoffLevel
-# and amavis*Admin, also
-%always_bcc_by_ccat = (
- CC_CATCHALL, undef,
-);
-%dsn_bcc_by_ccat = (
- CC_CATCHALL, undef,
-);
-
-# Never warn sender / recipient; don't forget to disallow setting
-# amavisWarn*Recip, also
-%warnsender_by_ccat = ( # deprecated use, except perhaps for CC_BADH
- CC_CATCHALL, undef,
-);
-%warnrecip_maps_by_ccat = (
- CC_CATCHALL, undef,
-);
-
-@message_size_limit_maps = (); # per-recipient limits
-
-
-%banned_rules = (
- 'NO-MS-EXEC'=> new_RE( qr'^\.exe-ms$' ),
- 'PASSALL' => new_RE( [qr'^' => 0] ),
- 'ALLOW_EXE' => new_RE( qr'.\.(vbs|pif|scr|bat)$'i, [qr'^\.exe$' => 0] ),
- 'ALLOW_VBS' => new_RE( [qr'.\.vbs$' => 0] ),
-);
-
-
-$enable_ldap = 1;
-$default_ldap = {
- hostname => 'ldapi://',
- sasl => 1,
- sasl_mech => 'EXTERNAL',
- deref => 'never',
- timeout => 5,
- scope => 'one',
- base => 'fvd=%d,ou=virtual,o=mailHosting,dc=fripost,dc=org',
- # XXX: ideally we would use %u in the base and the query_filter, but
- # it's not supported as of amavis 2.7 (see the 'lookup_ldap'
- # subroutine in /usr/sbin/amavisd-new)
- query_filter => '(&(objectClass=amavisAccount)(ObjectClass=FripostVirtualUser)(fvl=%m))'
-};
-
-
-$recipient_delimiter = '+';
-$enable_dkim_verification = 1; # enable DKIM signatures verification
-
-
-# Per-recipient Bayes Database.
-@sa_username_maps = (
- new_RE ( [ qr'^(.+@[^@]+)$'i => '$1' ] ),
- 'amavis' # catch-all
-);
-
-# http://www.ijs.si/software/amavisd/amavisd-new-docs.html#pbanks-ex
-
-$inet_socket_port = 10041;
-
-$interface_policy{'10041'} = 'INBOUND';
-
-{% if 'out' in group_names %}
-$notify_method = 'smtp:[127.0.0.1]:{{ postfix_instance.out.port }}';
-{% else %}
-$notify_method = 'smtp:[outgoing.fripost.org]:{{ postfix_instance.out.port }}';
-{% endif %}
-$forward_method = 'lmtp:/var/run/dovecot/lmtp';
-$requeue_method = $forward_method;
-
-$sa_tag_level_deflt = undef;
-$sa_tag2_level_deflt = 5;
-$sa_kill_level_deflt = 5;
-$sa_dsn_cutoff_level = undef;
-$sa_quarantine_cutoff_level = undef;
-
-$policy_bank{'INBOUND'} = {
- originating => 0, # indicates a remote client, allows checking
- smtpd_greeting_banner =>
- '${helo-name} ${protocol} ${product} INBOUND service ready',
- mynetworks_maps => [], # avoids loading MYNETS policy unnecessarily
-};
-
-#------------ Do not modify anything below this line -------------
-1; # ensure a defined return
diff --git a/roles/amavis/handlers/main.yml b/roles/amavis/handlers/main.yml
new file mode 100644
index 0000000..65287e3
--- /dev/null
+++ b/roles/amavis/handlers/main.yml
@@ -0,0 +1,10 @@
+---
+- name: Restart ClamAV
+ service: name=clamav-daemon state=restarted
+
+- name: Publish the public key in the DNS zone
+ # See the output of 'sudo genkeypair.sh dkim --privkey=/var/lib/dkim/outgoing.fripost.org.key'
+ fail: "msg={{ dkim.stdout }}"
+
+- name: Restart Amavis
+ service: name=amavis state=restarted
diff --git a/roles/IMAP/tasks/amavis.yml b/roles/amavis/tasks/main.yml
index 6f47328..354ade8 100644
--- a/roles/IMAP/tasks/amavis.yml
+++ b/roles/amavis/tasks/main.yml
@@ -1,56 +1,62 @@
- name: Install amavis and its decoders
apt: pkg={{ item }}
with_items:
- amavisd-new
- - libnet-ldap-perl
- - libauthen-sasl-perl
+ # Mail::DKIM
+ - libmail-dkim-perl
- gzip
- bzip2
- xz-utils
- lzop
- rpm2cpio
- pax
- binutils
- p7zip-full
- unrar-free
- arj
- nomarch
- zoo
- ripole
- cabextract
- unar
- tnef
notify:
- Restart Amavis
- name: Add 'clamav' to the group 'amavis'
user: name=clamav groups=amavis append=yes
register: r1
notify:
- Restart ClamAV
- Restart Amavis
-- name: Configure Amavis (1)
- copy: src=etc/amavis/conf.d/05-domain_id
- dest=/etc/amavis/conf.d/05-domain_id
+- name: Create directory /var/lib/dkim
+ file: path=/var/lib/dkim
+ state=directory
owner=root group=root
- mode=0644
- register: r2
+ mode=0755
+
+- name: Generate a private key for DKIM signing
+ command: genkeypair.sh dkim --privkey=/var/lib/dkim/outgoing.fripost.org.key --dns=outgoing -t rsa -b 2048
+ register: dkim
+ changed_when: dkim.rc == 0
+ failed_when: dkim.rc > 1
notify:
- Restart Amavis
+ - Publish the public key in the DNS zone
+ tags:
+ - genkey
-- name: Configure Amavis (2)
- template: src=etc/amavis/conf.d/{{ item }}.j2
- dest=/etc/amavis/conf.d/{{ item }}
+- name: Configure Amavis
+ template: src=etc/amavis/conf.d/50-user.j2
+ dest=/etc/amavis/conf.d/50-user
owner=root group=root
mode=0644
register: r3
- with_items:
- - 15-content_filter_mode
- - 50-user
notify:
- Restart Amavis
+- meta: flush_handlers
+
- name: Start Amavis
service: name=amavis state=started
- when: not (r1.changed or r2.changed or r3.changed)
diff --git a/roles/amavis/templates/etc/amavis/conf.d/50-user.j2 b/roles/amavis/templates/etc/amavis/conf.d/50-user.j2
new file mode 100644
index 0000000..adafd7f
--- /dev/null
+++ b/roles/amavis/templates/etc/amavis/conf.d/50-user.j2
@@ -0,0 +1,182 @@
+use strict;
+
+#
+# Place your configuration directives here. They will override those in
+# earlier files.
+#
+# See /usr/share/doc/amavisd-new/ for documentation and examples of
+# the directives you can use in this file
+#
+
+# $max_servers: num of pre-forked children (2..30 is common). It *must*
+# match the number set in /etc/postfix/master.cf "maxproc" column for
+# the amavisfeed service.
+$max_servers = 5;
+$recipient_delimiter = '+';
+
+$mydomain = 'fripost.org';
+$X_HEADER_LINE = "Debian $myproduct_name at $mydomain";
+undef $undecipherable_subject_tag;
+
+@mynetworks_maps = ();
+@remove_existing_spam_headers_maps = ();
+@bypass_virus_checks_maps = (); # load virus checking code
+
+
+$enable_dkim_verification = 1; # load DKIM signing/verifying code
+{% if 'out' not in group_names %}
+undef $enable_dkim_signing;
+@bypass_spam_checks_maps = (); # load spam checking code
+{% else %}
+$enable_dkim_signing = 1;
+# Sign *all* outgoing mails with *our* key (yes, amavis complains, but this is
+# safe as we force our domain with the 'd' tag).
+dkim_key(qr'^', 'outgoing', '/var/lib/dkim/outgoing.'.$mydomain.'.key');
+@dkim_signature_options_bysender_maps = (
+ { '.' => { d => $mydomain
+ , a => 'rsa-sha256'
+ , ttl => 21*24*3600
+ , c => 'relaxed/simple' } } );
+# Conform to RFC 4871 and don't sign Received: headers.
+$signed_header_fields{received} = 0;
+{% endif %}
+
+
+
+# Defang viruses only
+%defang_maps_by_ccat = ( CC_VIRUS, 1
+ , CC_CATCHALL, undef
+ );
+
+# Never BCC / DSN; don't forget to disallow setting amavisSpamDsnCutoffLevel
+# and amavis*Admin, also
+%always_bcc_by_ccat = ( CC_CATCHALL, undef );
+%dsn_bcc_by_ccat = ( CC_CATCHALL, undef );
+
+# Never warn sender or recipient; don't forget to disallow setting
+# amavisWarn*Recip, also
+%warnsender_by_ccat = ( CC_CATCHALL, undef );
+%warnrecip_maps_by_ccat = ( CC_CATCHALL, undef );
+
+
+# A couple of common banned rules one might can refer by their name
+%banned_rules = (
+ 'NO-MS-EXEC'=> new_RE( qr'^\.exe-ms$' ),
+ 'PASSALL' => new_RE( [qr'^' => 0] ),
+ 'ALLOW_EXE' => new_RE( qr'.\.(vbs|pif|scr|bat)$'i, [qr'^\.exe$' => 0] ),
+ 'ALLOW_VBS' => new_RE( [qr'.\.vbs$' => 0] ),
+);
+
+
+{% if 'MDA' in group_names %}
+$enable_ldap = 1; # Load Net::LDAP
+$default_ldap = {
+ hostname => 'ldapi://',
+ sasl => 1,
+ sasl_mech => 'EXTERNAL',
+ deref => 'never',
+ timeout => 5,
+ scope => 'one',
+ base => 'fvd=%d,ou=virtual,o=mailHosting,dc=fripost,dc=org',
+ # XXX: ideally we would use %u in the base and the query_filter, but
+ # it's not supported as of amavis 2.7 (see the 'lookup_ldap'
+ # subroutine in /usr/sbin/amavisd-new)
+ query_filter => '(&(objectClass=amavisAccount)(ObjectClass=FripostVirtualUser)(fvl=%m))'
+};
+{% endif %}
+
+
+# http://www.ijs.si/software/amavisd/amavisd-new-docs.html#pbanks-ex
+
+$protocol = 'LMTP';
+$inet_socket_port = [];
+
+{% if 'out' in group_names %}
+push @$inet_socket_port, 10040;
+$interface_policy{'10040'} = 'OUTGOING';
+{% endif %}
+{% if 'MDA' in group_names %}
+push @$inet_socket_port, 10041;
+$interface_policy{'10041'} = 'INCOMING';
+{% endif %}
+
+$QUARANTINEDIR = "$MYHOME/virusmails";
+$notify_method = 'smtp:[127.0.0.1]:16132'; # notifications
+$forward_method = 'smtp:[127.0.0.1]:10025'; # reinject
+$requeue_method = $notify_method; # requeue after quarantine
+
+# some defaults for spam checking
+$sa_tag_level_deflt = undef;
+$sa_tag2_level_deflt = 5;
+$sa_kill_level_deflt = 5;
+$sa_dsn_cutoff_level = undef;
+$sa_quarantine_cutoff_level = undef;
+
+
+# Here is an overall picture (sequence of events) of how pieces fit together
+#
+# bypass_virus_checks set for all recipients? ==> PASS
+# no viruses? ==> PASS
+# log virus if $log_templ is nonempty
+# quarantine if $virus_quarantine_to is nonempty
+# notify admin if $virus_admin (lookup) nonempty
+# notify recips if $warnvirusrecip and (recipient is local or $warn_offsite)
+# add address extensions for local recipients (when enabled)
+# send (non-)delivery notifications
+# to sender if DSN needed (BOUNCE or ($warnvirussender and D_PASS))
+# virus_lovers or final_destiny==D_PASS ==> PASS
+# DISCARD (2xx) or REJECT (5xx) (depending on final_*_destiny)
+
+
+# Mandatory DKIM signing and virus checking only
+$policy_bank{'OUTGOING'} = {
+ originating => 1,
+ enable_dkim_verification => 0,
+ smtpd_greeting_banner => '${helo-name} ${protocol} ${product} OUTGOING service ready',
+ forward_method => $forward_method,
+
+ # No black or white lists
+ message_size_limit_maps => [],
+ whitelist_sender_maps => [],
+ blacklist_sender_maps => [],
+
+ # Check for viruses (regardless of the recipient), but bypass all other checks
+ bypass_virus_checks_maps => undef,
+ bypass_banned_checks_maps => 1,
+ bypass_header_checks_maps => 1,
+ bypass_spam_checks_maps => 1,
+
+ # If found, notify postmaster, quarantine and discard
+ quarantine_to_maps_by_ccat => { CC_VIRUS, [$virus_quarantine_to], CC_CATCHALL, undef },
+ quarantine_method_by_ccat => { CC_VIRUS, [$virus_quarantine_method], CC_CATCHALL, undef },
+ admin_maps_by_ccat => { CC_VIRUS, ["postmaster\@$mydomain"], CC_CATCHALL, undef },
+ addr_extension_maps_by_ccat=> { CC_CATCHALL, undef },
+ lovers_maps_by_ccat => { CC_VIRUS, undef, CC_CATCHALL, 1 },
+ final_destiny_by_ccat => { CC_VIRUS, D_DISCARD, CC_CATCHALL, D_PASS },
+};
+
+$policy_bank{'INCOMING'} = {
+ originating => 0,
+ enable_dkim_verification => 1,
+ smtpd_greeting_banner => '${helo-name} ${protocol} ${product} INCOMING service ready',
+ forward_method => $forward_method,
+ message_size_limit_maps => [],
+
+ # Per-recipient Bayes Database
+ sa_username_maps => [ new_RE ( [ qr'^(.+@.+)$'i => '$1' ] )
+ , 'amavis' # catch-all
+ ],
+
+ # Never quarantine
+ # (Remember to disallow setting amavisSpamQuarantineCutoffLevel and
+ # amavisVirusQuarantine*To in the LDAP schema.)
+ quarantine_method_by_ccat => { CC_CATCHALL, undef },
+ admin_maps_by_ccat => { CC_CATCHALL, undef },
+
+ # Always deliver messages
+ final_destiny_by_ccat => { CC_CATCHALL, D_PASS },
+ lovers_maps_by_ccat => { CC_CATCHALL, 1 },
+};
+
+#------------ Do not modify anything below this line -------------
+1; # ensure a defined return
diff --git a/roles/common/files/etc/logcheck/ignore.d.server/postfix-local b/roles/common/files/etc/logcheck/ignore.d.server/postfix-local
index e153ce0..d3b7fae 100644
--- a/roles/common/files/etc/logcheck/ignore.d.server/postfix-local
+++ b/roles/common/files/etc/logcheck/ignore.d.server/postfix-local
@@ -16,20 +16,22 @@
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/qmgr\[[[:digit:]]+\]: [[:alnum:]]+: from=<[^[:space:]]*>, size=[[:digit:]]+, nrcpt=[[:digit:]]+ \(queue active\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:alnum:]]+: from=<.*>, status=expired, returned to sender$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:alnum:]]+: message-id=(<?[^[:space:]]+>?)?( \(added by [^[:space:]]+\))?
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:alnum:]]+: removed$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix(-\w+)?/n?qmgr\[[[:digit:]]+\]: [[:alnum:]]+: skipped, still being delivered$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: lost connection after AUTH from [._[:alnum:]-]+\[[[:digit:].]{7,15}\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: lost connection after (CONNECT|STARTTLS) from [._[:alnum:]-]+\[[[:digit:].]{7,15}\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/cleanup\[[[:digit:]]+\]: [[:xdigit:]]{10}: replace: header\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: [[:xdigit:]]{10}: client=[^[:space:]]+, sasl_method=[-[:alnum:]]+, sasl_username=[-_.@[:alnum:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: improper command pipelining after EHLO from [._[:alnum:]-]+\[[[:digit:].]{7,15}\]:\s
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: warning: hostname [._[:alnum:]-]+ does not resolve to address [[:xdigit:].:]{3,39}: Name or service not known$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|mx)/smtpd\[[[:digit:]]+\]: timeout after [-[:upper:]]+( \([[:digit:]]+ bytes\))? from [^[:space:]]+$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-((msa|mx)/smtpd|out/smtp)\[[[:digit:]]+\]: warning: (tls_text_name: [-._[:alnum:]]+\[[.[:digit:]]+\]: )?peer certificate has no (subject CN|issuer Organization)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mda/lmtp\[[[:digit:]]+\]: [[:xdigit:]]{10}: to=<[^[:space:]]+>,( orig_to=<[^[:space:]]+>,)? relay=[._[:alnum:]-]+\[private/dovecot-lmtpd\],( conn_use=[[:digit:]]+,)? delay=[.[:digit:]]+(, delays=([.[:digit:]]+/){3}[.[:digit:]]+)?(, dsn=2(\.[[:digit:]]+){2})?, status=sent \(2[[:digit:]][[:digit:]] .+\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/(error|n?qmgr|smtp)\[[[:digit:]]+\]: [[:alnum:]]+: to=<[^[:space:]]+>,( orig_to=<[^[:space:]]+>,)? relay=(none|[^[:space:]]+\[[[:digit:].]{7,15}\]:(25|587)),( conn_use=[[:digit:]]+,)? delay=[[:digit:].]+,( delays=[[:digit:]./]+,)?( dsn=[45]\.[[:digit:]]\.[[:digit:]],)? status=(deferred|undeliverable) \((delivery temporarily suspended: )?((lost connection with [^[:space:]]+|conversation with [^[:space:]]+ timed out) while (sending [[:alnum:]]+( [[:alnum:]]+)?|performing the (HELO|EHLO) handshake|receiving the initial server greeting|sending [[:alnum:]]+( [/[:alnum:]]+)?|sending end of data -- message may be sent more than once)|connect to [^[:space:]]+: (Connection timed out|read timeout|Connection refused)|Host or domain name not found. Name service errorfor name=[^[:space:]]+ type=MX: Host not found, try again|User unknown in virtual alias table)\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: connect to [^[:space:]]+: (read timeout|Connection (refused|timed out)|Network is unreachable|No route to host)( \(port [[:digit:]]+\))?$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/[ls]mtp\[[[:digit:]]+\]: [[:upper:][:digit:]]+: to=<[^[:space:]]+>, relay=[._[:alnum:]-]+\[[[:digit:].]{7,15}\](:[[:digit:]]{1,5})?, (conn_use=[[:digit:]]+, )?delay=[.[:digit:]]+(, delays=([.[:digit:]]+/){3}[.[:digit:]]+)?(, dsn=[45](\.[[:digit:]]+){2})?, status=(deferred|bounced|undeliverable) \(host [._[:alnum:]-]+\[[[:digit:].]{7,15}\] said: [45][[:digit:]][[:digit:]] .+ \(in reply to (HELO|EHLO|MAIL FROM|RCPT TO|end of DATA) command\)\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(mda|out)/smtpd\[[[:digit:]]+\]: [[:xdigit:]]{10}: client=[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\], orig_queue_id=[[:xdigit:]]{10}, orig_client=[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [A-Z[:digit:]]+: to=<[^[:space:]]+>,( orig_to=<[^[:space:]]+>,) relay=[^[:space:]]+, delay=[[:digit:]]+, status=deferred \(host [^[:space:]]+ said: [45][[:digit:]]{2} <[^[:space:]]*>: Recipient address rejected: Greylisted for [[:digit:]]+ (seconds|minutes)(\(see http://isg.ee.ethz.ch/tools/postgrey/help/[.[:alnum:]-]+.html\))? \(in reply to (HELO|EHLO|MAIL FROM|RCPT TO|DATA|end of DATA) command\)\)$
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-out/smtp\[[[:digit:]]+\]: [[:alnum:]]+: to=<.*>,( orig_to=<[^[:space:]]+>,)? relay=[^[:space:]]+\](:[[:digit:]]+)?,( conn_use=[[:digit:]]+,)? delay=[[:digit:].]+,( delays=[[:digit:]./]+,)?( dsn=4\.[[:digit:]]\.[[:digit:]],)? status=deferred \(host [^[:space:]]+\] said: .*$
+#
+^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) Passed CLEAN {RelayedOutbound}, OUTGOING LOCAL \[(IPv6:)?[[:xdigit:].:]{3,39}\]:[[:digit:]]+( \[[[:xdigit:].:]{3,39}\])? <[^>]*> -> <[^>]*>(,<[^>]*>)*,( Queue-ID: [[:xdigit:]]+,)?( Message-ID: <[^>]+>,)? mail_id: [_-+[:alnum:]]+, Hits: -, size: [[:digit:]]+, queued_as: [[:xdigit:]]+, dkim_new=[-.:[:alnum:]]+, [[:digit:]]+ ms$
diff --git a/roles/common/files/etc/postfix/master.cf b/roles/common/files/etc/postfix/master.cf
index 70f7f4e..02e1658 100644
--- a/roles/common/files/etc/postfix/master.cf
+++ b/roles/common/files/etc/postfix/master.cf
@@ -28,24 +28,49 @@ smtpl unix - - - - - smtp
relay unix - - - - - smtp
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq unix n - - - - showq
error unix - - - - - error
retry unix - - - - - error
discard unix - - - - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - - - - lmtp
anvil unix - - - - 1 anvil
scache unix - - - - 1 scache
127.0.0.1:16132 inet n - - - - smtpd
2525 inet n - - - - smtpd
2526 inet n - - - - smtpd
2527 inet n - - - - smtpd
127.0.0.1:2580 inet n - - - - smtpd
reserved-alias unix - n n - - pipe
flags=Rhu user=nobody argv=/usr/local/sbin/reserved-alias.pl ${sender} ${original_recipient} @fripost.org
mlmmj unix - n n - - pipe
flags=Rhu user=mlmmj argv=/usr/bin/mlmmj-receive -L /var/spool/mlmmj/${domain}/${user}
-amavisfeed unix - - n - 2 lmtp
+
+# Client part (lmtp) - amavis
+amavisfeed unix - - n - 5 lmtp
-o lmtp_destination_recipient_limit=1000
-o lmtp_send_xforward_command=yes
-o lmtp_data_done_timeout=1200s
+ -o disable_dns_lookups=yes
+
+# Server part (smtpd) - amavis (if the MDA and outgoing proxy are on the
+# same machine, we need to create another entry, on another port.)
+127.0.0.1:10025 inet n - n - - smtpd
+ -o content_filter=
+ -o smtpd_delay_reject=no
+ -o smtpd_client_restrictions=permit_mynetworks,reject
+ -o smtpd_helo_restrictions=
+ -o smtpd_sender_restrictions=
+ -o smtpd_recipient_restrictions=permit_mynetworks,reject
+ -o smtpd_data_restrictions=reject_unauth_pipelining
+ -o smtpd_end_of_data_restrictions=
+ -o smtpd_restriction_classes=
+ -o mynetworks_style=host
+ -o smtpd_error_sleep_time=0
+ -o smtpd_soft_error_limit=1001
+ -o smtpd_hard_error_limit=1000
+ -o smtpd_client_connection_count_limit=0
+ -o smtpd_client_connection_rate_limit=0
+ -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
+ -o local_header_rewrite_clients=
+ -o smtpd_authorized_xforward_hosts=127.0.0.0/8
diff --git a/roles/common/files/usr/local/bin/genkeypair.sh b/roles/common/files/usr/local/bin/genkeypair.sh
index 16f9658..c5dfb30 100755
--- a/roles/common/files/usr/local/bin/genkeypair.sh
+++ b/roles/common/files/usr/local/bin/genkeypair.sh
@@ -23,41 +23,41 @@ set -ue
PATH=/usr/bin:/bin
# Default values
type=rsa
bits=
hash=
force=
config=
pubkey=pubkey.pem
privkey=privkey.pem
dns=
usage() {
cat >&2 <<- EOF
Usage: $0 command [OPTIONS]
Command:
x509: generate a self-signed X.509 server certificate
csr: generate a Certificate Signing Request
- dkim: generate a DKIM private key
+ dkim: generate a private key (to use for DKIM signing)
Options:
-t type: key type (default: rsa)
-b bits: key length or EC curve (default: 2048 for RSA, 1024 for DSA, secp224r1 for ECDSA)
-h digest: digest algorithm
--dns CN: common name (default: \$(hostname --fqdn); can be repeated
-f force: overwrite key files if they exist
--config: configuration file
--pubkey: public key file (default: pubkey.pem)
--privkey: private key file (default: privkey.pem; created with og-rwx)
Return values:
0 The key pair was successfully generated
1 The public or private key file exists, and -f is not set
2 The key generation failed
EOF
}
[ $# -gt 0 ] || { usage; exit 2; }
cmd="$1"; shift
@@ -152,31 +152,31 @@ if [ -z "$config" -a \( "$cmd" = x509 -o "$cmd" = csr \) ]; then
organizationName = Fripost
commonName = $cn
[ v3_req ]
subjectAltName = email:admin@fripost.org, DNS:$cn$names
basicConstraints = critical, CA:FALSE
EOF
fi
if [ "$force" != 0 ]; then
# Ensure "$privkey" is created with umask 0077
mv "$(mktemp)" "$privkey" || exit 2
chmod og-rwx "$privkey" || exit 2
openssl $genkey -rand /dev/urandom $genkeyargs >"$privkey" || exit 2
fi
if [ "$cmd" = x509 -o "$cmd" = csr ]; then
[ "$cmd" = x509 ] && x509=-x509 || x509=
openssl req -config "$config" -new $x509 ${hash:+-$hash} -key "$privkey" >"$pubkey" || exit 2
elif [ "$cmd" = dkim ]; then
- echo "Add the following TXT record to your DNS zone:" >&2
+ echo "Add the following TXT record to your DNS zone:"
echo "${dns:-$(date +%Y%m%d)}._domainkey\tIN\tTXT ( "
# See https://tools.ietf.org/html/rfc4871#section-3.6.1
# t=s: the "i=" domain in signature headers MUST NOT be a subdomain of "d="
# s=email: limit DKIM signing to email
openssl pkey -pubout <"$privkey" | sed '/^--.*--$/d' \
| { echo -n "v=DKIM1; k=$type; t=s; s=email; p="; tr -d '\n'; } \
| fold -w 250 \
| { sed 's/.*/\t"&"/'; echo ' )'; }
[ "$force" != 0 ] || exit 1
fi
diff --git a/roles/out/templates/etc/postfix/main.cf.j2 b/roles/out/templates/etc/postfix/main.cf.j2
index 11bcc10..9bf5882 100644
--- a/roles/out/templates/etc/postfix/main.cf.j2
+++ b/roles/out/templates/etc/postfix/main.cf.j2
@@ -2,41 +2,41 @@
# Outgoing MTA (outgoing SMTP proxy) configuration
#
# {{ ansible_managed }}
# Do NOT edit this file directly!
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
readme_directory = no
mail_owner = postfix
delay_warning_time = 1d
maximal_queue_lifetime = 5d
myorigin = /etc/mailname
myhostname = outgoing{{ outgoingno | default('') }}.$mydomain
mydomain = fripost.org
append_dot_mydomain = no
# Turn off all TCP/IP listener ports except that necessary for the
# outgoing SMTP proxy.
-master_service_disable = !{{ postfix_instance.out.port }}.inet inet
+master_service_disable = !{{ postfix_instance.out.port }}.inet !127.0.0.1:10025.inet inet
queue_directory = /var/spool/postfix-{{ postfix_instance[inst].name }}
data_directory = /var/lib/postfix-{{ postfix_instance[inst].name }}
multi_instance_group = {{ postfix_instance[inst].group | default('') }}
multi_instance_name = postfix-{{ postfix_instance[inst].name }}
multi_instance_enable = yes
mynetworks_style = host
inet_interfaces = all
# No local delivery
mydestination =
local_transport = error:5.1.1 Mailbox unavailable
alias_maps =
alias_database =
local_recipient_maps =
message_size_limit = 67108864
recipient_delimiter = +
@@ -74,20 +74,22 @@ smtpd_client_restrictions =
defer
smtpd_helo_required = yes
smtpd_helo_restrictions =
reject_invalid_helo_hostname
smtpd_sender_restrictions =
reject_non_fqdn_sender
reject_unknown_sender_domain
smtpd_recipient_restrictions =
# RFC requirements
reject_non_fqdn_recipient
reject_unknown_recipient_domain
permit_mynetworks
permit_tls_clientcerts
reject
smtpd_data_restrictions =
reject_unauth_pipelining
+
+content_filter = amavisfeed:[127.0.0.1]:10040