Skip to content

Commit

Permalink
wip: implemented crypto reloader, as separated object from proxy itself
Browse files Browse the repository at this point in the history
  • Loading branch information
junkurihara committed Nov 24, 2023
1 parent 3c6e4e5 commit 5576389
Show file tree
Hide file tree
Showing 13 changed files with 468 additions and 56 deletions.
36 changes: 19 additions & 17 deletions rpxy-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ publish = false

[features]
default = ["http3-s2n", "sticky-cookie", "cache"]
http3-quinn = ["socket2"] #"quinn", "h3", "h3-quinn", ]
http3-s2n = [] #"h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"]
http3-quinn = ["socket2", "quinn", "h3", "h3-quinn"]
http3-s2n = ["h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"]
sticky-cookie = ["base64", "sha2", "chrono"]
cache = [] #"http-cache-semantics", "lru"]
native-roots = [] #"hyper-rustls/native-tokio"]
cache = [] #"http-cache-semantics", "lru"]
native-roots = [] #"hyper-rustls/native-tokio"]

[dependencies]
rand = "0.8.5"
Expand All @@ -39,8 +39,7 @@ async-trait = "0.1.74"
anyhow = "1.0.75"
thiserror = "1.0.50"

# http and tls
hot_reload = "0.1.4" # reloading certs
# http
http = "1.0.0"
# http-body-util = "0.1.0"
hyper = { version = "1.0.1", default-features = false }
Expand All @@ -52,22 +51,25 @@ hyper-util = { version = "0.1.1", features = ["full"] }
# "http2",
# ] }
# tokio-rustls = { version = "0.24.1", features = ["early-data"] }

# tls and cert management
hot_reload = "0.1.4"
rustls = { version = "0.21.9", default-features = false }
# webpki = "0.22.4"
# x509-parser = "0.15.1"
webpki = "0.22.4"
x509-parser = "0.15.1"

# logging
tracing = { version = "0.1.40" }

# # http/3
# quinn = { version = "0.10.2", optional = true }
# h3 = { path = "../submodules/h3/h3/", optional = true }
# h3-quinn = { path = "../submodules/h3/h3-quinn/", optional = true }
# s2n-quic = { version = "1.31.0", default-features = false, features = [
# "provider-tls-rustls",
# ], optional = true }
# s2n-quic-h3 = { path = "../submodules/s2n-quic-h3/", optional = true }
# s2n-quic-rustls = { version = "0.31.0", optional = true }
# http/3
quinn = { version = "0.10.2", optional = true }
h3 = { path = "../submodules/h3/h3/", optional = true }
h3-quinn = { path = "../submodules/h3/h3-quinn/", optional = true }
s2n-quic = { version = "1.31.0", default-features = false, features = [
"provider-tls-rustls",
], optional = true }
s2n-quic-h3 = { path = "../submodules/s2n-quic-h3/", optional = true }
s2n-quic-rustls = { version = "0.31.0", optional = true }
# for UDP socket wit SO_REUSEADDR when h3 with quinn
socket2 = { version = "0.5.5", features = ["all"], optional = true }

