wip: support rustls-0.23 for http1.1 and 1.2
This commit is contained in:
parent
2f9f0a1122
commit
0c6f3edf18
16 changed files with 80 additions and 393 deletions
|
|
@ -13,7 +13,8 @@ publish.workspace = true
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["http3-quinn", "cache", "rustls-backend"]
|
||||
# default = ["http3-quinn", "cache", "rustls-backend"]
|
||||
default = ["cache", "rustls-backend"]
|
||||
http3-quinn = ["rpxy-lib/http3-quinn"]
|
||||
http3-s2n = ["rpxy-lib/http3-s2n"]
|
||||
native-tls-backend = ["rpxy-lib/native-tls-backend"]
|
||||
|
|
@ -30,7 +31,6 @@ mimalloc = { version = "*", default-features = false }
|
|||
anyhow = "1.0.86"
|
||||
rustc-hash = "1.1.0"
|
||||
serde = { version = "1.0.202", default-features = false, features = ["derive"] }
|
||||
derive_builder = "0.20.0"
|
||||
tokio = { version = "1.37.0", default-features = false, features = [
|
||||
"net",
|
||||
"rt-multi-thread",
|
||||
|
|
@ -39,7 +39,6 @@ tokio = { version = "1.37.0", default-features = false, features = [
|
|||
"macros",
|
||||
] }
|
||||
async-trait = "0.1.80"
|
||||
rustls-pemfile = "1.0.4"
|
||||
|
||||
|
||||
# config
|
||||
|
|
|
|||
|
|
@ -1,183 +0,0 @@
|
|||
use crate::log::*;
|
||||
use async_trait::async_trait;
|
||||
use derive_builder::Builder;
|
||||
use rpxy_lib::{
|
||||
reexports::{Certificate, PrivateKey},
|
||||
CertsAndKeys, CryptoSource,
|
||||
};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufReader, Cursor, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Builder, Debug, Clone)]
|
||||
/// Crypto-related file reader implementing certs::CryptoRead trait
|
||||
pub struct CryptoFileSource {
|
||||
#[builder(setter(custom))]
|
||||
/// Always exist
|
||||
pub tls_cert_path: PathBuf,
|
||||
|
||||
#[builder(setter(custom))]
|
||||
/// Always exist
|
||||
pub tls_cert_key_path: PathBuf,
|
||||
|
||||
#[builder(setter(custom), default)]
|
||||
/// This may not exist
|
||||
pub client_ca_cert_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl CryptoFileSourceBuilder {
|
||||
pub fn tls_cert_path<T: AsRef<Path>>(&mut self, v: T) -> &mut Self {
|
||||
self.tls_cert_path = Some(v.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
pub fn tls_cert_key_path<T: AsRef<Path>>(&mut self, v: T) -> &mut Self {
|
||||
self.tls_cert_key_path = Some(v.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
pub fn client_ca_cert_path<T: AsRef<Path>>(&mut self, v: Option<T>) -> &mut Self {
|
||||
self.client_ca_cert_path = Some(v.map(|p| p.as_ref().to_path_buf()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CryptoSource for CryptoFileSource {
|
||||
type Error = io::Error;
|
||||
/// read crypto materials from source
|
||||
async fn read(&self) -> Result<CertsAndKeys, Self::Error> {
|
||||
read_certs_and_keys(
|
||||
&self.tls_cert_path,
|
||||
&self.tls_cert_key_path,
|
||||
self.client_ca_cert_path.as_ref(),
|
||||
)
|
||||
}
|
||||
/// Returns true when mutual tls is enabled
|
||||
fn is_mutual_tls(&self) -> bool {
|
||||
self.client_ca_cert_path.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read certificates and private keys from file
|
||||
fn read_certs_and_keys(
|
||||
cert_path: &PathBuf,
|
||||
cert_key_path: &PathBuf,
|
||||
client_ca_cert_path: Option<&PathBuf>,
|
||||
) -> Result<CertsAndKeys, io::Error> {
|
||||
debug!("Read TLS server certificates and private key");
|
||||
|
||||
let certs: Vec<_> = {
|
||||
let certs_path_str = cert_path.display().to_string();
|
||||
let mut reader = BufReader::new(
|
||||
File::open(cert_path)
|
||||
.map_err(|e| io::Error::new(e.kind(), format!("Unable to load the certificates [{certs_path_str}]: {e}")))?,
|
||||
);
|
||||
rustls_pemfile::certs(&mut reader)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Unable to parse the certificates"))?
|
||||
}
|
||||
.drain(..)
|
||||
.map(Certificate)
|
||||
.collect();
|
||||
|
||||
let cert_keys: Vec<_> = {
|
||||
let cert_key_path_str = cert_key_path.display().to_string();
|
||||
let encoded_keys = {
|
||||
let mut encoded_keys = vec![];
|
||||
File::open(cert_key_path)
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
e.kind(),
|
||||
format!("Unable to load the certificate keys [{cert_key_path_str}]: {e}"),
|
||||
)
|
||||
})?
|
||||
.read_to_end(&mut encoded_keys)?;
|
||||
encoded_keys
|
||||
};
|
||||
let mut reader = Cursor::new(encoded_keys);
|
||||
let pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut reader).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Unable to parse the certificates private keys (PKCS8)",
|
||||
)
|
||||
})?;
|
||||
reader.set_position(0);
|
||||
let mut rsa_keys = rustls_pemfile::rsa_private_keys(&mut reader)?;
|
||||
let mut keys = pkcs8_keys;
|
||||
keys.append(&mut rsa_keys);
|
||||
if keys.is_empty() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"No private keys found - Make sure that they are in PKCS#8/PEM format",
|
||||
));
|
||||
}
|
||||
keys.drain(..).map(PrivateKey).collect()
|
||||
};
|
||||
|
||||
let client_ca_certs = if let Some(path) = client_ca_cert_path {
|
||||
debug!("Read CA certificates for client authentication");
|
||||
// Reads client certificate and returns client
|
||||
let certs: Vec<_> = {
|
||||
let certs_path_str = path.display().to_string();
|
||||
let mut reader = BufReader::new(File::open(path).map_err(|e| {
|
||||
io::Error::new(
|
||||
e.kind(),
|
||||
format!("Unable to load the client certificates [{certs_path_str}]: {e}"),
|
||||
)
|
||||
})?);
|
||||
rustls_pemfile::certs(&mut reader)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Unable to parse the client certificates"))?
|
||||
}
|
||||
.drain(..)
|
||||
.map(Certificate)
|
||||
.collect();
|
||||
Some(certs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(CertsAndKeys {
|
||||
certs,
|
||||
cert_keys,
|
||||
client_ca_certs,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[tokio::test]
|
||||
async fn read_server_crt_key_files() {
|
||||
let tls_cert_path = "../example-certs/server.crt";
|
||||
let tls_cert_key_path = "../example-certs/server.key";
|
||||
let crypto_file_source = CryptoFileSourceBuilder::default()
|
||||
.tls_cert_key_path(tls_cert_key_path)
|
||||
.tls_cert_path(tls_cert_path)
|
||||
.build();
|
||||
assert!(crypto_file_source.is_ok());
|
||||
|
||||
let crypto_file_source = crypto_file_source.unwrap();
|
||||
let crypto_elem = crypto_file_source.read().await;
|
||||
assert!(crypto_elem.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_server_crt_key_files_with_client_ca_crt() {
|
||||
let tls_cert_path = "../example-certs/server.crt";
|
||||
let tls_cert_key_path = "../example-certs/server.key";
|
||||
let client_ca_cert_path = Some("../example-certs/client.ca.crt");
|
||||
let crypto_file_source = CryptoFileSourceBuilder::default()
|
||||
.tls_cert_key_path(tls_cert_key_path)
|
||||
.tls_cert_path(tls_cert_path)
|
||||
.client_ca_cert_path(client_ca_cert_path)
|
||||
.build();
|
||||
assert!(crypto_file_source.is_ok());
|
||||
|
||||
let crypto_file_source = crypto_file_source.unwrap();
|
||||
let crypto_elem = crypto_file_source.read().await;
|
||||
assert!(crypto_elem.is_ok());
|
||||
|
||||
let crypto_elem = crypto_elem.unwrap();
|
||||
assert!(crypto_elem.client_ca_certs.is_some());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
use super::toml::ConfigToml;
|
||||
use crate::{
|
||||
cert_file_reader::CryptoFileSource,
|
||||
error::{anyhow, ensure},
|
||||
};
|
||||
use crate::error::{anyhow, ensure};
|
||||
use clap::{Arg, ArgAction};
|
||||
use hot_reload::{ReloaderReceiver, ReloaderService};
|
||||
use rpxy_certs::{build_cert_reloader, CryptoFileSourceBuilder, CryptoReloader, ServerCryptoBase};
|
||||
|
|
@ -43,7 +40,7 @@ pub fn parse_opts() -> Result<Opts, anyhow::Error> {
|
|||
Ok(Opts { config_file_path, watch })
|
||||
}
|
||||
|
||||
pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig, AppConfigList<CryptoFileSource>), anyhow::Error> {
|
||||
pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig, AppConfigList), anyhow::Error> {
|
||||
// build proxy config
|
||||
let proxy_config: ProxyConfig = config.try_into()?;
|
||||
|
||||
|
|
@ -74,7 +71,7 @@ pub fn build_settings(config: &ConfigToml) -> std::result::Result<(ProxyConfig,
|
|||
}
|
||||
|
||||
// build applications
|
||||
let mut app_config_list_inner = Vec::<AppConfig<CryptoFileSource>>::new();
|
||||
let mut app_config_list_inner = Vec::<AppConfig>::new();
|
||||
|
||||
// let mut backends = Backends::new();
|
||||
for (app_name, app) in apps.0.iter() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{
|
||||
cert_file_reader::{CryptoFileSource, CryptoFileSourceBuilder},
|
||||
constants::*,
|
||||
error::{anyhow, ensure},
|
||||
};
|
||||
|
|
@ -214,7 +213,7 @@ impl ConfigToml {
|
|||
}
|
||||
|
||||
impl Application {
|
||||
pub fn build_app_config(&self, app_name: &str) -> std::result::Result<AppConfig<CryptoFileSource>, anyhow::Error> {
|
||||
pub fn build_app_config(&self, app_name: &str) -> std::result::Result<AppConfig, anyhow::Error> {
|
||||
let server_name_string = self.server_name.as_ref().ok_or(anyhow!("Missing server_name"))?;
|
||||
|
||||
// reverse proxy settings
|
||||
|
|
@ -224,11 +223,6 @@ impl Application {
|
|||
let tls_config = if self.tls.is_some() {
|
||||
let tls = self.tls.as_ref().unwrap();
|
||||
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());
|
||||
let inner = CryptoFileSourceBuilder::default()
|
||||
.tls_cert_path(tls.tls_cert_path.as_ref().unwrap())
|
||||
.tls_cert_key_path(tls.tls_cert_key_path.as_ref().unwrap())
|
||||
.client_ca_cert_path(tls.client_ca_cert_path.as_deref())
|
||||
.build()?;
|
||||
|
||||
let https_redirection = if tls.https_redirection.is_none() {
|
||||
true // Default true
|
||||
|
|
@ -236,10 +230,7 @@ impl Application {
|
|||
tls.https_redirection.unwrap()
|
||||
};
|
||||
|
||||
Some(TlsConfig {
|
||||
inner,
|
||||
https_redirection,
|
||||
})
|
||||
Some(TlsConfig { https_redirection })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#[global_allocator]
|
||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
||||
mod cert_file_reader;
|
||||
mod config;
|
||||
mod constants;
|
||||
mod error;
|
||||
|
|
@ -141,7 +140,7 @@ async fn rpxy_service_with_watcher(
|
|||
/// Wrapper of entry point for rpxy service with certificate management service
|
||||
async fn rpxy_entrypoint(
|
||||
proxy_config: &rpxy_lib::ProxyConfig,
|
||||
app_config_list: &rpxy_lib::AppConfigList<cert_file_reader::CryptoFileSource>,
|
||||
app_config_list: &rpxy_lib::AppConfigList,
|
||||
cert_service_and_rx: Option<&(
|
||||
ReloaderService<rpxy_certs::CryptoReloader, rpxy_certs::ServerCryptoBase>,
|
||||
ReloaderReceiver<rpxy_certs::ServerCryptoBase>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue