0d89a69c18
- stalwart namespace with Deployment + HPA (2-6 replicas) - CNPG PostgreSQL cluster (3 instances, 20Gi cephrbd-fast-delete) with PgBouncer pooler - S3/Ceph-RGW for blob storage (stalwart-maildata bucket, lz4 compressed) - Secrets from Vault: postgres-credentials, s3-credentials, stalwart-admin - TLS cert via cert-manager (vault-issuer) for mail.main.unkin.net - SMTP relay on port 25 (internal ClusterIP, trusted pod CIDRs) - Submission on port 587, IMAP 143/993, HTTPS 443 via LoadBalancer - HTTP port 8080 for Traefik reverse proxy (web admin at mail.k8s.syd1.au.unkin.net) - Outbound mail routed through postfix.mailgateway.svc.cluster.local:25 - Spam filtering offloaded to postfix/rspamd (disabled internally)
224 lines
4.6 KiB
TOML
224 lines
4.6 KiB
TOML
[server]
|
|
hostname = "mail.main.unkin.net"
|
|
greeting = "Stalwart ESMTP"
|
|
|
|
# SMTP relay listener (receives from postfix for local delivery)
|
|
[server.listener."smtp-relay"]
|
|
bind = ["0.0.0.0:25"]
|
|
protocol = "smtp"
|
|
greeting = "Stalwart SMTP Relay"
|
|
|
|
[server.listener."smtp-relay".proxy]
|
|
trusted-networks = ["127.0.0.0/8", "::1", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
|
|
|
# SMTP submission listener (user-facing, TLS required)
|
|
[server.listener."submission"]
|
|
bind = ["0.0.0.0:587"]
|
|
protocol = "smtp"
|
|
greeting = "Stalwart SMTP Submission"
|
|
tls.require = true
|
|
|
|
[server.listener."submission".proxy]
|
|
trusted-networks = ["127.0.0.0/8", "::1"]
|
|
|
|
# IMAP listener
|
|
[server.listener."imap"]
|
|
bind = ["0.0.0.0:143"]
|
|
protocol = "imap"
|
|
|
|
[server.listener."imap".proxy]
|
|
trusted-networks = ["127.0.0.0/8", "::1"]
|
|
|
|
# IMAPS listener (implicit TLS)
|
|
[server.listener."imaps"]
|
|
bind = ["0.0.0.0:993"]
|
|
protocol = "imap"
|
|
tls.implicit = true
|
|
|
|
[server.listener."imaps".proxy]
|
|
trusted-networks = ["127.0.0.0/8", "::1"]
|
|
|
|
# HTTPS (web admin + JMAP) for external LoadBalancer
|
|
[server.listener."https"]
|
|
bind = ["0.0.0.0:443"]
|
|
protocol = "http"
|
|
tls.implicit = true
|
|
|
|
[server.listener."https".proxy]
|
|
trusted-networks = ["127.0.0.0/8", "::1", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
|
|
|
# Plain HTTP for Traefik reverse proxy (TLS terminated at Traefik)
|
|
[server.listener."http-internal"]
|
|
bind = ["0.0.0.0:8080"]
|
|
protocol = "http"
|
|
|
|
[server.listener."http-internal".proxy]
|
|
trusted-networks = ["127.0.0.0/8", "::1", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
|
|
|
[server.tls]
|
|
enable = true
|
|
implicit = false
|
|
certificate = "default"
|
|
|
|
[server.http]
|
|
use-x-forwarded = true
|
|
permissive-cors = false
|
|
|
|
[webadmin]
|
|
path = "/var/lib/stalwart/webadmin"
|
|
auto-update = true
|
|
resource = "https://github.com/stalwartlabs/webadmin/releases/latest/download/webadmin.zip"
|
|
|
|
# PostgreSQL store (via CNPG pooler)
|
|
[store."postgresql"]
|
|
type = "postgresql"
|
|
host = "stalwart-postgres-pooler.stalwart.svc.cluster.local"
|
|
port = 5432
|
|
database = "stalwart"
|
|
user = "stalwart"
|
|
password = "%{env:POSTGRES_PASSWORD}%"
|
|
timeout = "15s"
|
|
|
|
[store."postgresql".tls]
|
|
enable = true
|
|
allow-invalid-certs = false
|
|
|
|
[store."postgresql".pool]
|
|
max-connections = 10
|
|
|
|
[store."postgresql".purge]
|
|
frequency = "0 3 *"
|
|
|
|
# S3/Ceph-RGW store for blobs
|
|
[store."s3"]
|
|
type = "s3"
|
|
bucket = "stalwart-maildata"
|
|
region = "syd1"
|
|
access-key = "%{env:S3_ACCESS_KEY}%"
|
|
secret-key = "%{env:S3_SECRET_KEY}%"
|
|
endpoint = "https://radosgw.service.consul"
|
|
timeout = "30s"
|
|
key-prefix = "stalwart/"
|
|
compression = "lz4"
|
|
|
|
[store."s3".purge]
|
|
frequency = "30 5 *"
|
|
|
|
# Storage assignment
|
|
[storage]
|
|
data = "postgresql"
|
|
fts = "postgresql"
|
|
blob = "s3"
|
|
lookup = "postgresql"
|
|
directory = "internal"
|
|
in-memory = "postgresql"
|
|
|
|
# Directory configuration
|
|
[directory.internal]
|
|
type = "internal"
|
|
store = "postgresql"
|
|
|
|
# Fallback admin (password hash from Vault)
|
|
[authentication.fallback-admin]
|
|
user = "admin"
|
|
secret = "%{env:ADMIN_PASSWORD_HASH}%"
|
|
|
|
[authentication]
|
|
[authentication.directory]
|
|
directories = ["internal"]
|
|
|
|
[authorization]
|
|
directory = "internal"
|
|
|
|
# JMAP configuration
|
|
[jmap]
|
|
directory = "internal"
|
|
|
|
[jmap.protocol]
|
|
request-max-size = 10485760
|
|
get.max-objects = 500
|
|
query.max-results = 5000
|
|
changes.max-results = 5000
|
|
upload.max-size = 50000000
|
|
upload.ttl = "1h"
|
|
|
|
# IMAP configuration
|
|
[imap]
|
|
directory = "internal"
|
|
|
|
[imap.protocol]
|
|
max-requests = 64
|
|
|
|
# Inbound rate limiting
|
|
[[queue.limiter.inbound]]
|
|
key = ["remote_ip"]
|
|
rate = "500/1s"
|
|
enable = true
|
|
|
|
# Outbound routing through postfix relay
|
|
[queue]
|
|
path = "/var/lib/stalwart/queue"
|
|
|
|
[queue.schedule]
|
|
retry = ["2s", "5s", "1m", "5m", "15m", "30m", "1h", "2h"]
|
|
notify = ["1d", "3d"]
|
|
expire = "5d"
|
|
|
|
[session.extensions]
|
|
future-release = "7d"
|
|
|
|
# Route local domain mail locally, everything else through postfix relay
|
|
[queue.strategy]
|
|
route = [
|
|
{ if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
{ else = "'relay'" }
|
|
]
|
|
|
|
[queue.route."local"]
|
|
type = "local"
|
|
|
|
[queue.route."relay"]
|
|
type = "relay"
|
|
address = "postfix.mailgateway.svc.cluster.local"
|
|
port = 25
|
|
protocol = "smtp"
|
|
|
|
[queue.route."relay".tls]
|
|
implicit = false
|
|
allow-invalid-certs = false
|
|
|
|
# Session configuration
|
|
[session.ehlo]
|
|
reject-non-fqdn = false
|
|
|
|
[session.rcpt]
|
|
type = "internal"
|
|
store = "postgresql"
|
|
max-recipients = 25
|
|
|
|
[session.data]
|
|
max-messages = 10
|
|
max-message-size = 52428800
|
|
|
|
# Spam filtering offloaded to postfix/rspamd — disable internal spam filter
|
|
[spam-filter]
|
|
enable = false
|
|
|
|
# TLS certificate (cert-manager issues to /etc/stalwart/tls/)
|
|
[certificate."default"]
|
|
cert = "%{file:/etc/stalwart/tls/tls.crt}%"
|
|
private-key = "%{file:/etc/stalwart/tls/tls.key}%"
|
|
default = true
|
|
|
|
# Logging
|
|
[tracer]
|
|
type = "log"
|
|
level = "info"
|
|
ansi = false
|
|
multiline = true
|
|
|
|
# Metrics
|
|
[metrics]
|
|
prometheus.enable = true
|
|
prometheus.port = 9090
|