Expand Down
2 changes: 1 addition & 1 deletion rpxy-lib/src/backend/backend_main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
certs::CryptoSource,
crypto::CryptoSource,
error::*,
log::*,
name_exp::{ByteName, ServerName},
Expand Down
2 changes: 1 addition & 1 deletion rpxy-lib/src/backend/upstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::load_balance::{
// use super::{BytesName, LbContext, PathNameBytesExp, UpstreamOption};
use super::upstream_opts::UpstreamOption;
use crate::{
certs::CryptoSource,
crypto::CryptoSource,
error::RpxyError,
globals::{AppConfig, UpstreamUri},
log::*,
Expand Down
22 changes: 0 additions & 22 deletions rpxy-lib/src/certs.rs

This file was deleted.

91 changes: 91 additions & 0 deletions rpxy-lib/src/crypto/certs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use async_trait::async_trait;
use rustc_hash::FxHashSet as HashSet;
use rustls::{
sign::{any_supported_type, CertifiedKey},
Certificate, OwnedTrustAnchor, PrivateKey,
};
use std::io;
use x509_parser::prelude::*;

#[async_trait]
// 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;
}

/// Certificates and private keys in rustls loaded from files
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CertsAndKeys {
pub certs: Vec<Certificate>,
pub cert_keys: Vec<PrivateKey>,
pub client_ca_certs: Option<Vec<Certificate>>,
}

impl CertsAndKeys {
pub fn parse_server_certs_and_keys(&self) -> Result<CertifiedKey, anyhow::Error> {
// for (server_name_bytes_exp, certs_and_keys) in self.inner.iter() {
let signing_key = self
.cert_keys
.iter()
.find_map(|k| {
if let Ok(sk) = any_supported_type(k) {
Some(sk)
} else {
None
}
})
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Unable to find a valid certificate and key",
)
})?;
Ok(CertifiedKey::new(self.certs.clone(), signing_key))
}

pub fn parse_client_ca_certs(&self) -> Result<(Vec<OwnedTrustAnchor>, HashSet<Vec<u8>>), anyhow::Error> {
let certs = self.client_ca_certs.as_ref().ok_or(anyhow::anyhow!("No client cert"))?;

let owned_trust_anchors: Vec<_> = certs
.iter()
.map(|v| {
// let trust_anchor = tokio_rustls::webpki::TrustAnchor::try_from_cert_der(&v.0).unwrap();
let trust_anchor = webpki::TrustAnchor::try_from_cert_der(&v.0).unwrap();
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
trust_anchor.subject,
trust_anchor.spki,
trust_anchor.name_constraints,
)
})
.collect();

// TODO: SKID is not used currently
let subject_key_identifiers: HashSet<_> = certs
.iter()
.filter_map(|v| {
// retrieve ca key id (subject key id)
let cert = parse_x509_certificate(&v.0).unwrap().1;
let subject_key_ids = cert
.iter_extensions()
.filter_map(|ext| match ext.parsed_extension() {
ParsedExtension::SubjectKeyIdentifier(skid) => Some(skid),
_ => None,
})
.collect::<Vec<_>>();
if !subject_key_ids.is_empty() {
Some(subject_key_ids[0].0.to_owned())
} else {
None
}
})
.collect();

Ok((owned_trust_anchors, subject_key_identifiers))
}
}
36 changes: 36 additions & 0 deletions rpxy-lib/src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
mod certs;
mod service;

use crate::{
backend::BackendAppManager,
constants::{CERTS_WATCH_DELAY_SECS, LOAD_CERTS_ONLY_WHEN_UPDATED},
error::RpxyResult,
};
use hot_reload::{ReloaderReceiver, ReloaderService};
use service::CryptoReloader;
use std::sync::Arc;

pub use certs::{CertsAndKeys, CryptoSource};
pub use service::ServerCryptoBase;

/// Result type inner of certificate reloader service
type ReloaderServiceResultInner<T> = (
ReloaderService<CryptoReloader<T>, ServerCryptoBase>,
ReloaderReceiver<ServerCryptoBase>,
);
/// Build certificate reloader service
pub(crate) async fn build_cert_reloader<T>(
app_manager: &Arc<BackendAppManager<T>>,
) -> RpxyResult<ReloaderServiceResultInner<T>>
where
T: CryptoSource + Clone + Send + Sync + 'static,
{
let (cert_reloader_service, cert_reloader_rx) = ReloaderService::<
service::CryptoReloader<T>,
service::ServerCryptoBase,
>::new(
app_manager, CERTS_WATCH_DELAY_SECS, !LOAD_CERTS_ONLY_WHEN_UPDATED
)
.await?;
Ok((cert_reloader_service, cert_reloader_rx))
}
Loading

0 comments on commit 5576389

Please sign in to comment.