feat: create stalwart module (#418)
- 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
This commit was merged in pull request #418.
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user