- add stalwart module - add psql database on the shared patroni instance - add ceph-rgw credentials to eyaml - ensure psql pass and s3 access key are converted to sensitive Reviewed-on: #418
7.2 KiB
Stalwart Mail Server Module
This Puppet module manages Stalwart Mail Server, a modern, secure, and scalable mail server implementation that supports IMAP, JMAP, WebDAV, and SMTP protocols.
Overview
The stalwart module provides a comprehensive solution for deploying Stalwart Mail Server in a clustered environment with:
- PostgreSQL backend for data, full-text search, and in-memory storage
- S3/Ceph-RGW backend for blob storage (emails, attachments, sieve scripts)
- Automatic cluster discovery using
query_nodes() - DNS autodiscovery records for email client configuration
- TLS certificate management integration
- Postfix relay integration for SMTP routing
Features
- ✅ Multi-node clustering with peer-to-peer coordination
- ✅ PostgreSQL authentication with SQL directory backend
- ✅ S3 blob storage with compression support
- ✅ IMAP/IMAPS protocols for email access
- ✅ HTTP/HTTPS protocols for JMAP, WebDAV, and autodiscovery
- ✅ SMTP relay for postfix integration
- ✅ DNS autodiscovery record management
- ✅ Automatic role distribution across cluster nodes
- ✅ TLS security with Vault PKI integration
Requirements
- Puppet 6+ with
query_nodes()function support - Stalwart RPM package (creates user, directories, systemd service)
- PostgreSQL cluster for data storage
- S3-compatible storage (Ceph-RGW, MinIO, AWS S3)
- DNS management via
profiles::dns::record - PKI management via
profiles::pki::vault::alt_names
Usage
Recommended Usage with Role
The recommended way to use this module is via the roles::infra::mail::backend role with hieradata configuration:
include roles::infra::mail::backend
Configure all parameters in hieradata/roles/infra/mail/backend.yaml - see examples/role-hieradata.yaml for a complete example.
Direct Class Usage
class { 'stalwart':
node_id => 1,
cluster_role => 'mail-backend',
postgresql_host => 'pgsql.example.com',
postgresql_database => 'stalwart',
postgresql_user => 'stalwart',
postgresql_password => Sensitive('secretpassword'),
s3_endpoint => 'https://ceph-rgw.example.com',
s3_bucket => 'stalwart-blobs',
s3_access_key => 'accesskey',
s3_secret_key => Sensitive('secretkey'),
domains => ['example.com'],
postfix_relay_host => 'postfix.example.com',
}
Hieradata Configuration
See examples/role-hieradata.yaml for a complete example of role-based hieradata configuration.
Required Parameters
# Cluster role for node discovery
stalwart::cluster_role: 'mail-backend'
# Optional: Unique node identifier (auto-calculated if not specified)
# stalwart::node_id: 1
# PostgreSQL connection
stalwart::postgresql_host: 'pgsql.example.com'
stalwart::postgresql_database: 'stalwart'
stalwart::postgresql_user: 'stalwart'
stalwart::postgresql_password: >
ENC[PKCS7,encrypted_password...]
# S3/Ceph-RGW connection
stalwart::s3_endpoint: 'https://ceph-rgw.example.com'
stalwart::s3_bucket: 'stalwart-blobs'
stalwart::s3_access_key: 'access_key'
stalwart::s3_secret_key: >
ENC[PKCS7,encrypted_secret...]
# Domains and relay
stalwart::domains:
- 'example.com'
stalwart::postfix_relay_host: 'postfix.example.com'
Architecture
Cluster Setup
The module automatically discovers cluster members using query_nodes() based on:
enc_rolematchingcluster_roleparametercountryfact matching the node's country factregionfact matching the node's region fact
Node ID Assignment:
- Node IDs are automatically extracted from the last 4 digits of the hostname
- Example:
ausyd1nxvm1234→ node ID1234 - Manual override available via
stalwart::node_idparameter if needed - Hostname must end with 4 digits for automatic extraction to work
- Ensures unique IDs when following consistent hostname patterns
Storage Layout
- Data Store: PostgreSQL (metadata, folders, settings)
- Full-Text Search: PostgreSQL (search indexes)
- In-Memory Store: PostgreSQL (caching, sessions)
- Blob Store: S3/Ceph-RGW (emails, attachments, files)
Directory Structure (Created by RPM)
- Config:
/opt/stalwart/etc/config.toml - Data:
/var/lib/stalwart/(queue, reports) - Logs:
/var/log/stalwart/stalwart.log - Binary:
/opt/stalwart/bin/stalwart - User:
stalwart:stalwart(system user)
Network Ports
- 143: IMAP (STARTTLS)
- 993: IMAPS (implicit TLS)
- 443: HTTPS (JMAP, WebDAV, autodiscovery)
- 2525: SMTP relay (postfix communication)
- 11200: Cluster coordination (peer-to-peer)
- 9090: Prometheus metrics
DNS Records
When manage_dns_records: true, the module creates:
autoconfig.domain.com→ server FQDN (Thunderbird)autodiscover.domain.com→ server FQDN (Outlook)_imap._tcp.domain.comSRV record_imaps._tcp.domain.comSRV record_caldav._tcp.domain.comSRV record_carddav._tcp.domain.comSRV record
PostgreSQL Schema
The module expects these tables in the PostgreSQL database:
CREATE TABLE accounts (
name TEXT PRIMARY KEY,
secret TEXT,
description TEXT,
type TEXT NOT NULL,
quota INTEGER DEFAULT 0,
active BOOLEAN DEFAULT true
);
CREATE TABLE group_members (
name TEXT NOT NULL,
member_of TEXT NOT NULL,
PRIMARY KEY (name, member_of)
);
CREATE TABLE emails (
name TEXT NOT NULL,
address TEXT NOT NULL,
type TEXT,
PRIMARY KEY (name, address)
);
Security
- TLS required for all connections
- PostgreSQL SSL enabled by default
- S3 HTTPS endpoints required
- Password hashing supported (SHA512, BCRYPT, etc.)
- Certificate management via Vault PKI
Fallback Administrator
Stalwart includes a fallback administrator account for initial setup and emergency access:
- Default username:
admin(configurable viastalwart::fallback_admin_user) - Default password:
admin(configurable viastalwart::fallback_admin_password) - Purpose: Initial server configuration and emergency access when directory services are unavailable
- Security: Password is automatically hashed using SHA-512 crypt format
Important: Change the default password in production by setting different hieradata values:
stalwart::fallback_admin_password: "your-secure-password"
The fallback admin should only be used for initial setup and emergencies. Create regular admin accounts in PostgreSQL for day-to-day management.
Monitoring
- Prometheus metrics on port 9090
- Log files in
/var/log/stalwart/ - Queue monitoring in
/var/lib/stalwart/queue/ - Service status via systemd (
stalwart.service)
Troubleshooting
Cluster Formation Issues
- Verify
query_nodes()returns expected nodes - Check
countryandregionfacts are consistent - Ensure
cluster_rolematches across all nodes
Storage Connection Issues
- Test PostgreSQL connectivity and credentials
- Verify S3 endpoint accessibility and credentials
- Check network connectivity between nodes
TLS Certificate Issues
- Ensure PKI alt_names include all required domains
- Verify certificate paths exist and are readable
- Check certificate expiration dates
License
This module is part of the internal infrastructure management system.