Skip to content

Commit

Permalink
Merge pull request #156 from junkurihara/feat/rustls-0.23
Browse files Browse the repository at this point in the history
feat: rustls-0.23
  • Loading branch information
junkurihara authored Jun 1, 2024
2 parents cbd3eb4 + 8422382 commit d66863a
Show file tree
Hide file tree
Showing 38 changed files with 934 additions and 1,309 deletions.
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "submodules/h3"]
path = submodules/h3
url = git@github.com:junkurihara/h3.git
[submodule "submodules/rusty-http-cache-semantics"]
path = submodules/rusty-http-cache-semantics
url = git@github.com:junkurihara/rusty-http-cache-semantics.git
[submodule "submodules/s2n-quic"]
path = submodules/s2n-quic
url = git@github.com:junkurihara/s2n-quic.git
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

## 0.8.0 (Unreleased)

## 0.7.1 -- 0.7.3
### Important Changes

- Breaking: Support for `rustls`-0.23.x for http/1.1, 2 and 3. No configuration update is needed at this point.
- Breaking: Along with `rustls`, the cert manager was split from `rpxy-lib` and moved to a new inner crate `rpxy-cert`. This change is to make the cert manager reusable for other projects and to support not only static file based certificates but also other types, e.g., dynamic fetching and management via ACME, in the future.

### Improvement

- Refactor: lots of minor improvements


## 0.7.1

- deps and patches

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace.package]
version = "0.7.2"
version = "0.8.0-alpha.0"
authors = ["Jun Kurihara"]
homepage = "https://github.com/junkurihara/rust-rpxy"
repository = "https://github.com/junkurihara/rust-rpxy"
Expand All @@ -9,7 +9,7 @@ edition = "2021"
publish = false

[workspace]
members = ["rpxy-bin", "rpxy-lib"]
members = ["rpxy-bin", "rpxy-lib", "rpxy-certs"]
exclude = ["submodules"]
resolver = "2"

Expand Down
15 changes: 10 additions & 5 deletions rpxy-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ publish.workspace = true

