From c77d6cf724dc9c85b8ff583e3d7b125a4ce41c40 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 8 Aug 2025 11:08:51 +0200 Subject: [PATCH 01/16] versioned core communication --- Cargo.lock | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 2 ++ src/http.rs | 18 ++++++++-- src/main.rs | 10 ++++-- 4 files changed, 117 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1dee73..324259c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -529,6 +529,7 @@ dependencies = [ "axum-extra", "base64", "clap", + "defguard_version", "dotenvy", "log", "mime_guess", @@ -537,13 +538,14 @@ dependencies = [ "rust-embed", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "tokio-stream", "toml", "tonic", "tonic-build", + "tonic-middleware", "tower-http", "tower_governor", "tracing", @@ -552,6 +554,21 @@ dependencies = [ "vergen-git2", ] +[[package]] +name = "defguard_version" +version = "0.0.0" +dependencies = [ + "http", + "os_info", + "semver", + "thiserror 2.0.12", + "tonic", + "tonic-middleware", + "tower 0.5.2", + "tracing", + "tracing-subscriber", +] + [[package]] name = "deranged" version = "0.4.0" @@ -694,7 +711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" dependencies = [ "nonempty", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1478,6 +1495,18 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "os_info" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +dependencies = [ + "log", + "plist", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "overload" version = "0.1.1" @@ -1561,6 +1590,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plist" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" +dependencies = [ + "base64", + "indexmap 2.10.0", + "quick-xml", + "serde", + "time", +] + [[package]] name = "polyval" version = "0.6.2" @@ -1689,6 +1731,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "quick-xml" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.40" @@ -1979,6 +2030,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + [[package]] name = "serde" version = "1.0.219" @@ -2181,7 +2238,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -2195,6 +2261,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -2392,6 +2469,18 @@ dependencies = [ "syn", ] +[[package]] +name = "tonic-middleware" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd11ca7918ee9f94e217285ace20caf6187476f399244ba8438cdc92ce665236" +dependencies = [ + "async-trait", + "futures-util", + "tonic", + "tower 0.4.13", +] + [[package]] name = "tower" version = "0.4.13" @@ -2476,7 +2565,7 @@ dependencies = [ "governor", "http", "pin-project", - "thiserror", + "thiserror 1.0.69", "tower 0.5.2", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 5f6a200..4c50dd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ homepage = "https://github.com/DefGuard/proxy" repository = "https://github.com/DefGuard/proxy" [dependencies] +defguard_version = { path = "../defguard/crates/defguard_version" } # base `axum` deps axum = { version = "0.7", features = ["macros", "tracing"] } axum-client-ip = "0.6" @@ -44,6 +45,7 @@ tower_governor = "0.4" rust-embed = { version = "8.5", features = ["include-exclude"] } mime_guess = "2.0" base64 = "0.22.1" +tonic-middleware = "0.2" [build-dependencies] tonic-build = { version = "0.12" } diff --git a/src/http.rs b/src/http.rs index f10210e..921e10d 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,7 +1,7 @@ use std::{ fs::read_to_string, net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::atomic::Ordering, + sync::{atomic::Ordering, Arc}, time::Duration, }; @@ -15,9 +15,11 @@ use axum::{ }; use axum_extra::extract::cookie::Key; use clap::crate_version; +use defguard_version::{server::DefguardVersionServerMiddleware, DefguardVersionSet}; use serde::Serialize; use tokio::{net::TcpListener, task::JoinSet}; use tonic::transport::{Identity, Server, ServerTlsConfig}; +use tonic_middleware::MiddlewareFor; use tower_governor::{ governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor, GovernorLayer, }; @@ -116,7 +118,10 @@ fn get_client_addr(request: &Request) -> String { ) } -pub async fn run_server(config: Config) -> anyhow::Result<()> { +pub async fn run_server( + config: Config, + version_set: Arc, +) -> anyhow::Result<()> { info!("Starting Defguard Proxy server"); debug!("Using config: {config:?}"); @@ -162,8 +167,15 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { } else { Server::builder() }; + let versioned_server = MiddlewareFor::new( + proxy_server::ProxyServer::new(grpc_server), + DefguardVersionServerMiddleware::new( + version_set.own.clone(), + Arc::clone(&version_set.proxy), + ), + ); builder - .add_service(proxy_server::ProxyServer::new(grpc_server)) + .add_service(versioned_server) .serve(addr) .await .context("Error running gRPC server") diff --git a/src/main.rs b/src/main.rs index 1e22f07..385b907 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use defguard_proxy::{config::get_config, http::run_server, logging::init_tracing, VERSION}; +use defguard_proxy::{config::get_config, http::run_server, VERSION}; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -9,11 +9,15 @@ async fn main() -> anyhow::Result<()> { // read config from env let config = get_config()?; - init_tracing(&config.log_level); + let version_set = defguard_version::tracing::init( + VERSION, + &config.log_level.to_string(), + &["send_grpc_message", "bidirectional_communication"], + ); tracing::info!("Starting ... version v{}", VERSION); // run API web server - run_server(config).await?; + run_server(config, version_set).await?; Ok(()) } From 85a38b5d945b4e986718bccef879d5439eca9e1a Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 11 Aug 2025 14:11:02 +0200 Subject: [PATCH 02/16] apply version tracing --- src/grpc.rs | 8 ++++++++ src/http.rs | 15 +++++---------- src/main.rs | 8 ++------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index 8cc6489..8554248 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -7,6 +7,7 @@ use std::{ }, }; +use defguard_version::parse_metadata; use tokio::sync::{mpsc, oneshot}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Request, Response, Status, Streaming}; @@ -98,6 +99,13 @@ impl proxy_server::Proxy for ProxyServer { error!("Failed to determine client address for request: {request:?}"); return Err(Status::internal("Failed to determine client address")); }; + let (version, info) = parse_metadata(request.metadata()).unwrap(); + let span = tracing::info_span!( + "core_bidi_stream", + core_version = %version, + core_info = %info, + ); + let _guard = span.enter(); info!("Defguard Core gRPC client connected from: {address}"); let (tx, rx) = mpsc::unbounded_channel(); diff --git a/src/http.rs b/src/http.rs index 921e10d..0e2e8b9 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,7 +1,7 @@ use std::{ fs::read_to_string, net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{atomic::Ordering, Arc}, + sync::atomic::Ordering, time::Duration, }; @@ -15,7 +15,7 @@ use axum::{ }; use axum_extra::extract::cookie::Key; use clap::crate_version; -use defguard_version::{server::DefguardVersionServerMiddleware, DefguardVersionSet}; +use defguard_version::server::DefguardVersionServerMiddleware; use serde::Serialize; use tokio::{net::TcpListener, task::JoinSet}; use tonic::transport::{Identity, Server, ServerTlsConfig}; @@ -35,6 +35,7 @@ use crate::{ grpc::ProxyServer, handlers::{desktop_client_mfa, enrollment, password_reset, polling}, proto::proxy_server, + VERSION, }; pub(crate) static ENROLLMENT_COOKIE_NAME: &str = "defguard_proxy"; @@ -118,10 +119,7 @@ fn get_client_addr(request: &Request) -> String { ) } -pub async fn run_server( - config: Config, - version_set: Arc, -) -> anyhow::Result<()> { +pub async fn run_server(config: Config) -> anyhow::Result<()> { info!("Starting Defguard Proxy server"); debug!("Using config: {config:?}"); @@ -169,10 +167,7 @@ pub async fn run_server( }; let versioned_server = MiddlewareFor::new( proxy_server::ProxyServer::new(grpc_server), - DefguardVersionServerMiddleware::new( - version_set.own.clone(), - Arc::clone(&version_set.proxy), - ), + DefguardVersionServerMiddleware::new(VERSION)?, ); builder .add_service(versioned_server) diff --git a/src/main.rs b/src/main.rs index 385b907..e316b4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,15 +9,11 @@ async fn main() -> anyhow::Result<()> { // read config from env let config = get_config()?; - let version_set = defguard_version::tracing::init( - VERSION, - &config.log_level.to_string(), - &["send_grpc_message", "bidirectional_communication"], - ); + defguard_version::tracing::init(VERSION, &config.log_level.to_string()); tracing::info!("Starting ... version v{}", VERSION); // run API web server - run_server(config, version_set).await?; + run_server(config).await?; Ok(()) } From 73167fa37e1e1cf6405279802b8969029db0786d Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 13 Aug 2025 10:17:13 +0200 Subject: [PATCH 03/16] integrate version and http logging --- src/logging.rs | 142 +++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 6 +-- 2 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/logging.rs b/src/logging.rs index eaef168..c2b5c6c 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,5 +1,9 @@ +use defguard_version::{ + tracing::{SpanFieldVisitor, VersionFieldLayer, VersionFilteredFields, VersionSuffixWriter}, + SystemInfo, +}; use log::LevelFilter; -use tracing::{Event, Subscriber}; +use tracing::{Event, Level, Subscriber}; use tracing_subscriber::{ fmt::{ self, @@ -13,39 +17,48 @@ use tracing_subscriber::{ EnvFilter, }; -// Initializes tracing with the specified log level. +// Initializes tracing with the specified log level and version information. // Allows fine-grained filtering with `EnvFilter` directives. // The directives are read from `DEFGUARD_PROXY_LOG_FILTER` env variable. // For more info read: -pub fn init_tracing(level: &LevelFilter) { +pub fn init_tracing(own_version: &str, level: &LevelFilter) { tracing_subscriber::registry() .with( EnvFilter::try_from_env("DEFGUARD_PROXY_LOG_FILTER") .unwrap_or_else(|_| level.to_string().into()), ) - .with(fmt::layer().event_format(HttpFormatter::default())) + .with(VersionFieldLayer) // Add custom layer to capture span fields + .with( + fmt::layer() + .event_format(HttpVersionFormatter::new(own_version, SystemInfo::get())) + .fmt_fields(VersionFilteredFields), + ) .init(); info!("Tracing initialized"); } -/// Implements fail2ban-friendly log format using `tracing_subscriber::fmt::format::FormatEvent` trait. +/// Implements fail2ban-friendly log format with version suffixes. /// HTTP info (if available) is extracted from the specified tracing span. The format is as follows: -/// TIMESTAMP LEVEL CLIENT_ADDR METHOD URI LOG_MESSAGE || TRACING_DATA -pub(crate) struct HttpFormatter<'a> { +/// TIMESTAMP LEVEL CLIENT_ADDR METHOD URI LOG_MESSAGE [VERSION_SUFFIXES] || TRACING_DATA +pub(crate) struct HttpVersionFormatter<'a> { span: &'a str, timer: SystemTime, + own_version: String, + own_info: SystemInfo, } -impl Default for HttpFormatter<'_> { - fn default() -> Self { +impl<'a> HttpVersionFormatter<'a> { + pub fn new(own_version: &str, own_info: SystemInfo) -> Self { Self { span: "http_request", timer: SystemTime, + own_version: own_version.to_string(), + own_info, } } } -impl HttpFormatter<'_> { +impl HttpVersionFormatter<'_> { fn format_timestamp(&self, writer: &mut Writer<'_>) -> std::fmt::Result { if self.timer.format_time(writer).is_err() { writer.write_str("")?; @@ -54,7 +67,7 @@ impl HttpFormatter<'_> { } } -impl FormatEvent for HttpFormatter<'_> +impl FormatEvent for HttpVersionFormatter<'_> where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, @@ -62,15 +75,18 @@ where fn format_event( &self, ctx: &FmtContext<'_, S, N>, - mut writer: format::Writer<'_>, + writer: format::Writer<'_>, event: &Event<'_>, ) -> std::fmt::Result { let meta = event.metadata(); - // timestamp, level & target - self.format_timestamp(&mut writer)?; - write!(writer, "{} ", meta.level())?; - write!(writer, "{}: ", meta.target(),)?; + // Extract version information from current span context (similar to VersionSuffixFormat) + let mut core_version = None; + let mut core_info = None; + let mut proxy_version = None; + let mut proxy_info = None; + let mut gateway_version = None; + let mut gateway_info = None; // iterate and accumulate spans storing our special span in separate variable if encountered let mut context_logs = String::new(); @@ -82,7 +98,30 @@ where context_logs.push_str(&format!(" {span_name}")); seen = true; - if let Some(fields) = span.extensions().get::>() { + // Extract version information from span extensions + let extensions = span.extensions(); + if let Some(stored_visitor) = extensions.get::() { + if core_version.is_none() && stored_visitor.core_version.is_some() { + core_version = stored_visitor.core_version.clone(); + } + if core_info.is_none() && stored_visitor.core_info.is_some() { + core_info = stored_visitor.core_info.clone(); + } + if proxy_version.is_none() && stored_visitor.proxy_version.is_some() { + proxy_version = stored_visitor.proxy_version.clone(); + } + if proxy_info.is_none() && stored_visitor.proxy_info.is_some() { + proxy_info = stored_visitor.proxy_info.clone(); + } + if gateway_version.is_none() && stored_visitor.gateway_version.is_some() { + gateway_version = stored_visitor.gateway_version.clone(); + } + if gateway_info.is_none() && stored_visitor.gateway_info.is_some() { + gateway_info = stored_visitor.gateway_info.clone(); + } + } + + if let Some(fields) = extensions.get::>() { if !fields.is_empty() { match span_name { x if x == self.span => http_log = Some(format!("{fields}")), @@ -97,6 +136,67 @@ where } }; + // Build version suffix + let mut version_suffix = String::new(); + let is_versioned_span = + core_version.is_some() || proxy_version.is_some() || gateway_version.is_some(); + let is_error = *event.metadata().level() == Level::ERROR; + + if is_versioned_span || is_error { + // Own version + let mut own_version_str = format!(" [{}", self.own_version); + if is_error { + own_version_str = format!("{own_version_str} {}", self.own_info); + } + own_version_str = format!("{own_version_str}]"); + version_suffix.push_str(&own_version_str); + } + + // Core version + if let Some(ref core_version) = core_version { + let mut core_version_str = format!("[C:{core_version}"); + if is_error { + if let Some(ref core_info) = core_info { + core_version_str = format!("{core_version_str} {core_info}"); + } + } + core_version_str = format!("{core_version_str}]"); + version_suffix.push_str(&core_version_str); + } + + // Proxy version + if let Some(ref proxy_version) = proxy_version { + let mut proxy_version_str = format!("[PX:{proxy_version}"); + if is_error { + if let Some(ref proxy_info) = proxy_info { + proxy_version_str = format!("{proxy_version_str} {proxy_info}"); + } + } + proxy_version_str = format!("{proxy_version_str}]"); + version_suffix.push_str(&proxy_version_str); + } + + // Gateway version + if let Some(ref gateway_version) = gateway_version { + let mut gateway_version_str = format!("[GW:{gateway_version}"); + if is_error { + if let Some(ref gateway_info) = gateway_info { + gateway_version_str = format!("{gateway_version_str} {gateway_info}"); + } + } + gateway_version_str = format!("{gateway_version_str}]"); + version_suffix.push_str(&gateway_version_str); + } + + // Create a wrapper writer that will append version info before newlines + let mut wrapper = VersionSuffixWriter::new(writer, version_suffix); + let mut versioned_writer = Writer::new(&mut wrapper); + + // timestamp, level & target + self.format_timestamp(&mut versioned_writer)?; + write!(versioned_writer, "{} ", meta.level())?; + write!(versioned_writer, "{}: ", meta.target(),)?; + // write http context log (ip, method, path) if let Some(log) = http_log { let split: Vec<&str> = log.split(['=', ' ']).collect(); @@ -107,16 +207,16 @@ where let ip = addr .and_then(|s| s.split(':').next().map(ToString::to_string)) .unwrap_or("unknown".to_string()); - write!(writer, "{ip} {method} {path} ")?; + write!(versioned_writer, "{ip} {method} {path} ")?; } // write actual log message - ctx.format_fields(writer.by_ref(), event)?; + ctx.format_fields(versioned_writer.by_ref(), event)?; // write span context if !context_logs.is_empty() { - write!(writer, " || Tracing data: {context_logs}")?; + write!(versioned_writer, " || Tracing data: {context_logs}")?; } - writeln!(writer) + writeln!(versioned_writer) } } diff --git a/src/main.rs b/src/main.rs index e316b4d..abf13bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use defguard_proxy::{config::get_config, http::run_server, VERSION}; +use defguard_proxy::{config::get_config, http::run_server, logging::init_tracing, VERSION}; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -7,9 +7,9 @@ async fn main() -> anyhow::Result<()> { dotenvy::dotenv().ok(); } + let config = get_config()?; + init_tracing(VERSION, &config.log_level); // read config from env - let config = get_config()?; - defguard_version::tracing::init(VERSION, &config.log_level.to_string()); tracing::info!("Starting ... version v{}", VERSION); // run API web server From 38b42b1519febd8944ff14000165be92faba72f6 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 13 Aug 2025 10:39:17 +0200 Subject: [PATCH 04/16] use defguard_version tracing methods to avoid code duplication --- src/logging.rs | 94 ++++++-------------------------------------------- 1 file changed, 11 insertions(+), 83 deletions(-) diff --git a/src/logging.rs b/src/logging.rs index c2b5c6c..7f6a7d3 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,5 +1,8 @@ use defguard_version::{ - tracing::{SpanFieldVisitor, VersionFieldLayer, VersionFilteredFields, VersionSuffixWriter}, + tracing::{ + ExtractedVersionInfo, VersionFieldLayer, VersionFilteredFields, VersionSuffixWriter, + build_version_suffix, extract_version_info_from_context, + }, SystemInfo, }; use log::LevelFilter; @@ -78,15 +81,12 @@ where writer: format::Writer<'_>, event: &Event<'_>, ) -> std::fmt::Result { - let meta = event.metadata(); - - // Extract version information from current span context (similar to VersionSuffixFormat) - let mut core_version = None; - let mut core_info = None; - let mut proxy_version = None; - let mut proxy_info = None; - let mut gateway_version = None; - let mut gateway_info = None; + // Extract version information using the utility function from defguard_version + let extracted = extract_version_info_from_context(ctx); + + // Build version suffix using the utility function from defguard_version + let is_error = *event.metadata().level() == Level::ERROR; + let version_suffix = build_version_suffix(&extracted, &self.own_version, &self.own_info, is_error); // iterate and accumulate spans storing our special span in separate variable if encountered let mut context_logs = String::new(); @@ -98,29 +98,7 @@ where context_logs.push_str(&format!(" {span_name}")); seen = true; - // Extract version information from span extensions let extensions = span.extensions(); - if let Some(stored_visitor) = extensions.get::() { - if core_version.is_none() && stored_visitor.core_version.is_some() { - core_version = stored_visitor.core_version.clone(); - } - if core_info.is_none() && stored_visitor.core_info.is_some() { - core_info = stored_visitor.core_info.clone(); - } - if proxy_version.is_none() && stored_visitor.proxy_version.is_some() { - proxy_version = stored_visitor.proxy_version.clone(); - } - if proxy_info.is_none() && stored_visitor.proxy_info.is_some() { - proxy_info = stored_visitor.proxy_info.clone(); - } - if gateway_version.is_none() && stored_visitor.gateway_version.is_some() { - gateway_version = stored_visitor.gateway_version.clone(); - } - if gateway_info.is_none() && stored_visitor.gateway_info.is_some() { - gateway_info = stored_visitor.gateway_info.clone(); - } - } - if let Some(fields) = extensions.get::>() { if !fields.is_empty() { match span_name { @@ -136,57 +114,6 @@ where } }; - // Build version suffix - let mut version_suffix = String::new(); - let is_versioned_span = - core_version.is_some() || proxy_version.is_some() || gateway_version.is_some(); - let is_error = *event.metadata().level() == Level::ERROR; - - if is_versioned_span || is_error { - // Own version - let mut own_version_str = format!(" [{}", self.own_version); - if is_error { - own_version_str = format!("{own_version_str} {}", self.own_info); - } - own_version_str = format!("{own_version_str}]"); - version_suffix.push_str(&own_version_str); - } - - // Core version - if let Some(ref core_version) = core_version { - let mut core_version_str = format!("[C:{core_version}"); - if is_error { - if let Some(ref core_info) = core_info { - core_version_str = format!("{core_version_str} {core_info}"); - } - } - core_version_str = format!("{core_version_str}]"); - version_suffix.push_str(&core_version_str); - } - - // Proxy version - if let Some(ref proxy_version) = proxy_version { - let mut proxy_version_str = format!("[PX:{proxy_version}"); - if is_error { - if let Some(ref proxy_info) = proxy_info { - proxy_version_str = format!("{proxy_version_str} {proxy_info}"); - } - } - proxy_version_str = format!("{proxy_version_str}]"); - version_suffix.push_str(&proxy_version_str); - } - - // Gateway version - if let Some(ref gateway_version) = gateway_version { - let mut gateway_version_str = format!("[GW:{gateway_version}"); - if is_error { - if let Some(ref gateway_info) = gateway_info { - gateway_version_str = format!("{gateway_version_str} {gateway_info}"); - } - } - gateway_version_str = format!("{gateway_version_str}]"); - version_suffix.push_str(&gateway_version_str); - } // Create a wrapper writer that will append version info before newlines let mut wrapper = VersionSuffixWriter::new(writer, version_suffix); @@ -194,6 +121,7 @@ where // timestamp, level & target self.format_timestamp(&mut versioned_writer)?; + let meta = event.metadata(); write!(versioned_writer, "{} ", meta.level())?; write!(versioned_writer, "{}: ", meta.target(),)?; From d65ee00bc892d1bebd7c71a6262548c748d05424 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 14 Aug 2025 08:22:11 +0200 Subject: [PATCH 05/16] versin_info_from_metadata --- src/grpc.rs | 8 ++++---- src/logging.rs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index 8554248..7bf34c5 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -7,7 +7,7 @@ use std::{ }, }; -use defguard_version::parse_metadata; +use defguard_version::version_info_from_metadata; use tokio::sync::{mpsc, oneshot}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Request, Response, Status, Streaming}; @@ -99,11 +99,11 @@ impl proxy_server::Proxy for ProxyServer { error!("Failed to determine client address for request: {request:?}"); return Err(Status::internal("Failed to determine client address")); }; - let (version, info) = parse_metadata(request.metadata()).unwrap(); + let (version, info) = version_info_from_metadata(request.metadata()); let span = tracing::info_span!( "core_bidi_stream", - core_version = %version, - core_info = %info, + core_version = version, + core_info = info, ); let _guard = span.enter(); info!("Defguard Core gRPC client connected from: {address}"); diff --git a/src/logging.rs b/src/logging.rs index 7f6a7d3..1359dbe 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,7 +1,7 @@ use defguard_version::{ tracing::{ - ExtractedVersionInfo, VersionFieldLayer, VersionFilteredFields, VersionSuffixWriter, - build_version_suffix, extract_version_info_from_context, + build_version_suffix, extract_version_info_from_context, VersionFieldLayer, + VersionFilteredFields, VersionSuffixWriter, }, SystemInfo, }; @@ -83,10 +83,11 @@ where ) -> std::fmt::Result { // Extract version information using the utility function from defguard_version let extracted = extract_version_info_from_context(ctx); - + // Build version suffix using the utility function from defguard_version let is_error = *event.metadata().level() == Level::ERROR; - let version_suffix = build_version_suffix(&extracted, &self.own_version, &self.own_info, is_error); + let version_suffix = + build_version_suffix(&extracted, &self.own_version, &self.own_info, is_error); // iterate and accumulate spans storing our special span in separate variable if encountered let mut context_logs = String::new(); @@ -114,7 +115,6 @@ where } }; - // Create a wrapper writer that will append version info before newlines let mut wrapper = VersionSuffixWriter::new(writer, version_suffix); let mut versioned_writer = Writer::new(&mut wrapper); From ded0da00e86b5254147a4f4d8395126a5c1313c1 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 14 Aug 2025 08:50:11 +0200 Subject: [PATCH 06/16] fmt --- src/grpc.rs | 7 ++----- src/main.rs | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index 7bf34c5..92f90d1 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -100,11 +100,8 @@ impl proxy_server::Proxy for ProxyServer { return Err(Status::internal("Failed to determine client address")); }; let (version, info) = version_info_from_metadata(request.metadata()); - let span = tracing::info_span!( - "core_bidi_stream", - core_version = version, - core_info = info, - ); + let span = + tracing::info_span!("core_bidi_stream", core_version = version, core_info = info,); let _guard = span.enter(); info!("Defguard Core gRPC client connected from: {address}"); diff --git a/src/main.rs b/src/main.rs index abf13bf..c493dc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,8 @@ async fn main() -> anyhow::Result<()> { dotenvy::dotenv().ok(); } - let config = get_config()?; - init_tracing(VERSION, &config.log_level); + let config = get_config()?; + init_tracing(VERSION, &config.log_level); // read config from env tracing::info!("Starting ... version v{}", VERSION); From 20b0fa60ba7fb85f6ce0161c097e53f761a5c284 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 18 Aug 2025 12:53:26 +0200 Subject: [PATCH 07/16] don't use tonic-middleware --- Cargo.lock | 162 +++++++--------------------------------------------- Cargo.toml | 2 +- src/http.rs | 13 ++--- 3 files changed, 29 insertions(+), 148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9fc979..bbca8f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,28 +117,6 @@ version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-trait" version = "0.1.89" @@ -191,7 +169,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -218,7 +196,7 @@ dependencies = [ "rustversion", "serde", "sync_wrapper", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", ] @@ -294,7 +272,7 @@ dependencies = [ "multer", "pin-project-lite", "serde", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", ] @@ -578,7 +556,7 @@ dependencies = [ "dotenvy", "log", "mime_guess", - "prost 0.14.1", + "prost", "rust-embed", "serde", "serde_json", @@ -587,10 +565,10 @@ dependencies = [ "tokio", "tokio-stream", "toml", - "tonic 0.14.1", - "tonic-middleware", + "tonic", "tonic-prost", "tonic-prost-build", + "tower", "tower-http", "tower_governor", "tracing", @@ -607,8 +585,8 @@ dependencies = [ "os_info", "semver", "thiserror 2.0.15", - "tonic 0.14.1", - "tower 0.5.2", + "tonic", + "tower", "tracing", "tracing-subscriber", ] @@ -960,19 +938,13 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.10.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.5" @@ -1116,7 +1088,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2 0.6.0", + "socket2", "tokio", "tower-service", "tracing", @@ -1235,16 +1207,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.10.0" @@ -1599,7 +1561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.10.0", + "indexmap", ] [[package]] @@ -1647,7 +1609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64", - "indexmap 2.10.0", + "indexmap", "quick-xml", "serde", "time", @@ -1714,15 +1676,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", -] - [[package]] name = "prost" version = "0.14.1" @@ -1746,7 +1699,7 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost 0.14.1", + "prost", "prost-types", "pulldown-cmark", "pulldown-cmark-to-cmark", @@ -1774,7 +1727,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ - "prost 0.14.1", + "prost", ] [[package]] @@ -2004,6 +1957,7 @@ checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2219,16 +2173,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.0" @@ -2418,7 +2362,7 @@ dependencies = [ "mio", "pin-project-lite", "slab", - "socket2 0.6.0", + "socket2", "tokio-macros", "windows-sys 0.59.0", ] @@ -2499,36 +2443,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.7.9", - "base64", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost 0.13.5", - "socket2 0.5.10", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tonic" version = "0.14.1" @@ -2550,12 +2464,12 @@ dependencies = [ "percent-encoding", "pin-project", "rustls-native-certs", - "socket2 0.6.0", + "socket2", "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -2573,18 +2487,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tonic-middleware" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd11ca7918ee9f94e217285ace20caf6187476f399244ba8438cdc92ce665236" -dependencies = [ - "async-trait", - "futures-util", - "tonic 0.12.3", - "tower 0.4.13", -] - [[package]] name = "tonic-prost" version = "0.14.1" @@ -2592,8 +2494,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" dependencies = [ "bytes", - "prost 0.14.1", - "tonic 0.14.1", + "prost", + "tonic", ] [[package]] @@ -2612,26 +2514,6 @@ dependencies = [ "tonic-build", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tower" version = "0.5.2" @@ -2640,7 +2522,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap 2.10.0", + "indexmap", "pin-project-lite", "slab", "sync_wrapper", @@ -2700,7 +2582,7 @@ dependencies = [ "http", "pin-project", "thiserror 1.0.69", - "tower 0.5.2", + "tower", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index eaf4c2f..938e4a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ tower_governor = "0.4" rust-embed = { version = "8.5", features = ["include-exclude"] } mime_guess = "2.0" base64 = "0.22.1" -tonic-middleware = "0.2" +tower = "0.5.2" [build-dependencies] tonic-prost-build = "0.14" diff --git a/src/http.rs b/src/http.rs index 0e2e8b9..7e16875 100644 --- a/src/http.rs +++ b/src/http.rs @@ -15,11 +15,11 @@ use axum::{ }; use axum_extra::extract::cookie::Key; use clap::crate_version; -use defguard_version::server::DefguardVersionServerMiddleware; +use defguard_version::server::DefguardVersionLayer; use serde::Serialize; use tokio::{net::TcpListener, task::JoinSet}; use tonic::transport::{Identity, Server, ServerTlsConfig}; -use tonic_middleware::MiddlewareFor; +use tower::ServiceBuilder; use tower_governor::{ governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor, GovernorLayer, }; @@ -165,12 +165,11 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { } else { Server::builder() }; - let versioned_server = MiddlewareFor::new( - proxy_server::ProxyServer::new(grpc_server), - DefguardVersionServerMiddleware::new(VERSION)?, - ); + let versioned_service = ServiceBuilder::new() + .layer(DefguardVersionLayer::new(VERSION)?) + .service(proxy_server::ProxyServer::new(grpc_server)); builder - .add_service(versioned_server) + .add_service(versioned_service) .serve(addr) .await .context("Error running gRPC server") From 26a0849f6e3e866571848e4adb5bb75a16d80b74 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 19 Aug 2025 09:25:21 +0200 Subject: [PATCH 08/16] use the new HttpVersionFormatter --- src/logging.rs | 28 ++++++++++++++++------------ src/main.rs | 2 +- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/logging.rs b/src/logging.rs index 6c7ac44..f9f999d 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -3,7 +3,7 @@ use defguard_version::{ build_version_suffix, extract_version_info_from_context, VersionFieldLayer, VersionFilteredFields, VersionSuffixWriter, }, - SystemInfo, + ComponentInfo, DefguardVersionError, }; use log::LevelFilter; use tracing::{Event, Level, Subscriber}; @@ -24,7 +24,7 @@ use tracing_subscriber::{ // Allows fine-grained filtering with `EnvFilter` directives. // The directives are read from `DEFGUARD_PROXY_LOG_FILTER` env variable. // For more info read: -pub fn init_tracing(own_version: &str, level: &LevelFilter) { +pub fn init_tracing(own_version: &str, level: &LevelFilter) -> Result<(), DefguardVersionError> { tracing_subscriber::registry() .with( EnvFilter::try_from_env("DEFGUARD_PROXY_LOG_FILTER") @@ -33,11 +33,13 @@ pub fn init_tracing(own_version: &str, level: &LevelFilter) { .with(VersionFieldLayer) // Add custom layer to capture span fields .with( fmt::layer() - .event_format(HttpVersionFormatter::new(own_version, SystemInfo::get())) + .event_format(HttpVersionFormatter::new(own_version)?) .fmt_fields(VersionFilteredFields), ) .init(); + info!("Tracing initialized"); + Ok(()) } /// Implements fail2ban-friendly log format with version suffixes. @@ -46,18 +48,16 @@ pub fn init_tracing(own_version: &str, level: &LevelFilter) { pub(crate) struct HttpVersionFormatter<'a> { span: &'a str, timer: SystemTime, - own_version: String, - own_info: SystemInfo, + component_info: ComponentInfo, } impl<'a> HttpVersionFormatter<'a> { - pub fn new(own_version: &str, own_info: SystemInfo) -> Self { - Self { + pub fn new(own_version: &str) -> Result { + Ok(Self { span: "http_request", timer: SystemTime, - own_version: own_version.to_string(), - own_info, - } + component_info: ComponentInfo::new(own_version)?, + }) } } @@ -86,8 +86,12 @@ where // Build version suffix using the utility function from defguard_version let is_error = *event.metadata().level() == Level::ERROR; - let version_suffix = - build_version_suffix(&extracted, &self.own_version, &self.own_info, is_error); + let version_suffix = build_version_suffix( + &extracted, + &self.component_info.version, + &self.component_info.system, + is_error, + ); // iterate and accumulate spans storing our special span in separate variable if encountered let mut context_logs = String::new(); diff --git a/src/main.rs b/src/main.rs index c493dc5..097d591 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ async fn main() -> anyhow::Result<()> { } let config = get_config()?; - init_tracing(VERSION, &config.log_level); + init_tracing(VERSION, &config.log_level)?; // read config from env tracing::info!("Starting ... version v{}", VERSION); From 1936404409d0d6bd64bd2a36586415f3cd685dc7 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 19 Aug 2025 09:33:51 +0200 Subject: [PATCH 09/16] tweak comments --- src/logging.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/logging.rs b/src/logging.rs index f9f999d..5c32b4d 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -30,7 +30,7 @@ pub fn init_tracing(own_version: &str, level: &LevelFilter) -> Result<(), Defgua EnvFilter::try_from_env("DEFGUARD_PROXY_LOG_FILTER") .unwrap_or_else(|_| level.to_string().into()), ) - .with(VersionFieldLayer) // Add custom layer to capture span fields + .with(VersionFieldLayer) .with( fmt::layer() .event_format(HttpVersionFormatter::new(own_version)?) @@ -81,10 +81,10 @@ where writer: format::Writer<'_>, event: &Event<'_>, ) -> std::fmt::Result { - // Extract version information using the utility function from defguard_version + // Extract version information let extracted = extract_version_info_from_context(ctx); - // Build version suffix using the utility function from defguard_version + // Build version suffix let is_error = *event.metadata().level() == Level::ERROR; let version_suffix = build_version_suffix( &extracted, From 2f8cca8e2240d8b5cb78947878f889affc1453ec Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 20 Aug 2025 12:58:19 +0200 Subject: [PATCH 10/16] use the new tracing fields --- src/grpc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index c0da587..fbd42c3 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -100,8 +100,7 @@ impl proxy_server::Proxy for ProxyServer { return Err(Status::internal("Failed to determine client address")); }; let (version, info) = version_info_from_metadata(request.metadata()); - let span = - tracing::info_span!("core_bidi_stream", core_version = version, core_info = info,); + let span = tracing::info_span!("core_bidi_stream", component = "core", version, info); let _guard = span.enter(); info!("Defguard Core gRPC client connected from: {address}"); From 5a23440a80a28bd5aac6432a10858e4bf822417b Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 21 Aug 2025 09:24:33 +0200 Subject: [PATCH 11/16] add tracing instrumentation to bidi thread --- src/grpc.rs | 59 ++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index fbd42c3..fd0936c 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -1,3 +1,4 @@ +use defguard_version::version_info_from_metadata; use std::{ collections::HashMap, net::SocketAddr, @@ -6,11 +7,10 @@ use std::{ Arc, Mutex, }, }; - -use defguard_version::version_info_from_metadata; use tokio::sync::{mpsc, oneshot}; use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Request, Response, Status, Streaming}; +use tracing::Instrument; use crate::{ error::ApiError, @@ -90,7 +90,7 @@ impl proxy_server::Proxy for ProxyServer { type BidiStream = UnboundedReceiverStream>; /// Handle bidirectional communication with Defguard core. - #[instrument(name = "bidirectional_communication", level = "debug", skip(self))] + #[instrument(name = "bidirectional_communication", level = "info", skip(self))] async fn bidi( &self, request: Request>, @@ -112,37 +112,40 @@ impl proxy_server::Proxy for ProxyServer { let results = Arc::clone(&self.results); let connected = Arc::clone(&self.connected); let mut stream = request.into_inner(); - tokio::spawn(async move { - loop { - match stream.message().await { - Ok(Some(response)) => { - debug!("Received message from Defguard core: {response:?}"); - connected.store(true, Ordering::Relaxed); - // Discard empty payloads. - if let Some(payload) = response.payload { - if let Some(rx) = results.lock().unwrap().remove(&response.id) { - if let Err(err) = rx.send(payload) { - error!("Failed to send message to rx: {err:?}"); + tokio::spawn( + async move { + loop { + match stream.message().await { + Ok(Some(response)) => { + debug!("Received message from Defguard core: {response:?}"); + connected.store(true, Ordering::Relaxed); + // Discard empty payloads. + if let Some(payload) = response.payload { + if let Some(rx) = results.lock().unwrap().remove(&response.id) { + if let Err(err) = rx.send(payload) { + error!("Failed to send message to rx: {err:?}"); + } + } else { + error!("Missing receiver for response #{}", response.id); } - } else { - error!("Missing receiver for response #{}", response.id); } } - } - Ok(None) => { - info!("gRPC stream has been closed"); - break; - } - Err(err) => { - error!("gRPC client error: {err}"); - break; + Ok(None) => { + info!("gRPC stream has been closed"); + break; + } + Err(err) => { + error!("gRPC client error: {err}"); + break; + } } } + info!("Defguard core client disconnected: {address}"); + connected.store(false, Ordering::Relaxed); + clients.lock().unwrap().remove(&address); } - info!("Defguard core client disconnected: {address}"); - connected.store(false, Ordering::Relaxed); - clients.lock().unwrap().remove(&address); - }); + .instrument(tracing::Span::current()), + ); Ok(Response::new(UnboundedReceiverStream::new(rx))) } From a8472d2fc9be0f209b2fd850231c0604819cb0c9 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 21 Aug 2025 09:57:10 +0200 Subject: [PATCH 12/16] use DefguardComponent enum in span definitions --- src/grpc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index fd0936c..6d5e0e7 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -1,4 +1,4 @@ -use defguard_version::version_info_from_metadata; +use defguard_version::{version_info_from_metadata, DefguardComponent}; use std::{ collections::HashMap, net::SocketAddr, @@ -100,7 +100,7 @@ impl proxy_server::Proxy for ProxyServer { return Err(Status::internal("Failed to determine client address")); }; let (version, info) = version_info_from_metadata(request.metadata()); - let span = tracing::info_span!("core_bidi_stream", component = "core", version, info); + let span = tracing::info_span!("core_bidi_stream", component = %DefguardComponent::Core, version, info); let _guard = span.enter(); info!("Defguard Core gRPC client connected from: {address}"); From dedf4931a83a4369d3c8c0f590bd410aa33ece9d Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 21 Aug 2025 12:35:23 +0200 Subject: [PATCH 13/16] improve version typing --- src/http.rs | 4 ++-- src/logging.rs | 8 ++++---- src/main.rs | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/http.rs b/src/http.rs index 7e16875..130b022 100644 --- a/src/http.rs +++ b/src/http.rs @@ -15,7 +15,7 @@ use axum::{ }; use axum_extra::extract::cookie::Key; use clap::crate_version; -use defguard_version::server::DefguardVersionLayer; +use defguard_version::{server::DefguardVersionLayer, Version}; use serde::Serialize; use tokio::{net::TcpListener, task::JoinSet}; use tonic::transport::{Identity, Server, ServerTlsConfig}; @@ -166,7 +166,7 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { Server::builder() }; let versioned_service = ServiceBuilder::new() - .layer(DefguardVersionLayer::new(VERSION)?) + .layer(DefguardVersionLayer::new(Version::parse(VERSION)?)) .service(proxy_server::ProxyServer::new(grpc_server)); builder .add_service(versioned_service) diff --git a/src/logging.rs b/src/logging.rs index 5c32b4d..9b97f93 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -3,7 +3,7 @@ use defguard_version::{ build_version_suffix, extract_version_info_from_context, VersionFieldLayer, VersionFilteredFields, VersionSuffixWriter, }, - ComponentInfo, DefguardVersionError, + ComponentInfo, DefguardVersionError, Version, }; use log::LevelFilter; use tracing::{Event, Level, Subscriber}; @@ -24,7 +24,7 @@ use tracing_subscriber::{ // Allows fine-grained filtering with `EnvFilter` directives. // The directives are read from `DEFGUARD_PROXY_LOG_FILTER` env variable. // For more info read: -pub fn init_tracing(own_version: &str, level: &LevelFilter) -> Result<(), DefguardVersionError> { +pub fn init_tracing(own_version: Version, level: &LevelFilter) -> Result<(), DefguardVersionError> { tracing_subscriber::registry() .with( EnvFilter::try_from_env("DEFGUARD_PROXY_LOG_FILTER") @@ -52,11 +52,11 @@ pub(crate) struct HttpVersionFormatter<'a> { } impl<'a> HttpVersionFormatter<'a> { - pub fn new(own_version: &str) -> Result { + pub fn new(own_version: Version) -> Result { Ok(Self { span: "http_request", timer: SystemTime, - component_info: ComponentInfo::new(own_version)?, + component_info: ComponentInfo::new(own_version), }) } } diff --git a/src/main.rs b/src/main.rs index 097d591..0c3ef87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use defguard_proxy::{config::get_config, http::run_server, logging::init_tracing, VERSION}; +use defguard_version::Version; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -8,7 +9,7 @@ async fn main() -> anyhow::Result<()> { } let config = get_config()?; - init_tracing(VERSION, &config.log_level)?; + init_tracing(Version::parse(VERSION)?, &config.log_level)?; // read config from env tracing::info!("Starting ... version v{}", VERSION); From 36becfcaf884ca3df5af7dc1a115a975b5bef701 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 22 Aug 2025 09:30:06 +0200 Subject: [PATCH 14/16] switch to git dependency for defguard_version --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index bbca8f2..a51f94f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,6 +580,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" +source = "git+https://github.com/DefGuard/defguard.git?rev=f61ce40927a4d21095ea53a691219d5ae46e3e4e#f61ce40927a4d21095ea53a691219d5ae46e3e4e" dependencies = [ "http", "os_info", diff --git a/Cargo.toml b/Cargo.toml index 938e4a3..9cdf44e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/DefGuard/proxy" repository = "https://github.com/DefGuard/proxy" [dependencies] -defguard_version = { path = "../defguard/crates/defguard_version" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "f61ce40927a4d21095ea53a691219d5ae46e3e4e" } # base `axum` deps axum = { version = "0.7", features = ["macros", "tracing"] } axum-client-ip = "0.6" From 904b6b4d28bea3b41993e7da5f43d933c9341335 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 22 Aug 2025 09:38:43 +0200 Subject: [PATCH 15/16] add cargo-deny exception for defguard_version crate --- deny.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deny.toml b/deny.toml index 955fa45..4c66fb4 100644 --- a/deny.toml +++ b/deny.toml @@ -108,9 +108,9 @@ confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ - # Each entry is the crate and version constraint, and its specific allow - # list - #{ allow = ["Zlib"], crate = "adler32" }, + { allow = [ + "AGPL-3.0-only", + ], crate = "defguard_version" }, ] # Some crates don't have (easily) machine readable licensing information, From 0916f0b1aa994dd192952612336b7c53602461b8 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 22 Aug 2025 10:14:36 +0200 Subject: [PATCH 16/16] sort imports --- src/grpc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/grpc.rs b/src/grpc.rs index 6d5e0e7..40450e1 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -1,4 +1,3 @@ -use defguard_version::{version_info_from_metadata, DefguardComponent}; use std::{ collections::HashMap, net::SocketAddr, @@ -12,6 +11,8 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{Request, Response, Status, Streaming}; use tracing::Instrument; +use defguard_version::{version_info_from_metadata, DefguardComponent}; + use crate::{ error::ApiError, proto::{core_request, core_response, proxy_server, CoreRequest, CoreResponse, DeviceInfo},