- 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
230 lines
7.2 KiB
Markdown
230 lines
7.2 KiB
Markdown
# 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. |