mailserver: add docs@ paperless ingest mailbox (sieve sender allowlist)
Some checks failed
ci/woodpecker/push/default Pipeline failed

Viktor asked to forward arbitrary emails with PDF attachments into
paperless-ngx, with the forwarding sender mapping 1:1 to the paperless
account that owns the document. paperless-ngx's built-in IMAP consumer
already does the sender->owner mapping, so the infra half is a dedicated
real mailbox docs@viktorbarzin.me: an explicit self-alias (the @domain
catch-all would otherwise divert it into the TripIt-swept spam@ mailbox,
whose sweeper LLM-parses and auto-replies to mail from linked senders)
plus a per-user Dovecot sieve that discards non-family senders at
delivery (chosen behaviour for unmatched senders: ignore and delete;
also keeps spam out of the guessable address). The mailbox credential
was added to Vault secret/platform.mailserver_accounts. Paperless-side
mail account + 5 per-sender rules are DB state, configured via the API
per the new runbook docs/runbooks/paperless-mail-ingest.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-07-03 14:06:19 +00:00
parent f5187806f9
commit 77fcb08e8e
6 changed files with 166 additions and 1 deletions

View file

@ -19,3 +19,12 @@ plans@viktorbarzin.me spam@viktorbarzin.me
# to trips@, or every verification/recovery send is rejected (550 sender). Also
# routes any inbound trips@ to spam@.
trips@viktorbarzin.me spam@viktorbarzin.me
# docs@ -> docs@: explicit self-alias for the paperless-ngx ingest MAILBOX
# (a real account in secret/platform.mailserver_accounts). Without this the
# @domain catch-all above (Vault-side aliases) rewrites docs@ to spam@ and the
# mail lands in the TripIt-swept catch-all mailbox instead. Same pattern as
# me@ -> me@. Delivery-time sender allowlist: docs-at-viktorbarzin.me
# .dovecot.sieve (mounted as docs@viktorbarzin.me.dovecot.sieve).
# Runbook: docs/runbooks/paperless-mail-ingest.md
docs@viktorbarzin.me docs@viktorbarzin.me

View file

@ -0,0 +1,17 @@
# Sender allowlist for the paperless-ngx ingest mailbox docs@viktorbarzin.me.
# Family members forward document emails here; paperless-ngx polls the INBOX
# over IMAP and maps each sender to a paperless account (1 mail rule per
# sender). Decision (Viktor, 2026-07-03): mail from any OTHER sender is
# ignored and deleted — discarded here at LMTP delivery, before paperless
# ever sees it. This also keeps spam to the guessable address out entirely.
#
# Keep this list in sync with the paperless mail rules (the sender -> owner
# map). Add-a-sender procedure: docs/runbooks/paperless-mail-ingest.md
if not address :is "from" ["me@viktorbarzin.me",
"vbarzin@gmail.com",
"viktorbarzin@meta.com",
"ancaelena98@gmail.com",
"emil.barzin@gmail.com"] {
discard;
stop;
}

View file

@ -110,6 +110,12 @@ resource "kubernetes_config_map" "mailserver_config" {
"postfix-main.cf" = var.postfix_cf
"postfix-virtual.cf" = local.postfix_virtual
# Per-user Dovecot sieve for the paperless-ngx ingest mailbox: DMS installs
# any /tmp/docker-mailserver/<login>.dovecot.sieve at startup. ConfigMap
# keys can't contain '@', so the key is sanitized ("-at-") and the
# volume_mount below restores the real filename.
"docs-at-viktorbarzin.me.dovecot.sieve" = file("${path.module}/extra/docs-at-viktorbarzin.me.dovecot.sieve")
KeyTable = "mail._domainkey.viktorbarzin.me viktorbarzin.me:mail:/etc/opendkim/keys/viktorbarzin.me-mail.key\n"
SigningTable = "*@viktorbarzin.me mail._domainkey.viktorbarzin.me\n"
TrustedHosts = "127.0.0.1\nlocalhost\n"
@ -404,6 +410,12 @@ resource "kubernetes_deployment" "mailserver" {
sub_path = "postfix-virtual.cf"
read_only = true
}
volume_mount {
name = "config"
mount_path = "/tmp/docker-mailserver/docs@viktorbarzin.me.dovecot.sieve"
sub_path = "docs-at-viktorbarzin.me.dovecot.sieve"
read_only = true
}
volume_mount {
name = "config"
mount_path = "/tmp/docker-mailserver/fetchmail.cf"