wip: support rustls-0.23 for http1.1 and 1.2

This commit is contained in:
Jun Kurihara 2024-05-28 20:49:11 +09:00
commit 0c6f3edf18
No known key found for this signature in database
GPG key ID: 48ADFD173ED22B03
16 changed files with 80 additions and 393 deletions

View file

@ -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

View file

@ -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());
}
}

View file

@ -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() {

View file

@ -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
};

View file

@ -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>,