Implement Postfix (basic) #2

Merged
giacchetta merged 2 commits from 1-implement-postfix into main 2025-07-29 18:56:21 -03:00
11 changed files with 253 additions and 1 deletions

View File

@ -1,2 +1,72 @@
# ansible_role_mailserver
# **Ansible Role: Postfix**
An Ansible role to install and configure Postfix on Debian-based systems.
## **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 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.
This role performs the following actions:
* 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.
## **Requirements**
* **Target OS**: This role is designed exclusively for **Debian-based** distributions (e.g., Debian, Ubuntu).
* **Ansible**: Version 2.10 or newer.
## **Role Variables**
The role's behavior can be customized using the following variables. The default values are defined in defaults/main.yml.
| 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). |
### **SASL Authentication**
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.
## **Dependencies**
This role has no dependencies on other Ansible roles or collections beyond the standard ansible.builtin modules.
## **Example Playbook**
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.
```
---
- 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"
postfix_mail_domain: "example.com"
```
## **License**
GPL-3.0-only
## **Author Information**
This role was created by Giacchetta Networks.

37
defaults/main.yml Normal file
View File

@ -0,0 +1,37 @@
#
# Default variables for the role. These can be overridden in your inventory
# or playbook to customize the deployment.
#
# The Internet protocols Postfix will attempt to use when making or accepting connections. Specify one or more of "ipv4" or "ipv6", separated by whitespace or commas.
# The form "all" is equivalent to "ipv4, ipv6" or "ipv4", depending on whether the operating system implements IPv6.
postfix_inet_protocols: "all"
# The local network interface addresses that this mail system receives mail on. Specify "all" to receive mail on all network interfaces (default),
# "loopback-only" to receive mail on loopback network interfaces only (Postfix version 2.2 and later), or zero or more IPv4 or IPv6 addresses
# (IPv6 is supported in Postfix version 2.2 and later)
postfix_inet_interfaces: "all"
# The primary mail domain for this server.
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 }}"
# The list of "trusted" remote SMTP clients that have more privileges than "strangers".
postfix_mynetworks: "127.0.0.0/8 [::1]/128"
# The relayhost (smarthost) for all outgoing mail.
# This variable MUST be set for the role to work as intended.
# Example: "[smtp.sendgrid.net]:587"
# Note: The square brackets [] are important to prevent MX record lookups.
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"

0
files/.gitkeep Normal file
View File

5
handlers/main.yml Normal file
View File

@ -0,0 +1,5 @@
---
- name: Restart Postfix
ansible.builtin.service:
name: postfix
state: restarted

21
meta/main.yml Normal file
View File

@ -0,0 +1,21 @@
galaxy_info:
role_name: "mailserver"
author: "Luciano Giacchetta"
description: "Complete Mail Server Role"
company: "Giacchetta Networks LLC"
issue_tracker_url: "https://gianet.us/engineering/ansible_role_mailserver/issues"
license: "GPL-3.0-only"
min_ansible_version: "2.12"
platforms:
- name: "Debian"
versions:
- "all"
- name: "Ubuntu"
versions:
- "all"
galaxy_tags:
- "mta"
- "mail"
- "postfix"
dependencies: []

55
tasks/main.yml Normal file
View File

@ -0,0 +1,55 @@
---
- name: "POSTFIX | Install postfix package"
ansible.builtin.apt:
name:
- postfix
- postfix-pcre # Often useful for advanced matching
- libsasl2-modules # Required for SASL authentication
state: present
update_cache: true
tags:
- postfix_install
- name: "POSTFIX | Configure /etc/mailname"
ansible.builtin.copy:
content: "{{ postfix_mail_domain }}\n"
dest: /etc/mailname
owner: root
group: root
mode: '0644'
tags:
- postfix_config
- name: "POSTFIX | Configure main.cf"
ansible.builtin.template:
src: main.cf.j2
dest: /etc/postfix/main.cf
owner: root
group: root
mode: '0644'
validate: 'postfix check -c %s' # Validates the template before deploying
notify: Restart Postfix # Triggers the handler to restart the service
tags:
- postfix_config
- name: "POSTFIX | Configure smarthost credentials (if defined)"
when: postfix_relayhost_user is defined and postfix_relayhost_password is defined
block:
- name: "POSTFIX | Template the SASL password file"
ansible.builtin.template:
src: sasl_passwd.j2
dest: /etc/postfix/sasl_passwd
owner: root
group: root
mode: '0600' # Secure permissions for file with credentials
no_log: true # Prevents credentials from being displayed in Ansible logs
notify: Restart Postfix
- name: "POSTFIX | Create hash map for SASL password file"
ansible.builtin.command:
cmd: postmap hash:/etc/postfix/sasl_passwd
changed_when: true # The postmap command always updates the .db file
notify: Restart Postfix
tags:
- postfix_config
- postfix_smarthost

50
templates/main.cf.j2 Normal file
View File

@ -0,0 +1,50 @@
# This Jinja2 template is used to generate the /etc/postfix/main.cf file.
# It uses variables to make the role reusable.
#
# See: https://www.postfix.org/postconf.5.html
#
# Ansible managed: {{ ansible_managed }}
#
# Basic configuration
smtpd_banner = $myhostname ESMTP
biff = no
append_dot_mydomain = no
readme_directory = no
compatibility_level = 3.6
inet_protocols = {{ postfix_inet_protocols }}
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_security_level=may
smtpd_use_tls=yes
# Host and domain configuration
myhostname = {{ postfix_myhostname }}
myorigin = /etc/mailname
mydestination = {{ postfix_mydestination }}
mynetworks = {{ postfix_mynetworks }}
# Relayhost (smarthost) configuration
# All outgoing mail will be sent through this host. This is the only
# supported outbound method in this configuration.
relayhost = {{ postfix_relayhost }}
# SASL configuration for the relayhost (if credentials are provided)
{% if postfix_relayhost_user is defined and postfix_relayhost_password is defined %}
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
# Use 'encrypt' for services like Gmail/O365 that require TLS
smtp_tls_security_level = encrypt
{% else %}
# If no auth, 'may' is a safe default for opportunistic TLS
smtp_tls_security_level = may
{% endif %}
# Other settings
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

5
templates/sasl_passwd.j2 Normal file
View File

@ -0,0 +1,5 @@
#
# This template creates the credential file for the smarthost.
#
# Ansible managed: {{ ansible_managed }}
{{ postfix_relayhost }} {{ postfix_relayhost_user }}:{{ postfix_relayhost_password }}

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:
- ansible_role_mailserver

2
vars/main.yml Normal file
View File

@ -0,0 +1,2 @@
---
# vars file for ansible_role_mailserver