[features]
default = ["http3-quinn", "cache", "rustls-backend"]
# default = ["http3-s2n", "cache", "rustls-backend"]
http3-quinn = ["rpxy-lib/http3-quinn"]
http3-s2n = ["rpxy-lib/http3-s2n"]
native-tls-backend = ["rpxy-lib/native-tls-backend"]
Expand All @@ -26,20 +27,19 @@ rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [
"sticky-cookie",
] }

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 = [
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
tokio = { version = "1.38.0", default-features = false, features = [
"net",
"rt-multi-thread",
"time",
"sync",
"macros",
] }
async-trait = "0.1.80"
rustls-pemfile = "1.0.4"
mimalloc = { version = "*", default-features = false }


# config
clap = { version = "4.5.4", features = ["std", "cargo", "wrap_help"] }
Expand All @@ -50,5 +50,10 @@ hot_reload = "0.1.5"
tracing = { version = "0.1.40" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

################################
# cert management
rpxy-certs = { path = "../rpxy-certs/", default-features = false, features = [
"http3",
] }

[dev-dependencies]
2 changes: 1 addition & 1 deletion rpxy-bin/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ mod toml;

pub use {
self::toml::ConfigToml,
parse::{build_settings, parse_opts},
parse::{build_cert_manager, build_settings, parse_opts},
service::ConfigTomlReloader,
};
54 changes: 39 additions & 15 deletions rpxy-bin/src/config/parse.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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};
use rpxy_lib::{AppConfig, AppConfigList, ProxyConfig};
use rustc_hash::FxHashMap as HashMap;

/// Parsed options
pub struct Opts {
Expand Down Expand Up @@ -37,20 +37,13 @@ pub fn parse_opts() -> Result<Opts, anyhow::Error> {
let config_file_path = matches.get_one::<String>("config_file").unwrap().to_owned();
let watch = matches.get_one::<bool>("watch").unwrap().to_owned();

Ok(Opts {
config_file_path,
watch,
})
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()?;

///////////////////////////////////
// backend_apps
let apps = config.apps.clone().ok_or(anyhow!("Missing application spec"))?;

Expand Down Expand Up @@ -78,9 +71,8 @@ pub fn build_settings(
}

// 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() {
let _server_name_string = app.server_name.as_ref().ok_or(anyhow!("No server name"))?;
let registered_app_name = app_name.to_ascii_lowercase();
Expand All @@ -95,3 +87,35 @@ pub fn build_settings(

Ok((proxy_config, app_config_list))
}

/* ----------------------- */
/// Build cert map
pub async fn build_cert_manager(
config: &ConfigToml,
) -> Result<
Option<(
ReloaderService<CryptoReloader, ServerCryptoBase>,
ReloaderReceiver<ServerCryptoBase>,
)>,
anyhow::Error,
> {
let apps = config.apps.as_ref().ok_or(anyhow!("No apps"))?;
if config.listen_port_tls.is_none() {
return Ok(None);
}
let mut crypto_source_map = HashMap::default();
for app in apps.0.values() {
if let Some(tls) = app.tls.as_ref() {
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());
let server_name = app.server_name.as_ref().ok_or(anyhow!("No server name"))?;
let crypto_file_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.as_deref())
.build()?;
crypto_source_map.insert(server_name.to_owned(), crypto_file_source);
}
}
let res = build_cert_reloader(&crypto_source_map, None).await?;
Ok(Some(res))
}
10 changes: 2 additions & 8 deletions rpxy-bin/src/config/toml.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::{
cert_file_reader::{CryptoFileSource, CryptoFileSourceBuilder},
constants::*,
error::{anyhow, ensure},
};
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -237,7 +231,7 @@ impl Application {
};

Some(TlsConfig {
inner,
mutual_tls: tls.client_ca_cert_path.is_some(),
https_redirection,
})
} else {
Expand Down
114 changes: 73 additions & 41 deletions rpxy-bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

mod cert_file_reader;
mod config;
mod constants;
mod error;
mod log;

use crate::{
config::{build_settings, parse_opts, ConfigToml, ConfigTomlReloader},
config::{build_cert_manager, build_settings, parse_opts, ConfigToml, ConfigTomlReloader},
constants::CONFIG_WATCH_DELAY_SECS,
error::*,
log::*,
};
use hot_reload::{ReloaderReceiver, ReloaderService};
Expand All @@ -36,13 +36,10 @@ fn main() {
std::process::exit(1);
}
} else {
let (config_service, config_rx) = ReloaderService::<ConfigTomlReloader, ConfigToml>::new(
&parsed_opts.config_file_path,
CONFIG_WATCH_DELAY_SECS,
false,
)
.await
.unwrap();
let (config_service, config_rx) =
ReloaderService::<ConfigTomlReloader, ConfigToml>::new(&parsed_opts.config_file_path, CONFIG_WATCH_DELAY_SECS, false)
.await
.unwrap();

tokio::select! {
Err(e) = config_service.start() => {
Expand All @@ -53,6 +50,9 @@ fn main() {
error!("rpxy service existed: {e}");
std::process::exit(1);
}
else => {
std::process::exit(0);
}
}
}
});
Expand All @@ -63,23 +63,16 @@ async fn rpxy_service_without_watcher(
runtime_handle: tokio::runtime::Handle,
) -> Result<(), anyhow::Error> {
info!("Start rpxy service");
let config_toml = match ConfigToml::new(config_file_path) {
Ok(v) => v,
Err(e) => {
error!("Invalid toml file: {e}");
std::process::exit(1);
}
};
let (proxy_conf, app_conf) = match build_settings(&config_toml) {
Ok(v) => v,
Err(e) => {
error!("Invalid configuration: {e}");
return Err(anyhow::anyhow!(e));
}
};
entrypoint(&proxy_conf, &app_conf, &runtime_handle, None)
let config_toml = ConfigToml::new(config_file_path).map_err(|e| anyhow!("Invalid toml file: {e}"))?;
let (proxy_conf, app_conf) = build_settings(&config_toml).map_err(|e| anyhow!("Invalid configuration: {e}"))?;

let cert_service_and_rx = build_cert_manager(&config_toml)
.await
.map_err(|e| anyhow!("Invalid cert configuration: {e}"))?;

rpxy_entrypoint(&proxy_conf, &app_conf, cert_service_and_rx.as_ref(), &runtime_handle, None)
.await
.map_err(|e| anyhow::anyhow!(e))
.map_err(|e| anyhow!(e))
}

