This commit is contained in:
Denis Knauf 2020-11-07 20:27:01 +01:00
commit 76228a0afb
12 changed files with 662 additions and 0 deletions

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# ---> Ansible
*.retry
# ---> Vim
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

50
README.adoc Normal file
View file

@ -0,0 +1,50 @@
mail - Secure Postfix & Dovecot
===============================
Mail is pain. For an easier setup, this role configures a mail-server
with secure default settings.
Requirements
------------
You need to have debian (or compatible, like ubuntu) already installed.
It will install all dependencies on host-machine:
* LDAP - Yes, you need a LDAP-server. It can be installed anywhere, but you need one.
It expects for TLS a PKI in `/etc/postfix/tls/` and `/etc/dovecot/tls/`,
where you have to place `**hostname**.key`, `**hostname**.crt`.
Role Variables
--------------
TODO
Example Playbook
----------------
TODO
----
---
# vim: set expandtab tabstop=2 shiftwidth=2:
- hosts: mailserver
remote_user: root
become: false
tasks:
- import_role:
name: mail
----
License
-------
AGPLv3
Author Information
------------------
Denis Knauf - https://git.denkn.at/deac/ansible-role-mail

5
defaults/main.yml Normal file
View file

@ -0,0 +1,5 @@
---
# vim: set et sw=2 ts=2 sts=2:
postfix_tls_policy: []

2
handlers/main.yml Normal file
View file

@ -0,0 +1,2 @@
---
# handlers file for mail

53
meta/main.yml Normal file
View file

@ -0,0 +1,53 @@
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: AGPL-3.0
min_ansible_version: 2.9
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
#
# Provide a list of supported platforms, and for each platform a list of versions.
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
# To view available platforms and versions (or releases), visit:
# https://galaxy.ansible.com/api/v1/platforms/
#
# platforms:
# - name: Fedora
# versions:
# - all
# - 25
# - name: SomePlatform
# versions:
# - all
# - 1.0
# - 7
# - 99.99
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

147
tasks/dovecot.yml Normal file
View file

