summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--group_vars/all.yml1
-rw-r--r--roles/common/files/etc/logcheck/ignore.d.server/postfix-local6
-rw-r--r--roles/common/files/etc/postfix/master.cf1
-rw-r--r--roles/webmail/handlers/main.yml4
-rw-r--r--roles/webmail/tasks/mail.yml42
-rw-r--r--roles/webmail/tasks/main.yml1
-rw-r--r--roles/webmail/tasks/roundcube.yml2
-rw-r--r--roles/webmail/templates/etc/postfix/main.cf.j2107
-rw-r--r--roles/webmail/templates/etc/stunnel/postfix.conf.j255
-rw-r--r--webmail.yml2
10 files changed, 96 insertions, 125 deletions
diff --git a/group_vars/all.yml b/group_vars/all.yml
index ef9fc82..25356bf 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -7,5 +7,4 @@ postfix_instance:
MX: { name: mx, group: mta }
out: { name: out, group: mta, port: 2525 }
MSA: { name: msa }
- webmail: { name: webmail }
lists: { name: lists, port: 2527 }
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
diff --git a/webmail.yml b/webmail.yml
index d8a6a1e..0ef0487 100644
--- a/webmail.yml
+++ b/webmail.yml
@@ -2,4 +2,4 @@
- name: Configure the webmail
hosts: webmail
roles:
- - { role: webmail, inst: webmail }
+ - webmail