From 2c925ea17fcb6f71826e5c0f30f99c5daba10e14 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 4 Jun 2015 20:26:53 +0200 Subject: Make the webmail connect directly to the outgoing SMTP proxy. (Hence delete the 'webmail' Postfix instance.) This shortens the delay caused by the recipient verification probes. --- .../etc/logcheck/ignore.d.server/postfix-local | 6 +- roles/common/files/etc/postfix/master.cf | 1 - roles/webmail/handlers/main.yml | 4 +- roles/webmail/tasks/mail.yml | 42 ++++++-- roles/webmail/tasks/main.yml | 1 + roles/webmail/tasks/roundcube.yml | 2 +- roles/webmail/templates/etc/postfix/main.cf.j2 | 107 --------------------- .../webmail/templates/etc/stunnel/postfix.conf.j2 | 55 +++++++++++ 8 files changed, 95 insertions(+), 123 deletions(-) delete mode 100644 roles/webmail/templates/etc/postfix/main.cf.j2 create mode 100644 roles/webmail/templates/etc/stunnel/postfix.conf.j2 (limited to 'roles') 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 fd6cbcb..45327b1 100644 --- a/roles/common/files/etc/logcheck/ignore.d.server/postfix-local +++ b/roles/common/files/etc/logcheck/ignore.d.server/postfix-local @@ -31,9 +31,9 @@ ^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-mx/smtpd\[[[:digit:]]+\]: warning: Connection concurrency limit exceeded: [0-9]+ from [._[:alnum:]-]+\[[[:digit:].]{7,15}\] for service smtpd$ ^\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:]]+\[[[:xdigit:].:]{3,39}\]: )?peer certificate has no (subject CN|issuer Organization)$ -^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|webmail)/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 450( 4\.1\.2)? <[^>]*>: Recipient address rejected: Domain not found;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ -^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|webmail)/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 450( 4\.1\.8)? <[^>]*>: Sender address rejected: Domain not found;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ -^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-(msa|webmail)/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 504( 5\.5\.2)? <[^>]*>: (Recipient|Sender) address rejected: need fully-qualified address;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ +^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 450( 4\.1\.2)? <[^>]*>: Recipient address rejected: Domain not found;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ +^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 450( 4\.1\.8)? <[^>]*>: Sender address rejected: Domain not found;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ +^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: (NOQUEUE|[[:xdigit:]]+): reject: [[:upper:]]+ from [^[:space:]]+: 504( 5\.5\.2)? <[^>]*>: (Recipient|Sender) address rejected: need fully-qualified address;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ ^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ postfix-msa/smtpd\[[[:digit:]]+\]: NOQUEUE: reject: RCPT from [._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]: 554 5\.7\.1 <[._[:alnum:]-]+\[[[:xdigit:].:]{3,39}\]>: Client host rejected: Access denied;( from=<[^>]*> to=<[^[:space:]]+>)? proto=E?SMTP( helo=<[^[:space:]]+>)?$ ^\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-\w+/(error|n?qmgr|smtp)\[[[:digit:]]+\]: [[:alnum:]]+: to=<[^[:space:]]+>,( orig_to=<[^[:space:]]+>,)? relay=(none|[^[:space:]]+\[[[:digit:].]{7,15}\]:[[:digit:]]+),( 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 [^[:space:]]+ refused to talk to me: [45][[:digit:]][[:digit:]].*|Host or domain name not found. Name service errorfor name=[^[:space:]]+ type=MX: Host not found, try again|User unknown in virtual alias table|host [^[:space:]]+\[[[:digit:].]{7,15}\] said: [45][[:digit:]][[:digit:]] [45](\.[[:digit:]]+){2} <[^>]+>: (Temporarily rejected\. Try again later\.|Recipient address rejected: (undeliverable|unverified) address:) .*)\)$ diff --git a/roles/common/files/etc/postfix/master.cf b/roles/common/files/etc/postfix/master.cf index 36c016b..9b81c70 100644 --- a/roles/common/files/etc/postfix/master.cf +++ b/roles/common/files/etc/postfix/master.cf @@ -45,7 +45,6 @@ scache unix - - - - 1 scache 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/bin/reserved-alias.pl ${sender} ${original_recipient} @fripost.org sympa unix - n n - - pipe diff --git a/roles/webmail/handlers/main.yml b/roles/webmail/handlers/main.yml index 76084e4..f7e403e 100644 --- a/roles/webmail/handlers/main.yml +++ b/roles/webmail/handlers/main.yml @@ -1,6 +1,6 @@ --- -- name: Reload Postfix - service: name=postfix state=reloaded +- name: Restart stunnel + service: name=stunnel4 pattern=/usr/bin/stunnel4 state=restarted - name: Restart Nginx service: name=nginx state=restarted diff --git a/roles/webmail/tasks/mail.yml b/roles/webmail/tasks/mail.yml index e2dea38..7603a56 100644 --- a/roles/webmail/tasks/mail.yml +++ b/roles/webmail/tasks/mail.yml @@ -1,15 +1,39 @@ -- name: Install Postfix - apt: pkg=postfix +- name: Install stunnel + apt: pkg=stunnel4 -- name: Configure Postfix - template: src=etc/postfix/main.cf.j2 - dest=/etc/postfix-{{ postfix_instance[inst].name }}/main.cf +- name: Auto-enable stunnel + lineinfile: dest=/etc/default/stunnel4 + regexp='^(\s*#)?\s*ENABLED=' + line='ENABLED=1' + owner=root group=root + mode=0644 + +- name: Create /etc/stunnel/certs + file: path=/etc/stunnel/certs + state=directory + owner=root group=root + mode=0755 + +- name: Copy the SMTP outgoing proxy's X.509 certificate + assemble: src=certs/postfix regexp="{{ groups.out | difference([inventory_hostname]) | join('|') }}\.pem$" remote_src=no + dest=/etc/stunnel/certs/postfix.pem owner=root group=root mode=0644 + register: r1 notify: - - Reload Postfix + - Restart stunnel -- meta: flush_handlers +- name: Configure stunnel + template: src=etc/stunnel/postfix.conf.j2 + dest=/etc/stunnel/postfix.conf + owner=root group=root + mode=0644 + register: r2 + notify: + - Restart stunnel -- name: Start Postfix - service: name=postfix state=started +- name: Start stunnel + service: name=stunnel4 pattern=/usr/bin/stunnel4 state=started + when: not (r1.changed or r2.changed) + +- meta: flush_handlers diff --git a/roles/webmail/tasks/main.yml b/roles/webmail/tasks/main.yml index a6eeee2..030a547 100644 --- a/roles/webmail/tasks/main.yml +++ b/roles/webmail/tasks/main.yml @@ -1,2 +1,3 @@ - include: mail.yml tags=postfix,mail + when: "'out' not in group_names" - include: roundcube.yml tags=roundcube,webmail diff --git a/roles/webmail/tasks/roundcube.yml b/roles/webmail/tasks/roundcube.yml index ebe93c8..5392242 100644 --- a/roles/webmail/tasks/roundcube.yml +++ b/roles/webmail/tasks/roundcube.yml @@ -36,7 +36,7 @@ - { var: messages_cache, value: "null" } # SMTP - { var: smtp_server, value: "'localhost'" } - - { var: smtp_port, value: "2580" } + - { var: smtp_port, value: "2525" } # System - { var: force_https, value: "TRUE" } - { var: login_autocomplete, value: "2" } diff --git a/roles/webmail/templates/etc/postfix/main.cf.j2 b/roles/webmail/templates/etc/postfix/main.cf.j2 deleted file mode 100644 index f4079d6..0000000 --- a/roles/webmail/templates/etc/postfix/main.cf.j2 +++ /dev/null @@ -1,107 +0,0 @@ -######################################################################## -# Webmail 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 = 4h -maximal_queue_lifetime = 5d - -myorigin = /etc/mailname -myhostname = webmail{{ webmailno | default('') }}.$mydomain -mydomain = fripost.org -append_dot_mydomain = no - -# Turn off all TCP/IP listener ports except that necessary for the webmail. -master_service_disable = !127.0.0.1:2580.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 - -# This server is a nullclient -mynetworks_style = host -inet_interfaces = loopback-only - -# 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 = + - -# Forward everything to our internal outgoing proxy -{% if 'out' in group_names %} -relayhost = [127.0.0.1]:{{ postfix_instance.out.port }} -{% else %} -relayhost = [outgoing.fripost.org]:{{ postfix_instance.out.port }} -{% endif %} -relay_domains = - - -# Don't rewrite remote headers -local_header_rewrite_clients = -# Avoid splitting the envelope and scanning messages multiple times -smtp_destination_recipient_limit = 1000 -# Tolerate occasional high latency -smtp_data_done_timeout = 1200s - -{% if 'out' in group_names %} -smtp_tls_security_level = none -smtp_bind_address = 127.0.0.1 -{% else %} -smtp_tls_security_level = encrypt -smtp_tls_cert_file = /etc/postfix/ssl/{{ ansible_fqdn }}.pem -smtp_tls_key_file = /etc/postfix/ssl/{{ ansible_fqdn }}.key -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 - - -strict_rfc821_envelopes = yes -smtpd_delay_reject = yes -disable_vrfy_command = yes - -# UCE control -unknown_client_reject_code = 554 -unverified_recipient_reject_code = 550 - -smtpd_client_restrictions = - permit_mynetworks - reject - -smtpd_helo_required = yes -smtpd_helo_restrictions = - permit_mynetworks - reject_non_fqdn_helo_hostname - reject_invalid_helo_hostname - -smtpd_sender_restrictions = - reject_non_fqdn_sender - reject_unknown_sender_domain - -smtpd_relay_restrictions = - permit_mynetworks - reject - -smtpd_recipient_restrictions = - reject_non_fqdn_recipient - reject_unknown_recipient_domain - reject_unverified_recipient - -smtpd_data_restrictions = - reject_unauth_pipelining - -# vim: set filetype=pfmain : diff --git a/roles/webmail/templates/etc/stunnel/postfix.conf.j2 b/roles/webmail/templates/etc/stunnel/postfix.conf.j2 new file mode 100644 index 0000000..78922c8 --- /dev/null +++ b/roles/webmail/templates/etc/stunnel/postfix.conf.j2 @@ -0,0 +1,55 @@ +; ************************************************************************** +; * Global options * +; ************************************************************************** + +; setuid()/setgid() to the specified user/group in daemon mode +setuid = stunnel4 +setgid = stunnel4 + +; PID is created inside the chroot jail +pid = /var/run/stunnel4/postfix.pid + +; Only log messages at severity warning (4) and higher +debug = 4 + +; ************************************************************************** +; * Service defaults may also be specified in individual service sections * +; ************************************************************************** + +; Certificate/key is needed in server mode and optional in client mode +cert = /etc/postfix/ssl/{{ ansible_fqdn }}.pem +key = /etc/postfix/ssl/{{ ansible_fqdn }}.key +client = yes +socket = a:SO_BINDTODEVICE=lo + +; Some performance tunings +socket = l:TCP_NODELAY=1 +socket = r:TCP_NODELAY=1 + +; Prevent MITM attacks +verify = 4 + +; Disable support for insecure protocols +options = NO_SSLv2 +options = NO_SSLv3 +options = NO_TLSv1 +options = NO_TLSv1.1 + +; These options provide additional security at some performance degradation +options = SINGLE_ECDH_USE +options = SINGLE_DH_USE + +; Select permitted SSL ciphers +ciphers = EECDH+AES:EDH+AES:!MEDIUM:!LOW:!EXP:!aNULL:!eNULL:!SSLv2:!SSLv3:!TLSv1:!TLSv1.1 + +; ************************************************************************** +; * Service definitions (remove all services for inetd mode) * +; ************************************************************************** + +[smtp] +accept = localhost:2525 +connect = outgoing.fripost.org:{{ postfix_instance.out.port }} +CAfile = /etc/stunnel/certs/postfix.pem +protocol = smtp + +; vim:ft=dosini -- cgit v1.2.3