@ -0,0 +1,147 @@
---
# vim: set et sw=2 ts=2 sts=2:
- name: dovecot-service.conf set file limit
copy:
dest: /etc/systemd/system/dovecot.service.d/service.conf
content: |
[Service]
LimitNOFILE=16384
- name: dovecot-configs
copy:
src: "{{item}}"
dest: /etc/dovecot
owner: root
group: dovecot
mode: 0444
with_fileglob:
- "dovecot/*"
- name: dovecot-configs in conf.d
copy:
src: "{{item}}"
dest: /etc/dovecot/conf.d
owner: root
group: dovecot
mode: 0444
with_fileglob:
- "dovecot/conf.d/*"
- name: 'deactivate auth-system'
lineinfile:
path: /etc/dovecot/conf.d/10-auth.conf
line: "#!include auth-system.conf.ext"
regexp: "^#?!include auth-system\\.conf\\.ext"
- name: 'activate auth-ldap,-passwdfile'
lineinfile:
path: /etc/dovecot/conf.d/10-auth.conf
line: "!include {{item}}.conf.ext"
regexp: "^#?!include {{item}}.conf.ext"
with_items:
- auth-ldap
- auth-passwdfile
- name: 'dovecot: 10-ssl.conf'
lineinfile:
path: /etc/dovecot/conf.d/10-ssl.conf
insertafter: "^#{{item.key}} *= *"
regexp: "^{{item.key}} *= *"
line: "{{item.key}} = {{item.value}}"
with_dict:
ssl: required
ssl_key: "</etc/dovecot/tls/{{fqdn}}.key"
ssl_cert: "</etc/dovecot/tls/{{fqdn}}.crt"
ssl_min_protocol: 'TLSv1.2'
#ssl_cipher_list: 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA'
ssl_cipher_list: ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH
ssl_prefer_server_ciphers: 'yes'
ssl_dh_parameters_length: 2048
- name: 'dovecot: 20-lmtp.conf'
lineinfile:
path: /etc/dovecot/conf.d/20-lmtp.conf
insertafter: "^[ \t]*#{{item.key}} *= *"
regexp: "^[ \t]*{{item.key}} *= *"
line: " {{item.key}} = {{item.value}}"
with_dict:
mail_plugins: '$mail_plugins quota sieve'
- name: 'dovecot: 20-imap.conf: activated plugins'
lineinfile:
path: /etc/dovecot/conf.d/20-imap.conf
insertafter: "^[\t ]*#{{item.key}} *= *"
regexp: "^[\t ]*{{item.key}} *= *"
line: " {{item.key}} = {{item.value}}"
with_dict:
mail_plugins: '$mail_plugins imap_sieve imap_acl'
- name: 'dovecot: 90-acl.conf: shared-mailboxes-dict'
lineinfile:
path: /etc/dovecot/conf.d/90-acl.conf
insertafter: "^[\t ]*#{{item.key}} *= *"
regexp: "^[\t ]*{{item.key}} *= *"
line: " {{item.key}} = {{item.value}}"
with_dict:
acl_shared_dict: 'file:/var/mail/%Ld/shared-mailboxes.db'
- name: 'dovecot: auth-ldap.conf.ext'
lineinfile:
path: /etc/dovecot/conf.d/auth-ldap.conf.ext
insertafter: "^[\t ]*#{{item.key}} *= *"
regexp: "^[\t ]*{{item.key}} *= *"
line: " {{item.key}} = {{item.value}}"
with_dict:
default_fields: 'home=/var/mail/%Ld/%Ln'
- name: 'dovecot: 90-sieve.conf'
lineinfile:
path: /etc/dovecot/conf.d/90-sieve.conf
insertafter: "^[\t ]*#{{item.key}} *= *"
regexp: "^[\t ]*{{item.key}} *= *"
line: " {{item.key}} = {{item.value}}"
with_dict:
sieve: 'file:/var/mail/%Ld/%Ln/sieve;active=/var/mail/%Ld/%Ln/active.sieve'
sieve_extensions: '+editheader +vnd.dovecot.debug +vnd.dovecot.pipe'
sieve_max_redirects: 25
sieve_max_actions: 64
- name: 'dovecot: dovecot-ldap.conf.ext'
lineinfile:
path: /etc/dovecot/dovecot-ldap.conf.ext
insertafter: "^#{{item.key}} *= *"
regexp: "^{{item.key}} *= *"
line: "{{item.key}} = {{item.value}}"
with_dict:
uris: ldapi://
ldaprc_path: /etc/ldap/ldap.conf
auth_bind: 'yes'
ldap_version: 3
base: 'ou=People,{{mail_ldap_basedn}}'
scope: onelevel
user_filter: '(&(objectClass=simpleSecurityObject)(mail=%u))'
user_attrs: mail=user
pass_filter: '(&(objectClass=simpleSecurityObject)(mail=%u))'
pass_attrs: mail=user,userPassword=password
iterate_attrs: 'mail=user'
iterate_filter: '(&(objectClass=simpleSecurityObject)(mail=*))'
- name: dummy files if needed
copy:
dest: '{{item.key}}'
content: '{{item.value}}'
force: no
with_dict:
"/etc/dovecot/users": ''
- name: 'dovecot: special users'
lineinfile:
path: /etc/dovecot/users
line: '{{item}}::::::::'
regexp: '{{item|regex_escape()}}'
- name: 'install sieve-scripts (templates)'
shell: 'doveadm sieve put -u {{item.user|quote}} -a {{item.name|default("main")|quote}}'
args:
stdin_add_newline: false
stdin: '{{lookup("file", item.file)|quote}}'
with_items: '{{mail_sieve_scripts}}'
- name: 'install sieve-scripts (templates)'
shell: 'doveadm sieve put -u {{item.user|quote}} -a {{item.name|default("main")|quote}}'
args:
stdin_add_newline: false
stdin: '{{lookup("template", item.file)|quote}}'
with_items: '{{mail_sieve_scripts_templates}}'

187
tasks/main.yml Normal file
View file

