From 9b293e13d6be9219a27404e6726cebd35a87764c Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 1 Aug 2025 10:09:43 +0200 Subject: [PATCH 1/6] configs: get the active one at startup, and deactivate old ones on updates --- src/db/datastore_wrapper.rs | 43 +++++++++++++++++++++++++++-- src/http_proxy/api/update_config.rs | 14 +++------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/db/datastore_wrapper.rs b/src/db/datastore_wrapper.rs index eaed560..41f0200 100644 --- a/src/db/datastore_wrapper.rs +++ b/src/db/datastore_wrapper.rs @@ -339,6 +339,14 @@ impl DatastoreWrapper { pub(crate) async fn get_configs(&mut self, token: String) -> Result { let table = DbTable::Config.to_str(); + let filter = AdvanceFilter { + r#type: String::from("criteria"), + field: String::from("active"), + operator: String::from("equal"), + entity: table.to_string(), + values: "[true]".to_string(), + }; + let request = GetByFilterRequest { params: Some(Params { id: String::new(), @@ -352,7 +360,7 @@ impl DatastoreWrapper { "retention_sec".to_string(), "ip_info_cache_size".to_string(), ], - advance_filters: vec![], + advance_filters: vec![filter], order_by: String::new(), limit: 1, offset: 0, @@ -381,7 +389,7 @@ impl DatastoreWrapper { let i = array .first() - .ok_or("No data found") + .ok_or("No active configs found for AppGuard") .handle_err(location!())?; let map = i @@ -1000,6 +1008,37 @@ impl DatastoreWrapper { Ok(()) } + + pub(crate) async fn deactivate_old_configs(&mut self, token: &str) -> Result { + let table = DbTable::Config.to_str(); + + let filter = AdvanceFilter { + r#type: "criteria".to_string(), + field: "active".to_string(), + operator: "equal".to_string(), + entity: table.to_string(), + values: "[true]".to_string(), + }; + + let updates = json!({"active": false}).to_string(); + + let request = BatchUpdateRequest { + params: Some(Params { + id: String::new(), + table: table.into(), + r#type: String::from("root"), + }), + body: Some(BatchUpdateBody { + advance_filters: vec![filter], + updates, + }), + }; + + log::trace!("Before batch update to {table}"); + let count = self.inner.batch_update(request, token).await?.count; + log::trace!("After batch update to {table}: {count}"); + Ok(count) + } } #[cfg(test)] diff --git a/src/http_proxy/api/update_config.rs b/src/http_proxy/api/update_config.rs index da1e425..1971698 100644 --- a/src/http_proxy/api/update_config.rs +++ b/src/http_proxy/api/update_config.rs @@ -8,8 +8,6 @@ use nullnet_liberror::{location, ErrorHandler, Location}; use crate::config::Config; use crate::db::entries::DbEntry; -use crate::db::tables::DbTable; -use crate::helpers::get_timestamp_string; use actix_web::web::Data; use actix_web::web::Json; use serde_json::json; @@ -25,9 +23,9 @@ pub async fn update_config( let body_config = body.into_inner(); - if delete_old_configs(&context).await.is_err() { + if deactivate_old_configs(&context).await.is_err() { return HttpResponse::InternalServerError().json(ErrorJson::from( - "Failed to delete old AppGuard configs from datastore", + "Failed to deactivate old AppGuard configs from datastore", )); } @@ -55,15 +53,11 @@ pub async fn update_config( HttpResponse::Ok().json(json!({})) } -async fn delete_old_configs(context: &AppContext) -> Result<(), nullnet_liberror::Error> { +async fn deactivate_old_configs(context: &AppContext) -> Result<(), nullnet_liberror::Error> { context .datastore .clone() - .delete_old_entries( - DbTable::Config, - get_timestamp_string().as_str(), - &context.root_token_provider.get().await?.jwt, - ) + .deactivate_old_configs(&context.root_token_provider.get().await?.jwt) .await?; Ok(()) From a725d5b71c6d98a92c8250c5d9b7615f03ccba06 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 1 Aug 2025 10:42:14 +0200 Subject: [PATCH 2/6] firewalls: get active ones at startup, and deactivate old ones on updates --- src/db/datastore_wrapper.rs | 46 +++++++++++++++++++- src/db/entries.rs | 4 +- src/http_proxy/api/update_client_firewall.rs | 19 ++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/db/datastore_wrapper.rs b/src/db/datastore_wrapper.rs index 41f0200..1003994 100644 --- a/src/db/datastore_wrapper.rs +++ b/src/db/datastore_wrapper.rs @@ -267,13 +267,20 @@ impl DatastoreWrapper { Ok(count) } - // SELECT app_id, firewall FROM {table} pub(crate) async fn get_firewalls( &mut self, token: String, ) -> Result, Error> { let table = DbTable::Firewall.to_str(); + let filter = AdvanceFilter { + r#type: String::from("criteria"), + field: String::from("active"), + operator: String::from("equal"), + entity: table.to_string(), + values: "[true]".to_string(), + }; + let request = GetByFilterRequest { params: Some(Params { id: String::new(), @@ -282,7 +289,7 @@ impl DatastoreWrapper { }), body: Some(GetByFilterBody { pluck: vec!["app_id".to_string(), "firewall".to_string()], - advance_filters: vec![], + advance_filters: vec![filter], order_by: String::new(), limit: i32::MAX, offset: 0, @@ -1039,6 +1046,41 @@ impl DatastoreWrapper { log::trace!("After batch update to {table}: {count}"); Ok(count) } + + pub(crate) async fn deactivate_old_firewalls( + &mut self, + token: &str, + device_id: &str, + ) -> Result { + let table = DbTable::Firewall.to_str(); + + let filter = AdvanceFilter { + r#type: "criteria".to_string(), + field: "app_id".to_string(), + operator: "equal".to_string(), + entity: table.to_string(), + values: format!("[\"{device_id}\"]"), + }; + + let updates = json!({"active": false}).to_string(); + + let request = BatchUpdateRequest { + params: Some(Params { + id: String::new(), + table: table.into(), + r#type: String::from("root"), + }), + body: Some(BatchUpdateBody { + advance_filters: vec![filter], + updates, + }), + }; + + log::trace!("Before batch update to {table}"); + let count = self.inner.batch_update(request, token).await?.count; + log::trace!("After batch update to {table}: {count}"); + Ok(count) + } } #[cfg(test)] diff --git a/src/db/entries.rs b/src/db/entries.rs index 6a8f6dc..c901c20 100644 --- a/src/db/entries.rs +++ b/src/db/entries.rs @@ -67,9 +67,7 @@ impl DbEntry { let _ = ds.insert_batch(self, token.as_str()).await?; } DbEntry::Firewall(_) => { - let _ = ds - .upsert(self, vec!["app_id".to_string()], token.as_str()) - .await?; + let _ = ds.insert(self, token.as_str()).await?; log::info!("Firewall inserted in datastore"); } DbEntry::DeniedIp((_, denied_ip, _)) => { diff --git a/src/http_proxy/api/update_client_firewall.rs b/src/http_proxy/api/update_client_firewall.rs index 180cb2e..dce52da 100644 --- a/src/http_proxy/api/update_client_firewall.rs +++ b/src/http_proxy/api/update_client_firewall.rs @@ -57,6 +57,12 @@ pub async fn update_client_firewall( let default_policy = firewall.default_policy; let timeout = firewall.timeout; + if deactivate_old_firewalls(&context, device_id).await.is_err() { + return HttpResponse::InternalServerError().json(ErrorJson::from( + "Failed to deactivate old firewalls from datastore", + )); + } + if DbEntry::Firewall((device_id.clone(), firewall.clone(), jwt)) .store(context.datastore.clone()) .await @@ -86,3 +92,16 @@ pub async fn update_client_firewall( HttpResponse::Ok().json(json!({})) } + +async fn deactivate_old_firewalls( + context: &AppContext, + device_id: &str, +) -> Result<(), nullnet_liberror::Error> { + context + .datastore + .clone() + .deactivate_old_firewalls(&context.root_token_provider.get().await?.jwt, device_id) + .await?; + + Ok(()) +} From 6a09c21301f3a9706a4c7909398062ef0cb8e4ff Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 1 Aug 2025 10:47:23 +0200 Subject: [PATCH 3/6] remove uneeded comments and completed TODOs --- src/app_guard_impl.rs | 52 -------------------- src/db/datastore_wrapper.rs | 94 ------------------------------------- 2 files changed, 146 deletions(-) diff --git a/src/app_guard_impl.rs b/src/app_guard_impl.rs index a28801b..e8f8881 100644 --- a/src/app_guard_impl.rs +++ b/src/app_guard_impl.rs @@ -208,58 +208,6 @@ impl AppGuardImpl { Ok(res) } - // pub(crate) async fn heartbeat_impl( - // &self, - // request: Request, - // ) -> Result::HeartbeatStream>, Error> { - // let datastore = self.ctx.datastore.clone(); - // let remote_address = request - // .remote_addr() - // .map_or_else(|| "Unknown".to_string(), |addr| addr.ip().to_string()); - // log::info!("Received heartbeat request from {remote_address}"); - // - // let authenticate_request = request.into_inner(); - // let token_provider = TokenProvider::new( - // authenticate_request.app_id.unwrap_or_default().clone(), - // authenticate_request.app_secret.unwrap_or_default().clone(), - // false, - // datastore.clone(), - // ); - // let token = token_provider.get().await?.jwt.clone(); - // let (_, token_info) = authenticate(token.clone())?; - // let Some(device) = token_info.account.device else { - // return Err("Device not found in token").handle_err(location!()); - // }; - // let device_id = device.id; - // - // let status = datastore.device_status(device_id.clone(), &token).await?; - // if status == DeviceStatus::Draft { - // datastore - // .device_setup(&token, device_id.clone(), remote_address) - // .await?; - // } - // - // let (tx, rx) = mpsc::channel(6); - // - // tokio::spawn(async move { - // loop { - // if let Ok(t) = token_provider.get().await { - // let token = t.jwt.clone(); - // if let Ok(response) = datastore.heartbeat(&token, device_id.clone()).await { - // let response = HeartbeatResponse { - // token, - // status: response.status.into(), - // }; - // tx.send(Ok(response)).await.unwrap(); - // } - // } - // tokio::time::sleep(Duration::from_secs(10)).await; - // } - // }); - // - // Ok(Response::new(ReceiverStream::new(rx))) - // } - pub(crate) fn control_channel_impl( &self, request: Request>, diff --git a/src/db/datastore_wrapper.rs b/src/db/datastore_wrapper.rs index 1003994..c841c8c 100644 --- a/src/db/datastore_wrapper.rs +++ b/src/db/datastore_wrapper.rs @@ -195,7 +195,6 @@ impl DatastoreWrapper { } // SELECT MIN(timestamp) FROM {table} - // TODO: An error occurred while processing your request pub(crate) async fn get_oldest_timestamp( &mut self, table: DbTable, @@ -236,7 +235,6 @@ impl DatastoreWrapper { } // DELETE FROM {table} WHERE timestamp <= {timestamp} - /// todo: error 'missing FROM-clause entry for table "ip_blacklists"' pub(crate) async fn delete_old_entries( &mut self, table: DbTable, @@ -534,44 +532,6 @@ impl DatastoreWrapper { Ok(response) } - // pub async fn register_device( - // &self, - // token: &str, - // account_id: &str, - // account_secret: &str, - // device: &Device, - // ) -> Result { - // let request = RegisterDeviceRequestBuilder::new() - // .account_id(account_id) - // .account_secret(account_secret) - // .account_organization_status("Active") - // .is_new_user(true) - // .add_account_organization_category("Device") - // .add_device_category("Device") - // .organization_id(&device.organization) - // .device_id(&device.id) - // .build(); - // - // let response = self.inner.clone().register_device(request, token).await?; - // - // Ok(response) - // } - - // pub async fn heartbeat( - // &self, - // token: &str, - // device_id: String, - // ) -> Result { - // let (create_result, fetch_result) = tokio::join!( - // Self::internal_hb_create_hb_record(self.inner.clone(), device_id.clone(), token), - // Self::internal_hb_fetch_device_info(self.inner.clone(), device_id, token) - // ); - // - // let _ = create_result?; - // - // fetch_result - // } - pub async fn logs_insert(&self, token: &str, logs: Vec) -> Result { match logs.as_slice() { [] => Ok(ResponseData { @@ -584,7 +544,6 @@ impl DatastoreWrapper { } } - // TODO: There was an error while creating the new record async fn logs_insert_single(&mut self, log: Log, token: &str) -> Result { let record = serde_json::to_string(&log).handle_err(location!())?; @@ -631,59 +590,6 @@ impl DatastoreWrapper { Ok(res) } - // TODO: There was an error while creating the new record - // async fn internal_hb_create_hb_record( - // mut client: DatastoreClient, - // device_id: String, - // token: &str, - // ) -> Result { - // let request = CreateRequest { - // params: Some(CreateParams { - // table: String::from("device_heartbeats"), - // }), - // query: Some(Query { - // pluck: String::new(), - // durability: String::from("soft"), - // }), - // body: Some(CreateBody { - // record: json!({ - // "device_id": device_id.clone(), - // "timestamp": Utc::now().to_rfc3339(), - // }) - // .to_string(), - // }), - // }; - // - // log::trace!("Before create heartbeat record"); - // let res = client.create(request, token).await?; - // log::trace!("After create heartbeat record"); - // - // Ok(res) - // } - - // async fn internal_hb_fetch_device_info( - // mut client: DatastoreClient, - // device_id: String, - // token: &str, - // ) -> Result { - // let request = GetByIdRequest { - // params: Some(Params { - // id: device_id, - // table: String::from("devices"), - // r#type: String::new(), - // }), - // query: Some(Query { - // pluck: String::from("status,is_monitoring_enabled,is_remote_access_enabled"), - // durability: String::from("soft"), - // }), - // }; - // - // log::trace!("Before fetch heartbeat device info"); - // let response = client.get_by_id(request, token).await?; - // log::trace!("After fetch heartbeat device info"); - // LatestDeviceInfo::from_response_data(&response) - // } - pub async fn obtain_device_by_id( &self, token: &str, From 9880c2ac7a50afb20de314f70529ee4f991070e0 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 1 Aug 2025 11:26:29 +0200 Subject: [PATCH 4/6] include full firewall rules in reasons --- src/firewall/firewall.rs | 12 +++++++++--- src/firewall/items/http_request.rs | 26 +++++++++++++------------- src/firewall/items/http_response.rs | 16 ++++++++-------- src/firewall/items/ip_info.rs | 26 +++++++++++++------------- src/firewall/items/smtp_request.rs | 18 +++++++++--------- src/firewall/items/smtp_response.rs | 12 ++++++------ src/firewall/items/tcp_connection.rs | 20 ++++++++++---------- src/firewall/items/tcp_info.rs | 2 +- src/firewall/rules.rs | 26 +++++++++++++------------- 9 files changed, 82 insertions(+), 76 deletions(-) diff --git a/src/firewall/firewall.rs b/src/firewall/firewall.rs index 0b92b61..d681035 100644 --- a/src/firewall/firewall.rs +++ b/src/firewall/firewall.rs @@ -47,7 +47,7 @@ impl Firewall { ) -> FirewallResult { // first let's check if this is blacklisted if item.is_blacklisted() { - return FirewallResult::new(FirewallPolicy::Deny, vec!["blacklist".to_string()]); + return FirewallResult::new(FirewallPolicy::Deny, vec!["IP is blacklisted".to_string()]); } // if not blacklisted, check the firewall expressions one by one for expr in &self.expressions { @@ -260,7 +260,10 @@ mod tests { firewall.match_item(&item_1), FirewallResult::new( FirewallPolicy::Deny, - vec!["protocol".to_string(), "country".to_string()] + vec![ + "{\"condition\":\"equal\",\"protocol\":[\"HTTP\",\"HTTPS\"],\"direction\":\"in\"}".to_string(), + "{\"condition\":\"equal\",\"country\":[\"US\"]}".to_string() + ] ) ); @@ -270,7 +273,10 @@ mod tests { item_2.body = Some("Hey! Hello World!!!".to_string()); assert_eq!( firewall.match_item(&item_2), - FirewallResult::new(FirewallPolicy::Allow, vec!["smtp_request_body".to_string()]) + FirewallResult::new( + FirewallPolicy::Allow, + vec!["{\"condition\":\"contains\",\"smtp_request_body\":[\"Hello\"]}".to_string()] + ) ); item_2.body = Some("Hey! World!!!".to_string()); diff --git a/src/firewall/items/http_request.rs b/src/firewall/items/http_request.rs index 4a69bff..6762f33 100644 --- a/src/firewall/items/http_request.rs +++ b/src/firewall/items/http_request.rs @@ -23,18 +23,18 @@ pub enum HttpRequestField { } impl HttpRequestField { - pub fn get_field_name(&self) -> &str { - match self { - HttpRequestField::HttpRequestUrl(_) => "http_request_url", - HttpRequestField::HttpRequestMethod(_) => "http_request_method", - HttpRequestField::HttpRequestQuery(_) => "http_request_query", - HttpRequestField::HttpRequestCookie(_) => "http_request_cookie", - HttpRequestField::HttpRequestHeader(_) => "http_request_header", - HttpRequestField::HttpRequestBody(_) => "http_request_body", - HttpRequestField::HttpRequestBodyLen(_) => "http_request_body_len", - HttpRequestField::HttpRequestUserAgent(_) => "http_request_user_agent", - } - } + // pub fn get_field_name(&self) -> &str { + // match self { + // HttpRequestField::HttpRequestUrl(_) => "http_request_url", + // HttpRequestField::HttpRequestMethod(_) => "http_request_method", + // HttpRequestField::HttpRequestQuery(_) => "http_request_query", + // HttpRequestField::HttpRequestCookie(_) => "http_request_cookie", + // HttpRequestField::HttpRequestHeader(_) => "http_request_header", + // HttpRequestField::HttpRequestBody(_) => "http_request_body", + // HttpRequestField::HttpRequestBodyLen(_) => "http_request_body_len", + // HttpRequestField::HttpRequestUserAgent(_) => "http_request_user_agent", + // } + // } fn get_compare_fields<'a>( &'a self, @@ -92,7 +92,7 @@ impl PredicateEvaluator for AppGuardHttpRequest { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.field.get_field_name() + serde_json::to_string(predicate).unwrap_or_default() } fn is_blacklisted(&self) -> bool { diff --git a/src/firewall/items/http_response.rs b/src/firewall/items/http_response.rs index 2283c95..e3ab662 100644 --- a/src/firewall/items/http_response.rs +++ b/src/firewall/items/http_response.rs @@ -18,13 +18,13 @@ pub enum HttpResponseField { } impl HttpResponseField { - pub fn get_field_name(&self) -> &str { - match self { - HttpResponseField::HttpResponseSize(_) => "http_response_size", - HttpResponseField::HttpResponseCode(_) => "http_response_code", - HttpResponseField::HttpResponseHeader(_) => "http_response_header", - } - } + // pub fn get_field_name(&self) -> &str { + // match self { + // HttpResponseField::HttpResponseSize(_) => "http_response_size", + // HttpResponseField::HttpResponseCode(_) => "http_response_code", + // HttpResponseField::HttpResponseHeader(_) => "http_response_header", + // } + // } fn get_compare_fields<'a>( &'a self, @@ -74,7 +74,7 @@ impl PredicateEvaluator for AppGuardHttpResponse { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.field.get_field_name() + serde_json::to_string(predicate).unwrap_or_default() } fn is_blacklisted(&self) -> bool { diff --git a/src/firewall/items/ip_info.rs b/src/firewall/items/ip_info.rs index 83491af..977165f 100644 --- a/src/firewall/items/ip_info.rs +++ b/src/firewall/items/ip_info.rs @@ -18,18 +18,18 @@ pub enum IpInfoField { } impl IpInfoField { - pub fn get_field_name(&self) -> &str { - match self { - IpInfoField::Country(_) => "country", - IpInfoField::Asn(_) => "asn", - IpInfoField::Org(_) => "org", - IpInfoField::Continent(_) => "continent", - IpInfoField::City(_) => "city", - IpInfoField::Region(_) => "region", - IpInfoField::Postal(_) => "postal", - IpInfoField::Timezone(_) => "timezone", - } - } + // pub fn get_field_name(&self) -> &str { + // match self { + // IpInfoField::Country(_) => "country", + // IpInfoField::Asn(_) => "asn", + // IpInfoField::Org(_) => "org", + // IpInfoField::Continent(_) => "continent", + // IpInfoField::City(_) => "city", + // IpInfoField::Region(_) => "region", + // IpInfoField::Postal(_) => "postal", + // IpInfoField::Timezone(_) => "timezone", + // } + // } fn get_compare_fields<'a>( &'a self, @@ -84,7 +84,7 @@ impl<'a> PredicateEvaluator for &'a AppGuardIpInfo { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.rule.field.get_field_name() + serde_json::to_string(predicate.rule).unwrap_or_default() } fn is_blacklisted(&self) -> bool { diff --git a/src/firewall/items/smtp_request.rs b/src/firewall/items/smtp_request.rs index d4b9357..6f27ad0 100644 --- a/src/firewall/items/smtp_request.rs +++ b/src/firewall/items/smtp_request.rs @@ -19,14 +19,14 @@ pub enum SmtpRequestField { } impl SmtpRequestField { - pub fn get_field_name(&self) -> &str { - match self { - SmtpRequestField::SmtpRequestHeader(_) => "smtp_request_header", - SmtpRequestField::SmtpRequestBody(_) => "smtp_request_body", - SmtpRequestField::SmtpRequestBodyLen(_) => "smtp_request_body_len", - SmtpRequestField::SmtpRequestUserAgent(_) => "smtp_request_user_agent", - } - } + // pub fn get_field_name(&self) -> &str { + // match self { + // SmtpRequestField::SmtpRequestHeader(_) => "smtp_request_header", + // SmtpRequestField::SmtpRequestBody(_) => "smtp_request_body", + // SmtpRequestField::SmtpRequestBodyLen(_) => "smtp_request_body_len", + // SmtpRequestField::SmtpRequestUserAgent(_) => "smtp_request_user_agent", + // } + // } fn get_compare_fields<'a>( &'a self, @@ -73,7 +73,7 @@ impl PredicateEvaluator for AppGuardSmtpRequest { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.field.get_field_name() + serde_json::to_string(predicate).unwrap_or_default() } fn is_blacklisted(&self) -> bool { diff --git a/src/firewall/items/smtp_response.rs b/src/firewall/items/smtp_response.rs index bccae2d..af11715 100644 --- a/src/firewall/items/smtp_response.rs +++ b/src/firewall/items/smtp_response.rs @@ -14,11 +14,11 @@ pub enum SmtpResponseField { } impl SmtpResponseField { - pub fn get_field_name(&self) -> &str { - match self { - SmtpResponseField::SmtpResponseCode(_) => "smtp_response_code", - } - } + // pub fn get_field_name(&self) -> &str { + // match self { + // SmtpResponseField::SmtpResponseCode(_) => "smtp_response_code", + // } + // } fn get_compare_fields<'a>( &'a self, @@ -56,7 +56,7 @@ impl PredicateEvaluator for AppGuardSmtpResponse { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.field.get_field_name() + serde_json::to_string(predicate).unwrap_or_default() } fn is_blacklisted(&self) -> bool { diff --git a/src/firewall/items/tcp_connection.rs b/src/firewall/items/tcp_connection.rs index 3cd0e74..6760d2d 100644 --- a/src/firewall/items/tcp_connection.rs +++ b/src/firewall/items/tcp_connection.rs @@ -17,15 +17,15 @@ pub enum TcpConnectionField { } impl TcpConnectionField { - pub fn get_field_name(&self) -> &str { - match self { - TcpConnectionField::SourceIp(_) => "source_ip", - TcpConnectionField::DestinationIp(_) => "destination_ip", - TcpConnectionField::SourcePort(_) => "source_port", - TcpConnectionField::DestinationPort(_) => "destination_port", - TcpConnectionField::Protocol(_) => "protocol", - } - } + // pub fn get_field_name(&self) -> &str { + // match self { + // TcpConnectionField::SourceIp(_) => "source_ip", + // TcpConnectionField::DestinationIp(_) => "destination_ip", + // TcpConnectionField::SourcePort(_) => "source_port", + // TcpConnectionField::DestinationPort(_) => "destination_port", + // TcpConnectionField::Protocol(_) => "protocol", + // } + // } fn get_compare_fields<'a>( &'a self, @@ -75,7 +75,7 @@ impl<'a> PredicateEvaluator for &'a AppGuardTcpConnection { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.rule.field.get_field_name() + serde_json::to_string(predicate.rule).unwrap_or_default() } fn get_remote_ip(&self) -> String { diff --git a/src/firewall/items/tcp_info.rs b/src/firewall/items/tcp_info.rs index 63deba0..ebf427b 100644 --- a/src/firewall/items/tcp_info.rs +++ b/src/firewall/items/tcp_info.rs @@ -24,7 +24,7 @@ impl<'a> PredicateEvaluator for &'a AppGuardTcpInfo { } fn get_reason(&self, predicate: &Self::Predicate) -> Self::Reason { - predicate.rule.field.get_field_name() + serde_json::to_string(predicate.rule).unwrap_or_default() } fn is_blacklisted(&self) -> bool { diff --git a/src/firewall/rules.rs b/src/firewall/rules.rs index 8647317..b949d62 100644 --- a/src/firewall/rules.rs +++ b/src/firewall/rules.rs @@ -50,19 +50,19 @@ pub enum FirewallRuleField { SmtpResponse(SmtpResponseField), } -impl FirewallRuleField { - pub fn get_field_name(&self) -> String { - match self { - FirewallRuleField::TcpConnection(f) => f.get_field_name(), - FirewallRuleField::IpInfo(f) => f.get_field_name(), - FirewallRuleField::HttpRequest(f) => f.get_field_name(), - FirewallRuleField::HttpResponse(f) => f.get_field_name(), - FirewallRuleField::SmtpRequest(f) => f.get_field_name(), - FirewallRuleField::SmtpResponse(f) => f.get_field_name(), - } - .to_string() - } -} +// impl FirewallRuleField { +// pub fn get_field_name(&self) -> String { +// match self { +// FirewallRuleField::TcpConnection(f) => f.get_field_name(), +// FirewallRuleField::IpInfo(f) => f.get_field_name(), +// FirewallRuleField::HttpRequest(f) => f.get_field_name(), +// FirewallRuleField::HttpResponse(f) => f.get_field_name(), +// FirewallRuleField::SmtpRequest(f) => f.get_field_name(), +// FirewallRuleField::SmtpResponse(f) => f.get_field_name(), +// } +// .to_string() +// } +// } #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[serde(rename_all = "snake_case")] From baf36f1cb7bbbf8a0b76a4888f89d8f317abcb3f Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 1 Aug 2025 11:33:12 +0200 Subject: [PATCH 5/6] add TODO --- src/app_guard_impl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app_guard_impl.rs b/src/app_guard_impl.rs index e8f8881..12731fc 100644 --- a/src/app_guard_impl.rs +++ b/src/app_guard_impl.rs @@ -225,6 +225,7 @@ impl AppGuardImpl { let logs = request.into_inner(); let (jwt_token, _) = authenticate(logs.token)?; + // TODO: call tx_store to store logs let _ = self .ctx .datastore From 25c1420cc2d894ab022b34e06c63d5eca19ac022 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 1 Aug 2025 11:39:10 +0200 Subject: [PATCH 6/6] improve reason message for blacklisted IPs --- src/firewall/firewall.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/firewall/firewall.rs b/src/firewall/firewall.rs index d681035..926020d 100644 --- a/src/firewall/firewall.rs +++ b/src/firewall/firewall.rs @@ -1,6 +1,7 @@ use rpn_predicate_interpreter::PredicateEvaluator; use serde::{Deserialize, Serialize}; +use crate::constants::BLACKLIST_LINK; use crate::firewall::infix_firewall::InfixFirewall; use crate::firewall::rules::{FirewallExpression, FirewallRule}; use crate::proto::appguard_commands::FirewallPolicy; @@ -47,7 +48,14 @@ impl Firewall { ) -> FirewallResult { // first let's check if this is blacklisted if item.is_blacklisted() { - return FirewallResult::new(FirewallPolicy::Deny, vec!["IP is blacklisted".to_string()]); + return FirewallResult::new( + FirewallPolicy::Deny, + vec![format!( + "IP {} is blacklisted by {}", + item.get_remote_ip(), + BLACKLIST_LINK.as_str() + )], + ); } // if not blacklisted, check the firewall expressions one by one for expr in &self.expressions {