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",
|
||||
"rmp-serde",
|
||||
"rpassword",
|
||||
"rustls 0.22.2",
|
||||
"rustls-pemfile 2.0.0",
|
||||
"serde",
|
||||
"smtp-message",
|
||||
"smtp-server",
|
||||
"sodiumoxide",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tokio-util",
|
||||
"toml",
|
||||
"tracing",
|
||||
|
@ -2662,10 +2665,24 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
|||
dependencies = [
|
||||
"log",
|
||||
"ring 0.17.7",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.101.7",
|
||||
"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]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.3"
|
||||
|
@ -2673,7 +2690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
@ -2687,6 +2704,22 @@ dependencies = [
|
|||
"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]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
|
@ -2697,6 +2730,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
|
@ -3186,6 +3230,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.14"
|
||||
|
|
|
@ -43,6 +43,9 @@ zstd = { version = "0.9", default-features = false }
|
|||
sodiumoxide = "0.2"
|
||||
argon2 = "0.5"
|
||||
rand = "0.8.5"
|
||||
rustls = "0.22"
|
||||
rustls-pemfile = "2.0"
|
||||
tokio-rustls = "0.25"
|
||||
hyper-rustls = { version = "0.24", features = ["http2"] }
|
||||
rpassword = "7.0"
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct CompanionConfig {
|
||||
pub pid: Option<PathBuf>,
|
||||
pub imap: ImapConfig,
|
||||
pub imap: ImapUnsecureConfig,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub users: LoginStaticConfig,
|
||||
|
@ -18,8 +18,9 @@ pub struct CompanionConfig {
|
|||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ProviderConfig {
|
||||
pub pid: Option<PathBuf>,
|
||||
pub imap: ImapConfig,
|
||||
pub lmtp: LmtpConfig,
|
||||
pub imap: Option<ImapConfig>,
|
||||
pub imap_unsecure: Option<ImapUnsecureConfig>,
|
||||
pub lmtp: Option<LmtpConfig>,
|
||||
pub users: UserManagement,
|
||||
}
|
||||
|
||||
|
@ -40,6 +41,13 @@ pub struct LmtpConfig {
|
|||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ImapConfig {
|
||||
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)]
|
||||
|
|
|
@ -26,8 +26,10 @@ use imap_codec::imap_types::response::{Code, CommandContinuationRequest, Respons
|
|||
use imap_codec::imap_types::{core::Text, response::Greeting};
|
||||
use imap_flow::server::{ServerFlow, ServerFlowEvent, ServerFlowOptions};
|
||||
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::request::Request;
|
||||
use crate::imap::response::{Body, ResponseOrIdle};
|
||||
|
@ -39,6 +41,7 @@ pub struct Server {
|
|||
bind_addr: SocketAddr,
|
||||
login_provider: ArcLoginProvider,
|
||||
capabilities: ServerCapability,
|
||||
tls: Option<TlsAcceptor>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -49,11 +52,29 @@ struct ClientContext {
|
|||
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 {
|
||||
bind_addr: config.bind_addr,
|
||||
login_provider: login,
|
||||
capabilities: ServerCapability::default(),
|
||||
tls: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +99,19 @@ impl Server {
|
|||
_ = must_exit.changed() => continue,
|
||||
};
|
||||
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 {
|
||||
addr: remote_addr.clone(),
|
||||
|
@ -85,7 +119,7 @@ impl Server {
|
|||
must_exit: must_exit.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);
|
||||
}
|
||||
drop(tcp);
|
||||
|
|
|
@ -167,13 +167,14 @@ async fn main() -> Result<()> {
|
|||
use std::net::*;
|
||||
AnyConfig::Provider(ProviderConfig {
|
||||
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),
|
||||
},
|
||||
lmtp: LmtpConfig {
|
||||
}),
|
||||
lmtp: Some(LmtpConfig {
|
||||
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1025),
|
||||
hostname: "example.tld".to_string(),
|
||||
},
|
||||
}),
|
||||
users: UserManagement::Demo,
|
||||
})
|
||||
} else {
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::login::{demo_provider::*, ldap_provider::*, static_provider::*};
|
|||
|
||||
pub struct Server {
|
||||
lmtp_server: Option<Arc<LmtpServer>>,
|
||||
imap_unsecure_server: Option<imap::Server>,
|
||||
imap_server: Option<imap::Server>,
|
||||
pid_file: Option<PathBuf>,
|
||||
}
|
||||
|
@ -25,10 +26,11 @@ impl Server {
|
|||
let login = Arc::new(StaticLoginProvider::new(config.users).await?);
|
||||
|
||||
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 {
|
||||
lmtp_server,
|
||||
imap_server,
|
||||
imap_unsecure_server,
|
||||
imap_server: None,
|
||||
pid_file: config.pid,
|
||||
})
|
||||
}
|
||||
|
@ -41,11 +43,13 @@ impl Server {
|
|||
UserManagement::Ldap(x) => Arc::new(LdapLoginProvider::new(x)?),
|
||||
};
|
||||
|
||||
let lmtp_server = Some(LmtpServer::new(config.lmtp, login.clone()));
|
||||
let imap_server = Some(imap::new(config.imap, login.clone()));
|
||||
let lmtp_server = config.lmtp.map(|lmtp| LmtpServer::new(lmtp, 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 {
|
||||
lmtp_server,
|
||||
imap_unsecure_server,
|
||||
imap_server,
|
||||
pid_file: config.pid,
|
||||
})
|
||||
|
@ -79,6 +83,12 @@ impl Server {
|
|||
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 {
|
||||
match self.imap_server {
|
||||
None => Ok(()),
|
||||
|
|
Loading…
Reference in a new issue