CA certificate HTTP endpoint for gurtca

This commit is contained in:
Face
2025-08-22 19:03:48 +03:00
parent 88c2495fc5
commit 770bcadcd4
8 changed files with 610 additions and 4 deletions

98
dns/Cargo.lock generated
View File

@@ -1027,6 +1027,30 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "headers"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270"
dependencies = [
"base64 0.21.7",
"bytes",
"headers-core",
"http 0.2.12",
"httpdate",
"mime",
"sha1",
]
[[package]]
name = "headers-core"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
dependencies = [
"http 0.2.12",
]
[[package]]
name = "heck"
version = "0.4.1"
@@ -1462,6 +1486,24 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "multer"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http 0.2.12",
"httparse",
"log",
"memchr",
"mime",
"spin 0.9.8",
"version_check",
]
[[package]]
name = "nom"
version = "7.1.3"
@@ -1658,6 +1700,26 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
@@ -2098,6 +2160,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -3176,6 +3244,35 @@ dependencies = [
"try-lock",
]
[[package]]
name = "warp"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"headers",
"http 0.2.12",
"hyper",
"log",
"mime",
"mime_guess",
"multer",
"percent-encoding",
"pin-project",
"scoped-tls",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-tungstenite",
"tokio-util",
"tower-service",
"tracing",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -3330,6 +3427,7 @@ dependencies = [
"tokio",
"toml",
"uuid",
"warp",
]
[[package]]

View File

@@ -30,3 +30,4 @@ sha2 = "0.10"
base64 = "0.22"
uuid = { version = "1.0", features = ["v4"] }
openssl = "0.10"
warp = "0.3"

View File

@@ -9,8 +9,13 @@ use colored::Colorize;
use macros_rs::fmt::{crashln, string};
use std::{sync::Arc, collections::HashMap};
use gurt::prelude::*;
use warp::Filter;
use gurt::{GurtStatusCode, Route};
#[derive(Debug)]
struct CertificateError;
impl warp::reject::Reject for CertificateError {}
#[derive(Clone)]
pub(crate) struct AppState {
config: Config,
@@ -245,8 +250,74 @@ pub async fn start(cli: crate::Cli) -> std::io::Result<()> {
.route(Route::get("/ca/certificate/*"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::GetCertificate })
.route(Route::get("/ca/root"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::GetCaCertificate });
let http_port = 8876;
let ca_bootstrap_server = start_ca_bootstrap_server(app_state.clone(), http_port, config.server.address.clone());
log::info!("Starting CA bootstrap HTTP server on {}:{}", config.server.address, http_port);
log::info!("GURT server listening on {}", config.get_address());
server.listen(&config.get_address()).await.map_err(|e| {
std::io::Error::new(std::io::ErrorKind::Other, format!("GURT server error: {}", e))
})
let result = tokio::try_join!(
ca_bootstrap_server,
async {
server.listen(&config.get_address()).await.map_err(|e| {
std::io::Error::new(std::io::ErrorKind::Other, format!("GURT server error: {}", e))
})
}
);
match result {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
async fn start_ca_bootstrap_server(app_state: AppState, port: u16, address: String) -> std::result::Result<(), std::io::Error> {
let ca_root = warp::path("ca")
.and(warp::path("root"))
.and(warp::path::end())
.and_then({
let app_state = app_state.clone();
move || {
let app_state = app_state.clone();
async move {
match get_ca_certificate_content(&app_state).await {
Ok(cert_pem) => {
Ok(warp::reply::with_header(
cert_pem,
"content-type",
"application/x-pem-file"
))
}
Err(e) => {
log::error!("Failed to get CA certificate: {}", e);
Err(warp::reject::custom(CertificateError))
}
}
}
}
});
let routes = ca_root
.with(warp::cors().allow_any_origin().allow_methods(vec!["GET"]));
let addr: std::net::SocketAddr = format!("{}:{}", address, port).parse()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
warp::serve(routes).run(addr).await;
Ok(())
}
async fn get_ca_certificate_content(app_state: &AppState) -> std::result::Result<String, Box<dyn std::error::Error + Send + Sync>> {
let query = "SELECT ca_cert_pem FROM ca_certificates WHERE is_active = TRUE ORDER BY created_at DESC LIMIT 1";
let row: std::result::Result<(String,), _> = sqlx::query_as(query)
.fetch_one(&app_state.db)
.await;
match row {
Ok((ca_cert_pem,)) => Ok(ca_cert_pem),
Err(e) => {
log::error!("Failed to retrieve CA certificate from database: {}", e);
Err(format!("No active CA certificate found in database: {}", e).into())
}
}
}