From d26e9b3d4edc9c9db8080bf1adb350bdbd4635df Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Sat, 6 Jun 2026 10:48:48 +0200 Subject: [PATCH] Fix MTA: Disabled aliases continue receiving messages --- CHANGELOG.md | 4 +- crates/common/src/auth/mod.rs | 9 +++- crates/common/src/cache/invalidate.rs | 22 +++++--- crates/common/src/cache/principals.rs | 78 ++++++++++++++++++++++----- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79887e67..c18c1748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,9 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - OAuth: Rework access tokens to an `AES-256-GCM-SIV` AEAD format that carries the account name for proxy routing and revokes tokens on credential change. ## Fixed -- MTA: Sub-addressing with external directories returns `550 Mailbox not found`. +- MTA: + - Sub-addressing with external directories returns `550 Mailbox not found`. + - Disabled aliases continue receiving messages. ## [0.16.7] - 2026-05-28 diff --git a/crates/common/src/auth/mod.rs b/crates/common/src/auth/mod.rs index e0f95a3d..9cdb656f 100644 --- a/crates/common/src/auth/mod.rs +++ b/crates/common/src/auth/mod.rs @@ -52,6 +52,8 @@ pub struct EmailAddressRef<'x> { pub enum EmailCache { Account(u32), MailingList(u32), + DisabledAccountAddress(u32), + DisabledListAddress(u32), } #[derive(Debug, Clone)] @@ -101,6 +103,7 @@ pub struct RoleCache { #[derive(Debug, Clone)] pub struct MailingListCache { + pub addresses: Box<[EmailAddress]>, pub recipients: Arc<[Box]>, } @@ -245,7 +248,11 @@ impl CacheItemWeight for RoleCache { impl CacheItemWeight for MailingListCache { fn weight(&self) -> u64 { std::mem::size_of::() as u64 - //+ self.addresses.iter().map(|s| s.len() as u64).sum::() + + self + .addresses + .iter() + .map(|s| s.local_part.len() as u64 + std::mem::size_of::() as u64) + .sum::() + self.recipients.iter().map(|s| s.len() as u64).sum::() } } diff --git a/crates/common/src/cache/invalidate.rs b/crates/common/src/cache/invalidate.rs index bb2ffaf5..59b31adc 100644 --- a/crates/common/src/cache/invalidate.rs +++ b/crates/common/src/cache/invalidate.rs @@ -327,9 +327,14 @@ impl Server { } CacheInvalidation::Account(id) => { cache.accounts.remove(id); - cache.emails.inner().retain( - |_, v| !matches!(v, EmailCache::Account(account_id) if account_id == id), - ); + cache.emails.inner().retain(|_, v| { + !matches!( + v, + EmailCache::Account(account_id) + | EmailCache::DisabledAccountAddress(account_id) + if account_id == id + ) + }); } CacheInvalidation::DkimSignature(id) => { cache.dkim_signers.remove(id); @@ -342,9 +347,14 @@ impl Server { } CacheInvalidation::List(id) => { cache.lists.remove(id); - cache.emails.inner().retain( - |_, v| !matches!(v, EmailCache::MailingList(list_id) if list_id == id), - ); + cache.emails.inner().retain(|_, v| { + !matches!( + v, + EmailCache::MailingList(list_id) + | EmailCache::DisabledListAddress(list_id) + if list_id == id + ) + }); } CacheInvalidation::DomainLogo(id) => { self.inner diff --git a/crates/common/src/cache/principals.rs b/crates/common/src/cache/principals.rs index 2c9eb5bd..1d592a89 100644 --- a/crates/common/src/cache/principals.rs +++ b/crates/common/src/cache/principals.rs @@ -230,8 +230,35 @@ impl Server { { let item_id = object.id().document_id(); let result = match object.object() { - ObjectType::Account => EmailCache::Account(item_id), - ObjectType::MailingList => EmailCache::MailingList(item_id), + ObjectType::Account => { + if self + .account(item_id) + .await + .caused_by(trc::location!())? + .addresses + .iter() + .any(|address| { + address.local_part.as_ref() == local_part + && address.domain_id == domain_id + }) + { + EmailCache::Account(item_id) + } else { + EmailCache::DisabledAccountAddress(item_id) + } + } + ObjectType::MailingList => { + if let Some(list) = self.try_list(item_id).await? + && !list.addresses.iter().any(|address| { + address.local_part.as_ref() == local_part + && address.domain_id == domain_id + }) + { + EmailCache::DisabledListAddress(item_id) + } else { + EmailCache::MailingList(item_id) + } + } _ => { return Err(trc::AuthEvent::Error .into_err() @@ -422,10 +449,16 @@ impl Server { domain_id: account.domain_id.document_id(), }] .into_iter() - .chain(account.aliases.into_iter().map(|alias| EmailAddress { - local_part: alias.name.into(), - domain_id: alias.domain_id.document_id(), - })) + .chain( + account + .aliases + .into_iter() + .filter(|alias| alias.enabled) + .map(|alias| EmailAddress { + local_part: alias.name.into(), + domain_id: alias.domain_id.document_id(), + }), + ) .collect(), id_tenant: account.member_tenant_id.map(|id| id.document_id()), id_member_of: account @@ -479,10 +512,16 @@ impl Server { domain_id: account.domain_id.document_id(), }] .into_iter() - .chain(account.aliases.into_iter().map(|alias| EmailAddress { - local_part: alias.name.into(), - domain_id: alias.domain_id.document_id(), - })) + .chain( + account + .aliases + .into_iter() + .filter(|alias| alias.enabled) + .map(|alias| EmailAddress { + local_part: alias.name.into(), + domain_id: alias.domain_id.document_id(), + }), + ) .collect(), id_tenant: account.member_tenant_id.map(|id| id.document_id()), id_member_of: Default::default(), @@ -764,9 +803,22 @@ impl Server { let Some(list) = self.registry().object::(id.into()).await? else { return Ok(None); }; - let cache = Arc::new(MailingListCache { - recipients: list.recipients.into_iter().map(Into::into).collect(), - }); + let cache = + Arc::new(MailingListCache { + addresses: [EmailAddress { + local_part: list.name.into(), + domain_id: list.domain_id.document_id(), + }] + .into_iter() + .chain(list.aliases.into_iter().filter(|alias| alias.enabled).map( + |alias| EmailAddress { + local_part: alias.name.into(), + domain_id: alias.domain_id.document_id(), + }, + )) + .collect(), + recipients: list.recipients.into_iter().map(Into::into).collect(), + }); let _ = guard.insert(cache.clone()); Ok(Some(cache)) }