async fn rpxy_service_with_watcher(
Expand All @@ -89,31 +82,31 @@ async fn rpxy_service_with_watcher(
info!("Start rpxy service with dynamic config reloader");
// Initial loading
config_rx.changed().await?;
let config_toml = config_rx.borrow().clone().unwrap();
let (mut proxy_conf, mut app_conf) = match build_settings(&config_toml) {
Ok(v) => v,
Err(e) => {
error!("Invalid configuration: {e}");
return Err(anyhow::anyhow!(e));
}
};
let config_toml = config_rx
.borrow()
.clone()
.ok_or(anyhow!("Something wrong in config reloader receiver"))?;
let (mut proxy_conf, mut app_conf) = build_settings(&config_toml).map_err(|e| anyhow!("Invalid configuration: {e}"))?;

let mut cert_service_and_rx = build_cert_manager(&config_toml)
.await
.map_err(|e| anyhow!("Invalid cert configuration: {e}"))?;

// Notifier for proxy service termination
let term_notify = std::sync::Arc::new(tokio::sync::Notify::new());

// Continuous monitoring
loop {
tokio::select! {
_ = entrypoint(&proxy_conf, &app_conf, &runtime_handle, Some(term_notify.clone())) => {
error!("rpxy entrypoint exited");
break;
rpxy_res = rpxy_entrypoint(&proxy_conf, &app_conf, cert_service_and_rx.as_ref(), &runtime_handle, Some(term_notify.clone())) => {
error!("rpxy entrypoint or cert service exited");
return rpxy_res.map_err(|e| anyhow!(e));
}
_ = config_rx.changed() => {
if config_rx.borrow().is_none() {
let Some(config_toml) = config_rx.borrow().clone() else {
error!("Something wrong in config reloader receiver");
break;
}
let config_toml = config_rx.borrow().clone().unwrap();
return Err(anyhow!("Something wrong in config reloader receiver"));
};
match build_settings(&config_toml) {
Ok((p, a)) => {
(proxy_conf, app_conf) = (p, a)
Expand All @@ -123,6 +116,16 @@ async fn rpxy_service_with_watcher(
continue;
}
};
match build_cert_manager(&config_toml).await {
Ok(c) => {
cert_service_and_rx = c;
},
Err(e) => {
error!("Invalid cert configuration. Configuration does not updated: {e}");
continue;
}
};

info!("Configuration updated. Terminate all spawned proxy services and force to re-bind TCP/UDP sockets");
term_notify.notify_waiters();
// tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
Expand All @@ -131,5 +134,34 @@ async fn rpxy_service_with_watcher(
}
}

Err(anyhow::anyhow!("rpxy or continuous monitoring service exited"))
Ok(())
}

/// 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_service_and_rx: Option<&(
ReloaderService<rpxy_certs::CryptoReloader, rpxy_certs::ServerCryptoBase>,
ReloaderReceiver<rpxy_certs::ServerCryptoBase>,
)>, // TODO:
runtime_handle: &tokio::runtime::Handle,
term_notify: Option<std::sync::Arc<tokio::sync::Notify>>,
) -> Result<(), anyhow::Error> {
if let Some((cert_service, cert_rx)) = cert_service_and_rx {
tokio::select! {
rpxy_res = entrypoint(proxy_config, app_config_list, Some(cert_rx), runtime_handle, term_notify) => {
error!("rpxy entrypoint exited");
rpxy_res.map_err(|e| anyhow!(e))
}
cert_res = cert_service.start() => {
error!("cert reloader service exited");
cert_res.map_err(|e| anyhow!(e))
}
}
} else {
entrypoint(proxy_config, app_config_list, None, runtime_handle, term_notify)
.await
.map_err(|e| anyhow!(e))
}
}
Loading

0 comments on commit d66863a

Please sign in to comment.