@ -0,0 +1,187 @@
---
# vim: set et sw=2 ts=2 sts=2:
- name: install gnutls, postfix & dovecot
apt:
name:
# TLS
- gnutls-bin
# ansible
- python-pip
- python-openssl
#- libpam-ldapd
# postfix
- postfix
#- postfix-ldap
- postfix-pcre
- postfix-cdb
- postfix-lmdb
# milter
- opendkim
- opendkim-tools
- opendmarc
- postfix-policyd-spf-python
# for rewriting sender (tries to fix forwarding+SPF-problem)
- postsrsd
# imap
- dovecot-core
- dovecot-imapd
- dovecot-managesieved
- dovecot-ldap
- dovecot-gssapi
- dovecot-lmtpd
- name: 'directory-structures (/etc/postfix-&/etc/dovecot&...)'
file:
dest: "{{item}}"
owner: root
group: root
mode: 0755
state: directory
with_items:
- /etc/postfix
- /etc/dovecot
- /etc/dovecot/conf.d
- /etc/dkimkeys
- /etc/systemd/system/dovecot.service.d
- name: Groups
group:
name: "{{item}}"
system: yes
with_items:
- vmail
- milter
- name: vmail-user for Mailboxes
user:
name: vmail
uid: 999
comment: Mailboxes
group: vmail
shell: /bin/false
createhome: no
home: /var/mail/vmail
move_home: no
skeleton: no
system: yes
- name: add milter-group-members
user:
name: "{{item}}"
append: yes
groups: milter
with_items:
- opendkim
- opendmarc
- postfix
- name: '/var/mail'
file:
dest: /var/mail
group: vmail
owner: vmail
state: directory
mode: 03700
- name: '/var/mail domains'
file:
dest: '/var/mail/{{item.key}}'
group: vmail
owner: vmail
state: directory
mode: 03700
with_dict: '{{mail_domains}}'
- name: opendkim.conf
copy:
src: opendkim.conf
dest: /etc
owner: root
group: root
mode: 0644
- name: /etc/mailname
copy:
dest: /etc/mailname
content: "{{fqdn}}"
- file:
dest: /var/spool/postfix/milter
owner: postfix
group: milter
mode: 0570
state: directory
- name: 'opendkim: config'
lineinfile:
path: /etc/opendkim.conf
regexp: '^{{item.key}}[ \t]'
insertafter: '^#{{item.key}}[ \t]'
line: '{{item.key}} {{item.value}}'
with_dict:
Domain: '{{mail_domain}}'
KeyFile: '/etc/dkimkeys/{{dkim_selector}}.key'
Socket: local:/var/spool/postfix/milter/opendkim
Selector: '{{dkim_selector}}'
- name: 'DKIM-key'
shell: |
set -e
f={{item|quote}}
ulimit 0400
opendkim-genkey --bits 2048 --domain {{mail_domain|quote}} --restrict --selector "$f"
chown opendkim:root "$f.private" "$f.txt"
mv "$f.private" "$f.key"
mv "$f.txt" "$f.zone"
args:
chdir: /etc/dkimkeys
creates: "{{item}}.key"
with_items:
- "{{mail_dkim_selector}}"
- name: 'opendmarc: config'
lineinfile:
path: /etc/opendmarc.conf
regexp: '^{{item.key}}[ \t]'
insertafter: '^#{{item.key}}[ \t]'
line: '{{item.key}} {{item.value}}'
with_dict:
Socket: local:/var/spool/postfix/milter/opendmarc
- name: copy systemd-services
copy:
src: "{{item}}"
dest: /etc/systemd/system
owner: root
group: root
mode: 0444
with_fileglob: "systemd/system/*"
- name: copy service-configs
copy:
src: "{{item}}"
dest: /etc/default
owner: root
group: root
mode: 0444
with_fileglob: "systemd/default/*"
- include_task:
name: postfix
- include_task:
name: dovecot
- name: enabled services
systemd:
name: '{{item}}'
daemon-reload: true
enabled: true
with-items: [dovecot, postfix, opendkim, opendmarc, postsrsd]
- name: reload/restart services
shell: 'systemctl reload-or-restart {{item|quote}}'
with-items: [dovecot, postfix, opendkim, opendmarc, postsrsd]

142
tasks/postfix.yml Normal file
View file

