feat(maddy): add mail stack

This commit is contained in:
il
2026-06-25 15:17:35 +09:00
parent fb2578d28a
commit a2334bb236
21 changed files with 350 additions and 42 deletions
@@ -15,7 +15,7 @@ root_ca_cert = "/etc/ssl/grafana/{{ root_cert_filename }}"
# client_key = "/path/to/client.key"
bind_dn = "uid=grafana,ou=people,{{ domain['dc'] }}"
bind_password = "$__file{/run/secrets/LDAP_BIND_PASSWORD}"
bind_password = "$__file{/run/secrets/GF_LDAP_BIND_PASSWORD}"
search_filter = "(&(|(uid=%s)(mail=%s))(|(memberOf=cn=admins,ou=groups,{{ domain['dc'] }})(memberOf=cn=users,ou=groups,{{ domain['dc'] }})))"
search_base_dns = ["{{ domain['dc'] }}"]
@@ -31,7 +31,7 @@ Environment="GF_PATHS_CONFIG=/etc/grafana/grafana.ini"
Environment="GF_FEATURE_TOGGLES_EXPAND_ENV_VARS=true"
Secret=GF_DB_PASSWORD,target=/run/secrets/GF_DB_PASSWORD
Secret=LDAP_BIND_PASSWORD,target=/run/secrets/LDAP_BIND_PASSWORD
Secret=GF_LDAP_BIND_PASSWORD,target=/run/secrets/GF_LDAP_BIND_PASSWORD
Secret=GF_ADMIN_PASSWORD,target=/run/secrets/GF_ADMIN_PASSWORD
[Service]
@@ -0,0 +1,114 @@
# Base
$(hostname) = {{ services['maddy']['domain']['internal'] }}.{{ domain['internal'] }}
$(primary_domain) = {{ domain['internal'] }}
$(local_domains) = $(primary_domain)
state_dir /data
runtime_dir /tmp/maddy
hostname $(hostname)
tls file /etc/ssl/maddy/maddy.crt /etc/ssl/maddy/maddy.key {
protocols tls1.2 tls1.3
}
# LDAP
auth.ldap smtp_ldap {
urls ldaps://{{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}
bind plain "uid=maddy,ou=people,{{ domain['dc'] }}" "{env:MADDY_LDAP_BIND_PASSWORD}"
base_dn "ou=people,{{ domain['dc'] }}"
filter "(&(uid={username})(objectClass=person)(|(memberOf=cn=admins,ou=groups,{{ domain['dc'] }})(memberOf=cn=users,ou=groups,{{ domain['dc'] }})(memberOf=cn=services,ou=groups,{{ domain['dc'] }})))"
starttls off
tls_client {
root_ca /etc/ssl/maddy/{{ root_cert_filename }}
}
}
auth.ldap imap_ldap {
urls ldaps://{{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}
bind plain "uid=maddy,ou=people,{{ domain['dc'] }}" "{env:MADDY_LDAP_BIND_PASSWORD}"
base_dn "ou=people,{{ domain['dc'] }}"
filter "(&(uid={username})(objectClass=person)(|(memberOf=cn=admins,ou=groups,{{ domain['dc'] }})(memberOf=cn=users,ou=groups,{{ domain['dc'] }})))"
starttls off
tls_client {
root_ca /etc/ssl/maddy/{{ root_cert_filename }}
}
}
storage.imapsql local_mailboxes {
driver postgres
dsn "{env:MADDY_POSTGRES_URL}"
msg_store fs messages/
delivery_map email_localpart
delivery_normalize precis_casefold_email
}
table.chain local_rewrites {
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
optional_step static {
entry postmaster postmaster@$(primary_domain)
}
}
msgpipeline local_routing {
destination postmaster $(local_domains) {
modify {
replace_rcpt &local_rewrites
}
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
submission tcp://0.0.0.0:587 {
hostname $(hostname)
limits {
all rate 20 1s
all concurrency 20
}
auth &smtp_ldap
insecure_auth no
sasl_login yes
max_message_size 32M
source $(local_domains) {
check {
authorize_sender {
prepare_email &local_rewrites
auth_normalize precis_casefold
from_normalize precis_casefold_email
user_to_email regexp "(.*)" "$1@$(primary_domain)"
check_header yes
}
}
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.7.1 "External delivery is disabled"
}
}
default_source {
reject 550 5.7.1 "Non-local sender domain"
}
}
imap tls://0.0.0.0:993 {
auth &imap_ldap
storage &local_mailboxes
insecure_auth no
sasl_login yes
storage_map identity
storage_map_normalize precis_casefold
}
@@ -0,0 +1,36 @@
[Quadlet]
DefaultDependencies=false
[Unit]
Description=Maddy
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/foxcpp/maddy:{{ version['containers']['maddy'] }}
ContainerName=maddy
HostName=maddy
PublishPort={{ services['maddy']['ports']['smtp'] }}:587/tcp
PublishPort={{ services['maddy']['ports']['imaps'] }}:993/tcp
AddHost={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
Volume=%h/containers/maddy/maddy.conf:/data/maddy.conf:ro
Volume=%h/containers/maddy/ssl:/etc/ssl/maddy:ro
Volume=%h/data/containers/maddy/data:/data:rw
Environment="TZ={{ timezone }}"
Secret=MADDY_LDAP_BIND_PASSWORD,type=env
Secret=MADDY_POSTGRES_URL,type=env
[Service]
Restart=always
RestartSec=10s
TimeoutStopSec=120
[Install]
WantedBy=default.target