Nothing advertises the legacy protocols while they are off (LP-7)
While the switch is off, the answers that tell a mail app where to connect stop offering what the switch closed, so a new phone or desktop app is not sent to a port that is shut or a sign-in that will be refused: - Thunderbird-style autoconfig (/mail/config-v1.1.xml and its other paths) and Outlook autodiscover leave out IMAP, POP3 and SMTP submission. - PACC (/.well-known/user-agent-configuration.json) offers JMAP, CalDAV, CardDAV and WebDAV, and no IMAP, POP3, SMTP or ManageSieve. The document is rendered once per configuration load, so the JMAP-only version is rendered beside it and chosen per request; the _ua-auto-config digest in the suggested zone follows, since it hashes the same document. - The suggested zone publishes _imap, _imaps, _pop3, _pop3s, _submission and _submissions with target "." -- "not offered", RFC 6186 section 3.4 -- the spec's decision, rather than dropping them: a client that looks is told, and an automatically managed zone replaces the old records instead of leaving them behind. - It also drops the TLSA records for ports 993 and 995. A TLS pin for a port the switch has closed advertises a service that is not there. Submission's 465 keeps its record: the SMTP lock keeps that port open. The switch is read per answer, as sign-in reads it, so every node agrees the moment it turns. Inbound mail, MX records and the JMAP, CalDAV and CardDAV answers are untouched. tests/e2e/legacy_protocols.py checks all four on a running server: with the switch on they offer IMAP, POP3 and SMTP (the control); while it is off they offer none of them and every legacy SRV name has target "."; and once it is back on, autoconfig and the zone read as they did before. All checks pass.
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
//! a legacy protocol is refused before any password is looked at, so a
|
||||
//! listener that exists by mistake still lets nobody in.
|
||||
//!
|
||||
//! And nothing advertises what is closed (LP-7): client configuration and
|
||||
//! the suggested DNS records leave the legacy services out, or mark them as
|
||||
//! not offered, while the switch is off.
|
||||
//!
|
||||
//! Nothing here touches the host's firewall, NAT port-forwards or any proxy
|
||||
//! (LP-20). The server stops answering; what still routes the port is the
|
||||
//! operator's to reconcile.
|
||||
@@ -31,6 +35,7 @@ use inbuxa_features::security::{
|
||||
listeners,
|
||||
protocol_policy::{self, ProtocolPolicy, SavedListener},
|
||||
};
|
||||
use registry::schema::enums::ServiceProtocol;
|
||||
use registry::types::{error::Error, id::ObjectId};
|
||||
use store::registry::bootstrap::Bootstrap;
|
||||
|
||||
@@ -302,6 +307,27 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
/// The services mail apps sign in to, which the switch turns off: nothing may
|
||||
/// offer them while it is (LP-7). SMTP here is submission -- mail apps
|
||||
/// sending -- since inbound mail is never a configured service.
|
||||
pub fn is_legacy_service(protocol: &ServiceProtocol) -> bool {
|
||||
matches!(
|
||||
protocol,
|
||||
ServiceProtocol::Imap
|
||||
| ServiceProtocol::Pop3
|
||||
| ServiceProtocol::Smtp
|
||||
| ServiceProtocol::Managesieve
|
||||
)
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Whether the server-wide switch is off, for the answers that must stop
|
||||
/// offering legacy services (LP-7). Read per answer, as sign-in reads it.
|
||||
pub async fn legacy_protocols_off(&self) -> trc::Result<bool> {
|
||||
Ok(self.protocol_policy().await?.legacy_protocols.is_disabled())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -349,6 +375,26 @@ mod tests {
|
||||
assert_eq!(err.value_as_str(trc::Key::AccountName), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_services_mail_apps_sign_in_to_are_legacy() {
|
||||
for protocol in [
|
||||
ServiceProtocol::Imap,
|
||||
ServiceProtocol::Pop3,
|
||||
ServiceProtocol::Smtp,
|
||||
ServiceProtocol::Managesieve,
|
||||
] {
|
||||
assert!(is_legacy_service(&protocol), "{protocol:?}");
|
||||
}
|
||||
for protocol in [
|
||||
ServiceProtocol::Jmap,
|
||||
ServiceProtocol::Caldav,
|
||||
ServiceProtocol::Carddav,
|
||||
ServiceProtocol::Webdav,
|
||||
] {
|
||||
assert!(!is_legacy_service(&protocol), "{protocol:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_domain_comes_from_the_name_given() {
|
||||
assert_eq!(domain_of(&basic("[email protected]")), Some("b.test".to_string()));
|
||||
|
||||
Reference in New Issue
Block a user