feat: add initial acme support (ugly!)

This commit is contained in:
Jun Kurihara 2024-07-17 20:48:37 +09:00
commit 7b0ca08e1e
No known key found for this signature in database
GPG key ID: D992B3E3DE1DED23
11 changed files with 277 additions and 89 deletions

View file

@ -105,4 +105,9 @@ pub enum RpxyError {
// Others
#[error("Infallible")]
Infallible(#[from] std::convert::Infallible),
/// No Acme server config for Acme challenge
#[cfg(feature = "acme")]
#[error("No Acme server config")]
NoAcmeServerConfig,
}

View file

@ -16,6 +16,10 @@ pub struct Globals {
pub term_notify: Option<Arc<tokio::sync::Notify>>,
/// Shared context - Certificate reloader service receiver // TODO: newer one
pub cert_reloader_rx: Option<ReloaderReceiver<ServerCryptoBase>>,
#[cfg(feature = "acme")]
/// ServerConfig used for only ACME challenge for ACME domains
pub server_configs_acme_challenge: Arc<rustc_hash::FxHashMap<String, Arc<rustls::ServerConfig>>>,
}
/// Configuration parameters for proxy transport and request handlers

View file

@ -30,13 +30,36 @@ pub mod reexports {
pub use hyper::Uri;
}
#[derive(derive_builder::Builder)]
/// rpxy entrypoint args
pub struct RpxyOptions {
/// Configuration parameters for proxy transport and request handlers
pub proxy_config: ProxyConfig,
/// List of application configurations
pub app_config_list: AppConfigList,
/// Certificate reloader service receiver
pub cert_rx: Option<ReloaderReceiver<ServerCryptoBase>>, // TODO:
/// Async task runtime handler
pub runtime_handle: tokio::runtime::Handle,
/// Notify object to stop async tasks
pub term_notify: Option<Arc<tokio::sync::Notify>>,
#[cfg(feature = "acme")]
/// ServerConfig used for only ACME challenge for ACME domains
pub server_configs_acme_challenge: Arc<rustc_hash::FxHashMap<String, Arc<rustls::ServerConfig>>>,
}
/// Entrypoint that creates and spawns tasks of reverse proxy services
pub async fn entrypoint(
proxy_config: &ProxyConfig,
app_config_list: &AppConfigList,
cert_rx: Option<&ReloaderReceiver<ServerCryptoBase>>, // TODO:
runtime_handle: &tokio::runtime::Handle,
term_notify: Option<Arc<tokio::sync::Notify>>,
RpxyOptions {
proxy_config,
app_config_list,
cert_rx, // TODO:
runtime_handle,
term_notify,
#[cfg(feature = "acme")]
server_configs_acme_challenge,
}: &RpxyOptions,
) -> RpxyResult<()> {
#[cfg(all(feature = "http3-quinn", feature = "http3-s2n"))]
warn!("Both \"http3-quinn\" and \"http3-s2n\" features are enabled. \"http3-quinn\" will be used");
@ -85,7 +108,10 @@ pub async fn entrypoint(
request_count: Default::default(),
runtime_handle: runtime_handle.clone(),
term_notify: term_notify.clone(),
cert_reloader_rx: cert_rx.cloned(),
cert_reloader_rx: cert_rx.clone(),
#[cfg(feature = "acme")]
server_configs_acme_challenge: server_configs_acme_challenge.clone(),
});
// 3. build message handler containing Arc-ed http_client and backends, and make it contained in Arc as well

View file

@ -167,6 +167,9 @@ where
let mut server_crypto_map: Option<Arc<super::SniServerCryptoMap>> = None;
loop {
#[cfg(feature = "acme")]
let server_configs_acme_challenge = self.globals.server_configs_acme_challenge.clone();
select! {
tcp_cnx = tcp_listener.accept().fuse() => {
if tcp_cnx.is_err() || server_crypto_map.is_none() {
@ -190,11 +193,35 @@ where
if server_name.is_none(){
return Err(RpxyError::NoServerNameInClientHello);
}
let server_crypto = sc_map_inner.as_ref().unwrap().get(server_name.as_ref().unwrap());
if server_crypto.is_none() {
return Err(RpxyError::NoTlsServingApp(server_name.as_ref().unwrap().try_into().unwrap_or_default()));
}
let stream = match start.into_stream(server_crypto.unwrap().clone()).await {
/* ------------------ */
// Check for ACME TLS ALPN challenge
#[cfg(feature = "acme")]
let server_crypto = {
if rpxy_acme::reexports::is_tls_alpn_challenge(&client_hello) {
info!("ACME TLS ALPN challenge received");
let Some(server_crypto_acme) = server_configs_acme_challenge.get(&sni.unwrap().to_ascii_lowercase()) else {
return Err(RpxyError::NoAcmeServerConfig);
};
server_crypto_acme
} else {
let server_crypto = sc_map_inner.as_ref().unwrap().get(server_name.as_ref().unwrap());
let Some(server_crypto) = server_crypto else {
return Err(RpxyError::NoTlsServingApp(server_name.as_ref().unwrap().try_into().unwrap_or_default()));
};
server_crypto
}
};
/* ------------------ */
#[cfg(not(feature = "acme"))]
let server_crypto = {
let server_crypto = sc_map_inner.as_ref().unwrap().get(server_name.as_ref().unwrap());
let Some(server_crypto) = server_crypto else {
return Err(RpxyError::NoTlsServingApp(server_name.as_ref().unwrap().try_into().unwrap_or_default()));
};
server_crypto
};
/* ------------------ */
let stream = match start.into_stream(server_crypto.clone()).await {
Ok(s) => TokioIo::new(s),
Err(e) => {
return Err(RpxyError::FailedToTlsHandshake(e.to_string()));