---
application_id: "web-app-stalwart"
http_port: "{{ lookup('config', application_id, 'services.stalwart.ports.local.http') }}"
database_password: "{{ lookup('config', application_id, 'secrets.credentials.database_password') }}" # nocheck: lookup-config-path
STALWART_VOLUMES_DIR: "{{ lookup('container', application_id, 'directories.volumes') }}"
STALWART_CONFIG_DIR: "{{ [ STALWART_VOLUMES_DIR, 'etc' ] | path_join }}"
STALWART_CONFIG_FILE: "{{ [ STALWART_CONFIG_DIR, 'config.json' ] | path_join }}"
container_port: "{{ lookup('config', application_id, 'services.stalwart.ports.internal.http') }}"
STALWART_LOCAL_BASE_URL: "http://localhost:{{ container_port }}"
STALWART_JMAP_URL: "{{ STALWART_LOCAL_BASE_URL }}/jmap/"
STALWART_LOG_DIR: "{{ lookup('volume', application_id, 'data').target }}"
STALWART_BAN_TRACER:
"@type": "Stdout"
buffered: false
level: "debug"
eventsPolicy: "include"
events:
"security.authentication-ban": true
"security.abuse-ban": true
"security.scan-ban": true
"security.loiter-ban": true
"auth.failed": true
STALWART_JMAP_APPLICATION_GET: >-
{{ {'using': ['urn:ietf:params:jmap:core', 'urn:stalwart:jmap'],
'methodCalls': [['x:Application/get', {'ids': none}, 'c0']]} | to_json }}
STALWART_JMAP_TRANSIENT_ERRORS:
- serverUnavailable
- rateLimit
STALWART_JMAP_MAX_METHOD_CALLS: 16
STALWART_CONTAINER_ADDRESS: "{{ lookup('container_address', application_id, 'stalwart') }}"
STALWART_DOMAIN: "{{ lookup('config', application_id, 'services.stalwart.domain') }}"
STALWART_DOMAIN_DNS_ZONE: "{{ STALWART_DOMAIN | to_zone }}"
STALWART_EMAIL: "{{ lookup('email', application_id) }}"
STALWART_HOSTNAME: "{{ STALWART_EMAIL.host }}"
STALWART_WEB_DOMAIN: "{{ lookup('domain', application_id) }}"
STALWART_HOSTNAME_DNS_ZONE: "{{ STALWART_HOSTNAME | to_zone }}"
container_hostname: "{{ lookup('container_hostname', application_id) }}"
STALWART_OIDC_ONION_ISSUER: "{{ lookup('tls', 'web-app-keycloak', 'domain').endswith('.onion') }}"
STALWART_OIDC_SOCKS_PROXY: "{{ lookup('tor_socks_proxy') if STALWART_OIDC_ONION_ISSUER | bool else '' }}"
# Keycloak access tokens carry neither a Stalwart audience nor a urn:ietf:params:oauth:scope:mail scope, so the v1 defaults reject every one of them.
STALWART_OIDC_TOKEN_VALIDATION:
skipAudienceValidation: true
unscopedTokens: "interoperable"
STALWART_CONTAINER: "{{ lookup('config', application_id, 'services.stalwart.name') }}"
STALWART_DATABASE_SHARED: "{{ lookup('database', application_id, 'shared') | bool }}"
STALWART_STORE_LAYOUT_VERSION: "1"
STALWART_STORE_MARKER_FILE: "{{ [ lookup('container', application_id, 'directories.instance'), 'store.layout' ] | path_join }}"
STALWART_MIGRATE_DIR: "{{ [ lookup('container', application_id, 'directories.instance'), 'migrate-v016' ] | path_join }}"
STALWART_MIGRATE_SCRIPT: "{{ [ STALWART_MIGRATE_DIR, 'migrate.sh' ] | path_join }}"
STALWART_MIGRATE_TARGET_CONFIG: "{{ [ STALWART_MIGRATE_DIR, 'target', 'config.json' ] | path_join }}"
STALWART_MIGRATE_IMAGE: "{{ lookup('image', application_id, 'stalwart_migrate') }}"
STALWART_MIGRATE_TARGET_DATABASE: "{{ lookup('database', application_id, 'name') }}_v1"
STALWART_MIGRATE_RETIRED_DATABASE: "{{ lookup('database', application_id, 'name') }}_v016"
STALWART_MIGRATE_ASYNC_TIME: "{{ lookup('timeout', 21600) | int }}"
STALWART_MIGRATE_ASYNC_POLL: 60
STALWART_ANTIVIRUS_ENABLED: "{{ lookup('config', application_id, 'services.clamav.enabled') | bool }}"
STALWART_CLAMAV_HOST: "clamav"
STALWART_CLAMAV_MILTER_PORT: "{{ lookup('config', application_id, 'services.clamav.ports.internal.milter') }}"
STALWART_CLAMAV_MILTER_TIMEOUT_CONNECT_MS: 5000
STALWART_CLAMAV_MILTER_TIMEOUT_COMMAND_MS: 5000
STALWART_CLAMAV_MILTER_TIMEOUT_DATA_MS: 15000
STALWART_CLAMAV_MILTER_CONF: "{{ [ STALWART_CONFIG_DIR, 'clamav-milter.conf' ] | path_join }}"
STALWART_WEBMAIL_BASE_URL: "{{ lookup('canonical_url', application_id, 'webmail') }}"
STALWART_WEBMAIL_DOMAIN: "{{ STALWART_WEBMAIL_BASE_URL | regex_replace('^https?://', '') }}"
STALWART_WEBMAIL_HTTP_PORT: "{{ lookup('config', application_id, 'services.webmail.ports.local.http') }}"
STALWART_WEBMAIL_CONFIG_DIR: "{{ [ STALWART_VOLUMES_DIR, 'webmail' ] | path_join }}"
STALWART_WEBMAIL_OAUTH_CONFIG: "{{ [ STALWART_WEBMAIL_CONFIG_DIR, 'oauth.inc.php' ] | path_join }}"
STALWART_WEBMAIL_DESIGN_CONFIG: "{{ [ STALWART_WEBMAIL_CONFIG_DIR, 'design.inc.php' ] | path_join }}"
STALWART_WEBMAIL_OIDC_CLIENT_ID: "{{ lookup('config', application_id, 'services.sso.oidc.clients.webmail') }}"
STALWART_WEBUI_VERSION: "{{ lookup('config', application_id, 'services.stalwart.webui_version') }}"
STALWART_WEBUI_RESOURCE_URL: "https://github.com/stalwartlabs/webui/releases/download/{{ STALWART_WEBUI_VERSION }}/webui.zip"
STALWART_WEBUI_CACHE_FILE: "/var/cache/infinito/core/cache/package/stalwart-webui/webui-{{ STALWART_WEBUI_VERSION }}.zip"
STALWART_WEBUI_LOCAL_FILE: "{{ [ STALWART_CONFIG_DIR, 'webui.zip' ] | path_join }}"
STALWART_WEBUI_CONTAINER_FILE: "{{ lookup('volume', application_id, 'webui_bundle').target }}"
STALWART_WEBUI_LOCAL_URL: "file://{{ STALWART_WEBUI_CONTAINER_FILE }}"
STALWART_WEBUI_APP_DESCRIPTION: "Stalwart Web Interface"
STALWART_WEBUI_OAUTH_CLIENT_ID: "{{ lookup('config', application_id, 'services.sso.oidc.clients.webui') }}"
STALWART_WEBUI_BASE_URL: "{{ lookup('canonical_url', application_id) }}"
STALWART_IP4_PUBLIC: "{{ DOCKER_PUBLIC_BIND_HOST }}"
STALWART_ACTIVE_PROVIDER: "{{ lookup('mail_provider') == application_id }}"
STALWART_ADMIN_USER: "{{ lookup('users', 'stalwartadmin').username }}"
STALWART_ADMIN_PASSWORD: "{{ lookup('users', 'stalwartadmin').password }}"
STALWART_USERS: "{{ lookup('stalwart_users') }}"
STALWART_ADMIN_ACCOUNT_NAMES: >-
{{ STALWART_USERS | dict2items | map(attribute='value')
| selectattr('roles', 'defined')
| selectattr('roles', 'contains', 'administrator')
| map(attribute='username') | list }}
STALWART_DMARC_RUF: "{{ lookup('config', application_id, 'users.platform_administrator.email') }}"
STALWART_DMARC_RUA_USER: "{{ lookup('config', application_id, 'services.stalwart.dmarc.rua_user') }}"
STALWART_DMARC_RUA: "{{ lookup('users', STALWART_DMARC_RUA_USER).username }}@{{ STALWART_DOMAIN }}"
STALWART_BOT_APP_PASSWORD_DESCRIPTION: "Infinito.Nexus submission"
STALWART_BOT_APP_PASSWORD_RETIRED_DESCRIPTION: "Infinito.Nexus submission, retired"
stalwart_app_password_stale: {}
STALWART_RELAY_RULE: "!is_empty(authenticated_as)"
STALWART_BAN_PERIOD_MS: "{{ (lookup('config', application_id, 'services.stalwart.ban_period_seconds') | int) * 1000 }}"
STALWART_BAN_PERIODS:
authBanPeriod: "{{ STALWART_BAN_PERIOD_MS | int }}"
abuseBanPeriod: "{{ STALWART_BAN_PERIOD_MS | int }}"
loiterBanPeriod: "{{ STALWART_BAN_PERIOD_MS | int }}"
scanBanPeriod: "{{ STALWART_BAN_PERIOD_MS | int }}"
STALWART_ALLOWED_IPS:
- "{{ lookup('subnet_gateway') }}"
STALWART_ALLOWED_IP_REASON: "Docker gateway: reverse-proxied HTTP and container hairpin traffic share this address"
STALWART_HTTP_SETTINGS:
clientIpSource: forwarded
forwardedHops: 1
STALWART_SSO_ENABLED: "{{ lookup('sso', application_id, 'is_enabled') | bool }}"
STALWART_IMPORT_MAILU: "{{ lookup('config', application_id, 'services.stalwart.migration.import_mailu') | bool }}"
STALWART_MAILBOX_MAP: "{{ lookup('config', application_id, 'services.stalwart.migration.mailbox_map') }}"
STALWART_MAILU_MAILDIR_VOLUME: "{{ lookup('volume', 'web-app-mailu', 'dovecot_mail').name }}"
STALWART_IMPORT_JMAP_URL: "http://127.0.0.1:{{ http_port }}/jmap/"
STALWART_IMPORT_IMAPS_HOST: "{{ '127.0.0.1' if STALWART_IP4_PUBLIC == '0.0.0.0' else STALWART_IP4_PUBLIC }}"
STALWART_IMPORT_IMAPS_PORT: "{{ lookup('config', application_id, 'services.stalwart.ports.public.imaps') }}"
STALWART_IMPORT_DIR: "{{ [ lookup('container', application_id, 'directories.instance'), '.mailu-import' ] | path_join }}"
STALWART_IMPORT_SCRIPTS:
- migrate_from_mailu.py
- mailu_maildir.py
- imap_layout.py
STALWART_IMPORT_ADMIN_FILE: "{{ [ STALWART_IMPORT_DIR, 'admin.json' ] | path_join }}"
STALWART_IMPORT_UNIT: "stalwart-mailu-import"
STALWART_IMPORT_DONE_FILE: "{{ [ lookup('container', application_id, 'directories.instance'), '.mailu-import.done' ] | path_join }}"
STALWART_IMPORT_WORKERS: "{{ lookup('config', application_id, 'services.stalwart.migration.workers') or ([[RESOURCE_HOST_CPUS | int - 1, 1] | max, 8] | min) }}"
STALWART_ALIASES: "{{ lookup('config', application_id, 'services.stalwart.aliases') | stalwart_aliases }}"
STALWART_ALIAS_DESCRIPTION: "Infinito.Nexus alias"
STALWART_FORWARDS: >-
{{ lookup('config', application_id, 'services.stalwart.forwards') | stalwart_forwards
| stalwart_user_forwards(STALWART_USERS, STALWART_DOMAIN) }}
STALWART_FORWARD_SCRIPT_NAME: "infinito-forward"
STALWART_ALIAS_DOMAINS: "{{ lookup('config', application_id, 'services.stalwart.alias_domains') }}"
STALWART_RCPT_REWRITE: "{{ STALWART_ALIAS_DOMAINS | stalwart_rcpt_rewrite }}"
STALWART_ALIAS_DOMAIN_MX_RECORDS: >-
{%- set records = [] -%}
{%- for alias_domain in STALWART_ALIAS_DOMAINS -%}
{%- set _ = records.append({'type': 'MX', 'zone': alias_domain.source | to_zone, 'name': alias_domain.source,
'solo': true, 'value': STALWART_HOSTNAME, 'priority': 10}) -%}
{%- endfor -%}
{{ records }}
STALWART_PRODUCTION_HOST: "{{ MAIL_PRODUCTION_HOST | trim }}"
STALWART_PRODUCTION_RESOLVER: "{{ MAIL_PRODUCTION_RESOLVER | trim }}"
STALWART_PROMETHEUS_ENABLED: "{{ lookup('config', application_id, 'services.prometheus.enabled') | bool }}"
STALWART_MAIL_FACTS_METRICS_FILE: "{{ [ DIR_METRICS_TEXTFILE, 'stalwart-mail-facts.prom' ] | path_join }}"
STALWART_ONION_ACTIVE: "{{ 'svc-net-tor' in group_names }}"
STALWART_ONION_GATEWAY_ID: "svc-net-tor-smtp"
STALWART_ONION_ROUTE_NAME: "onion"
STALWART_ONION_RELAY_HOST: "{{ lookup('config', STALWART_ONION_GATEWAY_ID, 'services.tor-smtp.name') }}"
STALWART_ONION_RELAY_PORT: "{{ lookup('config', STALWART_ONION_GATEWAY_ID, 'services.tor-smtp.ports.internal.smtp') | int }}"