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 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.
#[derive(Builder)]
@ -36,16 +36,11 @@ where
/// struct of reverse proxy serving incoming request
pub reverse_proxy: ReverseProxy,
/// tls settings
#[builder(setter(custom), default)]
pub tls_cert_path: Option<PathBuf>,
#[builder(setter(custom), default)]
pub tls_cert_key_path: Option<PathBuf>,
/// tls settings: https redirection with 30x
#[builder(default)]
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)]
pub crypto_source: Option<T>,
}
@ -57,22 +52,6 @@ where
self.server_name = Some(server_name.into().to_ascii_lowercase());
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.

View file

@ -36,8 +36,8 @@ impl CryptoFileSourceBuilder {
self.tls_cert_key_path = Some(PathBuf::from(v));
self
}
pub fn client_ca_cert_path(&mut self, v: &str) -> &mut Self {
self.client_ca_cert_path = Some(Some(PathBuf::from(v)));
pub fn client_ca_cert_path(&mut self, v: &Option<String>) -> &mut Self {
self.client_ca_cert_path = Some(v.to_owned().as_ref().map(PathBuf::from));
self
}
}
@ -45,6 +45,7 @@ impl CryptoFileSourceBuilder {
#[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,
@ -52,10 +53,14 @@ impl CryptoSource for CryptoFileSource {
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
pub(crate) fn read_certs_and_keys(
fn read_certs_and_keys(
cert_path: &PathBuf,
cert_key_path: &PathBuf,
client_ca_cert_path: Option<&PathBuf>,
@ -162,11 +167,11 @@ mod tests {
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 = "example-certs/client.ca.crt";
let client_ca_cert_path = Some("example-certs/client.ca.crt".to_string());
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)
.client_ca_cert_path(&client_ca_cert_path)
.build();
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.
pub trait CryptoSource {
type Error;
/// read crypto materials from source
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 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 tokio::runtime::Handle;
pub fn build_globals<T>(runtime_handle: Handle) -> std::result::Result<Globals<T>, anyhow::Error>
where
T: CryptoSource + Clone,
{
pub fn build_globals(runtime_handle: Handle) -> std::result::Result<Globals<CryptoFileSource>, anyhow::Error> {
let _ = include_str!("../../Cargo.toml");
let options = clap::command!().arg(
Arg::new("config_file")

View file

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

View file

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

View file

@ -209,7 +209,12 @@ where
#[cfg(feature = "http3")]
{
// 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 {
add_header_entry_overwrite_if_exist(
headers,

View file

@ -1,5 +1,4 @@
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},
globals::Globals,
log::*,
@ -55,13 +54,11 @@ where
let mut certs_and_keys_map = ServerCryptoBase::default();
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() {
let tls_cert_key_path = backend.tls_cert_key_path.as_ref().unwrap();
let tls_cert_path = backend.tls_cert_path.as_ref().unwrap();
let tls_client_ca_cert_path = backend.client_ca_cert_path.as_ref();
let certs_and_keys = read_certs_and_keys(tls_cert_path, tls_cert_key_path, tls_client_ca_cert_path)
if let Some(crypto_source) = &backend.crypto_source {
let certs_and_keys = crypto_source
.read()
.await
.map_err(|_e| ReloaderError::<ServerCryptoBase>::Reload("Failed to reload cert, key or ca cert"))?;
certs_and_keys_map
.inner
.insert(server_name_bytes_exp.to_owned(), certs_and_keys);