diff options
Diffstat (limited to 'roles/mailserver')
21 files changed, 1047 insertions, 0 deletions
| diff --git a/roles/mailserver/handlers/main.yml b/roles/mailserver/handlers/main.yml new file mode 100644 index 0000000..5c5caea --- /dev/null +++ b/roles/mailserver/handlers/main.yml @@ -0,0 +1,8 @@ +- name: restart postfix +  service: name=postfix state=restarted + +- name: restart dovecot +  service: name=dovecot state=restarted + +- name: restart rspamd +  service: name=rspamd state=restarted diff --git a/roles/mailserver/tasks/dovecot.yml b/roles/mailserver/tasks/dovecot.yml new file mode 100644 index 0000000..9d2c20e --- /dev/null +++ b/roles/mailserver/tasks/dovecot.yml @@ -0,0 +1,39 @@ +- name: Install Dovecot and related packages +  apt: pkg={{ item }} update_cache=yes state=installed +  with_items: +    - dovecot-core +    - dovecot-imapd +    - dovecot-lmtpd +    - dovecot-antispam +  tags: +    - dependencies + +#- name: Copy dovecot.conf into place +#  copy: src=etc_dovecot_dovecot.conf dest=/etc/dovecot/dovecot.conf + +#- name: Create before.d sieve scripts directory +#  file: path=/etc/dovecot/sieve/before.d state=directory owner=vmail group=dovecot recurse=yes mode=0770 +#  notify: restart dovecot + +#- name: Configure sieve script moving spam into Junk folder +#  copy: src=etc_dovecot_sieve_before.d_no-spam.sieve dest=/etc/dovecot/sieve/before.d/no-spam.sieve owner=vmail group=dovecot +#  notify: restart dovecot + +#- name: Copy additional Dovecot configuration files in place +#  copy: src=etc_dovecot_conf.d_{{ item }} dest=/etc/dovecot/conf.d/{{ item }} +#  with_items: +#    - 10-auth.conf +#    - 10-mail.conf +#    - 10-master.conf +#    - 90-antispam.conf +#    - 90-plugin.conf +#    - 90-sieve.conf +#  notify: restart dovecot + +#- name: Update post-certificate-renewal task +#  copy: +#    content: "#!/bin/bash\n\n/etc/init.d/dovecot restart\n" +#    dest: /etc/letsencrypt/postrenew/dovecot.sh +#    mode: 0755 +#    owner: root +#    group: root diff --git a/roles/mailserver/tasks/main.yml b/roles/mailserver/tasks/main.yml new file mode 100644 index 0000000..7691288 --- /dev/null +++ b/roles/mailserver/tasks/main.yml @@ -0,0 +1,6 @@ +- include: postfix.yml +  tags: postfix +- include: dovecot.yml +  tags: dovecot +- include: rspamd.yml +  tags: rspamd diff --git a/roles/mailserver/tasks/postfix.yml b/roles/mailserver/tasks/postfix.yml new file mode 100644 index 0000000..a36acd6 --- /dev/null +++ b/roles/mailserver/tasks/postfix.yml @@ -0,0 +1,18 @@ +- name: Install Postfix and related packages +  apt: pkg={{ item }} state=installed +  with_items: +    - libsasl2-modules +    - postfix +    - postfix-pcre +    - sasl2-bin +  tags: +    - dependencies + +#- name: Copy main.cf +#  template: src=etc_postfix_main.cf.j2 dest=/etc/postfix/main.cf owner=root group=root +#  notify: restart postfix + +#- name: Copy master.cf +#  copy: src=etc_postfix_master.cf dest=/etc/postfix/master.cf owner=root group=root +#  notify: restart postfix + diff --git a/roles/mailserver/tasks/rspamd.yml b/roles/mailserver/tasks/rspamd.yml new file mode 100644 index 0000000..4d870a8 --- /dev/null +++ b/roles/mailserver/tasks/rspamd.yml @@ -0,0 +1,52 @@ +--- +# Installs and configures the Rspamd spam filtering system. + +- name: Ensure repository key for Rspamd is in place +  apt_key: url=https://rspamd.com/apt-stable/gpg.key state=present +  when: ansible_architecture != "armv7l" +  tags: +    - dependencies + +- name: Ensure yunohost repository key for Rspamd is in place for ARM +  apt_key: url=http://repo.yunohost.org/debian/yunohost.asc state=present +  when: ansible_architecture == "armv7l" +  tags: +    - dependencies + +- name: Add Rspamd repository +  apt_repository: repo="deb https://rspamd.com/apt-stable/ {{ ansible_distribution_release }} main" +  when: ansible_architecture != "armv7l" +  tags: +    - dependencies + +- name: Add yunohost Rspamd repository for ARM +  apt_repository: repo="deb http://repo.yunohost.org/debian {{ ansible_distribution_release }} stable" +  when: ansible_architecture == "armv7l" +  tags: +    - dependencies + +- name: Install Rspamd and Redis +  apt: pkg={{ item }} state=installed update_cache=yes +  with_items: +    - rspamd +  tags: +    - dependencies + +#- name: Copy DMARC configuration into place +#  template: src=etc_rspamd_local.d_dmarc.conf.j2 dest=/etc/rspamd/local.d/dmarc.conf owner=root group=root mode="0644" +#  notify: restart rspamd + +#- name: Copy DKIM configuration into place +#  copy: src=etc_rspamd_override.d_dkim_signing.conf dest=/etc/rspamd/override.d/dkim_signing.conf owner=root group=root mode="0644" +#  notify: restart rspamd + +#- name: Create dkim key directory +#  file: path=/var/lib/rspamd/dkim state=directory owner=_rspamd group=_rspamd + +#- name: Generate DKIM keys +#  shell: rspamadm dkim_keygen -s default -d {{ item.name }} -k {{ item.name }}.default.key > {{ item.name }}.default.txt +#  args: +#    creates: /var/lib/rspamd/dkim/{{ item.name }}.default.key +#    chdir: /var/lib/rspamd/dkim/ +#  with_items: "{{ mail_virtual_domains }}" + diff --git a/roles/mailserver/templates/etc_apache2_sites-available_autoconfig.j2 b/roles/mailserver/templates/etc_apache2_sites-available_autoconfig.j2 new file mode 100644 index 0000000..1c3a07c --- /dev/null +++ b/roles/mailserver/templates/etc_apache2_sites-available_autoconfig.j2 @@ -0,0 +1,26 @@ +# NOTE: We don't permanently redirect clients to the HTTPS address because some clients, like +# Thunderbird, dont't follow redirections to the HTTPS URL. +# +# Additionally, documentation doesn't say whether the XML file should be served over either HTTP, +# HTTPS or both, even though only the former is mentioned. Still, we allow clients to choose +# between HTTP and HTTPS transports. + +<VirtualHost *:80> + +    ServerName {{ mail_server_autoconfig_hostname }} + +    DocumentRoot            "/var/www/autoconfig" +    Options                 -Indexes + +    HostnameLookups         Off +</VirtualHost> + +<VirtualHost *:443> +    ServerName {{ mail_server_autoconfig_hostname }} +    SSLEngine On + +    DocumentRoot            "/var/www/autoconfig" +    Options                 -Indexes + +    HostnameLookups         Off +</VirtualHost> diff --git a/roles/mailserver/templates/etc_dovecot_conf.d_10-ssl.conf.j2 b/roles/mailserver/templates/etc_dovecot_conf.d_10-ssl.conf.j2 new file mode 100644 index 0000000..8ba6ae5 --- /dev/null +++ b/roles/mailserver/templates/etc_dovecot_conf.d_10-ssl.conf.j2 @@ -0,0 +1,53 @@ +## +## SSL settings +## + +# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt> +ssl = required + +# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before +# dropping root privileges, so keep the key file unreadable by anyone but +# root. Included doc/mkcert.sh can be used to easily generate self-signed +# certificate, just make sure to update the domains in dovecot-openssl.cnf +ssl_cert = </etc/letsencrypt/live/{{ domain }}/fullchain.pem +ssl_key = </etc/letsencrypt/live/{{ domain }}/privkey.pem + +# If key file is password protected, give the password here. Alternatively +# give it when starting dovecot with -p parameter. Since this file is often +# world-readable, you may want to place this setting instead to a different +# root owned 0600 file by using ssl_key_password = <path. +#ssl_key_password = + +# PEM encoded trusted certificate authority. Set this only if you intend to use +# ssl_verify_client_cert=yes. The file should contain the CA certificate(s) +# followed by the matching CRL(s). (e.g. ssl_ca = </etc/ssl/certs/ca.pem) +#ssl_ca = /etc/ssl/ca.pem + +# Require that CRL check succeeds for client certificates. +#ssl_require_crl = yes + +# Request client to send a certificate. If you also want to require it, set +# auth_ssl_require_client_cert=yes in auth section. +#ssl_verify_client_cert = no + +# Which field from certificate to use for username. commonName and +# x500UniqueIdentifier are the usual choices. You'll also need to set +# auth_ssl_username_from_cert=yes. +#ssl_cert_username_field = commonName + +# How often to regenerate the SSL parameters file. Generation is quite CPU +# intensive operation. The value is in hours, 0 disables regeneration +# entirely. +#ssl_parameters_regenerate = 168 + +# SSL protocols to use +ssl_protocols = !SSLv2 !SSLv3 + +# SSL ciphers to use +#ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL + +# SSL crypto device to use, for valid values run "openssl engine" +#ssl_crypto_device = + +# DH parameters length to use. +ssl_dh_parameters_length = 2048 diff --git a/roles/mailserver/templates/etc_dovecot_conf.d_15-lda.conf.j2 b/roles/mailserver/templates/etc_dovecot_conf.d_15-lda.conf.j2 new file mode 100644 index 0000000..b464365 --- /dev/null +++ b/roles/mailserver/templates/etc_dovecot_conf.d_15-lda.conf.j2 @@ -0,0 +1,48 @@ +## +## LDA specific settings (also used by LMTP) +## + +# Address to use when sending rejection mails. +# Default is postmaster@<your domain>. +postmaster_address = postmaster@{{domain}} + +# Hostname to use in various parts of sent mails, eg. in Message-Id. +# Default is the system's real hostname. +hostname = {{ mail_server_hostname }} + +# If user is over quota, return with temporary failure instead of +# bouncing the mail. +#quota_full_tempfail = no + +# Binary to use for sending mails. +#sendmail_path = /usr/sbin/sendmail + +# If non-empty, send mails via this SMTP host[:port] instead of sendmail. +#submission_host = + +# Subject: header to use for rejection mails. You can use the same variables +# as for rejection_reason below. +#rejection_subject = Rejected: %s + +# Human readable error message for rejection mails. You can use variables: +#  %n = CRLF, %r = reason, %s = original subject, %t = recipient +#rejection_reason = Your message to <%t> was automatically rejected:%n%r + +# Delimiter character between local-part and detail in email address. +#recipient_delimiter = + + +# Header where the original recipient address (SMTP's RCPT TO: address) is taken +# from if not available elsewhere. With dovecot-lda -a parameter overrides this.  +# A commonly used header for this is X-Original-To. +#lda_original_recipient_header = + +# Should saving a mail to a nonexistent mailbox automatically create it? +#lda_mailbox_autocreate = no + +# Should automatically created mailboxes be also automatically subscribed? +#lda_mailbox_autosubscribe = no + +protocol lda { +  # Space separated list of plugins to load (default is global mail_plugins). +  mail_plugins = $mail_plugins sieve +} diff --git a/roles/mailserver/templates/etc_dovecot_conf.d_20-imap.conf.j2 b/roles/mailserver/templates/etc_dovecot_conf.d_20-imap.conf.j2 new file mode 100644 index 0000000..fcd59a5 --- /dev/null +++ b/roles/mailserver/templates/etc_dovecot_conf.d_20-imap.conf.j2 @@ -0,0 +1,64 @@ +## +## IMAP specific settings +## + +protocol imap { +  # Maximum IMAP command line length. Some clients generate very long command +  # lines with huge mailboxes, so you may need to raise this if you get +  # "Too long argument" or "IMAP command line too large" errors often. +  #imap_max_line_length = 64k + +  # Maximum number of IMAP connections allowed for a user from each IP address. +  # NOTE: The username is compared case-sensitively. +  #mail_max_userip_connections = 10 + +  # Space separated list of plugins to load (default is global mail_plugins). +  mail_plugins = $mail_plugins antispam fts fts_solr + +  # IMAP logout format string: +  #  %i - total number of bytes read from client +  #  %o - total number of bytes sent to client +  #imap_logout_format = bytes=%i/%o + +  # Override the IMAP CAPABILITY response. If the value begins with '+', +  # add the given capabilities on top of the defaults (e.g. +XFOO XBAR). +  #imap_capability =  + +  # How long to wait between "OK Still here" notifications when client is +  # IDLEing. +  #imap_idle_notify_interval = 2 mins + +  # ID field names and values to send to clients. Using * as the value makes +  # Dovecot use the default value. The following fields have default values +  # currently: name, version, os, os-version, support-url, support-email. +  #imap_id_send =  + +  # ID fields sent by client to log. * means everything. +  #imap_id_log = + +  # Workarounds for various client bugs: +  #   delay-newmail: +  #     Send EXISTS/RECENT new mail notifications only when replying to NOOP +  #     and CHECK commands. Some clients ignore them otherwise, for example OSX +  #     Mail (<v2.1). Outlook Express breaks more badly though, without this it +  #     may show user "Message no longer in server" errors. Note that OE6 still +  #     breaks even with this workaround if synchronization is set to +  #     "Headers Only". +  #   tb-extra-mailbox-sep: +  #     Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and +  #     adds extra '/' suffixes to mailbox names. This option causes Dovecot to +  #     ignore the extra '/' instead of treating it as invalid mailbox name. +  #   tb-lsub-flags: +  #     Show \Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox). +  #     This makes Thunderbird realize they aren't selectable and show them +  #     greyed out, instead of only later giving "not selectable" popup error. +  # +  # The list is space-separated. +  #imap_client_workarounds =  +} + +protocol lmtp { +  # Space separated list of plugins to load (default is global mail_plugins). +  mail_plugins = $mail_plugins sieve +  postmaster_address = postmaster@{{ domain }} +} diff --git a/roles/mailserver/templates/etc_dovecot_dovecot-sql.conf.ext.j2 b/roles/mailserver/templates/etc_dovecot_dovecot-sql.conf.ext.j2 new file mode 100644 index 0000000..743b83b --- /dev/null +++ b/roles/mailserver/templates/etc_dovecot_dovecot-sql.conf.ext.j2 @@ -0,0 +1,138 @@ +# This file is opened as root, so it should be owned by root and mode 0600. +# +# http://wiki2.dovecot.org/AuthDatabase/SQL +# +# For the sql passdb module, you'll need a database with a table that +# contains fields for at least the username and password. If you want to +# use the user@domain syntax, you might want to have a separate domain +# field as well. +# +# If your users all have the same uig/gid, and have predictable home +# directories, you can use the static userdb module to generate the home +# dir based on the username and domain. In this case, you won't need fields +# for home, uid, or gid in the database. +# +# If you prefer to use the sql userdb module, you'll want to add fields +# for home, uid, and gid. Here is an example table: +# +# CREATE TABLE users ( +#     username VARCHAR(128) NOT NULL, +#     domain VARCHAR(128) NOT NULL, +#     password VARCHAR(64) NOT NULL, +#     home VARCHAR(255) NOT NULL, +#     uid INTEGER NOT NULL, +#     gid INTEGER NOT NULL, +#     active CHAR(1) DEFAULT 'Y' NOT NULL +# ); + +# Database driver: mysql, pgsql, sqlite +driver = pgsql + +# Database connection string. This is driver-specific setting. +# +# HA / round-robin load-balancing is supported by giving multiple host +# settings, like: host=sql1.host.org host=sql2.host.org +# +# pgsql: +#   For available options, see the PostgreSQL documention for the +#   PQconnectdb function of libpq. +#   Use maxconns=n (default 5) to change how many connections Dovecot can +#   create to pgsql. +# +# mysql: +#   Basic options emulate PostgreSQL option names: +#     host, port, user, password, dbname +# +#   But also adds some new settings: +#     client_flags        - See MySQL manual +#     ssl_ca, ssl_ca_path - Set either one or both to enable SSL +#     ssl_cert, ssl_key   - For sending client-side certificates to server +#     ssl_cipher          - Set minimum allowed cipher security (default: HIGH) +#     option_file         - Read options from the given file instead of +#                           the default my.cnf location +#     option_group        - Read options from the given group (default: client) +#  +#   You can connect to UNIX sockets by using host: host=/var/run/mysql.sock +#   Note that currently you can't use spaces in parameters. +# +# sqlite: +#   The path to the database file. +# +# Examples: +#   connect = host=192.168.1.1 dbname=users +#   connect = host=sql.example.com dbname=virtual user=virtual password=blarg +#   connect = /etc/dovecot/authdb.sqlite +# +connect = "host=127.0.0.1 dbname={{ mail_db_database }} user={{ mail_db_username }} password='{{ mail_db_password }}'" + +# Default password scheme. +# +# List of supported schemes is in +# http://wiki2.dovecot.org/Authentication/PasswordSchemes +# +default_pass_scheme = SHA512-CRYPT + +# passdb query to retrieve the password. It can return fields: +#   password - The user's password. This field must be returned. +#   user - user@domain from the database. Needed with case-insensitive lookups. +#   username and domain - An alternative way to represent the "user" field. +# +# The "user" field is often necessary with case-insensitive lookups to avoid +# e.g. "name" and "nAme" logins creating two different mail directories. If +# your user and domain names are in separate fields, you can return "username" +# and "domain" fields instead of "user". +# +# The query can also return other fields which have a special meaning, see +# http://wiki2.dovecot.org/PasswordDatabase/ExtraFields +# +# Commonly used available substitutions (see http://wiki2.dovecot.org/Variables +# for full list): +#   %u = entire user@domain +#   %n = user part of user@domain +#   %d = domain part of user@domain +#  +# Note that these can be used only as input to SQL query. If the query outputs +# any of these substitutions, they're not touched. Otherwise it would be +# difficult to have eg. usernames containing '%' characters. +# +# Example: +#   password_query = SELECT userid AS user, pw AS password \ +#     FROM users WHERE userid = '%u' AND active = 'Y' +# +#password_query = \ +#  SELECT username, domain, password \ +#  FROM users WHERE username = '%n' AND domain = '%d' + +password_query = SELECT email AS user, password FROM virtual_users WHERE email = '%u'; + +# userdb query to retrieve the user information. It can return fields: +#   uid - System UID (overrides mail_uid setting) +#   gid - System GID (overrides mail_gid setting) +#   home - Home directory +#   mail - Mail location (overrides mail_location setting) +# +# None of these are strictly required. If you use a single UID and GID, and +# home or mail directory fits to a template string, you could use userdb static +# instead. For a list of all fields that can be returned, see +# http://wiki2.dovecot.org/UserDatabase/ExtraFields +# +# Examples: +#   user_query = SELECT home, uid, gid FROM users WHERE userid = '%u' +#   user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u' +#   user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u' +# +#user_query = \ +#  SELECT home, uid, gid \ +#  FROM users WHERE username = '%n' AND domain = '%d' + +# If you wish to avoid two SQL lookups (passdb + userdb), you can use +# userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll +# also have to return userdb fields in password_query prefixed with "userdb_" +# string. For example: +#password_query = \ +#  SELECT userid AS user, password, \ +#    home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ +#  FROM users WHERE userid = '%u' + +# Query to get a list of all usernames. +#iterate_query = SELECT username AS user FROM users diff --git a/roles/mailserver/templates/etc_postfix_main.cf.j2 b/roles/mailserver/templates/etc_postfix_main.cf.j2 new file mode 100644 index 0000000..2416789 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_main.cf.j2 @@ -0,0 +1,126 @@ +# See /usr/share/postfix/main.cf.dist for a commented, more complete version +# Modified as per http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/ + +smtpd_banner = $myhostname ESMTP $mail_name +biff = no + +# Accept messages up to 50MB +message_size_limit = 51200000 + +# appending .domain is the MUA's job. +append_dot_mydomain = no + +# Uncomment the next line to generate "delayed mail" warnings +#delay_warning_time = 4h + +readme_directory = no + +# antispam +smtpd_helo_required = yes +smtpd_helo_restrictions = permit_mynetworks, reject_invalid_hostname +smtpd_sender_restrictions = reject_unknown_address +disable_vrfy_command = yes +strict_rfc821_envelopes = yes +invalid_hostname_reject_code = 554 +multi_recipient_bounce_reject_code = 554 +non_fqdn_reject_code = 554 +relay_domains_reject_code = 554 +unknown_address_reject_code = 554 +unknown_client_reject_code = 554 +unknown_hostname_reject_code = 554 +unknown_local_recipient_reject_code = 554 +unknown_relay_recipient_reject_code = 554 +unknown_virtual_alias_reject_code = 554 +unknown_virtual_mailbox_reject_code = 554 +unverified_recipient_reject_code = 554 +unverified_sender_reject_code = 554 + +# TLS parameters +smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3 +smtp_tls_mandatory_protocols=!SSLv2,!SSLv3 +smtp_tls_protocols = !SSLv2,!SSLv3 +smtpd_tls_protocols = !SSLv2,!SSLv3 +smtpd_tls_cert_file=/etc/letsencrypt/live/{{ domain }}/fullchain.pem +smtpd_tls_key_file=/etc/letsencrypt/live/{{ domain }}/privkey.pem +smtpd_use_tls=yes +smtpd_tls_auth_only = yes +smtp_tls_security_level = may +smtp_tls_loglevel = 2 +smtpd_tls_received_header = yes +smtp_tls_note_starttls_offer = yes +smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt +# http://www.postfix.org/FORWARD_SECRECY_README.html +smtp_tls_ciphers = medium +smtpd_tls_dh1024_param_file = /etc/ssl/private/dhparam2048.pem + +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +smtpd_sasl_auth_enable = yes +broken_sasl_auth_clients = yes +smtpd_sasl_security_options = noanonymous + +# set to empty value for backwards compatibility +# as per http://www.postfix.org/postconf.5.html#smtpd_relay_restrictions +smtpd_relay_restrictions = + +smtpd_recipient_restrictions = +  permit_sasl_authenticated, +  permit_mynetworks, +  reject_unauth_pipelining, +  reject_unauth_destination, +  reject_invalid_hostname, +  reject_non_fqdn_hostname, +  reject_non_fqdn_recipient, +  reject_unknown_recipient_domain, +  permit + +# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for +# information on enabling SSL in the smtp client. + +myhostname = {{ mail_server_hostname }} +myorigin = $mydomain +alias_maps = hash:/etc/aliases +alias_database = hash:/etc/aliases +mydestination = localhost +relayhost = +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ ' '.join(friendly_networks) }} +#mailbox_command = procmail -a "$EXTENSION" +mailbox_size_limit = 0 +recipient_delimiter = + +inet_interfaces = all + +# dovecot db +virtual_transport = lmtp:unix:private/dovecot-lmtp +mailbox_transport = lmtp:unix:private/dovecot-lmtp + +dovecot_destination_recipient_limit = 1 +virtual_mailbox_domains = pgsql:/etc/postfix/pgsql-virtual-mailbox-domains.cf +virtual_mailbox_maps = pgsql:/etc/postfix/pgsql-virtual-mailbox-maps.cf +virtual_alias_maps = pgsql:/etc/postfix/pgsql-virtual-alias-maps.cf +local_recipient_maps = $virtual_mailbox_maps + +# Milters: Rspamd +smtpd_milters = inet:127.0.0.1:11332 +non_smtpd_milters = $smtpd_milters +milter_protocol = 6 +milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen} {auth_type} +milter_default_action = accept + +smtpd_client_restrictions = permit_sasl_authenticated + +# Postscreen +postscreen_access_list = permit_mynetworks +postscreen_dnsbl_sites = +  sbl-xbl.spamhaus.org*2 +  cbl.abuseat.org*2 +  bl.spamcop.net*2 +  dnsbl.sorbs.net*1 +  spam.spamrats.com*2 +postscreen_dnsbl_threshold = 3 +postscreen_dnsbl_action = enforce +postscreen_greet_action = enforce + +{% if mail_header_privacy == 1 %} +# Remove local client IP from headers +smtp_header_checks = pcre:/etc/postfix/maps/smtp_header_checks.pcre +{% endif %} diff --git a/roles/mailserver/templates/etc_postfix_mysql-virtual-alias-maps.cf.j2 b/roles/mailserver/templates/etc_postfix_mysql-virtual-alias-maps.cf.j2 new file mode 100644 index 0000000..daa0d00 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_mysql-virtual-alias-maps.cf.j2 @@ -0,0 +1,5 @@ +user = {{ mail_db_username }} +password = {{ mail_db_password }} +hosts = 127.0.0.1 +dbname = {{ mail_db_database }} +query = SELECT destination FROM virtual_aliases WHERE source='%s' diff --git a/roles/mailserver/templates/etc_postfix_mysql-virtual-mailbox-domains.cf.j2 b/roles/mailserver/templates/etc_postfix_mysql-virtual-mailbox-domains.cf.j2 new file mode 100644 index 0000000..d9d35a9 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_mysql-virtual-mailbox-domains.cf.j2 @@ -0,0 +1,5 @@ +user = {{ mail_db_username }} +password = {{ mail_db_password }} +hosts = 127.0.0.1 +dbname = {{ mail_db_database }} +query = SELECT 1 FROM virtual_domains WHERE name='%s' diff --git a/roles/mailserver/templates/etc_postfix_mysql-virtual-mailbox-maps.cf.j2 b/roles/mailserver/templates/etc_postfix_mysql-virtual-mailbox-maps.cf.j2 new file mode 100644 index 0000000..b2f7165 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_mysql-virtual-mailbox-maps.cf.j2 @@ -0,0 +1,5 @@ +user = {{ mail_db_username }} +password = {{ mail_db_password }} +hosts = 127.0.0.1 +dbname = {{ mail_db_database }} +query = SELECT 1 FROM virtual_users WHERE email='%s' diff --git a/roles/mailserver/templates/etc_postfix_pgsql-virtual-alias-maps.cf.j2 b/roles/mailserver/templates/etc_postfix_pgsql-virtual-alias-maps.cf.j2 new file mode 100644 index 0000000..daa0d00 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_pgsql-virtual-alias-maps.cf.j2 @@ -0,0 +1,5 @@ +user = {{ mail_db_username }} +password = {{ mail_db_password }} +hosts = 127.0.0.1 +dbname = {{ mail_db_database }} +query = SELECT destination FROM virtual_aliases WHERE source='%s' diff --git a/roles/mailserver/templates/etc_postfix_pgsql-virtual-mailbox-domains.cf.j2 b/roles/mailserver/templates/etc_postfix_pgsql-virtual-mailbox-domains.cf.j2 new file mode 100644 index 0000000..d9d35a9 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_pgsql-virtual-mailbox-domains.cf.j2 @@ -0,0 +1,5 @@ +user = {{ mail_db_username }} +password = {{ mail_db_password }} +hosts = 127.0.0.1 +dbname = {{ mail_db_database }} +query = SELECT 1 FROM virtual_domains WHERE name='%s' diff --git a/roles/mailserver/templates/etc_postfix_pgsql-virtual-mailbox-maps.cf.j2 b/roles/mailserver/templates/etc_postfix_pgsql-virtual-mailbox-maps.cf.j2 new file mode 100644 index 0000000..b2f7165 --- /dev/null +++ b/roles/mailserver/templates/etc_postfix_pgsql-virtual-mailbox-maps.cf.j2 @@ -0,0 +1,5 @@ +user = {{ mail_db_username }} +password = {{ mail_db_password }} +hosts = 127.0.0.1 +dbname = {{ mail_db_database }} +query = SELECT 1 FROM virtual_users WHERE email='%s' diff --git a/roles/mailserver/templates/etc_rspamd_local.d_dmarc.conf.j2 b/roles/mailserver/templates/etc_rspamd_local.d_dmarc.conf.j2 new file mode 100644 index 0000000..a850af9 --- /dev/null +++ b/roles/mailserver/templates/etc_rspamd_local.d_dmarc.conf.j2 @@ -0,0 +1,47 @@ +# Enables storing reporting information to redis +reporting = true; + +# Actions to enforce based on DMARC disposition +actions = { +  quarantine = "add_header"; +  reject = "reject"; +} + +# From Rspamd 1.6 experimental support for generation of DMARC reports is provided. +# send_reports MUST be true +send_reports = true; + +# report_settings MUST be present +report_settings { +  # The following elements MUST be present +  # organisation name to use for reports +  org_name = "{{ organization }}"; +   +  # organisation domain +  domain = "{{ domain }}"; + +  # sender address to use for reports +  email = "postmaster@{{ domain }}"; + +  # The following elements MAY be present +  # SMTP host to send reports to ("127.0.0.1" if unset) +  # smtp = "127.0.0.1"; +	 +  # TCP port to use for SMTP (25 if unset) +  # smtp_port = 25; +	 +  # HELO to use for SMTP ("rspamd" if unset) +  # helo = "rspamd"; +	 +  # Number of retries on temporary errors (2 if unset) +  # retries = 2; +	 +  # Send DMARC reports here instead of domain owners +  # override_address = "postmaster@example.net"; +	 +  # Send DMARC reports here in addition to domain owners +  additional_address = "postmaster@{{ domain }}"; +	 +  # Number of records to request with HSCAN +  # hscan_count = 200 +} diff --git a/roles/mailserver/templates/mailserver.sql.j2 b/roles/mailserver/templates/mailserver.sql.j2 new file mode 100644 index 0000000..203c3d8 --- /dev/null +++ b/roles/mailserver/templates/mailserver.sql.j2 @@ -0,0 +1,57 @@ +-- If tables are not dropped, have to truncate before insert or use "insert or replace" (not postgres compatible) + +DROP TABLE IF EXISTS "virtual_users"; +DROP TABLE IF EXISTS "virtual_aliases"; +DROP TABLE IF EXISTS "virtual_domains"; + +CREATE TABLE IF NOT EXISTS "virtual_domains" ( +        "id" SERIAL, +        "name" TEXT NOT NULL, +        PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX name_idx ON virtual_domains (name); + +CREATE TABLE IF NOT EXISTS "virtual_users" ( +        "id" SERIAL, +        "domain_id" int NOT NULL, +        "password" TEXT NOT NULL, +        "email" TEXT NOT NULL UNIQUE, +        PRIMARY KEY ("id"), +        FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE +); + + +CREATE UNIQUE INDEX email_idx ON virtual_users (email); + +CREATE TABLE IF NOT EXISTS "virtual_aliases" ( +        "id" SERIAL, +        "domain_id" int NOT NULL, +        "source" TEXT NOT NULL, +        "destination" TEXT NOT NULL, +        PRIMARY KEY ("id"), +        FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE +); + +CREATE INDEX source_idx ON virtual_aliases (source); + +{% for virtual_domain in mail_virtual_domains %} +INSERT INTO "virtual_domains" ("id", "name") +        VALUES ('{{ virtual_domain.pk_id }}', '{{ virtual_domain.name }}'); +{% endfor %} + +{% for virtual_user in mail_virtual_users %} +INSERT INTO "virtual_users"  ("domain_id", "password" , "email") +	VALUES ( +		'{{ virtual_user.domain_pk_id }}', +		'{{ virtual_user.password | doveadm_pw_hash }}', +		'{{ virtual_user.account }}@{{ virtual_user.domain }}' +	); +{% endfor %} + +{% if mail_virtual_aliases is defined %} +{% for virtual_alias in mail_virtual_aliases %} +INSERT INTO "virtual_aliases" ("domain_id", "source", "destination") +    VALUES ('{{ virtual_alias.domain_pk_id }}', '{{ virtual_alias.source }}', '{{virtual_alias.destination }}'); +{% endfor %} +{% endif %} diff --git a/roles/mailserver/templates/usr_share_z-push_config.php.j2 b/roles/mailserver/templates/usr_share_z-push_config.php.j2 new file mode 100644 index 0000000..a351df1 --- /dev/null +++ b/roles/mailserver/templates/usr_share_z-push_config.php.j2 @@ -0,0 +1,306 @@ +<?php +/*********************************************** +* File      :   config.php +* Project   :   Z-Push +* Descr     :   Main configuration file +* +* Created   :   01.10.2007 +* +* Copyright 2007 - 2013 Zarafa Deutschland GmbH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation with the following additional +* term according to sec. 7: +* +* According to sec. 7 of the GNU Affero General Public License, version 3, +* the terms of the AGPL are supplemented with the following terms: +* +* "Zarafa" is a registered trademark of Zarafa B.V. +* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH +* The licensing of the Program under the AGPL does not imply a trademark license. +* Therefore any rights, title and interest in our trademarks remain entirely with us. +* +* However, if you propagate an unmodified version of the Program you are +* allowed to use the term "Z-Push" to indicate that you distribute the Program. +* Furthermore you may use our trademarks where it is necessary to indicate +* the intended purpose of a product or service provided you use it in accordance +* with honest practices in industrial or commercial matters. +* If you want to propagate modified versions of the Program under the name "Z-Push", +* you may only do so if you have a written permission by Zarafa Deutschland GmbH +* (to acquire a permission please contact Zarafa at trademark@zarafa.com). +* +* 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 Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program.  If not, see <http://www.gnu.org/licenses/>. +* +* Consult LICENSE file for details +************************************************/ + +/********************************************************************************** + *  Default settings + */ +    // Defines the default time zone, change e.g. to "Europe/London" if necessary +    define('TIMEZONE', '{{ zpush_timezone }}'); + +    // Defines the base path on the server +    define('BASE_PATH', dirname($_SERVER['SCRIPT_FILENAME']). '/'); + +    // Try to set unlimited timeout +    define('SCRIPT_TIMEOUT', 0); + +    // When accessing through a proxy, the "X-Forwarded-For" header contains the original remote IP +    define('USE_X_FORWARDED_FOR_HEADER', false); + +    // When using client certificates, we can check if the login sent matches the owner of the certificate. +    // This setting specifies the owner parameter in the certificate to look at. +    define("CERTIFICATE_OWNER_PARAMETER", "SSL_CLIENT_S_DN_CN"); + +/********************************************************************************** + *  Default FileStateMachine settings + */ +    define('STATE_DIR', '/decrypted/zpush-state/'); + + +/********************************************************************************** + *  Logging settings + *  Possible LOGLEVEL and LOGUSERLEVEL values are: + *  LOGLEVEL_OFF            - no logging + *  LOGLEVEL_FATAL          - log only critical errors + *  LOGLEVEL_ERROR          - logs events which might require corrective actions + *  LOGLEVEL_WARN           - might lead to an error or require corrective actions in the future + *  LOGLEVEL_INFO           - usually completed actions + *  LOGLEVEL_DEBUG          - debugging information, typically only meaningful to developers + *  LOGLEVEL_WBXML          - also prints the WBXML sent to/from the device + *  LOGLEVEL_DEVICEID       - also prints the device id for every log entry + *  LOGLEVEL_WBXMLSTACK     - also prints the contents of WBXML stack + * + *  The verbosity increases from top to bottom. More verbose levels include less verbose + *  ones, e.g. setting to LOGLEVEL_DEBUG will also output LOGLEVEL_FATAL, LOGLEVEL_ERROR, + *  LOGLEVEL_WARN and LOGLEVEL_INFO level entries. + */ +    define('LOGFILEDIR', '/var/log/z-push/'); +    define('LOGFILE', LOGFILEDIR . 'z-push.log'); +    define('LOGERRORFILE', LOGFILEDIR . 'z-push-error.log'); +    define('LOGLEVEL', LOGLEVEL_INFO); +    define('LOGAUTHFAIL', false); + + +    // To save e.g. WBXML data only for selected users, add the usernames to the array +    // The data will be saved into a dedicated file per user in the LOGFILEDIR +    // Users have to be encapusulated in quotes, several users are comma separated, like: +    //   $specialLogUsers = array('info@domain.com', 'myusername'); +    define('LOGUSERLEVEL', LOGLEVEL_DEVICEID); +    $specialLogUsers = array(); + +    // Location of the trusted CA, e.g. '/etc/ssl/certs/EmailCA.pem' +    // Uncomment and modify the following line if the validation of the certificates fails. +    // define('CAINFO', '/etc/ssl/certs/EmailCA.pem'); + +/********************************************************************************** + *  Mobile settings + */ +    // Device Provisioning +    define('PROVISIONING', true); + +    // This option allows the 'loose enforcement' of the provisioning policies for older +    // devices which don't support provisioning (like WM 5 and HTC Android Mail) - dw2412 contribution +    // false (default) - Enforce provisioning for all devices +    // true - allow older devices, but enforce policies on devices which support it +    define('LOOSE_PROVISIONING', false); + +    // Default conflict preference +    // Some devices allow to set if the server or PIM (mobile) +    // should win in case of a synchronization conflict +    //   SYNC_CONFLICT_OVERWRITE_SERVER - Server is overwritten, PIM wins +    //   SYNC_CONFLICT_OVERWRITE_PIM    - PIM is overwritten, Server wins (default) +    define('SYNC_CONFLICT_DEFAULT', SYNC_CONFLICT_OVERWRITE_PIM); + +    // Global limitation of items to be synchronized +    // The mobile can define a sync back period for calendar and email items +    // For large stores with many items the time period could be limited to a max value +    // If the mobile transmits a wider time period, the defined max value is used +    // Applicable values: +    //   SYNC_FILTERTYPE_ALL (default, no limitation) +    //   SYNC_FILTERTYPE_1DAY, SYNC_FILTERTYPE_3DAYS, SYNC_FILTERTYPE_1WEEK, SYNC_FILTERTYPE_2WEEKS, +    //   SYNC_FILTERTYPE_1MONTH, SYNC_FILTERTYPE_3MONTHS, SYNC_FILTERTYPE_6MONTHS +    define('SYNC_FILTERTIME_MAX', SYNC_FILTERTYPE_3MONTHS); + +    // Interval in seconds before checking if there are changes on the server when in Ping. +    // It means the highest time span before a change is pushed to a mobile. Set it to +    // a higher value if you have a high load on the server. +    define('PING_INTERVAL', 30); + +    // Interval in seconds to force a re-check of potentially missed notifications when +    // using a changes sink. Default are 300 seconds (every 5 min). +    // This can also be disabled by setting it to false +    define('SINK_FORCERECHECK', 300); + +    // Set the fileas (save as) order for contacts in the webaccess/webapp/outlook. +    // It will only affect new/modified contacts on the mobile which then are synced to the server. +    // Possible values are: +    // SYNC_FILEAS_FIRSTLAST    - fileas will be "Firstname Middlename Lastname" +    // SYNC_FILEAS_LASTFIRST    - fileas will be "Lastname, Firstname Middlename" +    // SYNC_FILEAS_COMPANYONLY  - fileas will be "Company" +    // SYNC_FILEAS_COMPANYLAST  - fileas will be "Company (Lastname, Firstname Middlename)" +    // SYNC_FILEAS_COMPANYFIRST - fileas will be "Company (Firstname Middlename Lastname)" +    // SYNC_FILEAS_LASTCOMPANY  - fileas will be "Lastname, Firstname Middlename (Company)" +    // SYNC_FILEAS_FIRSTCOMPANY - fileas will be "Firstname Middlename Lastname (Company)" +    // The company-fileas will only be set if a contact has a company set. If one of +    // company-fileas is selected and a contact doesn't have a company set, it will default +    // to SYNC_FILEAS_FIRSTLAST or SYNC_FILEAS_LASTFIRST (depending on if last or first +    // option is selected for company). +    // If SYNC_FILEAS_COMPANYONLY is selected and company of the contact is not set +    // SYNC_FILEAS_LASTFIRST will be used +    define('FILEAS_ORDER', SYNC_FILEAS_LASTFIRST); + +    // Amount of items to be synchronized per request +    // Normally this value is requested by the mobile. Common values are 5, 25, 50 or 100. +    // Exporting too much items can cause mobile timeout on busy systems. +    // Z-Push will use the lowest value, either set here or by the mobile. +    // default: 100 - value used if mobile does not limit amount of items +    define('SYNC_MAX_ITEMS', 100); + +    // The devices usually send a list of supported properties for calendar and contact +    // items. If a device does not includes such a supported property in Sync request, +    // it means the property's value will be deleted on the server. +    // However some devices do not send a list of supported properties. It is then impossible +    // to tell if a property was deleted or it was not set at all if it does not appear in Sync. +    // This parameter defines Z-Push behaviour during Sync if a device does not issue a list with +    // supported properties. +    // See also https://jira.zarafa.com/browse/ZP-302. +    // Possible values: +    // false - do not unset properties which are not sent during Sync (default) +    // true  - unset properties which are not sent during Sync +    define('UNSET_UNDEFINED_PROPERTIES', false); + +    // ActiveSync specifies that a contact photo may not exceed 48 KB. This value is checked +    // in the semantic sanity checks and contacts with larger photos are not synchronized. +    // This limitation is not being followed by the ActiveSync clients which set much bigger +    // contact photos. You can override the default value of the max photo size. +    // default: 49152 - 48 KB default max photo size in bytes +    define('SYNC_CONTACTS_MAXPICTURESIZE', 49152); + +/********************************************************************************** + *  Backend settings + */ +    // the backend data provider +    define('BACKEND_PROVIDER', 'BackendIMAP'); + + +    // ************************ +    //  BackendZarafa settings +    // ************************ +    // Defines the server to which we want to connect +    define('MAPI_SERVER', 'file:///var/run/zarafa'); + + +    // ************************ +    //  BackendIMAP settings +    // ************************ +    // Defines the server to which we want to connect +    define('IMAP_SERVER', 'localhost'); +    // connecting to default port (143) +    define('IMAP_PORT', 993); +    // best cross-platform compatibility (see http://php.net/imap_open for options) +    define('IMAP_OPTIONS', '/ssl/novalidate-cert'); +    // overwrite the "from" header if it isn't set when sending emails +    // options: 'username'    - the username will be set (usefull if your login is equal to your emailaddress) +    //        'domain'    - the value of the "domain" field is used +    //        '@mydomain.com' - the username is used and the given string will be appended +    define('IMAP_DEFAULTFROM', ''); +    // copy outgoing mail to this folder. If not set d-push will try the default folders +    define('IMAP_SENTFOLDER', 'Sent'); +    // forward messages inline (default false - as attachment) +    define('IMAP_INLINE_FORWARD', false); +    // don't use imap_mail() to send emails. +    // true (default, uses imap_mail, which is broken - false uses mail(), +    // which handles cc and from in a more sane way) +    define('IMAP_USE_IMAPMAIL', false); + + +    // ************************ +    //  BackendMaildir settings +    // ************************ +    define('MAILDIR_BASE', '/tmp'); +    define('MAILDIR_SUBDIR', 'Maildir'); + +    // ********************** +    //  BackendVCardDir settings +    // ********************** +    define('VCARDDIR_DIR', '/home/%u/.kde/share/apps/kabc/stdvcf'); + + +/********************************************************************************** + *  Search provider settings + * + *  Alternative backend to perform SEARCH requests (GAL search) + *  By default the main Backend defines the preferred search functionality. + *  If set, the Search Provider will always be preferred. + *  Use 'BackendSearchLDAP' to search in a LDAP directory (see backend/searchldap/config.php) + */ +    define('SEARCH_PROVIDER', ''); +    // Time in seconds for the server search. Setting it too high might result in timeout. +    // Setting it too low might not return all results. Default is 10. +    define('SEARCH_WAIT', 10); +    // The maximum number of results to send to the client. Setting it too high +    // might result in timeout. Default is 10. +    define('SEARCH_MAXRESULTS', 10); + + +/********************************************************************************** + *  Synchronize additional folders to all mobiles + * + *  With this feature, special folders can be synchronized to all mobiles. + *  This is useful for e.g. global company contacts. + * + *  This feature is supported only by certain devices, like iPhones. + *  Check the compatibility list for supported devices: + *      http://z-push.sf.net/compatibility + * + *  To synchronize a folder, add a section setting all parameters as below: + *      store:      the ressource where the folder is located. + *                  Zarafa users use 'SYSTEM' for the 'Public Folder' + *      folderid:   folder id of the folder to be synchronized + *      name:       name to be displayed on the mobile device + *      type:       supported types are: + *                      SYNC_FOLDER_TYPE_USER_CONTACT + *                      SYNC_FOLDER_TYPE_USER_APPOINTMENT + *                      SYNC_FOLDER_TYPE_USER_TASK + *                      SYNC_FOLDER_TYPE_USER_MAIL + * + *  Additional notes: + *  - on Zarafa systems use backend/zarafa/listfolders.php script to get a list + *    of available folders + * + *  - all Z-Push users must have full writing permissions (secretary rights) so + *    the configured folders can be synchronized to the mobile + * + *  - this feature is only partly suitable for multi-tenancy environments, + *    as ALL users from ALL tenents need access to the configured store & folder. + *    When configuring a public folder, this will cause problems, as each user has + *    a different public folder in his tenant, so the folder are not available. + + *  - changing this configuration could cause HIGH LOAD on the system, as all + *    connected devices will be updated and load the data contained in the + *    added/modified folders. + */ + +    $additionalFolders = array( +        // demo entry for the synchronization of contacts from the public folder. +        // uncomment (remove '/*' '*/') and fill in the folderid +/* +        array( +            'store'     => "SYSTEM", +            'folderid'  => "", +            'name'      => "Public Contacts", +            'type'      => SYNC_FOLDER_TYPE_USER_CONTACT, +        ), +*/ +    ); + +?>
\ No newline at end of file diff --git a/roles/mailserver/templates/var_www_autoconfig_mail_config-v1.1.j2 b/roles/mailserver/templates/var_www_autoconfig_mail_config-v1.1.j2 new file mode 100644 index 0000000..8ca98ab --- /dev/null +++ b/roles/mailserver/templates/var_www_autoconfig_mail_config-v1.1.j2 @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<clientConfig version="1.1"> +    <emailProvider id="{{ domain }}"> +        <domain>{{ domain }}</domain> +        <displayName>{{ domain }}</displayName> +        <displayShortName>{{ domain }}</displayShortName> +        <incomingServer type="imap"> +            <hostname>{{ mail_server_hostname }}</hostname> +            <port>993</port> +            <socketType>SSL</socketType> +            <authentication>password-cleartext</authentication> +            <username>%EMAILADDRESS%</username> +        </incomingServer> +        <incomingServer type="pop3"> +            <hostname>{{ mail_server_hostname }}</hostname> +            <port>995</port> +            <socketType>SSL</socketType> +            <authentication>password-cleartext</authentication> +            <username>%EMAILADDRESS%</username> +        </incomingServer> +        <outgoingServer type="smtp"> +            <hostname>{{ mail_server_hostname }}</hostname> +            <port>587</port> +            <socketType>STARTTLS</socketType> +            <authentication>password-cleartext</authentication> +            <username>%EMAILADDRESS%</username> +        </outgoingServer> +    </emailProvider> +</clientConfig> | 
