diff --git a/Cargo.toml b/Cargo.toml index 862424132..139410434 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,4 +55,5 @@ colored = "2.1.0" idgenerator = "2.0.0" num_cpus = "1.16.0" config = "0.14.0" -shadow-rs = "0.28.0" \ No newline at end of file +shadow-rs = "0.28.0" +reqwest = "0.12.4" \ No newline at end of file diff --git a/common/src/config.rs b/common/src/config.rs index c24e45ef8..0950609ef 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -1,8 +1,7 @@ -use std::path::PathBuf; use c::{ConfigError, FileFormat}; use config as c; use serde::{Deserialize, Serialize}; - +use std::path::PathBuf; #[derive(Serialize, Deserialize, Debug, Default, Clone)] pub struct Config { @@ -12,6 +11,7 @@ pub struct Config { pub storage: StorageConfig, pub monorepo: MonoConfig, pub pack: PackConfig, + pub relay: RelayConfig, } impl Config { @@ -26,11 +26,8 @@ impl Config { // config.get::(env!("CARGO_PKG_NAME")) config.try_deserialize::() } - } - - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct LogConfig { pub log_path: PathBuf, @@ -120,7 +117,6 @@ impl Default for MonoConfig { } } - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PackConfig { pub pack_decode_mem_size: usize, @@ -138,4 +134,19 @@ impl Default for PackConfig { channel_message_size: 1_000_000, } } -} \ No newline at end of file +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RelayConfig { + pub ca: String, + pub hub: String, +} + +impl Default for RelayConfig { + fn default() -> Self { + Self { + ca: String::from("127.0.0.1:9999"), + hub: String::from("127.0.0.1:8888"), + } + } +} diff --git a/gateway/src/relay_server.rs b/gateway/src/relay_server.rs index 715c92c2e..6f49043d0 100644 --- a/gateway/src/relay_server.rs +++ b/gateway/src/relay_server.rs @@ -8,7 +8,8 @@ use axum::routing::get; use axum::Router; use clap::Args; use common::config::Config; -use common::model::{CommonOptions, GetParams}; +use common::model::CommonOptions; +use gemini::RelayGetParams; use jupiter::context::Context; use regex::Regex; use tower::ServiceBuilder; @@ -83,12 +84,15 @@ pub async fn app(config: Config, host: String, port: u16) -> Router { } async fn get_method_router( - _state: State, - Query(params): Query, + state: State, + Query(params): Query, uri: Uri, ) -> Result, (StatusCode, String)> { + let relay_config = state.context.config.relay.clone(); if Regex::new(r"/hello$").unwrap().is_match(uri.path()) { return gemini::http::handler::hello_gemini(params).await; + } else if Regex::new(r"/certificate$").unwrap().is_match(uri.path()) { + return gemini::ztm::handler::get_ztm_certificate(relay_config, params).await; } Err(( StatusCode::NOT_FOUND, diff --git a/gemini/Cargo.toml b/gemini/Cargo.toml index 3a952139c..08f916d36 100644 --- a/gemini/Cargo.toml +++ b/gemini/Cargo.toml @@ -12,4 +12,8 @@ path = "src/lib.rs" [dependencies] common = { workspace = true } -axum = { workspace = true } \ No newline at end of file +axum = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +reqwest = { workspace = true } +tracing = { workspace = true } \ No newline at end of file diff --git a/gemini/src/http/handler.rs b/gemini/src/http/handler.rs index e4750284c..7f15c0c05 100644 --- a/gemini/src/http/handler.rs +++ b/gemini/src/http/handler.rs @@ -2,9 +2,10 @@ use axum::{ body::Body, http::{Response, StatusCode}, }; -use common::model::GetParams; -pub async fn hello_gemini(_params: GetParams) -> Result, (StatusCode, String)> { +use crate::RelayGetParams; + +pub async fn hello_gemini(_params: RelayGetParams) -> Result, (StatusCode, String)> { Ok(Response::builder() .body(Body::from("hello gemini")) .unwrap()) diff --git a/gemini/src/lib.rs b/gemini/src/lib.rs index 3883215fc..198635f61 100644 --- a/gemini/src/lib.rs +++ b/gemini/src/lib.rs @@ -1 +1,9 @@ +use serde::Deserialize; + pub mod http; +pub mod ztm; + +#[derive(Deserialize, Debug)] +pub struct RelayGetParams { + pub name: Option, +} diff --git a/gemini/src/ztm/handler.rs b/gemini/src/ztm/handler.rs new file mode 100644 index 000000000..c9dc23f11 --- /dev/null +++ b/gemini/src/ztm/handler.rs @@ -0,0 +1,91 @@ +use axum::{ + body::Body, + http::{Response, StatusCode}, +}; +use common::config::RelayConfig; +use reqwest::Client; + +use crate::RelayGetParams; + +use super::{Agent, ZTMUserPermit}; + +pub async fn get_ztm_certificate( + config: RelayConfig, + params: RelayGetParams, +) -> Result, (StatusCode, String)> { + if params.name.is_none() { + return Err((StatusCode::BAD_REQUEST, "not enough paras".to_string())); + } + let name = params.name.unwrap(); + let ca_address = config.ca; + let hub_address = config.hub; + + //1. GET {ca}/api/certificates/ca -> ca certificate + let url = format!("http://{ca_address}/api/certificates/ca"); + let request_result = reqwest::get(url).await; + let ca_certificate = match handle_ztm_response(request_result).await { + Ok(s) => s, + Err(s) => { + return Err((StatusCode::INTERNAL_SERVER_ERROR, s)); + } + }; + + //2. POST {ca}/api/certificates/{username} -> user private key + let url = format!("http://{ca_address}/api/certificates/{name}"); + let client = Client::new(); + let request_result = client.post(url).send().await; + let user_key = match handle_ztm_response(request_result).await { + Ok(s) => s, + Err(s) => { + return Err((StatusCode::INTERNAL_SERVER_ERROR, s)); + } + }; + + //3. GET {ca}/api/certificates/{username} -> user certificate + let url = format!("http://{ca_address}/api/certificates/{name}"); + let request_result = reqwest::get(url).await; + let user_certificate = match handle_ztm_response(request_result).await { + Ok(s) => s, + Err(s) => { + return Err((StatusCode::INTERNAL_SERVER_ERROR, s)); + } + }; + + // Combine those into a json permit + let agent = Agent { + certificate: user_certificate.clone(), + private_key: user_key.clone(), + }; + + let permit = ZTMUserPermit { + ca: ca_certificate.clone(), + agent, + bootstraps: vec![hub_address], + }; + + let permit_json = serde_json::to_string(&permit).unwrap(); + tracing::info!("new permit [{name}]: {permit_json}"); + + Ok(Response::builder() + .header("Content-Type", "application/json") + .body(Body::from(permit_json)) + .unwrap()) +} + +pub async fn handle_ztm_response( + request_result: Result, +) -> Result { + match request_result { + Ok(res) => { + if res.status().is_success() { + Ok(res.text().await.unwrap()) + } else { + Err(res.text().await.unwrap()) + } + } + Err(e) => Err(e.to_string()), + } +} + +#[cfg(test)] +mod tests {} diff --git a/gemini/src/ztm/mod.rs b/gemini/src/ztm/mod.rs new file mode 100644 index 000000000..cb290b900 --- /dev/null +++ b/gemini/src/ztm/mod.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +pub mod handler; + +#[derive(Deserialize, Serialize, Debug)] +pub struct ZTMUserPermit { + pub ca: String, + pub agent: Agent, + pub bootstraps: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct Agent { + pub certificate: String, + pub private_key: String, +} diff --git a/libra/Cargo.toml b/libra/Cargo.toml index 5b17ad9f4..c1ded7889 100644 --- a/libra/Cargo.toml +++ b/libra/Cargo.toml @@ -24,7 +24,7 @@ sha1 = { workspace = true } bytes = { workspace = true } chrono = { workspace = true } futures = { workspace = true } -reqwest = { version = "0.12.4", features = ["stream"] } +reqwest = { workspace = true, features = ["stream"] } tokio-util = { version = "0.7.11", features = ["io"] } color-backtrace = "0.6.1" colored = "2.1.0" diff --git a/mega/config.toml b/mega/config.toml index 47310ac1d..35bd74be8 100644 --- a/mega/config.toml +++ b/mega/config.toml @@ -66,4 +66,9 @@ pack_decode_cache_path = "/tmp/.mega/cache" clean_cache_after_decode = true # The maximum meesage size in channel buffer while decode -channel_message_size = 1_000_000 \ No newline at end of file +channel_message_size = 1_000_000 + + +[relay] +ca = "127.0.0.1:9999" +hub = "127.0.0.1:8888" \ No newline at end of file