From c4b4f1d3f9f7cbdadf8feadb894f3c259c880202 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 19 Jan 2026 18:08:55 +0100 Subject: [PATCH 1/7] wip send cookie key via protos --- src/grpc.rs | 61 +++++++++++++++++++++++++++++++++++++---------------- src/http.rs | 2 ++ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index 5c20ae2..f167ce9 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -26,7 +26,10 @@ use tracing::Instrument; use crate::{ error::ApiError, http::GRPC_SERVER_RESTART_CHANNEL, - proto::{core_request, core_response, proxy_server, CoreRequest, CoreResponse, DeviceInfo}, + proto::{ + core_request, core_response, proxy_server, CoreRequest, CoreResponse, DeviceInfo, + InitialInfo, + }, MIN_CORE_VERSION, VERSION, }; @@ -234,24 +237,47 @@ impl proxy_server::Proxy for ProxyServer { info!("Defguard Core gRPC client connected from: {address}"); // Retrieve private cookies key from the header. - let cookie_key = request.metadata().get_bin(COOKIE_KEY_HEADER); - let key = match cookie_key { - Some(key) => Key::from(&key.to_bytes().map_err(|err| { - error!("Failed to decode private cookie key: {err:?}"); - Status::internal("Failed to decode private cookie key") - })?), - // If the header is missing, fall back to generating a local key. - // This preserves compatibility with older Core versions that did not - // provide a shared cookie key. In this mode, cookie-based sessions will - // not be shared across proxy instances and HA won't work. - None => { - warn!( - "Private cookie key not provided by Core; falling back to a locally generated key. \ - This typically indicates an older Core version and disables cookie sharing across proxies." - ); - Key::generate() + // let cookie_key = request.metadata().get_bin(COOKIE_KEY_HEADER); + // let key = match cookie_key { + // Some(key) => Key::from(&key.to_bytes().map_err(|err| { + // error!("Failed to decode private cookie key: {err:?}"); + // Status::internal("Failed to decode private cookie key") + // })?), + // // If the header is missing, fall back to generating a local key. + // // This preserves compatibility with older Core versions that did not + // // provide a shared cookie key. In this mode, cookie-based sessions will + // // not be shared across proxy instances and HA won't work. + // None => { + // warn!( + // "Private cookie key not provided by Core; falling back to a locally generated key. \ + // This typically indicates an older Core version and disables cookie sharing across proxies." + // ); + // Key::generate() + // } + // }; + + error!("### WAITING for key"); + let mut stream = request.into_inner(); + let key = match stream.message().await { + Ok(Some(response)) => match response.payload { + Some(core_response::Payload::InitialInfo(payload)) => { + error!("### got the key"); + Key::from(&payload.private_cookies_key) + } + Some(_) => todo!(), + None => todo!(), + }, + Ok(None) => { + info!("gRPC stream has been closed"); + todo!() + } + Err(err) => { + error!("gRPC client error: {err}"); + todo!() } }; + + error!("### KEY: {:?}", key.master()); self.http_channel.send(key).map_err(|err| { error!("Failed to send private cookies key to HTTP server: {err:?}"); Status::internal("Failed to send private cookies key to HTTP server") @@ -269,7 +295,6 @@ impl proxy_server::Proxy for ProxyServer { let clients = Arc::clone(&self.clients); let results = Arc::clone(&self.results); let connected = Arc::clone(&self.connected); - let mut stream = request.into_inner(); tokio::spawn( async move { loop { diff --git a/src/http.rs b/src/http.rs index 8d165df..fa6e869 100644 --- a/src/http.rs +++ b/src/http.rs @@ -196,8 +196,10 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { tasks.spawn(async move { let cert_dir = Path::new(&config.cert_dir); if !cert_dir.exists() { + debug!("Creating certs directory"); tokio::fs::create_dir_all(cert_dir).await?; } + debug!("DONE"); loop { let server_to_run = server_clone.clone(); From bce4f425bb075c57050b3eafad87d500e6501830 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 20 Jan 2026 08:13:00 +0100 Subject: [PATCH 2/7] cargo fmt --- src/grpc.rs | 14 +++++++------- src/http.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index f167ce9..a8cfac6 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -256,16 +256,16 @@ impl proxy_server::Proxy for ProxyServer { // } // }; - error!("### WAITING for key"); + error!("### WAITING for key"); let mut stream = request.into_inner(); let key = match stream.message().await { Ok(Some(response)) => match response.payload { Some(core_response::Payload::InitialInfo(payload)) => { - error!("### got the key"); - Key::from(&payload.private_cookies_key) - } - Some(_) => todo!(), - None => todo!(), + error!("### got the key"); + Key::from(&payload.private_cookies_key) + } + Some(_) => todo!(), + None => todo!(), }, Ok(None) => { info!("gRPC stream has been closed"); @@ -277,7 +277,7 @@ impl proxy_server::Proxy for ProxyServer { } }; - error!("### KEY: {:?}", key.master()); + error!("### KEY: {:?}", key.master()); self.http_channel.send(key).map_err(|err| { error!("Failed to send private cookies key to HTTP server: {err:?}"); Status::internal("Failed to send private cookies key to HTTP server") diff --git a/src/http.rs b/src/http.rs index fa6e869..e17e653 100644 --- a/src/http.rs +++ b/src/http.rs @@ -196,10 +196,10 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { tasks.spawn(async move { let cert_dir = Path::new(&config.cert_dir); if !cert_dir.exists() { - debug!("Creating certs directory"); + debug!("Creating certs directory"); tokio::fs::create_dir_all(cert_dir).await?; } - debug!("DONE"); + debug!("DONE"); loop { let server_to_run = server_clone.clone(); From dacff6f8382f659ca0361f6de082243d3cb83dbc Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 20 Jan 2026 09:34:32 +0100 Subject: [PATCH 3/7] wip working key retrieval --- src/grpc.rs | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index a8cfac6..1bfbca4 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -256,27 +256,7 @@ impl proxy_server::Proxy for ProxyServer { // } // }; - error!("### WAITING for key"); - let mut stream = request.into_inner(); - let key = match stream.message().await { - Ok(Some(response)) => match response.payload { - Some(core_response::Payload::InitialInfo(payload)) => { - error!("### got the key"); - Key::from(&payload.private_cookies_key) - } - Some(_) => todo!(), - None => todo!(), - }, - Ok(None) => { - info!("gRPC stream has been closed"); - todo!() - } - Err(err) => { - error!("gRPC client error: {err}"); - todo!() - } - }; - + let key = Key::from(&[0; 64]); error!("### KEY: {:?}", key.master()); self.http_channel.send(key).map_err(|err| { error!("Failed to send private cookies key to HTTP server: {err:?}"); @@ -297,6 +277,30 @@ impl proxy_server::Proxy for ProxyServer { let connected = Arc::clone(&self.connected); tokio::spawn( async move { + error!("### WAITING for key"); + let mut stream = request.into_inner(); + error!("### got the stream"); + let message = stream.message().await; + error!("### got the message, unpacking the key"); + let key = match message { + Ok(Some(response)) => match response.payload { + Some(core_response::Payload::InitialInfo(payload)) => { + error!("### got the key"); + Key::from(&payload.private_cookies_key) + } + Some(_) => todo!(), + None => todo!(), + }, + Ok(None) => { + info!("gRPC stream has been closed"); + todo!() + } + Err(err) => { + error!("gRPC client error: {err}"); + todo!() + } + }; + error!("### KEY: {:?}", key.master()); loop { match stream.message().await { Ok(Some(response)) => { From 9e37b188ac92db1c07ea1f8ecbf797f343965283 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 20 Jan 2026 09:38:55 +0100 Subject: [PATCH 4/7] cleanup --- src/grpc.rs | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index 1bfbca4..c23a106 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -236,26 +236,6 @@ impl proxy_server::Proxy for ProxyServer { info!("Defguard Core gRPC client connected from: {address}"); - // Retrieve private cookies key from the header. - // let cookie_key = request.metadata().get_bin(COOKIE_KEY_HEADER); - // let key = match cookie_key { - // Some(key) => Key::from(&key.to_bytes().map_err(|err| { - // error!("Failed to decode private cookie key: {err:?}"); - // Status::internal("Failed to decode private cookie key") - // })?), - // // If the header is missing, fall back to generating a local key. - // // This preserves compatibility with older Core versions that did not - // // provide a shared cookie key. In this mode, cookie-based sessions will - // // not be shared across proxy instances and HA won't work. - // None => { - // warn!( - // "Private cookie key not provided by Core; falling back to a locally generated key. \ - // This typically indicates an older Core version and disables cookie sharing across proxies." - // ); - // Key::generate() - // } - // }; - let key = Key::from(&[0; 64]); error!("### KEY: {:?}", key.master()); self.http_channel.send(key).map_err(|err| { @@ -277,15 +257,10 @@ impl proxy_server::Proxy for ProxyServer { let connected = Arc::clone(&self.connected); tokio::spawn( async move { - error!("### WAITING for key"); let mut stream = request.into_inner(); - error!("### got the stream"); - let message = stream.message().await; - error!("### got the message, unpacking the key"); - let key = match message { + let key = match stream.message().await { Ok(Some(response)) => match response.payload { Some(core_response::Payload::InitialInfo(payload)) => { - error!("### got the key"); Key::from(&payload.private_cookies_key) } Some(_) => todo!(), @@ -300,7 +275,6 @@ impl proxy_server::Proxy for ProxyServer { todo!() } }; - error!("### KEY: {:?}", key.master()); loop { match stream.message().await { Ok(Some(response)) => { From ca3b129cd8358c607c740aeb8039ddb4fe6ad3df Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 20 Jan 2026 10:30:04 +0100 Subject: [PATCH 5/7] handle InitialInfo in message loop --- src/grpc.rs | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/grpc.rs b/src/grpc.rs index 7fda421..4ccbb20 100644 --- a/src/grpc.rs +++ b/src/grpc.rs @@ -26,9 +26,7 @@ use tracing::Instrument; use crate::{ error::ApiError, http::GRPC_SERVER_RESTART_CHANNEL, - proto::{ - core_request, core_response, proxy_server, CoreRequest, CoreResponse, DeviceInfo, - }, + proto::{core_request, core_response, proxy_server, CoreRequest, CoreResponse, DeviceInfo}, MIN_CORE_VERSION, VERSION, }; @@ -249,37 +247,28 @@ impl proxy_server::Proxy for ProxyServer { tokio::spawn( async move { let mut stream = request.into_inner(); - let key = match stream.message().await { - Ok(Some(response)) => match response.payload { - Some(core_response::Payload::InitialInfo(payload)) => { - Key::from(&payload.private_cookies_key) - } - Some(_) => todo!(), - None => todo!(), - }, - Ok(None) => { - info!("gRPC stream has been closed"); - todo!() - } - Err(err) => { - error!("gRPC client error: {err}"); - todo!() - } - }; - *cookie_key.write().unwrap() = Some(key); loop { match stream.message().await { Ok(Some(response)) => { debug!("Received message from Defguard Core ID={}", response.id); connected.store(true, Ordering::Relaxed); if let Some(payload) = response.payload { - let maybe_rx = results.lock().expect("Failed to acquire lock on results hashmap when processing response").remove(&response.id); - if let Some(rx) = maybe_rx { - if let Err(err) = rx.send(payload) { - error!("Failed to send message to rx {:?}", err.type_id()); + match payload { + core_response::Payload::InitialInfo(payload) => { + info!("Received private cookies key"); + let key = Key::from(&payload.private_cookies_key); + *cookie_key.write().unwrap() = Some(key); + }, + _ => { + let maybe_rx = results.lock().expect("Failed to acquire lock on results hashmap when processing response").remove(&response.id); + if let Some(rx) = maybe_rx { + if let Err(err) = rx.send(payload) { + error!("Failed to send message to rx {:?}", err.type_id()); + } + } else { + error!("Missing receiver for response #{}", response.id); + } } - } else { - error!("Missing receiver for response #{}", response.id); } } } From 50dcd9cd01b52aea81f5fd1cb0a99e375cfff85f Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 20 Jan 2026 10:58:40 +0100 Subject: [PATCH 6/7] new protos --- proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto b/proto index c48340f..ec48aca 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit c48340f72b9de3a69cf71318c75ff1361ebd7897 +Subproject commit ec48aca9438e7cdcb4fcdb01ce6dcb5dac7f8dd3 From 82a396f0bb31af4aca6fde26a512d4b1b43b5dac Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 20 Jan 2026 11:01:22 +0100 Subject: [PATCH 7/7] remove debug log --- src/http.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/http.rs b/src/http.rs index 2cc73a5..0f298bb 100644 --- a/src/http.rs +++ b/src/http.rs @@ -229,7 +229,6 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { debug!("Creating certs directory"); tokio::fs::create_dir_all(cert_dir).await?; } - debug!("DONE"); loop { let server_to_run = server_clone.clone();