Compare commits
15 Commits
2861687888
...
main
| 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.
|
### Use Cases
|
||||||
* 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.
|
|
||||||
|
|
||||||
## **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).
|
### What This Role Does NOT Include
|
||||||
* **Ansible**: Version 2.10 or newer.
|
|
||||||
|
|
||||||
## **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 |
|
- **Target OS**: Debian-based distributions (Debian, Ubuntu)
|
||||||
| :---- | :---- | :---- |
|
- **Ansible**: Version 2.10 or newer
|
||||||
| 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). |
|
|
||||||
|
|
||||||
### **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
|
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`.
|
||||||
become: true
|
|
||||||
roles:
|
## Dependencies
|
||||||
- role: your_username.postfix
|
|
||||||
vars:
|
None.
|
||||||
postfix_relayhost: "[smtp.mailgun.org\]:587"
|
|
||||||
postfix_relayhost_user: "postmaster@mg.example.com"
|
## Example Playbook
|
||||||
postfix_relayhost_password: "{{ vaulted_mailgun_password }}" # Stored in Ansible Vault
|
|
||||||
postfix_inet_interfaces: "loopback-only"
|
```yaml
|
||||||
|
---
|
||||||
|
- hosts: mail_servers
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: giacchetta.mail
|
||||||
|
vars:
|
||||||
postfix_mail_domain: "example.com"
|
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
|
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.
|
# The Fully Qualified Domain Name of the mail server.
|
||||||
postfix_myhostname: "mail.{{ postfix_mail_domain }}"
|
postfix_myhostname: "mail.{{ postfix_mail_domain }}"
|
||||||
|
|
||||||
# Comma-separated list of domains this server accepts mail for.
|
# Comma-separated list of domains this server accepts mail for locally.
|
||||||
# It's critical that this includes the server's own hostname and mail domain.
|
# When using Dovecot with LMTP (virtual mailboxes), the mail domain is handled
|
||||||
postfix_mydestination: "$myhostname, localhost.{{ postfix_mail_domain }}, localhost, {{ postfix_mail_domain }}"
|
# 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".
|
# The list of "trusted" remote SMTP clients that have more privileges than "strangers".
|
||||||
postfix_mynetworks: "127.0.0.0/8 [::1]/128"
|
postfix_mynetworks: "127.0.0.0/8 [::1]/128"
|
||||||
@@ -34,4 +35,42 @@ postfix_relayhost: ""
|
|||||||
# Optional credentials for the relayhost. If these are defined,
|
# Optional credentials for the relayhost. If these are defined,
|
||||||
# SASL authentication will be automatically configured.
|
# SASL authentication will be automatically configured.
|
||||||
# postfix_relayhost_user: "apikey"
|
# 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
|
- name: Restart Postfix
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
name: postfix
|
name: postfix
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: Restart Dovecot
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: dovecot
|
||||||
state: restarted
|
state: restarted
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
galaxy_info:
|
galaxy_info:
|
||||||
role_name: "mailserver"
|
role_name: "mailserver"
|
||||||
|
namespace: "gianet"
|
||||||
author: "Luciano Giacchetta"
|
author: "Luciano Giacchetta"
|
||||||
description: "Complete Mail Server Role"
|
description: "Complete Mail Server Role"
|
||||||
company: "Giacchetta Networks LLC"
|
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
|
notify: Restart Postfix
|
||||||
tags:
|
tags:
|
||||||
- postfix_config
|
- 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 = +
|
recipient_delimiter = +
|
||||||
|
|
||||||
# TLS parameters for incoming connections
|
# TLS parameters for incoming connections
|
||||||
# For a production server, replace snakeoil with real certificates.
|
smtpd_tls_cert_file={{ mail_ssl_cert }}
|
||||||
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
|
smtpd_tls_key_file={{ mail_ssl_key }}
|
||||||
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
|
|
||||||
smtpd_tls_security_level=may
|
smtpd_tls_security_level=may
|
||||||
smtpd_use_tls=yes
|
smtpd_use_tls=yes
|
||||||
|
|
||||||
@@ -48,3 +47,20 @@ smtp_tls_security_level = may
|
|||||||
# Other settings
|
# Other settings
|
||||||
alias_maps = hash:/etc/aliases
|
alias_maps = hash:/etc/aliases
|
||||||
alias_database = 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
|
- hosts: localhost
|
||||||
remote_user: root
|
remote_user: root
|
||||||
roles:
|
roles:
|
||||||
- ansible_role_mailserver
|
- ansible_role_mail
|
||||||
|
|||||||
Reference in New Issue
Block a user