From db927a71c6caafc7db742af88e28a52cecc47b97 Mon Sep 17 00:00:00 2001 From: Nguyễn Gia Phong Date: Fri, 12 Jan 2024 02:46:35 +0900 Subject: Set up public-inbox --- mail.nix | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 155 insertions(+), 21 deletions(-) diff --git a/mail.nix b/mail.nix index 61b80c1..505210e 100644 --- a/mail.nix +++ b/mail.nix @@ -1,5 +1,5 @@ # Email server configuration -# Copyright (C) 2023 Nguyễn Gia Phong +# Copyright (C) 2023-2024 Nguyễn Gia Phong # # This file is part of loang configuration. # @@ -16,11 +16,12 @@ # You should have received a copy of the GNU Affero General Public License # along with loang configuration. If not, see . -{ config, options, pkgs, ... }: +{ config, lib, pkgs, ... }: let certDir = config.security.acme.certs.${hostname}.directory; domain = config.networking.domain; hostname = "tem.${domain}"; + publicHost = "chung.${domain}"; in { networking.firewall.allowedTCPPorts = [ 25 # SMTP-MTA @@ -38,31 +39,130 @@ in { maddy = { config = '' auth_map email_localpart - '' + (builtins.replaceStrings [ - '' - auth.pass_table local_authdb { - table sql_table { - driver sqlite3 - dsn credentials.db - table_name passwords + + auth.shadow local_authdb { + use_helper no + } + + storage.imapsql local_mailboxes { + driver sqlite3 + dsn imapsql.db + } + + table.chain local_rewrites { + optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3" + optional_step static { + entry postmaster cnx@$(primary_domain) + } + optional_step file /etc/maddy/aliases + } + + msgpipeline local_routing { + destination ${publicHost} { + deliver_to smtp tcp://localhost:2525 + } + destination postmaster $(local_domains) { + modify { + replace_rcpt &local_rewrites + } + deliver_to &local_mailboxes + } + default_destination { + reject 550 5.1.1 "User doesn't exist" + } + } + + smtp tcp://0.0.0.0:25 { + limits { + all rate 20 1s + all concurrency 10 + } + dmarc yes + check { + require_mx_record + dkim + spf + } + source $(local_domains) { + reject 501 5.1.8 "Use Submission for outgoing SMTP" + } + default_source { + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + reject 550 5.1.1 "User doesn't exist" + } + } + } + + submission tls://0.0.0.0:465 { + limits { + all rate 50 1s + } + auth &local_authdb + source $(local_domains) { + check { + authorize_sender { + prepare_email &local_rewrites + user_to_email identity + } + } + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + modify { + dkim $(primary_domain) $(local_domains) default + } + deliver_to &remote_queue + } + } + default_source { + reject 501 5.1.8 "Non-local sender domain" + } + } + + target.remote outbound_delivery { + limits { + destination rate 20 1s + destination concurrency 10 + } + mx_auth { + dane + mtasts { + cache fs + fs_dir mtasts_cache/ + } + local_policy { + min_tls_level encrypted + min_mx_level none } } - '' - "imap tcp://0.0.0.0:143" - "submission tcp://0.0.0.0:587" - ] [ - '' - auth.shadow local_authdb { - debug yes - use_helper no + } + + target.queue remote_queue { + target &outbound_delivery + autogenerated_msg_domain $(primary_domain) + bounce { + destination postmaster $(local_domains) { + deliver_to &local_routing + } + default_destination { + reject 550 5.0.0 "Refusing to send DSNs to non-local addresses" + } } - '' - "imap tls://0.0.0.0:993" - "submission tls://0.0.0.0:465" - ] options.services.maddy.config.default); + } + + imap tls://0.0.0.0:993 { + auth &local_authdb + storage &local_mailboxes + } + ''; enable = true; hostname = hostname; primaryDomain = domain; + localDomains = [ domain publicHost ]; tls = { loader = "file"; certificates = [{ @@ -72,6 +172,32 @@ in { }; }; + postfix = { + enable = true; # bridge between maddy and public-inbox + enableSmtp = false; # override default port 25 + extraMasterConf = '' + 2525 inet n - - - - smtpd + ''; + }; + + public-inbox = { + enable = true; + http = { + enable = true; + port = 1430; + }; + inboxes = { + test = { + address = [ "test@${publicHost}" ]; + description = "test list"; + url = "https://${publicHost}/test"; + }; + }; + mda.enable = true; + postfix.enable = true; + settings.publicinbox.wwwlisting = "match=domain"; + }; + nginx.virtualHosts = { "mta-sts.${domain}" = { enableACME = true; @@ -109,6 +235,14 @@ in { ''; }; + "${publicHost}" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = let + port = config.services.public-inbox.http.port; + in "http://localhost:${toString port}"; + }; + ${hostname} = let alps = config.services.alps; in { enableACME = true; -- cgit 1.4.1