aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock59
-rw-r--r--Cargo.toml3
-rw-r--r--src/config.rs14
-rw-r--r--src/imap/mod.rs40
-rw-r--r--src/main.rs9
-rw-r--r--src/server.rs18
6 files changed, 127 insertions, 16 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f5b2602..de5bce9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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,18 +2665,32 @@ 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"
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",
]
@@ -2688,6 +2705,22 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2698,6 +2731,17 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3187,6 +3231,17 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index a865170..2f2ce91 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/src/config.rs b/src/config.rs
index b9c1f09..0269773 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -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)]
diff --git a/src/imap/mod.rs b/src/imap/mod.rs
index 3f685e6..b08a4ff 100644
--- a/src/imap/mod.rs
+++ b/src/imap/mod.rs
@@ -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);
diff --git a/src/main.rs b/src/main.rs
index a7462bc..3e3674c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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 {
diff --git a/src/server.rs b/src/server.rs
index bd2fd5d..0df1caf 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -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,
})
@@ -80,6 +84,12 @@ impl Server {
}
},
async {
+ match self.imap_unsecure_server {
+ None => Ok(()),
+ Some(s) => s.run(exit_signal.clone()).await,
+ }
+ },
+ async {
match self.imap_server {
None => Ok(()),
Some(s) => s.run(exit_signal.clone()).await,