@ -0,0 +1,142 @@
---
# vim: set et sw=2 ts=2 sts=2:
- name: postfix-configs
copy:
src: "{{item}}"
dest: /etc/postfix
owner: root
group: root
mode: 0444
with_fileglob:
- "postfix/*"
- name: 'postfix: main.cf'
lineinfile:
path: /etc/postfix/main.cf
insertafter: "^#{{item.key}} *= *"
regexp: "^{{item.key}} *= *"
line: "{{item.key}} = {{item.value}}"
with_dict:
compatibility_level: "2"
html_directory: /usr/share/doc/postfix/html
default_database_type: lmdb
# Verbindungssicherheit / Verschluesselung:
smtpd_tls_cert_file: "/etc/postfix/tls/{{fqdn}}.crt"
smtpd_tls_key_file: "/etc/postfix/tls/{{fqdn}}.key"
smtpd_use_tls: 'yes'
smtpd_tls_session_cache_database: 'lmdb:${data_directory}/smtpd_scache'
smtp_tls_session_cache_database: 'lmdb:${data_directory}/smtp_scache'
smtpd_tls_loglevel: "1"
smtp_tls_loglevel: "1"
smtpd_tls_security_level: may
smtp_tls_security_level: may
smtpd_tls_auth_only: 'yes'
tls_ssl_options: NO_COMPRESSION
# Some servers are crapy. If we provide only TLSv1.2, he would try it unencrypted again.
smtpd_tls_mandatory_protocols: '>=TLSv1.2'
smtpd_tls_protocols: '>=TLSv1.2'
# Same for sending mails: :/
smtp_tls_mandatory_protocols: '>=TLSv1.2'
smtp_tls_protocols: '>=TLSv1.2'
# Internal/Clients must support better crypto:
lmtp_tls_mandatory_protocols: '>=TLSv1.2'
lmtp_tls_protocols: '>=TLSv1.2'
submission_tls_mandatory_protocols: '>=TLSv1.2'
submission_tls_protocols: '>=TLSv1.2'
smtpd_tls_mandatory_ciphers: high
#tls_high_cipherlist: 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA'
smtpd_tls_exclude_ciphers: MD5, DES, eNULL, 3DES, EXP, RC4, DSS, PSK, SEED, IDEA, ECDSA, aNULL
smtpd_tls_eecdh_grade: strong
myhostname: '{{mail_postfix_domain}}'
myorigin: '{{mail_postfix_myorigin}}'
mydestination: ''
relayhost: ''
mynetworks: '{{mynetworks}}'
recipient_delimiter: '+'
inet_interfaces: 'all'
#inet_protocols: 'ipv4'
alias_maps: 'cdb:/etc/aliases'
alias_database: 'cdb:/etc/aliases'
smtp_generic_maps: cdb:/etc/postfix/generic_map
smtpd_sasl_type: dovecot
smtpd_sasl_path: private/auth
smtpd_sasl_local_domain: '{{domain}}'
smtpd_sasl_security_options: noanonymous
smtpd_sasl_auth_enable: 'no'
strict_rfc821_envelopes: 'yes'
smtpd_reject_unlisted_sender: 'yes'
smtp_tls_policy_maps: 'cdb:/etc/postfix/tls_policy'
#### Zustellung und Ueberpruefung, ob Server fuer die Domain zustaendig ist und die Adresse existiert:
# domain ist virtuell und nicht lokal!
# zustellung via lmtp and dovecot:
virtual_transport: "lmtp:unix:private/dovecot-lmtp"
# ebenso. eigentlich nicht in verwendung.
local_transport: "lmtp:unix:private/dovecot-lmtp"
# welche domains sind moeglich?
virtual_mailbox_domains: "cdb:/etc/postfix/virtual_endpoint_map"
# aliases fuer virtuelle adressen.
virtual_alias_maps: "cdb:/etc/postfix/virtual_aliases, cdb:/etc/postfix/mailinglists"
sender_canonical_maps: "cdb:/etc/postfix/sender_canonical"
# virtual_mailbox_maps wird nicht gesezt, da virtual_transport die ueberpruefung vornimmt.
smtpd_relay_restrictions: 'permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination'
address_verify_map: 'lmdb:$data_directory/verify_cache'
unknown_address_reject_code: 550
smtpd_recipient_restrictions: 'reject_unknown_reverse_client_hostname, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_sender_domain, reject_unknown_recipient_domain, reject_invalid_hostname, permit_mynetworks, reject_unauth_destination, reject_unverified_recipient, check_policy_service unix:private/policy-spf'
# Postscreen
postscreen_greet_banner: 'Loving the dog most, oh human, you say is a sin. The dog stayed true to me during the storm, the human not even during the wind.'
postscreen_cache_map: 'lmdb:$data_directory/postscreen_cache'
postscreen_access_list: 'permit_mynetworks, cidr:/etc/postfix/postscreen_access.cidr'
postscreen_blacklist_action: 'enforce'
postscreen_greet_action: 'enforce'
postscreen_pipelining_enable: 'yes'
postscreen_dnsbl_threshold: '1'
postscreen_dnsbl_sites: 'ix.dnsbl.manitu.net b.barracudacentral.org dnsbl.sorbs.net dnsbl-3.uceprotect.net dnsbl-2.uceprotect.net dnsbl-1.uceprotect.net'
postscreen_dnsbl_action: 'enforce'
postscreen_dnsbl_ttl: '1h'
# TODO: greylisting custom message
# SPF
policy-spf_time_limit: 3600s
# DKIM
milter_default_action: accept
milter_protocol: "2"
smtpd_milters: 'unix:milter/opendkim, unix:milter/opendmarc'
non_smtpd_milters: 'unix:milter/opendkim'
- name: mailinglists-aliases
template:
src: mailinglist-aliases.j2
dest: /etc/postfix/mailinglists
mode: 0444
owner: root
group: root
- name: dummy files if needed
copy:
dest: '{{item.key}}'
content: '{{item.value}}'
force: no
with_dict: '{{postfix_default_file_content}}'
- name: force TLS for these
lineinfile:
path: /etc/postfix/tls_policy
regexp: '^{{item}}[ \t]'
line: '{{item}} encrypt'
with_items: '{{postfix_tls_policy}}'
- name: prepare aliases-lookup-tables
command: newaliases
- name: prepare lookup-tables
shell: 'postmap {{item|quote}}'
args:
chdir: /etc/postfix
with_items: '{{postfix_postmap}}'

