Skip to content

Commit

Permalink
feat: wip - configuration design
Browse files Browse the repository at this point in the history
  • Loading branch information
junkurihara committed Jul 12, 2024
1 parent 63ee953 commit 887e6b6
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ edition = "2021"
publish = false

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

Expand Down
17 changes: 17 additions & 0 deletions rpxy-acme/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "rpxy-acme"
description = "ACME manager library for `rpxy`"
version.workspace = true
authors.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
readme.workspace = true
edition.workspace = true
publish.workspace = true

[dependencies]
url = { version = "2.5.2" }
rustc-hash = "2.0.0"
thiserror = "1.0.62"
tracing = "0.1.40"
14 changes: 14 additions & 0 deletions rpxy-acme/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// ACME directory url
pub const ACME_DIR_URL: &str = "https://acme-v02.api.letsencrypt.org/directory";

/// ACME registry path that stores account key and certificate
pub const ACME_REGISTRY_PATH: &str = "./acme_registry";

/// ACME accounts directory, subdirectory of ACME_REGISTRY_PATH
pub(crate) const ACME_ACCOUNT_SUBDIR: &str = "account";

/// ACME private key file name
pub const ACME_PRIVATE_KEY_FILE_NAME: &str = "private_key.pem";

/// ACME certificate file name
pub const ACME_CERTIFICATE_FILE_NAME: &str = "certificate.pem";
15 changes: 15 additions & 0 deletions rpxy-acme/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use thiserror::Error;

