# Stalwart Mail Server Configuration # Generated by Puppet - DO NOT EDIT MANUALLY [server] hostname = "<%= @facts['networking']['fqdn'] %>" greeting = "Stalwart ESMTP" [server.listener."smtp-relay"] bind = ["<%= @bind_address %>:25"] protocol = "smtp" greeting = "Stalwart SMTP Relay" <% if @enable_imap -%> [server.listener."imap"] bind = ["<%= @bind_address %>:143"] protocol = "imap" <% end -%> <% if @enable_imap_tls -%> [server.listener."imaps"] bind = ["<%= @bind_address %>:993"] protocol = "imap" tls.implicit = true <% end -%> <% if @enable_http -%> [server.listener."https"] bind = ["<%= @bind_address %>:443"] protocol = "http" tls.implicit = true <% end -%> [server.tls] enable = true implicit = false certificate = "default" # Cluster Configuration <% if scope.lookupvar('stalwart::clustering::cluster_info') -%> <% cluster_info = scope.lookupvar('stalwart::clustering::cluster_info') -%> [cluster] node-id = <%= cluster_info['node_id'] %> <% else -%> [cluster] node-id = <%= @stalwart::effective_node_id %> <% end -%> <% if scope.lookupvar('stalwart::clustering::cluster_info') -%> <% cluster_info = scope.lookupvar('stalwart::clustering::cluster_info') -%> <% if cluster_info['size'] > 1 -%> # Peer-to-peer coordination [cluster.coordinator] type = "peer-to-peer" addr = "<%= @bind_address %>:11200" advertise-addr = "<%= @advertise_address %>:11200" <% cluster_info['other_nodes'].each do |node| -%> [[cluster.coordinator.peers]] addr = "<%= node %>:11200" <% end -%> # Cluster roles for 3-node setup [cluster.roles.purge] stores = ["1", "2", "3"] accounts = ["1", "2"] [cluster.roles.acme] renew = ["1"] [cluster.roles.metrics] calculate = ["1", "2"] push = ["1"] [cluster.roles.push-notifications] push-notifications = ["1", "3"] [cluster.roles.fts-indexing] fts-indexing = ["2", "3"] [cluster.roles.bayes-training] bayes-training = ["1"] [cluster.roles.imip-processing] imip-processing = ["2"] [cluster.roles.calendar-alerts] calendar-alerts = ["3"] <% end -%> <% end -%> # Storage Configuration <% postgresql_config = scope.lookupvar('stalwart::storage::postgresql_config') -%> <% s3_config = scope.lookupvar('stalwart::storage::s3_config') -%> # PostgreSQL store for data, FTS, and in-memory [store."postgresql"] type = "postgresql" host = "<%= postgresql_config['host'] %>" port = <%= postgresql_config['port'] %> database = "<%= postgresql_config['database'] %>" user = "<%= postgresql_config['user'] %>" password = "<%= postgresql_config['password'] %>" timeout = "15s" [store."postgresql".tls] enable = <%= postgresql_config['ssl'] %> allow-invalid-certs = false [store."postgresql".pool] max-connections = 10 [store."postgresql".purge] frequency = "0 3 *" # PostgreSQL directory queries for authentication [store."postgresql".query] name = "SELECT name, type, secret, description, quota FROM accounts WHERE name = $1 AND active = true" members = "SELECT member_of FROM group_members WHERE name = $1" recipients = "SELECT name FROM emails WHERE address = $1 ORDER BY name ASC" emails = "SELECT address FROM emails WHERE name = $1 ORDER BY type DESC, address ASC" # S3/Ceph-RGW store for blobs [store."s3"] type = "s3" bucket = "<%= s3_config['bucket'] %>" region = "<%= s3_config['region'] %>" access-key = "<%= s3_config['access_key'] %>" secret-key = "<%= s3_config['secret_key'] %>" endpoint = "<%= s3_config['endpoint'] %>" timeout = "30s" key-prefix = "<%= s3_config['key_prefix'] %>" compression = "lz4" [store."s3".purge] frequency = "30 5 *" # Storage assignment [storage] data = "postgresql" fts = "postgresql" blob = "s3" lookup = "postgresql" in-memory = "postgresql" # Directory configuration [directory."postgresql"] type = "sql" store = "postgresql" [directory."postgresql".columns] name = "name" description = "description" secret = "secret" email = "address" quota = "quota" class = "type" # Authentication configuration [authentication] fallback-admin = ["admin@<%= @domains.first %>"] [authentication.directory] directories = ["postgresql"] # Authorization configuration [authorization] directory = "postgresql" # JMAP configuration [jmap] directory = "postgresql" [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 = "postgresql" [imap.protocol] max-requests = 64 # SMTP configuration for postfix relay [session.rcpt] relay = true [session.data] pipe.command = "sendmail" pipe.arguments = ["-i", "-f", "{sender}", "{recipient}"] # Outbound SMTP configuration [queue] path = "<%= @data_dir %>/queue" [queue.schedule] retry = ["2s", "5s", "1m", "5m", "15m", "30m", "1h", "2h"] notify = ["1d", "3d"] expire = "5d" [session.extensions] future-release = "7d" # Relay configuration for postfix [remote."postfix"] address = "<%= @postfix_relay_host %>" port = 25 protocol = "smtp" [session.mail] rewrite = [ { if = "!is_local_domain(rcpt_domain)", then = "set('remote', 'postfix')" } ] # HTTP configuration [server.http] use-x-forwarded = false permissive-cors = false # Disable spam filtering (handled by postfix) [session.ehlo] reject-non-fqdn = false [session.mail] rewrite = [] [session.rcpt] directory = "postgresql" relay = true max-recipients = 25 [session.data] max-messages = 10 max-message-size = 52428800 # TLS configuration [certificate."default"] cert = "%{file:<%= @tls_cert %>}%" private-key = "%{file:<%= @tls_key %>}%" # Logging configuration [tracer] type = "log" level = "<%= @log_level %>" ansi = false multiline = true [tracer.file] path = "/var/log/stalwart/stalwart.log" rotate = "daily" keep = 30 # Report storage [report] path = "<%= @data_dir %>/reports" hash = "sha256" encrypt = false # Metrics configuration [metrics] prometheus.enable = true prometheus.port = 9090