# 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: ```puppet 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 ```puppet 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 ```yaml # 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_role` matching `cluster_role` parameter - `country` fact matching the node's country fact - `region` fact 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 ID `1234` - Manual override available via `stalwart::node_id` parameter 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.com` SRV record - `_imaps._tcp.domain.com` SRV record - `_caldav._tcp.domain.com` SRV record - `_carddav._tcp.domain.com` SRV record ## PostgreSQL Schema The module expects these tables in the PostgreSQL database: ```sql 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 via `stalwart::fallback_admin_user`) - **Default password**: `admin` (configurable via `stalwart::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: ```yaml 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 `country` and `region` facts are consistent - Ensure `cluster_role` matches 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.