#[derive(Error, Debug)]
/// Error type for rpxy-acme
pub enum RpxyAcmeError {
/// Invalid acme registry path
#[error("Invalid acme registry path")]
InvalidAcmeRegistryPath,
/// Invalid url
#[error("Invalid url: {0}")]
InvalidUrl(#[from] url::ParseError),
/// IO error
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
12 changes: 12 additions & 0 deletions rpxy-acme/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
mod constants;
mod error;
mod targets;

#[allow(unused_imports)]
mod log {
pub(super) use tracing::{debug, error, info, warn};
}

pub use constants::{ACME_CERTIFICATE_FILE_NAME, ACME_DIR_URL, ACME_PRIVATE_KEY_FILE_NAME, ACME_REGISTRY_PATH};
pub use error::RpxyAcmeError;
pub use targets::AcmeTargets;
7 changes: 5 additions & 2 deletions rpxy-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ 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-s2n", "cache", "rustls-backend"]
default = ["http3-quinn", "cache", "rustls-backend", "acme"]
# default = ["http3-s2n", "cache", "rustls-backend", "acme"]
http3-quinn = ["rpxy-lib/http3-quinn"]
http3-s2n = ["rpxy-lib/http3-s2n"]
native-tls-backend = ["rpxy-lib/native-tls-backend"]
rustls-backend = ["rpxy-lib/rustls-backend"]
webpki-roots = ["rpxy-lib/webpki-roots"]
cache = ["rpxy-lib/cache"]
acme = ["rpxy-lib/acme", "rpxy-acme"]

[dependencies]
rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [
Expand Down Expand Up @@ -56,4 +57,6 @@ rpxy-certs = { path = "../rpxy-certs/", default-features = false, features = [
"http3",
] }

rpxy-acme = { path = "../rpxy-acme/", default-features = false, optional = true }

[dev-dependencies]
5 changes: 4 additions & 1 deletion rpxy-bin/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ mod service;
mod toml;

pub use {
self::toml::ConfigToml,
parse::{build_cert_manager, build_settings, parse_opts},
service::ConfigTomlReloader,
toml::ConfigToml,
};

#[cfg(feature = "acme")]
pub use parse::build_acme_manager;
58 changes: 57 additions & 1 deletion rpxy-bin/src/config/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ use rpxy_certs::{build_cert_reloader, CryptoFileSourceBuilder, CryptoReloader, S
use rpxy_lib::{AppConfig, AppConfigList, ProxyConfig};
use rustc_hash::FxHashMap as HashMap;

#[cfg(feature = "acme")]
use rpxy_acme::{AcmeTargets, ACME_CERTIFICATE_FILE_NAME, ACME_PRIVATE_KEY_FILE_NAME, ACME_REGISTRY_PATH};

/// Parsed options
pub struct Opts {
pub config_file_path: String,
Expand Down Expand Up @@ -103,11 +106,34 @@ pub async fn build_cert_manager(
if config.listen_port_tls.is_none() {
return Ok(None);
}

#[cfg(feature = "acme")]
let acme_option = config.experimental.as_ref().and_then(|v| v.acme.clone());
#[cfg(feature = "acme")]
let registry_path = acme_option
.as_ref()
.and_then(|v| v.registry_path.as_deref())
.unwrap_or(ACME_REGISTRY_PATH);

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"))?;

#[cfg(not(feature = "acme"))]
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());

#[cfg(feature = "acme")]
let tls = {
let mut tls = tls.clone();
if let Some(true) = tls.acme {
ensure!(acme_option.is_some() && tls.tls_cert_key_path.is_none() && tls.tls_cert_path.is_none());
tls.tls_cert_key_path = Some(format!("{registry_path}/{server_name}/{ACME_CERTIFICATE_FILE_NAME}"));
tls.tls_cert_path = Some(format!("{registry_path}/{server_name}/{ACME_PRIVATE_KEY_FILE_NAME}"));
}
tls
};

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())
Expand All @@ -119,3 +145,33 @@ pub async fn build_cert_manager(
let res = build_cert_reloader(&crypto_source_map, None).await?;
Ok(Some(res))
}

/* ----------------------- */
#[cfg(feature = "acme")]
/// Build acme manager and dummy cert and key as initial states if not exists
/// TODO: CURRENTLY NOT IMPLEMENTED, UNDER DESIGNING
pub async fn build_acme_manager(config: &ConfigToml) -> Result<(), anyhow::Error> {
let acme_option = config.experimental.as_ref().and_then(|v| v.acme.clone());
if acme_option.is_none() {
return Ok(());
}
let acme_option = acme_option.unwrap();
let mut acme_targets = AcmeTargets::try_new(
acme_option.email.as_ref(),
acme_option.dir_url.as_deref(),
acme_option.registry_path.as_deref(),
)
.map_err(|e| anyhow!("Invalid acme configuration: {e}"))?;

let apps = config.apps.as_ref().unwrap();
for app in apps.0.values() {
if let Some(tls) = app.tls.as_ref() {
if tls.acme.unwrap_or(false) {
acme_targets.add_target(app.server_name.as_ref().unwrap())?;
}
}
}
// TODO: remove later
println!("ACME targets: {:#?}", acme_targets);
Ok(())
}
28 changes: 28 additions & 0 deletions rpxy-bin/src/config/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,25 @@ pub struct CacheOption {
pub max_cache_each_size_on_memory: Option<usize>,
}

#[cfg(feature = "acme")]
#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
pub struct AcmeOption {
pub dir_url: Option<String>,
pub email: String,
pub registry_path: Option<String>,
}

#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
pub struct Experimental {
#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))]
pub h3: Option<Http3Option>,

#[cfg(feature = "cache")]
pub cache: Option<CacheOption>,

#[cfg(feature = "acme")]
pub acme: Option<AcmeOption>,

pub ignore_sni_consistency: Option<bool>,
pub connection_handling_timeout: Option<u64>,
}
Expand All @@ -67,6 +80,8 @@ pub struct TlsOption {
pub tls_cert_key_path: Option<String>,
pub https_redirection: Option<bool>,
pub client_ca_cert_path: Option<String>,
#[cfg(feature = "acme")]
pub acme: Option<bool>,
}

#[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -222,8 +237,19 @@ impl Application {
// tls settings
let tls_config = if self.tls.is_some() {
let tls = self.tls.as_ref().unwrap();

#[cfg(not(feature = "acme"))]
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());

#[cfg(feature = "acme")]
{
if tls.acme.unwrap_or(false) {
ensure!(tls.tls_cert_key_path.is_none() && tls.tls_cert_path.is_none());
} else {
ensure!(tls.tls_cert_key_path.is_some() && tls.tls_cert_path.is_some());
}
}

let https_redirection = if tls.https_redirection.is_none() {
true // Default true
} else {
Expand All @@ -233,6 +259,8 @@ impl Application {
Some(TlsConfig {
mutual_tls: tls.client_ca_cert_path.is_some(),
https_redirection,
#[cfg(feature = "acme")]
acme: tls.acme.unwrap_or(false),
})
} else {
None
Expand Down
8 changes: 8 additions & 0 deletions rpxy-bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod constants;
mod error;
mod log;

#[cfg(feature = "acme")]
use crate::config::build_acme_manager;
use crate::{
config::{build_cert_manager, build_settings, parse_opts, ConfigToml, ConfigTomlReloader},
constants::CONFIG_WATCH_DELAY_SECS,
Expand Down Expand Up @@ -66,6 +68,9 @@ async fn rpxy_service_without_watcher(
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}"))?;

#[cfg(feature = "acme")] // TODO: CURRENTLY NOT IMPLEMENTED, UNDER DESIGNING
let acme_manager = build_acme_manager(&config_toml).await;

let cert_service_and_rx = build_cert_manager(&config_toml)
.await
.map_err(|e| anyhow!("Invalid cert configuration: {e}"))?;
Expand All @@ -88,6 +93,9 @@ async fn rpxy_service_with_watcher(
.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}"))?;

#[cfg(feature = "acme")] // TODO: CURRENTLY NOT IMPLEMENTED, UNDER DESIGNING
let acme_manager = build_acme_manager(&config_toml).await;

let mut cert_service_and_rx = build_cert_manager(&config_toml)
.await
.map_err(|e| anyhow!("Invalid cert configuration: {e}"))?;
Expand Down
11 changes: 7 additions & 4 deletions rpxy-certs/src/reloader_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ impl Reload<ServerCryptoBase> for CryptoReloader {
let mut server_crypto_base = ServerCryptoBase::default();

for (server_name_bytes, crypto_source) in self.inner.iter() {
let certs_keys = crypto_source.read().await.map_err(|e| {
error!("Failed to reload cert, key or ca cert: {e}");
ReloaderError::<ServerCryptoBase>::Reload("Failed to reload cert, key or ca cert")
})?;
let certs_keys = match crypto_source.read().await {
Ok(certs_keys) => certs_keys,
Err(e) => {
error!("Failed to read certs and keys, skip at this time: {}", e);
continue;
}
};
server_crypto_base.inner.insert(server_name_bytes.clone(), certs_keys);
}

Expand Down
5 changes: 3 additions & 2 deletions rpxy-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ publish.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
# default = ["http3-s2n", "sticky-cookie", "cache", "rustls-backend"]
default = ["http3-quinn", "sticky-cookie", "cache", "rustls-backend"]
# default = ["http3-s2n", "sticky-cookie", "cache", "rustls-backend", "acme"]
# default = ["http3-quinn", "sticky-cookie", "cache", "rustls-backend", "acme"]
http3-quinn = ["socket2", "quinn", "h3", "h3-quinn", "rpxy-certs/http3"]
http3-s2n = [
"s2n-quic",
Expand All @@ -29,6 +29,7 @@ sticky-cookie = ["base64", "sha2", "chrono"]
native-tls-backend = ["hyper-tls"]
rustls-backend = ["hyper-rustls"]
webpki-roots = ["rustls-backend", "hyper-rustls/webpki-tokio"]
acme = []

[dependencies]
rand = "0.8.5"
Expand Down
2 changes: 2 additions & 0 deletions rpxy-lib/src/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,6 @@ pub struct UpstreamUri {
pub struct TlsConfig {
pub mutual_tls: bool,
pub https_redirection: bool,
#[cfg(feature = "acme")]
pub acme: bool,
}

0 comments on commit 887e6b6

Please sign in to comment.