Compare commits
15 Commits
2861687888
...
latest
| Author | SHA1 | Date | |
|---|---|---|---|
| 059c600229 | |||
| d61a4ddcef | |||
| 1300e7acc6 | |||
| c853c75f04 | |||
| fc1900838b | |||
| b4930c3c7d | |||
| f76e0a31ae | |||
| c9892b9e51 | |||
| 672082cf64 | |||
| a24007383d | |||
| e350a39a29 | |||
| 589d3e0d12 | |||
| dfd5d89905 | |||
| e209749f74 | |||
| 87ce53d1d3 |
0
.ansible/.lock
Normal file
0
.ansible/.lock
Normal file
43
.gitea/workflows/molecule-tests.yml
Normal file
43
.gitea/workflows/molecule-tests.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
#SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
name: Molecule Tests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 7 * * 1' # Every Monday at 07:00 UTC
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
molecule-tests:
|
||||
runs-on: fedora-latest
|
||||
steps:
|
||||
- name: Add ~/.local/bin to PATH
|
||||
run: echo "$HOME/.local/bin" >> "$GITEA_PATH"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ansible_role_mail
|
||||
|
||||
- name: Install Molecule
|
||||
run: pip install ansible molecule molecule-plugins[podman]
|
||||
|
||||
- name: Install Ansible collections
|
||||
run: ansible-galaxy collection install containers.podman
|
||||
|
||||
- name: Run Molecule tests
|
||||
working-directory: ansible_role_mail
|
||||
run: molecule test
|
||||
|
||||
- name: Tag latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
working-directory: ansible_role_mail
|
||||
run: |
|
||||
git config user.name "giabot"
|
||||
git config user.email "bot@mail.gianet.us"
|
||||
git remote set-url origin "https://giabot:${{ secrets.GITEA_TOKEN }}@gianet.us/engineering/ansible_role_mail.git"
|
||||
git tag -f latest
|
||||
git push -f origin latest
|
||||
4
AGENTS.md
Normal file
4
AGENTS.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# AGENTS.md
|
||||
|
||||
- Only use `ansible.builtin`
|
||||
- Always update `README.md`
|
||||
134
README.md
134
README.md
@@ -1,72 +1,114 @@
|
||||
# **Ansible Role: Postfix**
|
||||
# Ansible Role: Mail
|
||||
|
||||
An Ansible role to install and configure Postfix on Debian-based systems.
|
||||
An Ansible role to deploy and configure an internal mail server on Debian-based systems.
|
||||
|
||||
## **Description**
|
||||
## Description
|
||||
|
||||
This role sets up Postfix to function as a local mail server designed for internal use. Its primary function is to accept mail from local services and relay all outbound messages through a configured **smarthost**.
|
||||
This role sets up a complete internal mail server stack, currently including:
|
||||
|
||||
This is the perfect setup for environments where internal applications (like cron, monitoring systems, or web applications) need to send email notifications without the complexity of managing a full, internet-facing mail server.
|
||||
- **Postfix** - Mail Transfer Agent (MTA) for sending and receiving mail
|
||||
- **Dovecot** - IMAP/POP3 server for mail retrieval
|
||||
|
||||
This role performs the following actions:
|
||||
The role is designed for **internal use cases** where applications, services, and users within your infrastructure need to send and receive email. Outbound mail is relayed through a configured smarthost (e.g., SendGrid, Mailgun, or your ISP's SMTP server).
|
||||
|
||||
* Installs the Postfix package and necessary SASL modules on Debian/Ubuntu.
|
||||
* Manages the main Postfix configuration file (/etc/postfix/main.cf) via a template.
|
||||
* Manages the /etc/mailname file for defining the mail domain.
|
||||
* Configures Postfix to route all outgoing mail through a specified smarthost.
|
||||
* Securely configures SASL authentication for the smarthost if credentials are provided.
|
||||
### Use Cases
|
||||
|
||||
## **Requirements**
|
||||
- Internal applications sending notifications (cron jobs, monitoring, CI/CD pipelines)
|
||||
- Service accounts that need to receive and process email
|
||||
- Development and testing environments
|
||||
- Private mail infrastructure for small teams
|
||||
|
||||
* **Target OS**: This role is designed exclusively for **Debian-based** distributions (e.g., Debian, Ubuntu).
|
||||
* **Ansible**: Version 2.10 or newer.
|
||||
### What This Role Does NOT Include
|
||||
|
||||
## **Role Variables**
|
||||
This role intentionally omits antispam and antivirus components. Since it's designed for internal mail that doesn't interact with external/untrusted sources, these features are unnecessary and would add complexity.
|
||||
|
||||
The role's behavior can be customized using the following variables. The default values are defined in defaults/main.yml.
|
||||
## Requirements
|
||||
|
||||
| Variable | Default Value | Description |
|
||||
| :---- | :---- | :---- |
|
||||
| postfix_relayhost | "" (empty string) | **Required.** The smarthost for relaying all mail. Use square brackets [] to prevent MX lookups (e.g., \[smtp.sendgrid.net\]:587). |
|
||||
| postfix_relayhost_user | (undefined) | The username for SASL authentication with the smarthost. If defined with a password, SASL auth will be enabled. |
|
||||
| postfix_relayhost_password | (undefined) | The password or API key for the smarthost user. **It** is strongly recommended to store this in Ansible **Vault.** |
|
||||
| postfix_mail_domain | `{{ ansible_domain \| default('internal.local') }}` | The primary mail domain for this server |
|
||||
| postfix_myhostname | `mail.{{ postfix_mail_domain }}` | The fully qualified domain name (FQDN) of the mail server itself (e.g., mail.example.com). |
|
||||
| postfix_mydestination | `$myhostname, localhost.{{ postfix_mail_domain }}, localhost, {{ postfix_mail_domain }}` | A comma-separated list of domains this server will accept mail for. The default is usually sufficient for an internal relay. |
|
||||
| postfix_mynetworks | `"127.0.0.0/8 [::1]/128"` | The list of "trusted" remote SMTP clients that have more privileges than "strangers"|
|
||||
| postfix_inet_interfaces | all | The network interfaces Postfix listens on. Set to loopback-only to only accept mail from the server itself. |
|
||||
| postfix_inet_protocols | all | The IP protocols to use (ipv4, ipv6, or all). |
|
||||
- **Target OS**: Debian-based distributions (Debian, Ubuntu)
|
||||
- **Ansible**: Version 2.10 or newer
|
||||
|
||||
### **SASL Authentication**
|
||||
## Role Variables
|
||||
|
||||
SASL authentication for the smarthost is **automatically enabled** if both postfix_relayhost_user and postfix_relayhost_password are defined. If they are not defined, Postfix will attempt to send mail without authentication.
|
||||
Default values are defined in `defaults/main.yml`.
|
||||
|
||||
## **Dependencies**
|
||||
### General Settings
|
||||
|
||||
This role has no dependencies on other Ansible roles or collections beyond the standard ansible.builtin modules.
|
||||
| Variable | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| mail_ssl_cert | snakeoil | Path to SSL certificate (shared by Postfix and Dovecot). |
|
||||
| mail_ssl_key | snakeoil | Path to SSL private key (shared by Postfix and Dovecot). |
|
||||
|
||||
## **Example Playbook**
|
||||
### Postfix Configuration
|
||||
|
||||
Here is a basic example of how to use this role in your playbook. You must define postfix_relayhost. It is also highly recommended to use Ansible Vault to encrypt the smarthost password.
|
||||
| Variable | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| postfix_relayhost | "" | **Required.** Smarthost for relaying outbound mail. Use brackets to skip MX lookups (e.g., `[smtp.sendgrid.net]:587`). |
|
||||
| postfix_relayhost_user | (undefined) | Username for smarthost SASL authentication. |
|
||||
| postfix_relayhost_password | (undefined) | Password/API key for smarthost. Store in Ansible Vault. |
|
||||
| postfix_mail_domain | `{{ ansible_domain }}` | Primary mail domain for this server. |
|
||||
| postfix_myhostname | `mail.{{ postfix_mail_domain }}` | FQDN of the mail server. |
|
||||
| postfix_mydestination | `$myhostname, localhost...` | Domains accepted for local delivery. |
|
||||
| postfix_mynetworks | `127.0.0.0/8 [::1]/128` | Trusted networks allowed to relay. |
|
||||
| postfix_inet_interfaces | all | Network interfaces to listen on. Use `loopback-only` for local-only access. |
|
||||
| postfix_inet_protocols | all | IP protocols to use (ipv4, ipv6, or all). |
|
||||
|
||||
SASL authentication for the smarthost is automatically enabled when both `postfix_relayhost_user` and `postfix_relayhost_password` are defined.
|
||||
|
||||
### Dovecot Configuration
|
||||
|
||||
| Variable | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| dovecot_enabled | true | Install and configure Dovecot. |
|
||||
| dovecot_protocols | "imap pop3 lmtp" | Protocols to enable. |
|
||||
| dovecot_mail_location | "maildir:~/Maildir" | Mail storage format and location. |
|
||||
| dovecot_ssl | "yes" | SSL/TLS mode: `yes`, `no`, or `required`. |
|
||||
| dovecot_auth_mechanisms | "plain login" | Allowed authentication mechanisms. |
|
||||
| dovecot_postfix_sasl_enable | true | Allow Postfix to authenticate users via Dovecot. |
|
||||
| dovecot_postfix_lmtp_enable | true | Deliver mail to Dovecot via LMTP. |
|
||||
| dovecot_imap_capability | "" | Adjust advertised IMAP capabilities (e.g., `+IMAP4rev1 -LITERAL+`). |
|
||||
| dovecot_users | [] | List of virtual mailbox users. See below. |
|
||||
|
||||
### Virtual Mailbox Users
|
||||
|
||||
Define users for Dovecot virtual mailboxes:
|
||||
|
||||
```yaml
|
||||
dovecot_users:
|
||||
- name: "service1"
|
||||
pass: "mysecretpassword"
|
||||
```
|
||||
---
|
||||
- hosts: all
|
||||
become: true
|
||||
roles:
|
||||
- role: your_username.postfix
|
||||
vars:
|
||||
postfix_relayhost: "[smtp.mailgun.org\]:587"
|
||||
postfix_relayhost_user: "postmaster@mg.example.com"
|
||||
postfix_relayhost_password: "{{ vaulted_mailgun_password }}" # Stored in Ansible Vault
|
||||
postfix_inet_interfaces: "loopback-only"
|
||||
|
||||
For security, the role generates a random 16-character token on the server (stored in `/etc/dovecot/dovecot_token`). The actual password is `token + password`. For example, if the token is `He5rN5SPH33AbFLn`, the user must authenticate with `He5rN5SPH33AbFLnmysecretpassword`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None.
|
||||
|
||||
## Example Playbook
|
||||
|
||||
```yaml
|
||||
---
|
||||
- hosts: mail_servers
|
||||
become: true
|
||||
roles:
|
||||
- role: giacchetta.mail
|
||||
vars:
|
||||
postfix_mail_domain: "example.com"
|
||||
postfix_relayhost: "[smtp.mailgun.org]:587"
|
||||
postfix_relayhost_user: "postmaster@mg.example.com"
|
||||
postfix_relayhost_password: "{{ vault_mailgun_password }}"
|
||||
mail_ssl_cert: "/etc/letsencrypt/live/mail.example.com/fullchain.pem"
|
||||
mail_ssl_key: "/etc/letsencrypt/live/mail.example.com/privkey.pem"
|
||||
dovecot_ssl: "required"
|
||||
dovecot_users:
|
||||
- name: "alerts"
|
||||
pass: "{{ vault_alerts_password }}"
|
||||
```
|
||||
|
||||
## **License**
|
||||
## License
|
||||
|
||||
GPL-3.0-only
|
||||
|
||||
## **Author Information**
|
||||
## Author Information
|
||||
|
||||
This role was created by Giacchetta Networks.
|
||||
This role was created by Giacchetta Networks.
|
||||
|
||||
@@ -18,9 +18,10 @@ postfix_mail_domain: "{{ ansible_domain | default('internal.local') }}"
|
||||
# The Fully Qualified Domain Name of the mail server.
|
||||
postfix_myhostname: "mail.{{ postfix_mail_domain }}"
|
||||
|
||||
# Comma-separated list of domains this server accepts mail for.
|
||||
# It's critical that this includes the server's own hostname and mail domain.
|
||||
postfix_mydestination: "$myhostname, localhost.{{ postfix_mail_domain }}, localhost, {{ postfix_mail_domain }}"
|
||||
# Comma-separated list of domains this server accepts mail for locally.
|
||||
# When using Dovecot with LMTP (virtual mailboxes), the mail domain is handled
|
||||
# separately via virtual_mailbox_domains, so it should NOT be included here.
|
||||
postfix_mydestination: "$myhostname, localhost.{{ postfix_mail_domain }}, localhost"
|
||||
|
||||
# The list of "trusted" remote SMTP clients that have more privileges than "strangers".
|
||||
postfix_mynetworks: "127.0.0.0/8 [::1]/128"
|
||||
@@ -34,4 +35,42 @@ postfix_relayhost: ""
|
||||
# Optional credentials for the relayhost. If these are defined,
|
||||
# SASL authentication will be automatically configured.
|
||||
# postfix_relayhost_user: "apikey"
|
||||
# postfix_relayhost_password: "YourVeryLongAndComplexApiKey"
|
||||
# postfix_relayhost_password: "YourVeryLongAndComplexApiKey"
|
||||
|
||||
# --- Dovecot Configuration ---
|
||||
|
||||
# Whether to install and configure Dovecot
|
||||
dovecot_enabled: true
|
||||
|
||||
# Protocols to enable (imap, pop3, lmtp)
|
||||
dovecot_protocols: "imap pop3 lmtp"
|
||||
|
||||
# IMAP capability adjustments. Set to modify advertised IMAP capabilities.
|
||||
# Use +CAPABILITY to add, -CAPABILITY to remove.
|
||||
# Example: "+IMAP4rev1 -LITERAL+ -NOTIFY" removes modern extensions that
|
||||
# might suppress standard untagged responses.
|
||||
# Leave empty to use Dovecot defaults.
|
||||
dovecot_imap_capability: ""
|
||||
|
||||
# Mail storage location. Using Maildir in the user's home directory.
|
||||
dovecot_mail_location: "maildir:~/Maildir"
|
||||
|
||||
# SSL/TLS configuration
|
||||
# Use 'yes', 'no' or 'required'. 'required' is recommended for production.
|
||||
dovecot_ssl: "yes"
|
||||
mail_ssl_cert: "/etc/ssl/certs/ssl-cert-snakeoil.pem"
|
||||
mail_ssl_key: "/etc/ssl/private/ssl-cert-snakeoil.key"
|
||||
|
||||
# Authentication mechanisms
|
||||
dovecot_auth_mechanisms: "plain login"
|
||||
|
||||
# Postfix integration
|
||||
dovecot_postfix_sasl_enable: true
|
||||
dovecot_postfix_lmtp_enable: true
|
||||
|
||||
# Local Dovecot Users
|
||||
# Example:
|
||||
# dovecot_users:
|
||||
# - name: "service1"
|
||||
# pass: "secret123"
|
||||
dovecot_users: []
|
||||
@@ -2,4 +2,9 @@
|
||||
- name: Restart Postfix
|
||||
ansible.builtin.service:
|
||||
name: postfix
|
||||
state: restarted
|
||||
|
||||
- name: Restart Dovecot
|
||||
ansible.builtin.service:
|
||||
name: dovecot
|
||||
state: restarted
|
||||
@@ -1,5 +1,6 @@
|
||||
galaxy_info:
|
||||
role_name: "mailserver"
|
||||
namespace: "gianet"
|
||||
author: "Luciano Giacchetta"
|
||||
description: "Complete Mail Server Role"
|
||||
company: "Giacchetta Networks LLC"
|
||||
|
||||
14
molecule/default/Dockerfile
Normal file
14
molecule/default/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
ARG MOLECULE_DISTRO=docker.io/library/debian:stable
|
||||
FROM ${MOLECULE_DISTRO}
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
systemd \
|
||||
systemd-sysv \
|
||||
dbus \
|
||||
ca-certificates && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
CMD ["/usr/sbin/init"]
|
||||
11
molecule/default/converge.yml
Normal file
11
molecule/default/converge.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
#SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
vars:
|
||||
postfix_mail_domain: "test.local"
|
||||
dovecot_users:
|
||||
- name: "testuser"
|
||||
pass: "testpass"
|
||||
roles:
|
||||
- role: ansible_role_mail
|
||||
72
molecule/default/molecule.yml
Normal file
72
molecule/default/molecule.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
#SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
options:
|
||||
requirements-file: molecule/default/requirements.yml
|
||||
|
||||
driver:
|
||||
name: podman
|
||||
|
||||
platforms:
|
||||
- name: debian-stable
|
||||
image: docker.io/library/debian:stable
|
||||
pre_build_image: false
|
||||
dockerfile: Dockerfile
|
||||
buildargs:
|
||||
MOLECULE_DISTRO: docker.io/library/debian:stable
|
||||
privileged: true
|
||||
systemd: always
|
||||
command: /usr/sbin/init
|
||||
|
||||
- name: debian-oldstable
|
||||
image: docker.io/library/debian:oldstable
|
||||
pre_build_image: false
|
||||
dockerfile: Dockerfile
|
||||
buildargs:
|
||||
MOLECULE_DISTRO: docker.io/library/debian:oldstable
|
||||
privileged: true
|
||||
systemd: always
|
||||
command: /usr/sbin/init
|
||||
|
||||
- name: ubuntu-latest
|
||||
image: docker.io/library/ubuntu:latest
|
||||
pre_build_image: false
|
||||
dockerfile: Dockerfile
|
||||
buildargs:
|
||||
MOLECULE_DISTRO: docker.io/library/ubuntu:latest
|
||||
privileged: true
|
||||
systemd: always
|
||||
command: /usr/sbin/init
|
||||
|
||||
- name: ubuntu-jammy
|
||||
image: docker.io/library/ubuntu:jammy
|
||||
pre_build_image: false
|
||||
dockerfile: Dockerfile
|
||||
buildargs:
|
||||
MOLECULE_DISTRO: docker.io/library/ubuntu:jammy
|
||||
privileged: true
|
||||
systemd: always
|
||||
command: /usr/sbin/init
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
env:
|
||||
ANSIBLE_ROLES_PATH: "${MOLECULE_PROJECT_DIRECTORY}/.."
|
||||
playbooks:
|
||||
converge: converge.yml
|
||||
verify: verify.yml
|
||||
|
||||
scenario:
|
||||
test_sequence:
|
||||
- dependency
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- converge
|
||||
- idempotence
|
||||
- verify
|
||||
- destroy
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
4
molecule/default/requirements.yml
Normal file
4
molecule/default/requirements.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
#SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
collections:
|
||||
- name: containers.podman
|
||||
37
molecule/default/verify.yml
Normal file
37
molecule/default/verify.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
#SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Gather service facts
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Assert postfix service is present
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'postfix.service' in ansible_facts.services"
|
||||
|
||||
- name: Assert dovecot service is present
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'dovecot.service' in ansible_facts.services"
|
||||
|
||||
- name: Check postfix main.cf exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/postfix/main.cf
|
||||
register: postfix_main_cf
|
||||
|
||||
- name: Assert postfix main.cf exists
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- postfix_main_cf.stat.exists
|
||||
|
||||
- name: Check dovecot.conf exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/dovecot/dovecot.conf
|
||||
register: dovecot_conf
|
||||
|
||||
- name: Assert dovecot.conf exists
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- dovecot_conf.stat.exists
|
||||
165
tasks/main.yml
165
tasks/main.yml
@@ -52,4 +52,167 @@
|
||||
notify: Restart Postfix
|
||||
tags:
|
||||
- postfix_config
|
||||
- postfix_smarthost
|
||||
- postfix_smarthost
|
||||
|
||||
- name: "POSTFIX | Configure virtual mailbox maps"
|
||||
when: dovecot_enabled | default(false) and dovecot_postfix_lmtp_enable | default(false)
|
||||
ansible.builtin.template:
|
||||
src: virtual_mailbox_maps.j2
|
||||
dest: /etc/postfix/virtual_mailbox_maps
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
register: virtual_mailbox_maps_template
|
||||
notify: Restart Postfix
|
||||
tags:
|
||||
- postfix_config
|
||||
- dovecot_config
|
||||
|
||||
- name: "POSTFIX | Create hash map for virtual mailbox maps"
|
||||
when:
|
||||
- dovecot_enabled | default(false) and dovecot_postfix_lmtp_enable | default(false)
|
||||
- virtual_mailbox_maps_template.changed
|
||||
ansible.builtin.command:
|
||||
cmd: postmap hash:/etc/postfix/virtual_mailbox_maps
|
||||
changed_when: true
|
||||
notify: Restart Postfix
|
||||
tags:
|
||||
- postfix_config
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Install Dovecot packages"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.apt:
|
||||
name: "{{ ['dovecot-core', 'dovecot-imapd', 'dovecot-pop3d', 'openssl'] + (['dovecot-lmtpd'] if dovecot_postfix_lmtp_enable | default(false) else []) }}"
|
||||
state: present
|
||||
tags:
|
||||
- dovecot_install
|
||||
|
||||
- name: "DOVECOT | Install pwgen"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.apt:
|
||||
name: pwgen
|
||||
state: present
|
||||
tags:
|
||||
- dovecot_install
|
||||
|
||||
- name: "DOVECOT | Generate Dovecot token"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.shell:
|
||||
cmd: "pwgen -s 16 1 > /etc/dovecot/dovecot_token"
|
||||
creates: /etc/dovecot/dovecot_token
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Read Dovecot token"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.slurp:
|
||||
src: /etc/dovecot/dovecot_token
|
||||
register: dovecot_token_file
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Create vmail group"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.group:
|
||||
name: vmail
|
||||
gid: 5000
|
||||
state: present
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Create vmail user"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.user:
|
||||
name: vmail
|
||||
uid: 5000
|
||||
group: vmail
|
||||
home: /var/vmail
|
||||
create_home: true
|
||||
system: true
|
||||
shell: /usr/sbin/nologin
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Ensure vmail directory permissions"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.file:
|
||||
path: /var/vmail
|
||||
state: directory
|
||||
owner: vmail
|
||||
group: vmail
|
||||
mode: '0700'
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Generate user password hashes"
|
||||
when: dovecot_enabled | default(false) and dovecot_users | length > 0
|
||||
ansible.builtin.command:
|
||||
cmd: "openssl passwd -6 -salt {{ dovecot_token_value | quote }} {{ (dovecot_token_value + item.pass) | quote }}"
|
||||
loop: "{{ dovecot_users }}"
|
||||
register: dovecot_user_hashes
|
||||
changed_when: false
|
||||
vars:
|
||||
dovecot_token_value: "{{ dovecot_token_file['content'] | b64decode | trim }}"
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Create users password file"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.template:
|
||||
src: dovecot-users.j2
|
||||
dest: /etc/dovecot/users
|
||||
owner: root
|
||||
group: dovecot
|
||||
mode: '0640'
|
||||
vars:
|
||||
dovecot_token_value: "{{ dovecot_token_file['content'] | b64decode | trim }}"
|
||||
notify: Restart Dovecot
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Detect Dovecot version"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.shell:
|
||||
cmd: "dovecot --version | awk '{print $1}' | cut -d'(' -f1"
|
||||
register: dovecot_version_raw
|
||||
changed_when: false
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Set Dovecot major version fact"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.set_fact:
|
||||
dovecot_major_version: "{{ dovecot_version_raw.stdout.split('.')[0] | int }}.{{ dovecot_version_raw.stdout.split('.')[1] | int }}"
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Configure dovecot.conf"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.template:
|
||||
src: dovecot.conf.j2
|
||||
dest: /etc/dovecot/dovecot.conf
|
||||
owner: root
|
||||
group: dovecot
|
||||
mode: '0644'
|
||||
notify: Restart Dovecot
|
||||
tags:
|
||||
- dovecot_config
|
||||
|
||||
- name: "DOVECOT | Configure conf.d files"
|
||||
when: dovecot_enabled | default(false)
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.src }}"
|
||||
dest: "/etc/dovecot/conf.d/{{ item.dest }}"
|
||||
owner: root
|
||||
group: dovecot
|
||||
mode: '0644'
|
||||
loop:
|
||||
- { src: '10-auth.conf.j2', dest: '10-auth.conf' }
|
||||
- { src: 'auth-dovecot-users.conf.ext.j2', dest: 'auth-dovecot-users.conf.ext' }
|
||||
- { src: '10-master.conf.j2', dest: '10-master.conf' }
|
||||
- { src: '10-ssl.conf.j2', dest: '10-ssl.conf' }
|
||||
- { src: '10-mail.conf.j2', dest: '10-mail.conf' }
|
||||
notify: Restart Dovecot
|
||||
tags:
|
||||
- dovecot_config
|
||||
12
templates/10-auth.conf.j2
Normal file
12
templates/10-auth.conf.j2
Normal file
@@ -0,0 +1,12 @@
|
||||
# Dovecot authentication configuration
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
|
||||
{% if dovecot_major_version is defined and dovecot_major_version is version('2.4', '>=') %}
|
||||
auth_allow_cleartext = {{ 'no' if dovecot_ssl == 'required' else 'yes' }}
|
||||
{% else %}
|
||||
disable_plaintext_auth = {{ 'yes' if dovecot_ssl == 'required' else 'no' }}
|
||||
{% endif %}
|
||||
auth_mechanisms = {{ dovecot_auth_mechanisms }}
|
||||
|
||||
!include auth-dovecot-users.conf.ext
|
||||
!include auth-system.conf.ext
|
||||
33
templates/10-mail.conf.j2
Normal file
33
templates/10-mail.conf.j2
Normal file
@@ -0,0 +1,33 @@
|
||||
# Dovecot mail location configuration
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
{% if dovecot_major_version is defined and dovecot_major_version is version('2.4', '>=') %}
|
||||
{% set _driver = dovecot_mail_location.split(':')[0] %}
|
||||
{% set _path = dovecot_mail_location.split(':')[1] %}
|
||||
mail_driver = {{ _driver }}
|
||||
mail_path = {{ _path }}
|
||||
{% else %}
|
||||
mail_location = {{ dovecot_mail_location }}
|
||||
{% endif %}
|
||||
namespace inbox {
|
||||
inbox = yes
|
||||
|
||||
mailbox Drafts {
|
||||
special_use = \Drafts
|
||||
auto = subscribe
|
||||
}
|
||||
mailbox Junk {
|
||||
special_use = \Junk
|
||||
auto = subscribe
|
||||
}
|
||||
mailbox Trash {
|
||||
special_use = \Trash
|
||||
auto = subscribe
|
||||
}
|
||||
mailbox Sent {
|
||||
special_use = \Sent
|
||||
auto = subscribe
|
||||
}
|
||||
mailbox "Sent Messages" {
|
||||
special_use = \Sent
|
||||
}
|
||||
}
|
||||
52
templates/10-master.conf.j2
Normal file
52
templates/10-master.conf.j2
Normal file
@@ -0,0 +1,52 @@
|
||||
# Dovecot master configuration
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
|
||||
service imap-login {
|
||||
inet_listener imap {
|
||||
port = 143
|
||||
}
|
||||
inet_listener imaps {
|
||||
port = 993
|
||||
ssl = yes
|
||||
}
|
||||
}
|
||||
|
||||
service pop3-login {
|
||||
inet_listener pop3 {
|
||||
port = 110
|
||||
}
|
||||
inet_listener pop3s {
|
||||
port = 995
|
||||
ssl = yes
|
||||
}
|
||||
}
|
||||
|
||||
service lmtp {
|
||||
unix_listener /var/spool/postfix/private/dovecot-lmtp {
|
||||
mode = 0600
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
}
|
||||
|
||||
service auth {
|
||||
unix_listener /var/spool/postfix/private/auth {
|
||||
mode = 0660
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
|
||||
unix_listener auth-userdb {
|
||||
mode = 0600
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
}
|
||||
|
||||
service auth-worker {
|
||||
}
|
||||
|
||||
service dict {
|
||||
unix_listener dict {
|
||||
}
|
||||
}
|
||||
11
templates/10-ssl.conf.j2
Normal file
11
templates/10-ssl.conf.j2
Normal file
@@ -0,0 +1,11 @@
|
||||
# Dovecot SSL configuration
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
|
||||
ssl = {{ dovecot_ssl }}
|
||||
{% if dovecot_major_version is defined and dovecot_major_version is version('2.4', '>=') %}
|
||||
ssl_server_cert_file = {{ mail_ssl_cert }}
|
||||
ssl_server_key_file = {{ mail_ssl_key }}
|
||||
{% else %}
|
||||
ssl_cert = <{{ mail_ssl_cert }}
|
||||
ssl_key = <{{ mail_ssl_key }}
|
||||
{% endif %}
|
||||
29
templates/auth-dovecot-users.conf.ext.j2
Normal file
29
templates/auth-dovecot-users.conf.ext.j2
Normal file
@@ -0,0 +1,29 @@
|
||||
# Dovecot local users authentication
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
{% if dovecot_major_version is defined and dovecot_major_version is version('2.4', '>=') %}
|
||||
|
||||
passdb passwd-file {
|
||||
default_password_scheme = SHA512-CRYPT
|
||||
auth_username_format = %{user|username}
|
||||
passwd_file_path = /etc/dovecot/users
|
||||
}
|
||||
|
||||
userdb static {
|
||||
fields {
|
||||
uid = vmail
|
||||
gid = vmail
|
||||
home = /var/vmail/%{user|username}
|
||||
}
|
||||
}
|
||||
{% else %}
|
||||
|
||||
passdb {
|
||||
driver = passwd-file
|
||||
args = scheme=SHA512-CRYPT username_format=%n /etc/dovecot/users
|
||||
}
|
||||
|
||||
userdb {
|
||||
driver = static
|
||||
args = uid=vmail gid=vmail home=/var/vmail/%n
|
||||
}
|
||||
{% endif %}
|
||||
8
templates/dovecot-users.j2
Normal file
8
templates/dovecot-users.j2
Normal file
@@ -0,0 +1,8 @@
|
||||
# Dovecot users file
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
# user:{scheme}hash:uid:gid:gecos:home:shell:extra_fields
|
||||
{% if dovecot_user_hashes.results is defined %}
|
||||
{% for res in dovecot_user_hashes.results %}
|
||||
{{ res.item.name }}:{SHA512-CRYPT}{{ res.stdout | trim }}::::::
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
17
templates/dovecot.conf.j2
Normal file
17
templates/dovecot.conf.j2
Normal file
@@ -0,0 +1,17 @@
|
||||
# Dovecot configuration file
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
{% if dovecot_major_version is defined and dovecot_major_version is version('2.4', '>=') %}
|
||||
dovecot_config_version = 2.4.0
|
||||
dovecot_storage_version = 2.4.0
|
||||
{% endif %}
|
||||
|
||||
protocols = {{ dovecot_protocols }}
|
||||
{% if dovecot_imap_capability | default('') | length > 0 %}
|
||||
|
||||
protocol imap {
|
||||
imap_capability = {{ dovecot_imap_capability }}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
# Dictionary of configuration files
|
||||
!include conf.d/*.conf
|
||||
@@ -16,9 +16,8 @@ inet_interfaces = {{ postfix_inet_interfaces }}
|
||||
recipient_delimiter = +
|
||||
|
||||
# TLS parameters for incoming connections
|
||||
# For a production server, replace snakeoil with real certificates.
|
||||
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
|
||||
smtpd_tls_cert_file={{ mail_ssl_cert }}
|
||||
smtpd_tls_key_file={{ mail_ssl_key }}
|
||||
smtpd_tls_security_level=may
|
||||
smtpd_use_tls=yes
|
||||
|
||||
@@ -48,3 +47,20 @@ smtp_tls_security_level = may
|
||||
# Other settings
|
||||
alias_maps = hash:/etc/aliases
|
||||
alias_database = hash:/etc/aliases
|
||||
|
||||
# Dovecot Integration
|
||||
{% if dovecot_enabled | default(false) %}
|
||||
{% if dovecot_postfix_sasl_enable | default(false) %}
|
||||
# SASL Authentication via Dovecot
|
||||
smtpd_sasl_type = dovecot
|
||||
smtpd_sasl_path = private/auth
|
||||
smtpd_sasl_auth_enable = yes
|
||||
{% endif %}
|
||||
|
||||
{% if dovecot_postfix_lmtp_enable | default(false) %}
|
||||
# Virtual mailbox configuration for Dovecot users
|
||||
virtual_mailbox_domains = {{ postfix_mail_domain }}
|
||||
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps
|
||||
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
8
templates/virtual_mailbox_maps.j2
Normal file
8
templates/virtual_mailbox_maps.j2
Normal file
@@ -0,0 +1,8 @@
|
||||
# Virtual mailbox maps for Postfix
|
||||
# Ansible managed: {{ ansible_managed }}
|
||||
# Format: user@domain OK
|
||||
{% if dovecot_users is defined and dovecot_users | length > 0 %}
|
||||
{% for user in dovecot_users %}
|
||||
{{ user.name }}@{{ postfix_mail_domain }} OK
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -2,4 +2,4 @@
|
||||
- hosts: localhost
|
||||
remote_user: root
|
||||
roles:
|
||||
- ansible_role_mailserver
|
||||
- ansible_role_mail
|
||||
|
||||
Reference in New Issue
Block a user