ACME TLS implementation using TLS-ALPN-01 - closes #160

This commit is contained in:
mdecimus
2024-01-05 18:44:22 +01:00
parent 172c8afae0
commit ffba9b5a61
43 changed files with 2285 additions and 464 deletions

View File

@@ -30,7 +30,7 @@ use hyper::{body::Bytes, Method, StatusCode};
use jmap_proto::error::request::RequestError;
use serde_json::json;
use crate::JMAP;
use crate::{services::housekeeper, JMAP};
use super::{http::ToHttpResponse, HttpRequest, JsonResponse};
@@ -299,6 +299,17 @@ impl JMAP {
.into_http_response(),
}
}
("certificates", Some("reload"), &Method::GET) => {
let _ = self
.housekeeper_tx
.send(housekeeper::Event::ReloadCertificates)
.await;
JsonResponse::new(json!({
"data": [],
}))
.into_http_response()
}
(path_1 @ ("queue" | "report"), Some(path_2), &Method::GET) => {
self.smtp
.handle_manage_request(req.uri(), req.method(), path_1, path_2)

View File

@@ -42,7 +42,7 @@ use tokio::{
io::{AsyncRead, AsyncWrite},
net::TcpStream,
};
use utils::listener::{ServerInstance, SessionData, SessionManager};
use utils::listener::{ServerInstance, SessionData, SessionManager, TcpAcceptorResult};
use crate::{
auth::{oauth::OAuthMetadata, AccessToken},
@@ -291,40 +291,45 @@ pub async fn parse_jmap_request(
}
impl SessionManager for JmapSessionManager {
fn spawn(&self, session: SessionData<TcpStream>) {
fn spawn(&self, mut session: SessionData<TcpStream>) {
let jmap = self.inner.clone();
tokio::spawn(async move {
if let Some(tls_acceptor) = &session.instance.tls_acceptor {
let span = session.span;
match tls_acceptor.accept(session.stream).await {
Ok(stream) => {
handle_request(
jmap,
SessionData {
stream,
local_ip: session.local_ip,
remote_ip: session.remote_ip,
remote_port: session.remote_port,
span,
in_flight: session.in_flight,
instance: session.instance,
},
)
.await;
}
Err(err) => {
tracing::debug!(
parent: &span,
context = "tls",
event = "error",
"Failed to accept TLS connection: {}",
err
);
match session.instance.acceptor.accept(session.stream).await {
TcpAcceptorResult::Tls(accept) => {
let span = session.span;
match accept.await {
Ok(stream) => {
handle_request(
jmap,
SessionData {
stream,
local_ip: session.local_ip,
remote_ip: session.remote_ip,
remote_port: session.remote_port,
span,
in_flight: session.in_flight,
instance: session.instance,
},
)
.await;
}
Err(err) => {
tracing::debug!(
parent: &span,
context = "tls",
event = "error",
"Failed to accept TLS connection: {}",
err
);
}
}
}
} else {
handle_request(jmap, session).await;
TcpAcceptorResult::Plain(stream) => {
session.stream = stream;
handle_request(jmap, session).await;
}
TcpAcceptorResult::Close => (),
}
});
}

View File

@@ -60,6 +60,7 @@ use tokio::sync::mpsc;
use utils::{
config::Rate,
ipc::DeliveryEvent,
listener::tls::Certificate,
map::ttl_dashmap::{TtlDashMap, TtlMap},
snowflake::SnowflakeIdGenerator,
UnwrapFailure,
@@ -186,6 +187,7 @@ impl JMAP {
config: &utils::config::Config,
stores: &Stores,
directories: &Directories,
certificates: Vec<Arc<Certificate>>,
delivery_rx: mpsc::Receiver<DeliveryEvent>,
smtp: Arc<SMTP>,
) -> Result<Arc<Self>, String> {
@@ -420,7 +422,7 @@ impl JMAP {
spawn_state_manager(jmap_server.clone(), config, state_rx);
// Spawn housekeeper
spawn_housekeeper(jmap_server.clone(), config, housekeeper_rx);
spawn_housekeeper(jmap_server.clone(), config, certificates, housekeeper_rx);
Ok(jmap_server)
}

View File

@@ -26,6 +26,7 @@ use std::sync::Arc;
use tokio::sync::mpsc;
use utils::{
config::{cron::SimpleCron, Config},
listener::tls::Certificate,
map::ttl_dashmap::TtlMap,
UnwrapFailure,
};
@@ -36,6 +37,7 @@ use super::IPC_CHANNEL_BUFFER;
pub enum Event {
PurgeSessions,
ReloadCertificates,
IndexStart,
IndexDone,
#[cfg(feature = "test_mode")]
@@ -43,7 +45,12 @@ pub enum Event {
Exit,
}
pub fn spawn_housekeeper(core: Arc<JMAP>, settings: &Config, mut rx: mpsc::Receiver<Event>) {
pub fn spawn_housekeeper(
core: Arc<JMAP>,
settings: &Config,
certificates: Vec<Arc<Certificate>>,
mut rx: mpsc::Receiver<Event>,
) {
let purge_cache = settings
.property_or_static::<SimpleCron>("jmap.session.purge.frequency", "15 * *")
.failed("Initialize housekeeper");
@@ -69,6 +76,32 @@ pub fn spawn_housekeeper(core: Arc<JMAP>, settings: &Config, mut rx: mpsc::Recei
Event::PurgeSessions => {
do_purge = true;
}
Event::ReloadCertificates => {
let certificates = certificates.clone();
tokio::spawn(async move {
for cert in certificates {
match cert.reload().await {
Ok(_) => {
tracing::info!(
context = "tls",
event = "reload",
path = cert.path[0].to_string_lossy().as_ref(),
"Reloaded certificate."
);
}
Err(err) => {
tracing::error!(
context = "tls",
event = "error",
path = cert.path[0].to_string_lossy().as_ref(),
error = ?err,
"Failed to reload certificate."
);
}
}
}
});
}
Event::IndexStart => {
if !index_busy {
index_busy = true;
@@ -104,7 +137,9 @@ pub fn spawn_housekeeper(core: Arc<JMAP>, settings: &Config, mut rx: mpsc::Recei
tracing::debug!("Housekeeper task exiting.");
return;
}
Err(_) => (),
Err(_) => {
do_purge = true;
}
}
if do_purge {