diff --git a/Cargo.lock b/Cargo.lock index edc53f7..2c88978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -450,6 +450,7 @@ dependencies = [ "axum 0.8.4", "base64", "clap", + "defguard_version", "defguard_wireguard_rs", "env_logger", "gethostname", @@ -468,10 +469,26 @@ dependencies = [ "toml", "tonic", "tonic-build", + "tracing", "vergen-git2", "x25519-dalek", ] +[[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 = "defguard_wireguard_rs" version = "0.7.5" @@ -646,6 +663,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -665,9 +693,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1065,6 +1095,12 @@ dependencies = [ "libc", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.174" @@ -1113,6 +1149,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -1301,6 +1346,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1343,6 +1398,24 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "paste" version = "1.0.15" @@ -1403,6 +1476,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 = "portable-atomic" version = "1.11.1" @@ -1513,6 +1599,15 @@ dependencies = [ "prost", ] +[[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" @@ -1566,8 +1661,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1578,9 +1682,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1798,6 +1908,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1946,6 +2065,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "time" version = "0.3.41" @@ -2133,6 +2261,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" @@ -2211,6 +2351,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -2254,6 +2424,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2323,6 +2499,28 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index c39680f..aa9d347 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] +defguard_version = { path = "../defguard/crates/defguard_version" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } @@ -26,6 +27,7 @@ tonic = { version = "0.12", default-features = false, features = [ "prost", "tls-native-roots", ] } +tracing = "0.1.41" [target.'cfg(target_os = "linux")'.dependencies] nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "1a1147271f43b9d7182a114bb056a5224c35d38f" } diff --git a/src/config.rs b/src/config.rs index 8235fe4..87d42a5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,9 @@ use crate::error::GatewayError; #[clap(about = "Defguard VPN gateway service")] #[command(version)] pub struct Config { + #[arg(long, short = 'l', env = "DEFGUARD_LOG_LEVEL", default_value = "info")] + pub log_level: String, + /// Token received from Defguard after completing the network wizard #[arg( long, @@ -113,6 +116,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + log_level: "info".to_string(), token: "TOKEN".into(), name: None, grpc_url: "http://localhost:50051".into(), diff --git a/src/error.rs b/src/error.rs index d05e5d4..5a81aba 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +use defguard_version::DefguardVersionError; use defguard_wireguard_rs::error::WireguardInterfaceError; use thiserror::Error; @@ -43,4 +44,7 @@ pub enum GatewayError { #[error("Firewall error: {0}")] FirewallError(#[from] FirewallError), + + #[error(transparent)] + DefguardVersionError(#[from] DefguardVersionError), } diff --git a/src/gateway.rs b/src/gateway.rs index ce66471..cdc6a85 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -9,6 +9,9 @@ use std::{ time::{Duration, SystemTime}, }; +use defguard_version::{ + version_info_from_metadata, SystemInfo, SYSTEM_INFO_HEADER, VERSION_HEADER, +}; use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; use gethostname::gethostname; use tokio::{ @@ -69,13 +72,16 @@ impl From for InterfaceConfiguration { } } +/// Intercepts all grpc requests adding authentication and version metadata #[derive(Clone)] -struct AuthInterceptor { +struct RequestInterceptor { hostname: MetadataValue, token: MetadataValue, + version: MetadataValue, + system_info: MetadataValue, } -impl AuthInterceptor { +impl RequestInterceptor { fn new(token: &str) -> Result { let token = MetadataValue::try_from(token)?; let hostname = MetadataValue::try_from( @@ -83,14 +89,26 @@ impl AuthInterceptor { .to_str() .expect("Unable to get current hostname during gRPC connection setup."), )?; + let version = MetadataValue::try_from(VERSION)?; + let system_info = MetadataValue::try_from(SystemInfo::get().to_string())?; - Ok(Self { hostname, token }) + Ok(Self { + hostname, + token, + version, + system_info, + }) } } -impl Interceptor for AuthInterceptor { +impl Interceptor for RequestInterceptor { fn call(&mut self, mut request: Request<()>) -> Result, Status> { let metadata = request.metadata_mut(); + // Add version headers + metadata.insert(VERSION_HEADER, self.version.clone()); + metadata.insert(SYSTEM_INFO_HEADER, self.system_info.clone()); + + // Add auth headers metadata.insert("authorization", self.token.clone()); metadata.insert("hostname", self.hostname.clone()); @@ -110,7 +128,8 @@ pub struct Gateway { #[cfg_attr(not(target_os = "linux"), allow(unused))] firewall_config: Option, pub connected: Arc, - client: GatewayServiceClient>, + client: GatewayServiceClient>, + core_version: (String, String), stats_thread: Option>, } @@ -131,6 +150,7 @@ impl Gateway { stats_thread: None, firewall_api, firewall_config: None, + core_version: Default::default(), }) } @@ -248,7 +268,7 @@ impl Gateway { } async fn handle_stats_thread( - mut client: GatewayServiceClient>, + mut client: GatewayServiceClient>, rx: UnboundedReceiverStream, ) { let status = client.stats(rx).await; @@ -451,6 +471,15 @@ impl Gateway { }; match (response, stream) { (Ok(response), Ok(stream)) => { + let (version, info) = version_info_from_metadata(response.metadata()); + self.core_version = (version.to_string(), info.to_string()); + let span = tracing::info_span!( + "core_connect", + core_version = version, + core_info = info, + ); + let _guard = span.enter(); + if let Err(err) = self.configure(response.into_inner()) { error!("Interface configuration failed: {err}"); continue; @@ -477,7 +506,7 @@ impl Gateway { fn setup_client( config: &Config, - ) -> Result>, GatewayError> + ) -> Result>, GatewayError> { debug!("Preparing gRPC client configuration"); let tls = ClientTlsConfig::new(); @@ -498,8 +527,8 @@ impl Gateway { .tls_config(tls)?; let channel = endpoint.connect_lazy(); - let auth_interceptor = AuthInterceptor::new(&config.token)?; - let client = GatewayServiceClient::with_interceptor(channel, auth_interceptor); + let request_interceptor = RequestInterceptor::new(&config.token)?; + let client = GatewayServiceClient::with_interceptor(channel, request_interceptor); debug!("gRPC client configuration done"); Ok(client) @@ -642,6 +671,12 @@ impl Gateway { debug!("Executing specified POST_UP command: {post_up}"); execute_command(post_up)?; } + let span = tracing::info_span!( + "core_grpc_loop", + core_version = %self.core_version.0, + core_info = %self.core_version.1, + ); + let _guard = span.enter(); let stats_stream = self.spawn_stats_thread(); let client = self.client.clone(); select! { diff --git a/src/main.rs b/src/main.rs index ce5b231..68655ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,11 @@ use std::{fs::File, io::Write, process, sync::Arc}; use defguard_gateway::{ config::get_config, enterprise::firewall::api::FirewallApi, error::GatewayError, - execute_command, gateway::Gateway, init_syslog, server::run_server, + execute_command, gateway::Gateway, init_syslog, server::run_server, VERSION, }; #[cfg(not(any(target_os = "macos", target_os = "netbsd")))] use defguard_wireguard_rs::Kernel; use defguard_wireguard_rs::{Userspace, WGApi}; -use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use tokio::task::JoinSet; #[tokio::main] @@ -30,8 +29,8 @@ async fn main() -> Result<(), GatewayError> { return Err(error); } } else { - init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); - } + defguard_version::tracing::init(VERSION, &config.log_level.to_string()) + }; if let Some(pre_up) = &config.pre_up { log::info!("Executing specified PRE_UP command: {pre_up}");