refactor: remove explict cert file path from backend mods and define abstracted trait for the cert source preparing librarization

This commit is contained in:
Jun Kurihara 2023-07-12 21:40:08 +09:00
commit 05b2aab8b0
No known key found for this signature in database
GPG key ID: 48ADFD173ED22B03
8 changed files with 55 additions and 60 deletions

View file

@ -19,7 +19,7 @@ use crate::{
}; };
use derive_builder::Builder; use derive_builder::Builder;
use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashMap as HashMap;
use std::{borrow::Cow, path::PathBuf}; use std::borrow::Cow;
/// Struct serving information to route incoming connections, like server name to be handled and tls certs/keys settings. /// Struct serving information to route incoming connections, like server name to be handled and tls certs/keys settings.
#[derive(Builder)] #[derive(Builder)]
@ -36,16 +36,11 @@ where
/// struct of reverse proxy serving incoming request /// struct of reverse proxy serving incoming request
pub reverse_proxy: ReverseProxy, pub reverse_proxy: ReverseProxy,
/// tls settings /// tls settings: https redirection with 30x
#[builder(setter(custom), default)]
pub tls_cert_path: Option<PathBuf>,
#[builder(setter(custom), default)]
pub tls_cert_key_path: Option<PathBuf>,
#[builder(default)] #[builder(default)]
pub https_redirection: Option<bool>, pub https_redirection: Option<bool>,
#[builder(setter(custom), default)]
pub client_ca_cert_path: Option<PathBuf>,
/// TLS settings: source meta for server cert, key, client ca cert
#[builder(default)] #[builder(default)]
pub crypto_source: Option<T>, pub crypto_source: Option<T>,
} }
@ -57,22 +52,6 @@ where
self.server_name = Some(server_name.into().to_ascii_lowercase()); self.server_name = Some(server_name.into().to_ascii_lowercase());
self self
} }
pub fn tls_cert_path(&mut self, v: &Option<String>) -> &mut Self {
self.tls_cert_path = Some(opt_string_to_opt_pathbuf(v));
self
}
pub fn tls_cert_key_path(&mut self, v: &Option<String>) -> &mut Self {
self.tls_cert_key_path = Some(opt_string_to_opt_pathbuf(v));
self
}
pub fn client_ca_cert_path(&mut self, v: &Option<String>) -> &mut Self {
self.client_ca_cert_path = Some(opt_string_to_opt_pathbuf(v));
self
}
}
fn opt_string_to_opt_pathbuf(input: &Option<String>) -> Option<PathBuf> {
input.to_owned().as_ref().map(PathBuf::from)
} }
/// HashMap and some meta information for multiple Backend structs. /// HashMap and some meta information for multiple Backend structs.

View file

