mirror of
https://github.com/chatmail/relay.git
synced 2026-08-22 00:03:14 +00:00
feat!: Remove IP verification for domain-literals (#90)
BREAKING CHANGE: messages using domain-literal addresses don't require to match the origin SMTP connection IP anymore. Signed-off-by: Jagoda Ślązak <jslazak@jslazak.com>
This commit is contained in:
@@ -104,3 +104,25 @@ impl Config {
|
||||
NonZeroU32::new(10).expect("10 != 0")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Default for Config {
|
||||
/// Creates a default configuration with example.org domain.
|
||||
///
|
||||
/// Used for tests.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
filtermail_smtp_port: Self::default_filtermail_smtp_port(),
|
||||
filtermail_smtp_port_incoming: Self::default_filtermail_smtp_port_incoming(),
|
||||
postfix_reinject_port: Self::default_postfix_reinject_port(),
|
||||
postfix_reinject_port_incoming: Self::default_postfix_reinject_port_incoming(),
|
||||
max_message_size: Self::default_max_message_size(),
|
||||
max_user_send_per_minute: Self::default_max_user_send_per_minute(),
|
||||
max_user_send_burst_size: Self::default_max_user_send_burst_size(),
|
||||
passthrough_senders: Vec::new(),
|
||||
passthrough_recipients: Vec::new(),
|
||||
mail_domain: "example.org".to_string(),
|
||||
mailboxes_dir: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-13
@@ -27,23 +27,16 @@ impl IncomingBeforeQueueHandler {
|
||||
})
|
||||
}
|
||||
|
||||
/// Verify the origin of the email either by performing DKIM verification or by matching the FROM
|
||||
/// domain literal with the origin IP.
|
||||
/// Verify the origin of the email by performing a DKIM verification on a regular domain.
|
||||
///
|
||||
/// Currently a no-op for valid domain-literals.
|
||||
async fn verify_origin(&self, envelope: &Envelope, from_addr: &str) -> Result<(), String> {
|
||||
let from_domain = AddressDomain::from_str(from_addr).map_err(|e| e.smtp_response())?;
|
||||
|
||||
match from_domain {
|
||||
AddressDomain::Literal(ip) => {
|
||||
if !envelope.origin_ip.eq_ignore_ascii_case(&ip) {
|
||||
log::warn!(
|
||||
"Received invalid origin address: {ip}, actual: {}",
|
||||
envelope.origin_ip
|
||||
);
|
||||
return Err(format!(
|
||||
"500 Invalid FROM domain literal: {ip} does not match origin IP {}",
|
||||
envelope.origin_ip
|
||||
));
|
||||
}
|
||||
AddressDomain::Literal(_) => {
|
||||
// Subject to change: we currently don't perform any additional authentication
|
||||
// for domain-literals and rely purely on encryption.
|
||||
}
|
||||
AddressDomain::Name(domain) => {
|
||||
if !self.skip_dkim
|
||||
@@ -144,3 +137,35 @@ impl SmtpHandler for IncomingBeforeQueueHandler {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rstest::{fixture, rstest};
|
||||
use testresult::TestResult;
|
||||
|
||||
#[fixture]
|
||||
fn config() -> Config {
|
||||
Config::default()
|
||||
}
|
||||
|
||||
/// Test that domain-literals are not rejected by origin check.
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
#[case::ipv4(include_bytes!("../test_data/encrypted-ipv4.eml"), "one@[192.0.2.0]")]
|
||||
#[case::ipv6(include_bytes!("../test_data/encrypted-ipv6.eml"), "one@[IPv6:2001:db8::1]")]
|
||||
async fn test_domain_literals_allowed(
|
||||
#[case] eml: &[u8],
|
||||
#[case] address: &str,
|
||||
config: Config,
|
||||
) -> TestResult {
|
||||
let handler = IncomingBeforeQueueHandler::new(config, false)?;
|
||||
let mut envelope = Envelope {
|
||||
mail_from: address.to_string(),
|
||||
origin_ip: "".to_string(), // Currently shouldn't be relevant.
|
||||
data: eml.to_vec(),
|
||||
rcpt_to: vec!["does.not.matter@example.org".to_string()],
|
||||
};
|
||||
Ok(handler.check_data(&mut envelope).await?)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user