Files
k8scd/ansible/mailserver/README.md
T
2026-03-15 10:58:50 +01:00

3.6 KiB

Mailserver Ansible

Ansible playbook that deploys and hardens a Stalwart all-in-one mail server on Ubuntu.

What it does

Role Purpose
base apt upgrades, unattended-upgrades, timezone, swap, sysctl hardening
ssh Key-only root login, disable password auth, rate limiting
ufw Firewall — deny all incoming except SSH, SMTP, IMAP, HTTPS
fail2ban Brute force protection for SSH + Stalwart auth
stalwart Stalwart mail server with built-in ACME (Let's Encrypt)

Prerequisites

  • Ubuntu server with root SSH key access
  • DNS: mail.rubenhensen.nl → server IP (A record)
  • DNS: MX record for your domain pointing to mail.rubenhensen.nl
  • Ansible installed locally (brew install ansible)

Setup

cd ~/Repos/k8scd/ansible/mailserver

# 1. Create vault password file (gitignored)
echo 'your-vault-password' > .vault_pass
chmod 600 .vault_pass

# 2. Create encrypted secrets
ansible-vault create host_vars/mailserver/vault.yml
# Add:
#   ---
#   vault_stalwart_fallback_admin_password: "your-admin-password"

# 3. Edit inventory if server IP changed
#    inventory.yml → ansible_host

# 4. Run
ansible-playbook playbook.yml

Day-to-day operations

Re-run after config changes:

ansible-playbook playbook.yml

Edit encrypted secrets:

ansible-vault edit host_vars/mailserver/vault.yml

Run only a specific role:

ansible-playbook playbook.yml --tags stalwart

(Note: tags aren't configured yet — use --start-at-task "task name" or add tags if needed)

Upgrade Stalwart: Bump stalwart_version in roles/stalwart/defaults/main.yml and re-run. It only re-downloads when the version changes.

File structure

├── ansible.cfg              # Ansible settings + vault password file path
├── inventory.yml            # Server IP, SSH user, python interpreter
├── .vault_pass              # Vault password (gitignored)
├── .gitignore
├── host_vars/mailserver/
│   ├── vars.yml             # Maps variables to vault references
│   └── vault.yml            # Encrypted secrets (committed as ciphertext)
└── roles/
    ├── base/                # OS hardening + swap
    ├── ssh/                 # sshd_config template
    ├── ufw/                 # Firewall rules
    ├── fail2ban/            # Jails for SSH + Stalwart
    └── stalwart/            # Mail server install + config.toml template

Stalwart admin

Web admin: https://mail.rubenhensen.nl Login: admin / (password from vault)

From the web UI you can manage domains, accounts, DKIM keys, and other mail settings.

TLS certificates

Managed by certbot (Let's Encrypt). On first run, certbot uses standalone mode (temporarily stops Stalwart to bind port 80/443). Renewals happen automatically via certbot.timer and a deploy hook restarts Stalwart. Certs are at /etc/letsencrypt/live/mail.rubenhensen.nl/.

Ports

Port Service
22 SSH
25 SMTP
465 SMTP submission (implicit TLS)
587 SMTP submission (STARTTLS)
993 IMAP (implicit TLS)
443 HTTPS (web admin + JMAP + ACME)
80 HTTP (ACME + redirect)

If something breaks

  • Stalwart logs: /opt/stalwart/logs/
  • Stalwart config: /opt/stalwart/etc/config.toml
  • Service status: systemctl status stalwart
  • fail2ban status: fail2ban-client status / fail2ban-client status sshd
  • Firewall: ufw status
  • Check banned IPs: fail2ban-client status stalwart-auth
  • Unban an IP: fail2ban-client set <jail> unbanip <ip>