17
tasks/tls.yml Normal file
View file

@ -0,0 +1,17 @@
---
# vim: set et sw=2 ts=2 sts=2:
- name: DHs for Postfix
community.crypto.openssl_dhparam:
path: '/etc/postfix/tls/mail_{{item}}.dh'
size: '{{item}}'
owner: postfix
mode: 0400
with_items: [512,2048,4192]
- name: DHs for Dovecot
community.crypto.openssl_dhparam:
path: '/etc/dovecot/tls/mail_{{item}}.dh'
size: '{{item}}'
owner: dovecot
mode: 0400
with_items: [512,2048,4192]

2
tests/inventory Normal file
View file

@ -0,0 +1,2 @@
localhost

5
tests/test.yml Normal file
View file

@ -0,0 +1,5 @@
---
- hosts: localhost
remote_user: root
roles:
- mail

28
vars/main.yml Normal file
View file

@ -0,0 +1,28 @@
---
# vim: set et sw=2 ts=2 sts=2:
postfix_default_file_content:
"/etc/aliases": ''
"/etc/postfix/virtual_aliases": ''
"/etc/postfix/recipient_access": 'public@ REJECT'
"/etc/postfix/relaydomains": ''
"/etc/postfix/access": ''
"/etc/postfix/postscreen_access.cidr": ''
"/etc/postfix/sender_canonical": ''
"/etc/postfix/tls_policy": ''
"/etc/postfix/generic_map": ''
"/etc/postfix/virtual_domain_map": "nfotex.com OK"
"/etc/postfix/virtual_endpoint_map": ''
"/etc/postfix/transport_map": ''
postfix_postmap:
- cdb:access
- cdb:generic_map
- cdb:recipient_access
- cdb:relaydomains
- cdb:sender_canonical
- cdb:tls_policy
- cdb:transport_map
- cdb:virtual_aliases
- cdb:mailinglists
- cdb:virtual_domain_map
- cdb:virtual_endpoint_map