diff options
Diffstat (limited to 'roles')
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> |