@ -36,8 +36,8 @@ impl CryptoFileSourceBuilder {
self.tls_cert_key_path = Some(PathBuf::from(v)); self.tls_cert_key_path = Some(PathBuf::from(v));
self self
} }
pub fn client_ca_cert_path(&mut self, v: &str) -> &mut Self { pub fn client_ca_cert_path(&mut self, v: &Option<String>) -> &mut Self {
self.client_ca_cert_path = Some(Some(PathBuf::from(v))); self.client_ca_cert_path = Some(v.to_owned().as_ref().map(PathBuf::from));
self self
} }
} }
@ -45,6 +45,7 @@ impl CryptoFileSourceBuilder {
#[async_trait] #[async_trait]
impl CryptoSource for CryptoFileSource { impl CryptoSource for CryptoFileSource {
type Error = io::Error; type Error = io::Error;
/// read crypto materials from source
async fn read(&self) -> Result<CertsAndKeys, Self::Error> { async fn read(&self) -> Result<CertsAndKeys, Self::Error> {
read_certs_and_keys( read_certs_and_keys(
&self.tls_cert_path, &self.tls_cert_path,
@ -52,10 +53,14 @@ impl CryptoSource for CryptoFileSource {
self.client_ca_cert_path.as_ref(), 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 /// Read certificates and private keys from file
pub(crate) fn read_certs_and_keys( fn read_certs_and_keys(
cert_path: &PathBuf, cert_path: &PathBuf,
cert_key_path: &PathBuf, cert_key_path: &PathBuf,
client_ca_cert_path: Option<&PathBuf>, client_ca_cert_path: Option<&PathBuf>,
@ -162,11 +167,11 @@ mod tests {
async fn read_server_crt_key_files_with_client_ca_crt() { async fn read_server_crt_key_files_with_client_ca_crt() {
let tls_cert_path = "example-certs/server.crt"; let tls_cert_path = "example-certs/server.crt";
let tls_cert_key_path = "example-certs/server.key"; let tls_cert_key_path = "example-certs/server.key";
let client_ca_cert_path = "example-certs/client.ca.crt"; let client_ca_cert_path = Some("example-certs/client.ca.crt".to_string());
let crypto_file_source = CryptoFileSourceBuilder::default() let crypto_file_source = CryptoFileSourceBuilder::default()
.tls_cert_key_path(tls_cert_key_path) .tls_cert_key_path(tls_cert_key_path)
.tls_cert_path(tls_cert_path) .tls_cert_path(tls_cert_path)
.client_ca_cert_path(client_ca_cert_path) .client_ca_cert_path(&client_ca_cert_path)
.build(); .build();
assert!(crypto_file_source.is_ok()); assert!(crypto_file_source.is_ok());

View file

@ -13,5 +13,10 @@ pub struct CertsAndKeys {
// Trait to read certs and keys anywhere from KVS, file, sqlite, etc. // Trait to read certs and keys anywhere from KVS, file, sqlite, etc.
pub trait CryptoSource { pub trait CryptoSource {
type Error; type Error;
/// read crypto materials from source
async fn read(&self) -> Result<CertsAndKeys, Self::Error>; async fn read(&self) -> Result<CertsAndKeys, Self::Error>;
/// Returns true when mutual tls is enabled
fn is_mutual_tls(&self) -> bool;
} }

View file

@ -1,12 +1,16 @@
use super::toml::ConfigToml; use super::toml::ConfigToml;
use crate::{backend::Backends, certs::CryptoSource, error::*, globals::*, log::*, utils::BytesName}; use crate::{
backend::Backends,
cert_file_reader::CryptoFileSource,
error::{anyhow, ensure},
globals::*,
log::*,
utils::BytesName,
};
use clap::Arg; use clap::Arg;
use tokio::runtime::Handle; use tokio::runtime::Handle;
pub fn build_globals<T>(runtime_handle: Handle) -> std::result::Result<Globals<T>, anyhow::Error> pub fn build_globals(runtime_handle: Handle) -> std::result::Result<Globals<CryptoFileSource>, anyhow::Error> {
where
T: CryptoSource + Clone,
{
let _ = include_str!("../../Cargo.toml"); let _ = include_str!("../../Cargo.toml");
let options = clap::command!().arg( let options = clap::command!().arg(
Arg::new("config_file") Arg::new("config_file")

View file

@ -1,8 +1,8 @@
use crate::{ use crate::{
backend::{Backend, BackendBuilder, ReverseProxy, Upstream, UpstreamGroup, UpstreamGroupBuilder, UpstreamOption}, backend::{Backend, BackendBuilder, ReverseProxy, Upstream, UpstreamGroup, UpstreamGroupBuilder, UpstreamOption},
certs::CryptoSource, cert_file_reader::{CryptoFileSource, CryptoFileSourceBuilder},
constants::*, constants::*,
error::*, error::{anyhow, ensure},
globals::ProxyConfig, globals::ProxyConfig,
utils::PathNameBytesExp, utils::PathNameBytesExp,
}; };
@ -164,20 +164,17 @@ impl TryInto<ProxyConfig> for &ConfigToml {
} }
impl ConfigToml { impl ConfigToml {
pub fn new(config_file: &str) -> std::result::Result<Self, RpxyError> { pub fn new(config_file: &str) -> std::result::Result<Self, anyhow::Error> {
let config_str = fs::read_to_string(config_file).map_err(RpxyError::Io)?; let config_str = fs::read_to_string(config_file)?;
toml::from_str(&config_str).map_err(RpxyError::TomlDe) toml::from_str(&config_str).map_err(|e| anyhow!(e))
} }
} }
impl<T> TryInto<Backend<T>> for &Application impl TryInto<Backend<CryptoFileSource>> for &Application {
where
T: CryptoSource + Clone,
{
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_into(self) -> std::result::Result<Backend<T>, Self::Error> { fn try_into(self) -> std::result::Result<Backend<CryptoFileSource>, Self::Error> {
let server_name_string = self.server_name.as_ref().ok_or(anyhow!("Missing server_name"))?; let server_name_string = self.server_name.as_ref().ok_or(anyhow!("Missing server_name"))?;
// backend builder // backend builder
@ -203,11 +200,15 @@ where
tls.https_redirection tls.https_redirection
}; };
backend_builder let crypto_source = CryptoFileSourceBuilder::default()
.tls_cert_path(&tls.tls_cert_path) .tls_cert_path(tls.tls_cert_path.as_ref().unwrap())
.tls_cert_key_path(&tls.tls_cert_key_path) .tls_cert_key_path(tls.tls_cert_key_path.as_ref().unwrap())
.https_redirection(https_redirection)
.client_ca_cert_path(&tls.client_ca_cert_path) .client_ca_cert_path(&tls.client_ca_cert_path)
.build()?;
backend_builder
.https_redirection(https_redirection)
.crypto_source(Some(crypto_source))
.build()? .build()?
}; };
Ok(backend) Ok(backend)
@ -255,7 +256,7 @@ impl TryInto<ReverseProxy> for &Application {
} }
impl TryInto<Upstream> for &UpstreamParams { impl TryInto<Upstream> for &UpstreamParams {
type Error = RpxyError; type Error = anyhow::Error;
fn try_into(self) -> std::result::Result<Upstream, Self::Error> { fn try_into(self) -> std::result::Result<Upstream, Self::Error> {
let scheme = match self.tls { let scheme = match self.tls {

View file

@ -29,9 +29,8 @@ pub enum RpxyError {
#[error("I/O Error")] #[error("I/O Error")]
Io(#[from] io::Error), Io(#[from] io::Error),
#[error("Toml Deserialization Error")] // #[error("Toml Deserialization Error")]
TomlDe(#[from] toml::de::Error), // TomlDe(#[from] toml::de::Error),
#[cfg(feature = "http3")] #[cfg(feature = "http3")]
#[error("Quic Connection Error")] #[error("Quic Connection Error")]
QuicConn(#[from] quinn::ConnectionError), QuicConn(#[from] quinn::ConnectionError),

View file

@ -209,7 +209,12 @@ where
#[cfg(feature = "http3")] #[cfg(feature = "http3")]
{ {
// TODO: Workaround for avoid h3 for client authentication // TODO: Workaround for avoid h3 for client authentication
if self.globals.proxy_config.http3 && chosen_backend.client_ca_cert_path.is_none() { if self.globals.proxy_config.http3
&& chosen_backend
.crypto_source
.as_ref()
.is_some_and(|v| !v.is_mutual_tls())
{
if let Some(port) = self.globals.proxy_config.https_port { if let Some(port) = self.globals.proxy_config.https_port {
add_header_entry_overwrite_if_exist( add_header_entry_overwrite_if_exist(
headers, headers,

View file

@ -1,5 +1,4 @@
use crate::{ use crate::{
cert_file_reader::read_certs_and_keys, // TODO: Trait defining read_certs_and_keys and add struct implementing the trait to backend when build backend
certs::{CertsAndKeys, CryptoSource}, certs::{CertsAndKeys, CryptoSource},
globals::Globals, globals::Globals,
log::*, log::*,
@ -55,13 +54,11 @@ where
let mut certs_and_keys_map = ServerCryptoBase::default(); let mut certs_and_keys_map = ServerCryptoBase::default();
for (server_name_bytes_exp, backend) in self.globals.backends.apps.iter() { for (server_name_bytes_exp, backend) in self.globals.backends.apps.iter() {
if backend.tls_cert_key_path.is_some() && backend.tls_cert_path.is_some() { if let Some(crypto_source) = &backend.crypto_source {
let tls_cert_key_path = backend.tls_cert_key_path.as_ref().unwrap(); let certs_and_keys = crypto_source
let tls_cert_path = backend.tls_cert_path.as_ref().unwrap(); .read()
let tls_client_ca_cert_path = backend.client_ca_cert_path.as_ref(); .await
let certs_and_keys = read_certs_and_keys(tls_cert_path, tls_cert_key_path, tls_client_ca_cert_path)
.map_err(|_e| ReloaderError::<ServerCryptoBase>::Reload("Failed to reload cert, key or ca cert"))?; .map_err(|_e| ReloaderError::<ServerCryptoBase>::Reload("Failed to reload cert, key or ca cert"))?;
certs_and_keys_map certs_and_keys_map
.inner .inner
.insert(server_name_bytes_exp.to_owned(), certs_and_keys); .insert(server_name_bytes_exp.to_owned(), certs_and_keys);