From b7e3e63b41362ff47064a1212697b01f4e691f9b Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 22 Aug 2025 11:56:38 +0200 Subject: [PATCH 1/7] implement core version check --- src/gateway.rs | 5 +++++ src/lib.rs | 1 + src/version.rs | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/version.rs diff --git a/src/gateway.rs b/src/gateway.rs index e5155881..5cd47b0e 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -41,6 +41,7 @@ use crate::{ gateway_service_client::GatewayServiceClient, stats_update::Payload, update, Configuration, ConfigurationRequest, Peer, StatsUpdate, Update, }, + version::ensure_core_version_supported, VERSION, }; @@ -508,6 +509,10 @@ impl Gateway { ); let _guard = span.enter(); + // check core version and exit if it's not supported + let version = self.core_info.as_ref().map(|info| &info.version); + ensure_core_version_supported(version); + if let Err(err) = self.configure(response.into_inner()) { error!("Interface configuration failed: {err}"); continue; diff --git a/src/lib.rs b/src/lib.rs index 251696cf..dd37ed39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod config; pub mod error; pub mod gateway; pub mod server; +mod version; pub mod proto { pub mod gateway { diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 00000000..dcd9e985 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,18 @@ +use defguard_version::Version; + +const MIN_CORE_VERSION: Version = Version::new(1, 5, 0); + +/// Ensures the core version meets minimum version requirements. +/// Terminates the process if it doesn't. +pub(crate) fn ensure_core_version_supported(core_version: Option<&Version>) { + let Some(core_version) = core_version else { + error!("Missing core component version information. This most likely means that core component uses unsupported version. Exiting."); + std::process::exit(1); + }; + if core_version < &MIN_CORE_VERSION { + error!("Core version {core_version} is not supported. Minimal supported core version is {MIN_CORE_VERSION}. Exiting."); + std::process::exit(1); + } + + info!("Core version {core_version} is supported"); +} From bb6acbab169b66f23575cf21e1e4e837fbe52097 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 22 Aug 2025 12:47:36 +0200 Subject: [PATCH 2/7] fix semver version --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index dd37ed39..66c5ef14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ use syslog::{BasicLogger, Facility, Formatter3164}; pub mod enterprise; -pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "-", env!("VERGEN_GIT_SHA")); +pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("VERGEN_GIT_SHA")); /// Masks object's field with "***" string. /// Used to log sensitive/secret objects. From e96eef1db2677a056017240615e8b665beeeb8d5 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 25 Aug 2025 09:59:43 +0200 Subject: [PATCH 3/7] use get_tracing_variables function from defguard_version crate --- Cargo.lock | 1 - Cargo.toml | 2 +- src/gateway.rs | 20 ++++---------------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7c3ca72..9f1851bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,7 +409,6 @@ 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 96915c03..610bd138 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "f61ce40927a4d21095ea53a691219d5ae46e3e4e" } +defguard_version = { path = "../defguard/crates/defguard_version" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } diff --git a/src/gateway.rs b/src/gateway.rs index 5cd47b0e..39cdf139 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,5 +1,6 @@ use defguard_version::{ - client::version_interceptor, parse_metadata, ComponentInfo, DefguardComponent, Version, + client::version_interceptor, get_tracing_variables, parse_metadata, ComponentInfo, + DefguardComponent, Version, }; use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; use gethostname::gethostname; @@ -464,19 +465,6 @@ impl Gateway { Ok(()) } - fn get_tracing_variables(&self) -> (String, String) { - let version = self - .core_info - .as_ref() - .map_or(String::from("?"), |info| info.version.to_string()); - let info = self - .core_info - .as_ref() - .map_or(String::from("?"), |info| info.system.to_string()); - - (version, info) - } - /// Continuously tries to connect to gRPC endpoint. Once the connection is established /// configures the interface, starts the stats thread, connects and returns the updates stream. async fn connect(&mut self) -> Streaming { @@ -500,7 +488,7 @@ impl Gateway { match (response, stream) { (Ok(response), Ok(stream)) => { self.core_info = parse_metadata(response.metadata()); - let (version, info) = self.get_tracing_variables(); + let (version, info) = get_tracing_variables(&self.core_info); let span = tracing::info_span!( "core_configuration", component = %DefguardComponent::Core, @@ -705,7 +693,7 @@ impl Gateway { debug!("Executing specified POST_UP command: {post_up}"); execute_command(post_up)?; } - let (version, info) = self.get_tracing_variables(); + let (version, info) = get_tracing_variables(&self.core_info); let span = tracing::info_span!( "core_grpc", component = %DefguardComponent::Core, From 0ae432860b2a2121257b3c9ce206de708a78c3a8 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 28 Aug 2025 14:08:02 +0200 Subject: [PATCH 4/7] use the new defguard_version::client::ClientVersionInterceptor --- Cargo.lock | 1 + Cargo.toml | 1 + src/gateway.rs | 55 +++++++++++++++++++++----------------------------- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f1851bb..e4c4ec8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,7 @@ dependencies = [ "tonic", "tonic-prost", "tonic-prost-build", + "tower", "tracing", "vergen-git2", "x25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 610bd138..548c1491 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ tonic = { version = "0.14", default-features = false, features = [ ] } tracing = "0.1" tonic-prost = "0.14" +tower = "0.5.2" [target.'cfg(target_os = "linux")'.dependencies] nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "1a1147271f43b9d7182a114bb056a5224c35d38f" } diff --git a/src/gateway.rs b/src/gateway.rs index 39cdf139..4f3d78e8 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,5 +1,5 @@ use defguard_version::{ - client::version_interceptor, get_tracing_variables, parse_metadata, ComponentInfo, + client::ClientVersionInterceptor, get_tracing_variables, parse_metadata, ComponentInfo, DefguardComponent, Version, }; use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; @@ -24,10 +24,11 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{ codegen::InterceptedService, metadata::{Ascii, MetadataValue}, - service::Interceptor, + service::{Interceptor, InterceptorLayer}, transport::{Certificate, Channel, ClientTlsConfig, Endpoint}, Request, Status, Streaming, }; +use tower::ServiceBuilder; use tracing::{instrument, Instrument}; use crate::{ @@ -74,29 +75,23 @@ impl From for InterfaceConfiguration { } } -type InterceptorFn = Box) -> Result, Status> + Send + Sync>; - /// Intercepts all grpc requests adding authentication and version metadata -struct RequestInterceptor { +struct AuthInterceptor { hostname: MetadataValue, token: MetadataValue, - version: defguard_version::Version, - version_interceptor_fn: InterceptorFn, } -impl Clone for RequestInterceptor { +impl Clone for AuthInterceptor { fn clone(&self) -> Self { Self { hostname: self.hostname.clone(), token: self.token.clone(), - version: self.version.clone(), - version_interceptor_fn: Box::new(version_interceptor(self.version.clone())), } } } -impl RequestInterceptor { - fn new(token: &str, version: Version) -> Result { +impl AuthInterceptor { + fn new(token: &str) -> Result { let token = MetadataValue::try_from(token)?; let hostname = MetadataValue::try_from( gethostname() @@ -104,20 +99,12 @@ impl RequestInterceptor { .expect("Unable to get current hostname during gRPC connection setup."), )?; - Ok(Self { - hostname, - token, - version: version.clone(), - version_interceptor_fn: Box::new(version_interceptor(version)), - }) + Ok(Self { hostname, token }) } } -impl Interceptor for RequestInterceptor { - fn call(&mut self, request: Request<()>) -> Result, Status> { - // Apply version interceptor - adds version headers - let mut request = (self.version_interceptor_fn)(request)?; - +impl Interceptor for AuthInterceptor { + fn call(&mut self, mut request: Request<()>) -> Result, Status> { // Add auth headers let metadata = request.metadata_mut(); metadata.insert("authorization", self.token.clone()); @@ -128,6 +115,9 @@ impl Interceptor for RequestInterceptor { } type PubKey = String; +type GatewayClientType = GatewayServiceClient< + InterceptedService, ClientVersionInterceptor>, +>; pub struct Gateway { config: Config, @@ -139,7 +129,7 @@ pub struct Gateway { #[cfg_attr(not(target_os = "linux"), allow(unused))] firewall_config: Option, pub connected: Arc, - client: GatewayServiceClient>, + client: GatewayClientType, core_info: Option, stats_thread: Option>, } @@ -284,7 +274,7 @@ impl Gateway { #[instrument(skip_all)] async fn handle_stats_thread( - mut client: GatewayServiceClient>, + mut client: GatewayClientType, rx: UnboundedReceiverStream, ) { let status = client.stats(rx).await; @@ -525,10 +515,7 @@ impl Gateway { } } - fn setup_client( - config: &Config, - ) -> Result>, GatewayError> - { + fn setup_client(config: &Config) -> Result { debug!("Preparing gRPC client configuration"); let tls = ClientTlsConfig::new(); // Use CA if provided, otherwise load certificates from system. @@ -547,9 +534,13 @@ impl Gateway { .keep_alive_while_idle(true) .tls_config(tls)?; let channel = endpoint.connect_lazy(); - let version = Version::parse(VERSION)?; - let request_interceptor = RequestInterceptor::new(&config.token, version)?; - let client = GatewayServiceClient::with_interceptor(channel, request_interceptor); + let version_interceptor = ClientVersionInterceptor::new(Version::parse(VERSION)?); + let auth_interceptor = AuthInterceptor::new(&config.token)?; + let channel = ServiceBuilder::new() + .layer(InterceptorLayer::new(version_interceptor)) + .layer(InterceptorLayer::new(auth_interceptor)) + .service(channel); + let client = GatewayServiceClient::new(channel); debug!("gRPC client configuration done"); Ok(client) From 1c721af17dd8c95a886b0af142d27bc3deba65af Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 28 Aug 2025 15:36:27 +0200 Subject: [PATCH 5/7] update defguard_version dependency --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e4c4ec8b..03633082 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,6 +410,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" +source = "git+https://github.com/DefGuard/defguard.git?rev=a5709e7117103458ad8417d4437a8a369ca5bbce#a5709e7117103458ad8417d4437a8a369ca5bbce" dependencies = [ "http", "os_info", diff --git a/Cargo.toml b/Cargo.toml index 548c1491..b7d63934 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { path = "../defguard/crates/defguard_version" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "a5709e7117103458ad8417d4437a8a369ca5bbce" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } From c38e9adf7e173258c4e4d581ff25cfd474680c73 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 28 Aug 2025 15:41:39 +0200 Subject: [PATCH 6/7] use aws cached docker image --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba41d349..84e8b05a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: test: runs-on: - codebuild-defguard-gateway-runner-${{ github.run_id }}-${{ github.run_attempt }} - container: rust:1 + container: public.ecr.aws/docker/library/rust:1 steps: - name: Debug From 576c8a544aa7e6f9cf63bf89ca4d31d3ac8bc423 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 28 Aug 2025 15:45:11 +0200 Subject: [PATCH 7/7] don't use cargo-deny gh action --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84e8b05a..406a34df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,8 @@ jobs: rustup component add clippy cargo clippy --all-targets --all-features -- -D warnings - name: Run cargo deny - uses: EmbarkStudios/cargo-deny-action@v2 + run: | + cargo install cargo-deny + cargo deny check - name: Run tests run: cargo test --locked --no-fail-fast