Add TLS support
This commit is contained in:
parent
1f449dc7e9
commit
f67f04129a
6 changed files with 127 additions and 16 deletions
59
Cargo.lock
generated
59
Cargo.lock
generated
|
@ -56,12 +56,15 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
|
"rustls 0.22.2",
|
||||||
|
"rustls-pemfile 2.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
"smtp-message",
|
"smtp-message",
|
||||||
"smtp-server",
|
"smtp-server",
|
||||||
"sodiumoxide",
|
"sodiumoxide",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-rustls 0.25.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -2662,10 +2665,24 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"ring 0.17.7",
|
"ring 0.17.7",
|
||||||
"rustls-webpki",
|
"rustls-webpki 0.101.7",
|
||||||
"sct",
|
"sct",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring 0.17.7",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki 0.102.1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-native-certs"
|
name = "rustls-native-certs"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
@ -2673,7 +2690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"openssl-probe",
|
"openssl-probe",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile 1.0.4",
|
||||||
"schannel",
|
"schannel",
|
||||||
"security-framework",
|
"security-framework",
|
||||||
]
|
]
|
||||||
|
@ -2687,6 +2704,22 @@ dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.101.7"
|
version = "0.101.7"
|
||||||
|
@ -2697,6 +2730,17 @@ dependencies = [
|
||||||
"untrusted 0.9.0",
|
"untrusted 0.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.102.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b"
|
||||||
|
dependencies = [
|
||||||
|
"ring 0.17.7",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
|
@ -3186,6 +3230,17 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||||
|
dependencies = [
|
||||||
|
"rustls 0.22.2",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
|
|
|
@ -43,6 +43,9 @@ zstd = { version = "0.9", default-features = false }
|
||||||
sodiumoxide = "0.2"
|
sodiumoxide = "0.2"
|
||||||
argon2 = "0.5"
|
argon2 = "0.5"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
rustls = "0.22"
|
||||||
|
rustls-pemfile = "2.0"
|
||||||
|
tokio-rustls = "0.25"
|
||||||
hyper-rustls = { version = "0.24", features = ["http2"] }
|
hyper-rustls = { version = "0.24", features = ["http2"] }
|
||||||
rpassword = "7.0"
|
rpassword = "7.0"
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct CompanionConfig {
|
pub struct CompanionConfig {
|
||||||
pub pid: Option<PathBuf>,
|
pub pid: Option<PathBuf>,
|
||||||
pub imap: ImapConfig,
|
pub imap: ImapUnsecureConfig,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub users: LoginStaticConfig,
|
pub users: LoginStaticConfig,
|
||||||
|
@ -18,8 +18,9 @@ pub struct CompanionConfig {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct ProviderConfig {
|
pub struct ProviderConfig {
|
||||||
pub pid: Option<PathBuf>,
|
pub pid: Option<PathBuf>,
|
||||||
pub imap: ImapConfig,
|
pub imap: Option<ImapConfig>,
|
||||||
pub lmtp: LmtpConfig,
|
pub imap_unsecure: Option<ImapUnsecureConfig>,
|
||||||
|
pub lmtp: Option<LmtpConfig>,
|
||||||
pub users: UserManagement,
|
pub users: UserManagement,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +41,13 @@ pub struct LmtpConfig {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct ImapConfig {
|
pub struct ImapConfig {
|
||||||
pub bind_addr: SocketAddr,
|
pub bind_addr: SocketAddr,
|
||||||
|
pub certs: PathBuf,
|
||||||
|
pub key: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct ImapUnsecureConfig {
|
||||||
|
pub bind_addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
|
@ -26,8 +26,10 @@ use imap_codec::imap_types::response::{Code, CommandContinuationRequest, Respons
|
||||||
use imap_codec::imap_types::{core::Text, response::Greeting};
|
use imap_codec::imap_types::{core::Text, response::Greeting};
|
||||||
use imap_flow::server::{ServerFlow, ServerFlowEvent, ServerFlowOptions};
|
use imap_flow::server::{ServerFlow, ServerFlowEvent, ServerFlowOptions};
|
||||||
use imap_flow::stream::AnyStream;
|
use imap_flow::stream::AnyStream;
|
||||||
|
use tokio_rustls::TlsAcceptor;
|
||||||
|
use rustls_pemfile::{certs, private_key};
|
||||||
|
|
||||||
use crate::config::ImapConfig;
|
use crate::config::{ImapConfig, ImapUnsecureConfig};
|
||||||
use crate::imap::capability::ServerCapability;
|
use crate::imap::capability::ServerCapability;
|
||||||
use crate::imap::request::Request;
|
use crate::imap::request::Request;
|
||||||
use crate::imap::response::{Body, ResponseOrIdle};
|
use crate::imap::response::{Body, ResponseOrIdle};
|
||||||
|
@ -39,6 +41,7 @@ pub struct Server {
|
||||||
bind_addr: SocketAddr,
|
bind_addr: SocketAddr,
|
||||||
login_provider: ArcLoginProvider,
|
login_provider: ArcLoginProvider,
|
||||||
capabilities: ServerCapability,
|
capabilities: ServerCapability,
|
||||||
|
tls: Option<TlsAcceptor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -49,11 +52,29 @@ struct ClientContext {
|
||||||
server_capabilities: ServerCapability,
|
server_capabilities: ServerCapability,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(config: ImapConfig, login: ArcLoginProvider) -> Server {
|
pub fn new(config: ImapConfig, login: ArcLoginProvider) -> Result<Server> {
|
||||||
|
let loaded_certs = certs(&mut std::io::BufReader::new(std::fs::File::open(config.certs)?)).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let loaded_key = private_key(&mut std::io::BufReader::new(std::fs::File::open(config.key)?))?.unwrap();
|
||||||
|
|
||||||
|
let tls_config = rustls::ServerConfig::builder()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(loaded_certs, loaded_key)?;
|
||||||
|
let acceptor = TlsAcceptor::from(Arc::new(tls_config));
|
||||||
|
|
||||||
|
Ok(Server {
|
||||||
|
bind_addr: config.bind_addr,
|
||||||
|
login_provider: login,
|
||||||
|
capabilities: ServerCapability::default(),
|
||||||
|
tls: Some(acceptor),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_unsecure(config: ImapUnsecureConfig, login: ArcLoginProvider) -> Server {
|
||||||
Server {
|
Server {
|
||||||
bind_addr: config.bind_addr,
|
bind_addr: config.bind_addr,
|
||||||
login_provider: login,
|
login_provider: login,
|
||||||
capabilities: ServerCapability::default(),
|
capabilities: ServerCapability::default(),
|
||||||
|
tls: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +99,19 @@ impl Server {
|
||||||
_ = must_exit.changed() => continue,
|
_ = must_exit.changed() => continue,
|
||||||
};
|
};
|
||||||
tracing::info!("IMAP: accepted connection from {}", remote_addr);
|
tracing::info!("IMAP: accepted connection from {}", remote_addr);
|
||||||
|
let stream = match self.tls.clone() {
|
||||||
|
Some(acceptor) => {
|
||||||
|
let stream = match acceptor.accept(socket).await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!(err=?e, "TLS negociation failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AnyStream::new(stream)
|
||||||
|
},
|
||||||
|
None => AnyStream::new(socket),
|
||||||
|
};
|
||||||
|
|
||||||
let client = ClientContext {
|
let client = ClientContext {
|
||||||
addr: remote_addr.clone(),
|
addr: remote_addr.clone(),
|
||||||
|
@ -85,7 +119,7 @@ impl Server {
|
||||||
must_exit: must_exit.clone(),
|
must_exit: must_exit.clone(),
|
||||||
server_capabilities: self.capabilities.clone(),
|
server_capabilities: self.capabilities.clone(),
|
||||||
};
|
};
|
||||||
let conn = tokio::spawn(NetLoop::handler(client, AnyStream::new(socket)));
|
let conn = tokio::spawn(NetLoop::handler(client, stream));
|
||||||
connections.push(conn);
|
connections.push(conn);
|
||||||
}
|
}
|
||||||
drop(tcp);
|
drop(tcp);
|
||||||
|
|
|
@ -167,13 +167,14 @@ async fn main() -> Result<()> {
|
||||||
use std::net::*;
|
use std::net::*;
|
||||||
AnyConfig::Provider(ProviderConfig {
|
AnyConfig::Provider(ProviderConfig {
|
||||||
pid: None,
|
pid: None,
|
||||||
imap: ImapConfig {
|
imap: None,
|
||||||
|
imap_unsecure: Some(ImapUnsecureConfig {
|
||||||
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1143),
|
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1143),
|
||||||
},
|
}),
|
||||||
lmtp: LmtpConfig {
|
lmtp: Some(LmtpConfig {
|
||||||
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1025),
|
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1025),
|
||||||
hostname: "example.tld".to_string(),
|
hostname: "example.tld".to_string(),
|
||||||
},
|
}),
|
||||||
users: UserManagement::Demo,
|
users: UserManagement::Demo,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::login::{demo_provider::*, ldap_provider::*, static_provider::*};
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
lmtp_server: Option<Arc<LmtpServer>>,
|
lmtp_server: Option<Arc<LmtpServer>>,
|
||||||
|
imap_unsecure_server: Option<imap::Server>,
|
||||||
imap_server: Option<imap::Server>,
|
imap_server: Option<imap::Server>,
|
||||||
pid_file: Option<PathBuf>,
|
pid_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
@ -25,10 +26,11 @@ impl Server {
|
||||||
let login = Arc::new(StaticLoginProvider::new(config.users).await?);
|
let login = Arc::new(StaticLoginProvider::new(config.users).await?);
|
||||||
|
|
||||||
let lmtp_server = None;
|
let lmtp_server = None;
|
||||||
let imap_server = Some(imap::new(config.imap, login.clone()));
|
let imap_unsecure_server = Some(imap::new_unsecure(config.imap, login.clone()));
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
lmtp_server,
|
lmtp_server,
|
||||||
imap_server,
|
imap_unsecure_server,
|
||||||
|
imap_server: None,
|
||||||
pid_file: config.pid,
|
pid_file: config.pid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -41,11 +43,13 @@ impl Server {
|
||||||
UserManagement::Ldap(x) => Arc::new(LdapLoginProvider::new(x)?),
|
UserManagement::Ldap(x) => Arc::new(LdapLoginProvider::new(x)?),
|
||||||
};
|
};
|
||||||
|
|
||||||
let lmtp_server = Some(LmtpServer::new(config.lmtp, login.clone()));
|
let lmtp_server = config.lmtp.map(|lmtp| LmtpServer::new(lmtp, login.clone()));
|
||||||
let imap_server = Some(imap::new(config.imap, login.clone()));
|
let imap_unsecure_server = config.imap_unsecure.map(|imap| imap::new_unsecure(imap, login.clone()));
|
||||||
|
let imap_server = config.imap.map(|imap| imap::new(imap, login.clone())).transpose()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
lmtp_server,
|
lmtp_server,
|
||||||
|
imap_unsecure_server,
|
||||||
imap_server,
|
imap_server,
|
||||||
pid_file: config.pid,
|
pid_file: config.pid,
|
||||||
})
|
})
|
||||||
|
@ -79,6 +83,12 @@ impl Server {
|
||||||
Some(s) => s.run(exit_signal.clone()).await,
|
Some(s) => s.run(exit_signal.clone()).await,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async {
|
||||||
|
match self.imap_unsecure_server {
|
||||||
|
None => Ok(()),
|
||||||
|
Some(s) => s.run(exit_signal.clone()).await,
|
||||||
|
}
|
||||||
|
},
|
||||||
async {
|
async {
|
||||||
match self.imap_server {
|
match self.imap_server {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
|
|
Loading…
Reference in a new issue