From 7449b744eff645b271c767efbe296f5f11949987 Mon Sep 17 00:00:00 2001 From: Marine LM Date: Tue, 9 Dec 2025 20:00:24 +0100 Subject: [PATCH 1/9] feat(openaev): add openaev part --- config/default.yaml | 2 +- src/api/mod.rs | 22 +++- src/api/openaev/api_handler.rs | 99 ++++++++++++++ .../connector/get_connector_instances.rs | 18 +++ src/api/openaev/connector/mod.rs | 109 ++++++++++++++++ src/api/openaev/connector/patch_health.rs | 40 ++++++ src/api/openaev/connector/patch_status.rs | 67 ++++++++++ src/api/openaev/connector/post_logs.rs | 53 ++++++++ src/api/openaev/manager/get_version.rs | 7 + src/api/openaev/manager/mod.rs | 11 ++ src/api/openaev/manager/ping_alive.rs | 15 +++ src/api/openaev/manager/post_register.rs | 42 ++++++ src/api/openaev/mod.rs | 123 ++++++++++++++++++ src/api/openbas/mod.rs | 87 ------------- src/api/opencti/connector/mod.rs | 33 +---- src/config/settings.rs | 4 +- src/engine/mod.rs | 2 +- src/engine/{openbas.rs => openaev.rs} | 14 +- src/main.rs | 20 +-- 19 files changed, 623 insertions(+), 145 deletions(-) create mode 100644 src/api/openaev/api_handler.rs create mode 100644 src/api/openaev/connector/get_connector_instances.rs create mode 100644 src/api/openaev/connector/mod.rs create mode 100644 src/api/openaev/connector/patch_health.rs create mode 100644 src/api/openaev/connector/patch_status.rs create mode 100644 src/api/openaev/connector/post_logs.rs create mode 100644 src/api/openaev/manager/get_version.rs create mode 100644 src/api/openaev/manager/mod.rs create mode 100644 src/api/openaev/manager/ping_alive.rs create mode 100644 src/api/openaev/manager/post_register.rs create mode 100644 src/api/openaev/mod.rs delete mode 100644 src/api/openbas/mod.rs rename src/engine/{openbas.rs => openaev.rs} (58%) diff --git a/config/default.yaml b/config/default.yaml index 4b2633b..367b018 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -47,7 +47,7 @@ opencti: env_type: docker api_version: v1.41 -openbas: +openaev: enable: false url: http://host.docker.internal:4000 token: ChangeMe diff --git a/src/api/mod.rs b/src/api/mod.rs index 20c46ec..9aca900 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -7,8 +7,9 @@ use std::str::FromStr; use std::time::Duration; use tracing::info; -pub mod openbas; +pub mod openaev; pub mod opencti; +mod decrypt_value; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ContractsManifest { @@ -99,11 +100,20 @@ impl ApiConnector { is_sensitive: config.is_sensitive, }) .collect::>(); - envs.push(EnvVariable { - key: "OPENCTI_URL".into(), - value: settings.opencti.url.clone(), - is_sensitive: false, - }); + if settings.opencti.enable { + envs.push(EnvVariable { + key: "OPENCTI_URL".into(), + value: settings.opencti.url.clone(), + is_sensitive: false, + }); + } + if settings.openaev.enable { + envs.push(EnvVariable { + key: "OPENAEV_URL".into(), + value: settings.openaev.url.clone(), + is_sensitive: false, + }); + } envs.push(EnvVariable { key: "OPENCTI_CONFIG_HASH".into(), value: self.contract_hash.clone(), diff --git a/src/api/openaev/api_handler.rs b/src/api/openaev/api_handler.rs new file mode 100644 index 0000000..d118b71 --- /dev/null +++ b/src/api/openaev/api_handler.rs @@ -0,0 +1,99 @@ +use serde::de::DeserializeOwned; +use tracing::error; + +pub async fn handle_api_response( + response: Result, + operation_name: &str, +) -> Option +where + T: DeserializeOwned +{ + match response { + Ok(resp) if resp.status().is_success() => { + match resp.json::().await { + Ok(data) => Some(data), + Err(err) => { + error!("Failed to parse JSON for {}: {}", operation_name, err); + None + } + } + } + Ok(resp) => { + error!( + status = resp.status().as_u16(), + "Failed to {}: non-success status code", operation_name + ); + None + } + Err(err) => { + error!( + error = err.to_string(), + "Failed to {}, check your configuration", operation_name + ); + None + } + } +} + +pub async fn handle_api_text_response( + response: Result, + operation_name: &str, +) -> Option { + match response { + Ok(resp) if resp.status().is_success() => { + resp.text().await.ok().or_else(|| { + error!("Failed to read response body for {}", operation_name); + None + }) + } + Ok(resp) => { + error!( + status = resp.status().as_u16(), + "Failed to {}: non-success status code", operation_name + ); + None + } + Err(err) => { + error!( + error = err.to_string(), + "Failed to {}, check your configuration", operation_name + ); + None + } + } +} + +// pub async fn handle_api_response( +// response: Result, +// operation_name: &str, +// unsupported_parse_message: &str, +// )-> Option where T: DeserializeOwned { +// match response { +// Ok(resp) if resp.status().is_success() => { +// match resp.json::().await { +// Ok(data) => Some(data), +// Err(err) => { +// error!( +// error = err.to_string(), +// "Failed to parse response for {}", unsupported_parse_message +// ); +// None +// } +// } +// } +// Ok(resp) => { +// error!( +// status = resp.status().as_u16(), +// "Failed to {}: non-success status code", operation_name +// ); +// None +// } +// Err(err) => { +// error!( +// error = err.to_string(), +// "Failed to {}, check your configuration", operation_name +// ); +// None +// } +// } +// } diff --git a/src/api/openaev/connector/get_connector_instances.rs b/src/api/openaev/connector/get_connector_instances.rs new file mode 100644 index 0000000..f3e462e --- /dev/null +++ b/src/api/openaev/connector/get_connector_instances.rs @@ -0,0 +1,18 @@ +use crate::api::ApiConnector; +use crate::api::openaev::api_handler::handle_api_response; +use crate::api::openaev::connector::ConnectorInstances; + +pub async fn get_connector_instances(api: &crate::api::openaev::ApiOpenAEV) -> Option> { + let settings = crate::settings(); + let get_connectors = api.get(&format!("/xtm-composer/{}/connector-instances", settings.clone().manager.id)) + .send() + .await; + + handle_api_response::>(get_connectors, "fetch connector instances") + .await.map(|connectors| { + connectors + .into_iter() + .map(|connector| connector.to_api_connector(&api.private_key)) + .collect() + }) +} \ No newline at end of file diff --git a/src/api/openaev/connector/mod.rs b/src/api/openaev/connector/mod.rs new file mode 100644 index 0000000..9e27bd6 --- /dev/null +++ b/src/api/openaev/connector/mod.rs @@ -0,0 +1,109 @@ +use rsa::{RsaPrivateKey}; +use serde::Deserialize; +use tracing::warn; +use crate::api::{ApiConnector, ApiContractConfig}; +use crate::api::decrypt_value::parse_aes_encrypted_value; + +pub mod get_connector_instances; +pub mod patch_health; +pub mod patch_status; +pub mod post_logs; + +#[derive(Debug, Deserialize)] +pub struct ConnectorContractConfiguration { + pub configuration_key: String, + pub configuration_value: Option, + pub configuration_is_encrypted: bool, +} + +#[derive(Debug, Deserialize)] +pub struct ConnectorInstances { + pub connector_instance_id: cynic::Id, + pub connector_instance_name: String, + pub connector_instance_hash: Option, + pub connector_image: Option, + pub connector_instance_current_status: Option, + pub connector_instance_requested_status: Option, + pub connector_instance_configuration: Option>, +} + +impl ConnectorInstances { + + // pub fn parse_aes_encrypted_value(&self, private_key: &RsaPrivateKey, encrypted_value: String) -> Result> { + // let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; + // + // let version = encrypted_bytes[0]; + // if version != 1 { + // warn!(version, "Encryption version not handled"); + // Ok(String::from("")) + // } else { + // let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; + // let aes_key_iv_decrypted_bytes = private_key.decrypt(Pkcs1v15Encrypt, &aes_key_iv_encrypted_bytes)?; + // let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; + // let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; + // let encrypted_value_bytes = &encrypted_bytes[513..]; + // + // let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; + // let nonce = Nonce::from_slice(&aes_iv_bytes); + // let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); + // match plaintext_result { + // Ok(plaintext) => { + // let decoded_value = str::from_utf8(&plaintext).unwrap().to_string(); + // Ok(decoded_value) + // }, + // Err(e) => { + // warn!(error = e.to_string(), "Fail to decode value"); + // Ok(String::from("")) + // } + // } + // } + // } + + pub fn to_api_connector(&self, private_key: &RsaPrivateKey )->ApiConnector { + let contract_configuration = self + .connector_instance_configuration + .as_ref() + .unwrap() + .into_iter() + .map(|c| { + let is_sensitive = c.configuration_is_encrypted.clone(); + if is_sensitive { + let encrypted_value = c.configuration_value.clone().unwrap_or_default(); + let decoded_value_result = parse_aes_encrypted_value(private_key, encrypted_value); + println!("Configuration key: {}", c.configuration_key.clone()); + println!("Decoded value result: {:?}", decoded_value_result); + match decoded_value_result { + Ok(decoded_value) => ApiContractConfig { + key: c.configuration_key.clone(), + value: decoded_value, + is_sensitive: true, + }, + Err(e) => { + warn!(error = e.to_string(), "Fail to decode value"); + ApiContractConfig { + key: c.configuration_key.clone(), + value: String::from(""), + is_sensitive: true, + } + } + } + } else { + ApiContractConfig { + key: c.configuration_key.clone(), + value: c.configuration_value.clone().unwrap_or_default(), + is_sensitive: false, + } + } + }) + .collect(); + ApiConnector { + id: self.connector_instance_id.clone().into_inner(), + name: self.connector_instance_name.clone(), + image: self.connector_image.clone().unwrap(), + contract_hash: self.connector_instance_hash.clone().unwrap(), + current_status: self.connector_instance_current_status.clone(), + requested_status: self.connector_instance_requested_status.clone().unwrap(), + contract_configuration, + } + } +} \ No newline at end of file diff --git a/src/api/openaev/connector/patch_health.rs b/src/api/openaev/connector/patch_health.rs new file mode 100644 index 0000000..af0856f --- /dev/null +++ b/src/api/openaev/connector/patch_health.rs @@ -0,0 +1,40 @@ +use serde::Serialize; +use tracing::{error, info}; +use crate::api::openaev::api_handler::handle_api_response; +use crate::api::openaev::ApiOpenAEV; +use crate::api::openaev::connector::ConnectorInstances; + +#[derive(Serialize)] +struct ConnectorInstanceHealthInput { + connector_instance_restart_count: u32, + connector_instance_started_at: String, + connector_instance_is_in_reboot_loop: bool +} + +pub async fn update_health( + id: String, + restart_count: u32, + started_at: String, + is_in_reboot_loop: bool, + api: &ApiOpenAEV, +)-> Option { + let settings = crate::settings(); + let health_check_input = ConnectorInstanceHealthInput { + connector_instance_restart_count: restart_count, + connector_instance_started_at: started_at, + connector_instance_is_in_reboot_loop: is_in_reboot_loop + }; + + let health_check_response = api.put(&format!("/xtm-composer/{}/connector-instances/{}/health-check", settings.clone().manager.id, id.clone())) + .json(&health_check_input) + .send() + .await; + + // Discard the result + let _ = handle_api_response::( + health_check_response, + "push health metrics" + ).await; + + Some(cynic::Id::new(id)) +} \ No newline at end of file diff --git a/src/api/openaev/connector/patch_status.rs b/src/api/openaev/connector/patch_status.rs new file mode 100644 index 0000000..24a1fe1 --- /dev/null +++ b/src/api/openaev/connector/patch_status.rs @@ -0,0 +1,67 @@ +use serde::Serialize; +use tracing::{error, info}; +use crate::api::{ApiConnector, ConnectorStatus}; +use crate::api::openaev::api_handler::handle_api_response; +use crate::api::openaev::ApiOpenAEV; +use crate::api::openaev::connector::ConnectorInstances; +use crate::api::opencti::connector::post_status::ConnectorCurrentStatus; + +#[derive(Serialize)] +struct UpdateConnectorInstanceStatusInput { + connector_instance_current_status: ConnectorCurrentStatus, +} + +pub async fn update_status(id: String, status: ConnectorStatus, api: &ApiOpenAEV) -> Option { + let update_status = match status { + ConnectorStatus::Started => ConnectorCurrentStatus::Started, + _ => ConnectorCurrentStatus::Stopped, + }; + + let status_input = UpdateConnectorInstanceStatusInput { + connector_instance_current_status: update_status + }; + + let settings = crate::settings(); + let update_status_response = api.put(&format!("/xtm-composer/{}/connector-instances/{}/status", settings.clone().manager.id, id)) + .json(&status_input) + .send() + .await; + + handle_api_response::(update_status_response, "patch connector instance status") + .await + .map(|connector| connector.to_api_connector(&api.private_key)) +} + +// match update_status_response { +// Ok(response) => { +// if response.status().is_success() { +// match response.json::().await { +// Ok(connector) => { +// let instance = connector.to_api_connector(&api.private_key); +// info!("Connector instance updated successfully: {:?}", instance); +// Some(instance) +// } +// Err(err) => { +// error!( +// error = err.to_string(), +// "Failed to parse connector instance response" +// ); +// None +// } +// } +// } else { +// error!( +// status = response.status().as_u16(), +// "Failed to fetch patch status" +// ); +// None +// } +// } +// Err(err) => { +// error!( +// error = err.to_string(), +// "Fail to patch status" +// ); +// None +// } +// } \ No newline at end of file diff --git a/src/api/openaev/connector/post_logs.rs b/src/api/openaev/connector/post_logs.rs new file mode 100644 index 0000000..9eea33f --- /dev/null +++ b/src/api/openaev/connector/post_logs.rs @@ -0,0 +1,53 @@ +use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::JSON; +use serde::Serialize; +use serde_json::json; +use tracing::{error, info}; +use crate::api::openaev::api_handler::handle_api_response; +use crate::api::openaev::ApiOpenAEV; +use crate::api::openaev::connector::ConnectorInstances; + +#[derive(Serialize)] +struct InstanceConnectorLogsInput { + connector_instance_logs: Vec, +} + +pub async fn add_logs(id: String, logs: Vec, api: &ApiOpenAEV)-> Option { + let logs_input = InstanceConnectorLogsInput{ + connector_instance_logs: logs + }; + let settings = crate::settings(); + let add_logs_response = api.post(&format!("/xtm-composer/{}/connector-instances/{}/logs",settings.clone().manager.id, id)) + .json(&logs_input) + .send() + .await; + + // Discard the result + let _ = handle_api_response::( + add_logs_response, + "push logs for connector instance" + ).await; + + Some(cynic::Id::new(id)) + // match add_logs_response { + // Ok(response) => { + // if response.status().is_success() { + // info!("Successfully pushed logs for connector instance: {}", id); + // Some(cynic::Id::new(id)) + // } else { + // let status = response.status(); + // let body = response.text().await.unwrap_or_default(); + // error!( + // status = status.as_u16(), + // body = body, + // "Failed to push logs for connector instance: {}", + // id + // ); + // None + // } + // } + // Err(e) => { + // error!(error = e.to_string(), "Failed to send request to push logs"); + // None + // } + // } +} \ No newline at end of file diff --git a/src/api/openaev/manager/get_version.rs b/src/api/openaev/manager/get_version.rs new file mode 100644 index 0000000..a51e9bb --- /dev/null +++ b/src/api/openaev/manager/get_version.rs @@ -0,0 +1,7 @@ +use crate::api::openaev::api_handler::{handle_api_text_response}; +use crate::api::openaev::ApiOpenAEV; + +pub async fn get_version(api: &ApiOpenAEV) -> Option { + let response = api.get("/settings/version").send().await; + handle_api_text_response(response, "fetch version").await +} \ No newline at end of file diff --git a/src/api/openaev/manager/mod.rs b/src/api/openaev/manager/mod.rs new file mode 100644 index 0000000..d960bbb --- /dev/null +++ b/src/api/openaev/manager/mod.rs @@ -0,0 +1,11 @@ +pub mod get_version; +pub mod post_register; +pub mod ping_alive; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct ConnectorManager { + pub xtm_composer_id: cynic::Id, + pub xtm_composer_version: String, +} \ No newline at end of file diff --git a/src/api/openaev/manager/ping_alive.rs b/src/api/openaev/manager/ping_alive.rs new file mode 100644 index 0000000..44712ae --- /dev/null +++ b/src/api/openaev/manager/ping_alive.rs @@ -0,0 +1,15 @@ +use crate::api::openaev::api_handler::{handle_api_response}; +use crate::api::openaev::ApiOpenAEV; +use crate::api::openaev::manager::ConnectorManager; + +pub async fn ping_alive(api: &ApiOpenAEV) -> Option { + let settings = crate::settings(); + let response = api.put("/xtm-composer/refresh-connectivity") + .body(settings.manager.id.clone()) + .send() + .await; + + handle_api_response::(response, "ping OpenAEV backend") + .await + .map(|manager| manager.xtm_composer_version) +} \ No newline at end of file diff --git a/src/api/openaev/manager/post_register.rs b/src/api/openaev/manager/post_register.rs new file mode 100644 index 0000000..8b452ab --- /dev/null +++ b/src/api/openaev/manager/post_register.rs @@ -0,0 +1,42 @@ +use rsa::{RsaPublicKey}; +use rsa::pkcs1::LineEnding; +use rsa::pkcs8::EncodePublicKey; +use serde::Serialize; +use tracing::{error, info}; +use crate::api::openaev::api_handler::handle_api_response; +use crate::api::openaev::ApiOpenAEV; +use crate::api::openaev::manager::ConnectorManager; + +#[derive(Serialize)] +struct RegisterInput { + id: String, + name: String, + public_key: String, +} + +pub async fn register(api: &ApiOpenAEV) { + let settings = crate::settings(); + let priv_key = crate::private_key(); + let pub_key = RsaPublicKey::from(priv_key); + let public_key: String = pub_key + .to_public_key_pem(LineEnding::LF) + .expect("Failed to encode public key as PKCS#8"); + + let register_input = RegisterInput { + id: settings.clone().manager.id, + name: settings.manager.name.clone(), + public_key: public_key.clone(), + }; + + let register_response = api.post("/xtm-composer/register") + .json(®ister_input) + .send() + .await; + + // Discard the result + let _ = handle_api_response::( + register_response, + "register into OpenAEV backend" + ).await; +} + diff --git a/src/api/openaev/mod.rs b/src/api/openaev/mod.rs new file mode 100644 index 0000000..159c0f1 --- /dev/null +++ b/src/api/openaev/mod.rs @@ -0,0 +1,123 @@ +mod connector; +mod manager; +mod api_handler; + +use crate::api::{ApiConnector, ComposerApi, ConnectorStatus}; +use crate::config::settings::Daemon; +use async_trait::async_trait; +use std::time::Duration; +use rsa::RsaPrivateKey; + +const BEARER: &str = "Bearer"; +const AUTHORIZATION_HEADER: &str = "Authorization"; + +pub struct ApiOpenAEV { + api_uri: String, + http_client: reqwest::Client, + bearer: String, + daemon: Daemon, + logs_schedule: u64, + private_key: RsaPrivateKey, + // TODO: Implement timeout configuration when OpenBAS API methods are implemented + // These fields are stored for future use when the todo!() macros are replaced with actual implementations + #[allow(dead_code)] + request_timeout: u64, + #[allow(dead_code)] + connect_timeout: u64, +} + +impl ApiOpenAEV { + pub fn new() -> Self { + let settings = crate::settings(); + let bearer = format!("{} {}", BEARER, settings.openaev.token); + let api_uri = format!("{}/api", &settings.openaev.url); + let daemon = settings.openaev.daemon.clone(); + let logs_schedule = settings.openaev.logs_schedule; + let request_timeout = settings.openaev.request_timeout; + let connect_timeout = settings.openaev.connect_timeout; + + let http_client = reqwest::Client::builder() + .connect_timeout(Duration::from_secs(2)) + .timeout(Duration::from_secs(5)) + .build() + .unwrap(); // or handle the error appropriately + + let private_key = crate::private_key().clone(); + + Self { + api_uri, + http_client, + bearer, + daemon, + logs_schedule, + private_key, + request_timeout, + connect_timeout, + } + } + + pub fn post(&self, route: &str) -> reqwest::RequestBuilder { + let api_route = format!("{}{}", self.api_uri.clone(), route); + + self.http_client + .post(&api_route) + .header("Content-Type", "application/json") + .header(AUTHORIZATION_HEADER, self.bearer.as_str()) + } + + pub fn put(&self, route: &str) -> reqwest::RequestBuilder { + let api_route = format!("{}{}", self.api_uri.clone(), route); + + self.http_client + .put(&api_route) + .header(AUTHORIZATION_HEADER, self.bearer.as_str()) + } + + pub fn get(&self, route: &str) -> reqwest::RequestBuilder { + let api_route = format!("{}{}", self.api_uri.clone(), route); + + self.http_client + .get(&api_route) + .header(AUTHORIZATION_HEADER, self.bearer.as_str()) + } + +} + +#[async_trait] +impl ComposerApi for ApiOpenAEV { + fn daemon(&self) -> &Daemon { + &self.daemon + } + + fn post_logs_schedule(&self) -> Duration { + Duration::from_secs(self.logs_schedule) + } + + async fn version(&self) -> Option { + manager::get_version::get_version(self).await + } + + async fn ping_alive(&self) -> Option { + manager::ping_alive::ping_alive(self).await + } + + async fn register(&self) { + manager::post_register::register(self).await + } + + async fn connectors(&self) -> Option> { + connector::get_connector_instances::get_connector_instances(self).await + } + + async fn patch_status(&self, id: String, status: ConnectorStatus) -> Option { + connector::patch_status::update_status(id, status, self).await + } + + async fn patch_logs(&self, id: String, logs: Vec) -> Option { + connector::post_logs::add_logs(id, logs, self).await + } + + async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { + connector::patch_health::update_health(id, restart_count, started_at, is_in_reboot_loop, self).await + } +} diff --git a/src/api/openbas/mod.rs b/src/api/openbas/mod.rs deleted file mode 100644 index ed297d7..0000000 --- a/src/api/openbas/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -// TODO Remove macro after implementation -#![allow(unused_variables)] - -use crate::api::{ApiConnector, ComposerApi, ConnectorStatus}; -use crate::config::settings::Daemon; -use async_trait::async_trait; -use std::time::Duration; -use tracing::debug; - -const BEARER: &str = "Bearer"; - -pub struct ApiOpenBAS { - api_uri: String, - bearer: String, - daemon: Daemon, - logs_schedule: u64, - // TODO: Implement timeout configuration when OpenBAS API methods are implemented - // These fields are stored for future use when the todo!() macros are replaced with actual implementations - #[allow(dead_code)] - request_timeout: u64, - #[allow(dead_code)] - connect_timeout: u64, -} - -impl ApiOpenBAS { - pub fn new() -> Self { - let settings = crate::settings(); - let bearer = format!("{} {}", BEARER, settings.openbas.token); - let api_uri = format!("{}/api", &settings.openbas.url); - let daemon = settings.openbas.daemon.clone(); - let logs_schedule = settings.openbas.logs_schedule; - let request_timeout = settings.openbas.request_timeout; - let connect_timeout = settings.openbas.connect_timeout; - Self { - api_uri, - bearer, - daemon, - logs_schedule, - request_timeout, - connect_timeout, - } - } -} - -#[async_trait] -impl ComposerApi for ApiOpenBAS { - fn daemon(&self) -> &Daemon { - &self.daemon - } - - fn post_logs_schedule(&self) -> Duration { - Duration::from_secs(self.logs_schedule) - } - - async fn version(&self) -> Option { - todo!() - } - - async fn ping_alive(&self) -> Option { - todo!() - } - - async fn register(&self) { - debug!( - api_uri = self.api_uri, - bearer = self.bearer, - "OpenBAS register" - ); - todo!() - } - - async fn connectors(&self) -> Option> { - todo!() - } - - async fn patch_status(&self, id: String, status: ConnectorStatus) -> Option { - todo!() - } - - async fn patch_logs(&self, id: String, logs: Vec) -> Option { - todo!() - } - - async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { - todo!() - } -} diff --git a/src/api/opencti/connector/mod.rs b/src/api/opencti/connector/mod.rs index cd7a3a3..09d7af4 100644 --- a/src/api/opencti/connector/mod.rs +++ b/src/api/opencti/connector/mod.rs @@ -16,6 +16,7 @@ use aes_gcm::{ aead::{Aead, KeyInit}, Aes256Gcm, Nonce }; +use crate::api::decrypt_value::parse_aes_encrypted_value; #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] pub struct ConnectorContractConfiguration { @@ -42,36 +43,6 @@ pub struct ManagedConnector { impl ManagedConnector { - pub fn parse_aes_encrypted_value(&self, private_key: &RsaPrivateKey, encrypted_value: String) -> Result> { - let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; - - let version = encrypted_bytes[0]; - if version != 1 { - warn!(version, "Encryption version not handled"); - Ok(String::from("")) - } else { - let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; - let aes_key_iv_decrypted_bytes = private_key.decrypt(Pkcs1v15Encrypt, &aes_key_iv_encrypted_bytes)?; - let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; - let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; - let encrypted_value_bytes = &encrypted_bytes[513..]; - - let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; - let nonce = Nonce::from_slice(&aes_iv_bytes); - let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); - match plaintext_result { - Ok(plaintext) => { - let decoded_value = str::from_utf8(&plaintext).unwrap().to_string(); - Ok(decoded_value) - }, - Err(e) => { - warn!(error = e.to_string(), "Fail to decode value"); - Ok(String::from("")) - } - } - } - } - pub fn to_api_connector(&self, private_key: &RsaPrivateKey) -> ApiConnector { let contract_configuration = self .manager_contract_configuration @@ -82,7 +53,7 @@ impl ManagedConnector { let is_sensitive = c.encrypted.unwrap_or_default(); if is_sensitive { let encrypted_value = c.value.unwrap_or_default(); - let decoded_value_result = self.parse_aes_encrypted_value(private_key, encrypted_value); + let decoded_value_result = parse_aes_encrypted_value(private_key, encrypted_value); match decoded_value_result { Ok(decoded_value) => ApiContractConfig { key: c.key, diff --git a/src/config/settings.rs b/src/config/settings.rs index 65f8191..687fd43 100644 --- a/src/config/settings.rs +++ b/src/config/settings.rs @@ -66,7 +66,7 @@ pub struct OpenCTI { #[derive(Debug, Deserialize, Clone)] #[allow(unused)] -pub struct OpenBAS { +pub struct OpenAEV { pub enable: bool, pub url: String, pub token: String, @@ -124,7 +124,7 @@ pub struct Docker { pub struct Settings { pub manager: Manager, pub opencti: OpenCTI, - pub openbas: OpenBAS, + pub openaev: OpenAEV, } impl Settings { diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 5a563c9..0f39049 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,4 +1,4 @@ -pub mod openbas; +pub mod openaev; pub mod opencti; use crate::api::ComposerApi; diff --git a/src/engine/openbas.rs b/src/engine/openaev.rs similarity index 58% rename from src/engine/openbas.rs rename to src/engine/openaev.rs index a7a4a9c..4eadda6 100644 --- a/src/engine/openbas.rs +++ b/src/engine/openaev.rs @@ -1,21 +1,21 @@ use tokio::task::JoinHandle; use tracing::info; use crate::api::ComposerApi; -use crate::api::openbas::ApiOpenBAS; +use crate::api::openaev::ApiOpenAEV; use crate::engine::{alive, orchestration}; -pub fn openbas_orchestration() -> JoinHandle<()> { - info!("Starting OpenBAS connectors orchestration"); +pub fn openaev_orchestration() -> JoinHandle<()> { + info!("Starting OpenAEV connectors orchestration"); tokio::spawn(async move { - let api: Box = Box::new(ApiOpenBAS::new()); + let api: Box = Box::new(ApiOpenAEV::new()); orchestration(api).await; }) } -pub fn openbas_alive() -> JoinHandle<()> { - info!("Starting OpenBAS Composer ping alive"); +pub fn openaev_alive() -> JoinHandle<()> { + info!("Starting OpenAEV Composer ping alive"); tokio::spawn(async move { - let api: Box = Box::new(ApiOpenBAS::new()); + let api: Box = Box::new(ApiOpenAEV::new()); alive(api).await; }) } diff --git a/src/main.rs b/src/main.rs index d280caf..56f5e4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod orchestrator; mod system; use crate::config::settings::Settings; -use crate::engine::openbas::{openbas_alive, openbas_orchestration}; +use crate::engine::openaev::{openaev_alive, openaev_orchestration}; use crate::engine::opencti::{opencti_alive, opencti_orchestration}; use futures::future::join_all; use rolling_file::{BasicRollingFileAppender, RollingConditionBasic}; @@ -153,16 +153,16 @@ fn opencti_orchestrate(orchestrations: &mut Vec>) { } } -// Init openbas -fn openbas_orchestrate(orchestrations: &mut Vec>) { +// Init openaev +fn openaev_orchestrate(orchestrations: &mut Vec>) { let setting = settings(); - if setting.openbas.enable { - let openbas_alive = openbas_alive(); - orchestrations.push(openbas_alive); - let openbas_orchestration = openbas_orchestration(); - orchestrations.push(openbas_orchestration); + if setting.openaev.enable { + let openaev_alive = openaev_alive(); + orchestrations.push(openaev_alive); + let openaev_orchestration = openaev_orchestration(); + orchestrations.push(openaev_orchestration); } else { - info!("OpenBAS connectors orchestration disabled"); + info!("OpenAEV connectors orchestration disabled"); } } @@ -176,7 +176,7 @@ async fn main() { // Start orchestration threads let mut orchestrations = Vec::new(); opencti_orchestrate(&mut orchestrations); - openbas_orchestrate(&mut orchestrations); + openaev_orchestrate(&mut orchestrations); // Wait for threads to terminate join_all(orchestrations).await; } From d212c4ff9f1249272d0748ee92ac942edbc19f45 Mon Sep 17 00:00:00 2001 From: Marine LM Date: Wed, 10 Dec 2025 15:33:32 +0100 Subject: [PATCH 2/9] feat(openaev): remove useless comments --- src/api/openaev/api_handler.rs | 35 ---------------------- src/api/openaev/connector/mod.rs | 30 ------------------- src/api/openaev/connector/patch_status.rs | 36 +---------------------- src/api/openaev/connector/post_logs.rs | 22 -------------- 4 files changed, 1 insertion(+), 122 deletions(-) diff --git a/src/api/openaev/api_handler.rs b/src/api/openaev/api_handler.rs index d118b71..073e222 100644 --- a/src/api/openaev/api_handler.rs +++ b/src/api/openaev/api_handler.rs @@ -62,38 +62,3 @@ pub async fn handle_api_text_response( } } } - -// pub async fn handle_api_response( -// response: Result, -// operation_name: &str, -// unsupported_parse_message: &str, -// )-> Option where T: DeserializeOwned { -// match response { -// Ok(resp) if resp.status().is_success() => { -// match resp.json::().await { -// Ok(data) => Some(data), -// Err(err) => { -// error!( -// error = err.to_string(), -// "Failed to parse response for {}", unsupported_parse_message -// ); -// None -// } -// } -// } -// Ok(resp) => { -// error!( -// status = resp.status().as_u16(), -// "Failed to {}: non-success status code", operation_name -// ); -// None -// } -// Err(err) => { -// error!( -// error = err.to_string(), -// "Failed to {}, check your configuration", operation_name -// ); -// None -// } -// } -// } diff --git a/src/api/openaev/connector/mod.rs b/src/api/openaev/connector/mod.rs index 9e27bd6..628260a 100644 --- a/src/api/openaev/connector/mod.rs +++ b/src/api/openaev/connector/mod.rs @@ -29,36 +29,6 @@ pub struct ConnectorInstances { impl ConnectorInstances { - // pub fn parse_aes_encrypted_value(&self, private_key: &RsaPrivateKey, encrypted_value: String) -> Result> { - // let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; - // - // let version = encrypted_bytes[0]; - // if version != 1 { - // warn!(version, "Encryption version not handled"); - // Ok(String::from("")) - // } else { - // let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; - // let aes_key_iv_decrypted_bytes = private_key.decrypt(Pkcs1v15Encrypt, &aes_key_iv_encrypted_bytes)?; - // let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; - // let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; - // let encrypted_value_bytes = &encrypted_bytes[513..]; - // - // let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; - // let nonce = Nonce::from_slice(&aes_iv_bytes); - // let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); - // match plaintext_result { - // Ok(plaintext) => { - // let decoded_value = str::from_utf8(&plaintext).unwrap().to_string(); - // Ok(decoded_value) - // }, - // Err(e) => { - // warn!(error = e.to_string(), "Fail to decode value"); - // Ok(String::from("")) - // } - // } - // } - // } - pub fn to_api_connector(&self, private_key: &RsaPrivateKey )->ApiConnector { let contract_configuration = self .connector_instance_configuration diff --git a/src/api/openaev/connector/patch_status.rs b/src/api/openaev/connector/patch_status.rs index 24a1fe1..074f583 100644 --- a/src/api/openaev/connector/patch_status.rs +++ b/src/api/openaev/connector/patch_status.rs @@ -30,38 +30,4 @@ pub async fn update_status(id: String, status: ConnectorStatus, api: &ApiOpenAEV handle_api_response::(update_status_response, "patch connector instance status") .await .map(|connector| connector.to_api_connector(&api.private_key)) -} - -// match update_status_response { -// Ok(response) => { -// if response.status().is_success() { -// match response.json::().await { -// Ok(connector) => { -// let instance = connector.to_api_connector(&api.private_key); -// info!("Connector instance updated successfully: {:?}", instance); -// Some(instance) -// } -// Err(err) => { -// error!( -// error = err.to_string(), -// "Failed to parse connector instance response" -// ); -// None -// } -// } -// } else { -// error!( -// status = response.status().as_u16(), -// "Failed to fetch patch status" -// ); -// None -// } -// } -// Err(err) => { -// error!( -// error = err.to_string(), -// "Fail to patch status" -// ); -// None -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/api/openaev/connector/post_logs.rs b/src/api/openaev/connector/post_logs.rs index 9eea33f..2f479ce 100644 --- a/src/api/openaev/connector/post_logs.rs +++ b/src/api/openaev/connector/post_logs.rs @@ -28,26 +28,4 @@ pub async fn add_logs(id: String, logs: Vec, api: &ApiOpenAEV)-> Option< ).await; Some(cynic::Id::new(id)) - // match add_logs_response { - // Ok(response) => { - // if response.status().is_success() { - // info!("Successfully pushed logs for connector instance: {}", id); - // Some(cynic::Id::new(id)) - // } else { - // let status = response.status(); - // let body = response.text().await.unwrap_or_default(); - // error!( - // status = status.as_u16(), - // body = body, - // "Failed to push logs for connector instance: {}", - // id - // ); - // None - // } - // } - // Err(e) => { - // error!(error = e.to_string(), "Failed to send request to push logs"); - // None - // } - // } } \ No newline at end of file From 750bd1170d0ba9f82cdfbb79dfe03cce9b10b9c4 Mon Sep 17 00:00:00 2001 From: Marine LM Date: Fri, 12 Dec 2025 16:29:47 +0100 Subject: [PATCH 3/9] feat(openaev): add file + remove useless import --- src/api/decrypt_value.rs | 40 +++++++++++++++++++++++ src/api/openaev/connector/patch_health.rs | 1 - src/api/openaev/connector/patch_status.rs | 1 - src/api/openaev/connector/post_logs.rs | 3 -- src/api/openaev/manager/ping_alive.rs | 3 +- src/api/openaev/manager/post_register.rs | 1 - src/api/opencti/connector/mod.rs | 7 +--- 7 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 src/api/decrypt_value.rs diff --git a/src/api/decrypt_value.rs b/src/api/decrypt_value.rs new file mode 100644 index 0000000..9b24103 --- /dev/null +++ b/src/api/decrypt_value.rs @@ -0,0 +1,40 @@ +use base64::{engine::general_purpose, Engine as _}; +use aes_gcm::{ + aead::{Aead, KeyInit}, + Aes256Gcm, Nonce +}; +use rsa::{Pkcs1v15Encrypt, RsaPrivateKey}; +use tracing::warn; + +pub fn parse_aes_encrypted_value( + private_key: &RsaPrivateKey, + encrypted_value: String +) -> Result> { + let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; + + let version = encrypted_bytes[0]; + if version != 1 { + warn!(version, "Encryption version not handled"); + Ok(String::from("")) + } else { + let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; + let aes_key_iv_decrypted_bytes = private_key.decrypt(Pkcs1v15Encrypt, &aes_key_iv_encrypted_bytes)?; + let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; + let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; + let encrypted_value_bytes = &encrypted_bytes[513..]; + + let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; + let nonce = Nonce::from_slice(&aes_iv_bytes); + let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); + match plaintext_result { + Ok(plaintext) => { + let decoded_value = str::from_utf8(&plaintext)?.to_string(); // Fixed: use ? instead of unwrap + Ok(decoded_value) + }, + Err(e) => { + warn!(error = e.to_string(), "Fail to decode value"); + Ok(String::from("")) + } + } + } +} \ No newline at end of file diff --git a/src/api/openaev/connector/patch_health.rs b/src/api/openaev/connector/patch_health.rs index af0856f..d080d07 100644 --- a/src/api/openaev/connector/patch_health.rs +++ b/src/api/openaev/connector/patch_health.rs @@ -1,5 +1,4 @@ use serde::Serialize; -use tracing::{error, info}; use crate::api::openaev::api_handler::handle_api_response; use crate::api::openaev::ApiOpenAEV; use crate::api::openaev::connector::ConnectorInstances; diff --git a/src/api/openaev/connector/patch_status.rs b/src/api/openaev/connector/patch_status.rs index 074f583..3e785a7 100644 --- a/src/api/openaev/connector/patch_status.rs +++ b/src/api/openaev/connector/patch_status.rs @@ -1,5 +1,4 @@ use serde::Serialize; -use tracing::{error, info}; use crate::api::{ApiConnector, ConnectorStatus}; use crate::api::openaev::api_handler::handle_api_response; use crate::api::openaev::ApiOpenAEV; diff --git a/src/api/openaev/connector/post_logs.rs b/src/api/openaev/connector/post_logs.rs index 2f479ce..c614bd4 100644 --- a/src/api/openaev/connector/post_logs.rs +++ b/src/api/openaev/connector/post_logs.rs @@ -1,10 +1,7 @@ use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::JSON; use serde::Serialize; -use serde_json::json; -use tracing::{error, info}; use crate::api::openaev::api_handler::handle_api_response; use crate::api::openaev::ApiOpenAEV; -use crate::api::openaev::connector::ConnectorInstances; #[derive(Serialize)] struct InstanceConnectorLogsInput { diff --git a/src/api/openaev/manager/ping_alive.rs b/src/api/openaev/manager/ping_alive.rs index 44712ae..7e1a954 100644 --- a/src/api/openaev/manager/ping_alive.rs +++ b/src/api/openaev/manager/ping_alive.rs @@ -4,8 +4,7 @@ use crate::api::openaev::manager::ConnectorManager; pub async fn ping_alive(api: &ApiOpenAEV) -> Option { let settings = crate::settings(); - let response = api.put("/xtm-composer/refresh-connectivity") - .body(settings.manager.id.clone()) + let response = api.put(&format!("/xtm-composer/{}/refresh-connectivity", settings.clone().manager.id)) .send() .await; diff --git a/src/api/openaev/manager/post_register.rs b/src/api/openaev/manager/post_register.rs index 8b452ab..df1e269 100644 --- a/src/api/openaev/manager/post_register.rs +++ b/src/api/openaev/manager/post_register.rs @@ -2,7 +2,6 @@ use rsa::{RsaPublicKey}; use rsa::pkcs1::LineEnding; use rsa::pkcs8::EncodePublicKey; use serde::Serialize; -use tracing::{error, info}; use crate::api::openaev::api_handler::handle_api_response; use crate::api::openaev::ApiOpenAEV; use crate::api::openaev::manager::ConnectorManager; diff --git a/src/api/opencti/connector/mod.rs b/src/api/opencti/connector/mod.rs index 09d7af4..df36c02 100644 --- a/src/api/opencti/connector/mod.rs +++ b/src/api/opencti/connector/mod.rs @@ -1,6 +1,6 @@ use serde::Serialize; use crate::api::{ApiConnector, ApiContractConfig}; -use rsa::{Pkcs1v15Encrypt, RsaPrivateKey}; +use rsa::{RsaPrivateKey}; use tracing::{warn}; use std::str; @@ -10,12 +10,7 @@ pub mod post_logs; pub mod post_health; use cynic; -use base64::{Engine as _, engine::general_purpose}; use crate::api::opencti::opencti as schema; -use aes_gcm::{ - aead::{Aead, KeyInit}, - Aes256Gcm, Nonce -}; use crate::api::decrypt_value::parse_aes_encrypted_value; #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] From 4e9389c8839133cc875b9855fbcc941ad13f027d Mon Sep 17 00:00:00 2001 From: Marine LM Date: Tue, 16 Dec 2025 10:26:03 +0100 Subject: [PATCH 4/9] [composer] Upgrade rust version in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 92e075d..025fb52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.85.0-alpine AS builder +FROM rust:1.92-alpine AS builder WORKDIR /opt/xtm-composer COPY . . From 1a5a9c0aeca2835a9eddf5dc175aa8bcbf99223d Mon Sep 17 00:00:00 2001 From: Marine LM Date: Tue, 16 Dec 2025 14:24:09 +0100 Subject: [PATCH 5/9] [composer] fix connector instance configurations name --- src/api/openaev/connector/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/openaev/connector/mod.rs b/src/api/openaev/connector/mod.rs index 628260a..d4ffe06 100644 --- a/src/api/openaev/connector/mod.rs +++ b/src/api/openaev/connector/mod.rs @@ -24,14 +24,14 @@ pub struct ConnectorInstances { pub connector_image: Option, pub connector_instance_current_status: Option, pub connector_instance_requested_status: Option, - pub connector_instance_configuration: Option>, + pub connector_instance_configurations: Option>, } impl ConnectorInstances { pub fn to_api_connector(&self, private_key: &RsaPrivateKey )->ApiConnector { let contract_configuration = self - .connector_instance_configuration + .connector_instance_configurations .as_ref() .unwrap() .into_iter() @@ -40,8 +40,6 @@ impl ConnectorInstances { if is_sensitive { let encrypted_value = c.configuration_value.clone().unwrap_or_default(); let decoded_value_result = parse_aes_encrypted_value(private_key, encrypted_value); - println!("Configuration key: {}", c.configuration_key.clone()); - println!("Decoded value result: {:?}", decoded_value_result); match decoded_value_result { Ok(decoded_value) => ApiContractConfig { key: c.configuration_key.clone(), From 0011ac73a4fa7c9943ea991ab465571190641d2c Mon Sep 17 00:00:00 2001 From: Marine LM Date: Mon, 22 Dec 2025 11:41:34 +0100 Subject: [PATCH 6/9] [composer] update decryptage function with OAEP --- Cargo.toml | 2 ++ src/api/decrypt_value.rs | 39 +++++++++++++++++++++-- src/api/openaev/api_handler.rs | 2 +- src/api/openaev/connector/patch_health.rs | 1 - src/api/opencti/connector/mod.rs | 4 +-- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02634b6..84d091c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ kube = { version = "0.98.0", features = ["runtime", "derive"] } k8s-openapi = { version = "0.24.0", features = ["latest"] } base64 = "0.22.1" aes-gcm = "0.10.3" +sha2 = "0.10.8" +digest = "0.10.7" [build-dependencies] cynic-codegen = { version = "3" } diff --git a/src/api/decrypt_value.rs b/src/api/decrypt_value.rs index 9b24103..cf8de0b 100644 --- a/src/api/decrypt_value.rs +++ b/src/api/decrypt_value.rs @@ -3,10 +3,12 @@ use aes_gcm::{ aead::{Aead, KeyInit}, Aes256Gcm, Nonce }; -use rsa::{Pkcs1v15Encrypt, RsaPrivateKey}; +use rsa::{Oaep, Pkcs1v15Encrypt, RsaPrivateKey}; use tracing::warn; +use sha2::Sha256; -pub fn parse_aes_encrypted_value( +#[deprecated] +pub fn parse_aes_encrypted_value_deprecated_fct( private_key: &RsaPrivateKey, encrypted_value: String ) -> Result> { @@ -23,6 +25,39 @@ pub fn parse_aes_encrypted_value( let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; let encrypted_value_bytes = &encrypted_bytes[513..]; + let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; + let nonce = Nonce::from_slice(&aes_iv_bytes); + let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); + match plaintext_result { + Ok(plaintext) => { + let decoded_value = str::from_utf8(&plaintext)?.to_string(); // Fixed: use ? instead of unwrap + Ok(decoded_value) + }, + Err(e) => { + warn!(error = e.to_string(), "Fail to decode value"); + Ok(String::from("")) + } + } + } +} + +pub fn parse_aes_encrypted_value( + private_key: &RsaPrivateKey, + encrypted_value: String +) -> Result> { + let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; + + let version = encrypted_bytes[0]; + if version != 1 { + warn!(version, "Encryption version not handled"); + Ok(String::from("")) + } else { + let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; + let aes_key_iv_decrypted_bytes = private_key.decrypt(Oaep::new::(), &aes_key_iv_encrypted_bytes)?; + let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; + let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; + let encrypted_value_bytes = &encrypted_bytes[513..]; + let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; let nonce = Nonce::from_slice(&aes_iv_bytes); let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); diff --git a/src/api/openaev/api_handler.rs b/src/api/openaev/api_handler.rs index 073e222..f24317d 100644 --- a/src/api/openaev/api_handler.rs +++ b/src/api/openaev/api_handler.rs @@ -13,7 +13,7 @@ where match resp.json::().await { Ok(data) => Some(data), Err(err) => { - error!("Failed to parse JSON for {}: {}", operation_name, err); + error!("Failed to parse JSON for {}: {}", operation_name, err.to_string()); None } } diff --git a/src/api/openaev/connector/patch_health.rs b/src/api/openaev/connector/patch_health.rs index d080d07..e745a0b 100644 --- a/src/api/openaev/connector/patch_health.rs +++ b/src/api/openaev/connector/patch_health.rs @@ -29,7 +29,6 @@ pub async fn update_health( .send() .await; - // Discard the result let _ = handle_api_response::( health_check_response, "push health metrics" diff --git a/src/api/opencti/connector/mod.rs b/src/api/opencti/connector/mod.rs index df36c02..fb5b54c 100644 --- a/src/api/opencti/connector/mod.rs +++ b/src/api/opencti/connector/mod.rs @@ -11,7 +11,7 @@ pub mod post_health; use cynic; use crate::api::opencti::opencti as schema; -use crate::api::decrypt_value::parse_aes_encrypted_value; +use crate::api::decrypt_value::parse_aes_encrypted_value_deprecated_fct; #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] pub struct ConnectorContractConfiguration { @@ -48,7 +48,7 @@ impl ManagedConnector { let is_sensitive = c.encrypted.unwrap_or_default(); if is_sensitive { let encrypted_value = c.value.unwrap_or_default(); - let decoded_value_result = parse_aes_encrypted_value(private_key, encrypted_value); + let decoded_value_result = parse_aes_encrypted_value_deprecated_fct(private_key, encrypted_value); match decoded_value_result { Ok(decoded_value) => ApiContractConfig { key: c.key, From 811103925a455b9857eb0ff132a60d581bc5e960 Mon Sep 17 00:00:00 2001 From: Marine LM Date: Mon, 22 Dec 2025 15:23:19 +0100 Subject: [PATCH 7/9] [composer] improve code --- src/api/decrypt_value.rs | 43 ++++------------------- src/api/mod.rs | 4 +-- src/api/openaev/connector/mod.rs | 4 +-- src/api/openaev/connector/patch_health.rs | 4 +-- src/api/openaev/connector/post_logs.rs | 4 +-- src/api/openaev/manager/mod.rs | 2 +- src/api/openaev/mod.rs | 9 ++--- src/api/opencti/connector/mod.rs | 4 +-- src/api/opencti/connector/post_health.rs | 4 +-- src/api/opencti/connector/post_logs.rs | 4 +-- src/api/opencti/mod.rs | 4 +-- 11 files changed, 29 insertions(+), 57 deletions(-) diff --git a/src/api/decrypt_value.rs b/src/api/decrypt_value.rs index cf8de0b..0de2db8 100644 --- a/src/api/decrypt_value.rs +++ b/src/api/decrypt_value.rs @@ -7,40 +7,6 @@ use rsa::{Oaep, Pkcs1v15Encrypt, RsaPrivateKey}; use tracing::warn; use sha2::Sha256; -#[deprecated] -pub fn parse_aes_encrypted_value_deprecated_fct( - private_key: &RsaPrivateKey, - encrypted_value: String -) -> Result> { - let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; - - let version = encrypted_bytes[0]; - if version != 1 { - warn!(version, "Encryption version not handled"); - Ok(String::from("")) - } else { - let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; - let aes_key_iv_decrypted_bytes = private_key.decrypt(Pkcs1v15Encrypt, &aes_key_iv_encrypted_bytes)?; - let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; - let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; - let encrypted_value_bytes = &encrypted_bytes[513..]; - - let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; - let nonce = Nonce::from_slice(&aes_iv_bytes); - let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); - match plaintext_result { - Ok(plaintext) => { - let decoded_value = str::from_utf8(&plaintext)?.to_string(); // Fixed: use ? instead of unwrap - Ok(decoded_value) - }, - Err(e) => { - warn!(error = e.to_string(), "Fail to decode value"); - Ok(String::from("")) - } - } - } -} - pub fn parse_aes_encrypted_value( private_key: &RsaPrivateKey, encrypted_value: String @@ -48,12 +14,17 @@ pub fn parse_aes_encrypted_value( let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; let version = encrypted_bytes[0]; - if version != 1 { + if !matches!(version, 1 | 2) { warn!(version, "Encryption version not handled"); Ok(String::from("")) + } else { let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; - let aes_key_iv_decrypted_bytes = private_key.decrypt(Oaep::new::(), &aes_key_iv_encrypted_bytes)?; + let aes_key_iv_decrypted_bytes = match version { + 1 => private_key.decrypt(Pkcs1v15Encrypt, aes_key_iv_encrypted_bytes)?, + 2 => private_key.decrypt(Oaep::new::(), aes_key_iv_encrypted_bytes)?, + _ => unreachable!(), // Already checked above + }; let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; let encrypted_value_bytes = &encrypted_bytes[513..]; diff --git a/src/api/mod.rs b/src/api/mod.rs index 9aca900..f65fcb1 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -181,7 +181,7 @@ pub trait ComposerApi { async fn patch_status(&self, id: String, status: ConnectorStatus) -> Option; - async fn patch_logs(&self, id: String, logs: Vec) -> Option; + async fn patch_logs(&self, id: String, logs: Vec) -> Option; - async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option; + async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option; } diff --git a/src/api/openaev/connector/mod.rs b/src/api/openaev/connector/mod.rs index d4ffe06..25a9b6e 100644 --- a/src/api/openaev/connector/mod.rs +++ b/src/api/openaev/connector/mod.rs @@ -18,7 +18,7 @@ pub struct ConnectorContractConfiguration { #[derive(Debug, Deserialize)] pub struct ConnectorInstances { - pub connector_instance_id: cynic::Id, + pub connector_instance_id: String, pub connector_instance_name: String, pub connector_instance_hash: Option, pub connector_image: Option, @@ -65,7 +65,7 @@ impl ConnectorInstances { }) .collect(); ApiConnector { - id: self.connector_instance_id.clone().into_inner(), + id: self.connector_instance_id.clone(), name: self.connector_instance_name.clone(), image: self.connector_image.clone().unwrap(), contract_hash: self.connector_instance_hash.clone().unwrap(), diff --git a/src/api/openaev/connector/patch_health.rs b/src/api/openaev/connector/patch_health.rs index e745a0b..5217410 100644 --- a/src/api/openaev/connector/patch_health.rs +++ b/src/api/openaev/connector/patch_health.rs @@ -16,7 +16,7 @@ pub async fn update_health( started_at: String, is_in_reboot_loop: bool, api: &ApiOpenAEV, -)-> Option { +)-> Option { let settings = crate::settings(); let health_check_input = ConnectorInstanceHealthInput { connector_instance_restart_count: restart_count, @@ -34,5 +34,5 @@ pub async fn update_health( "push health metrics" ).await; - Some(cynic::Id::new(id)) + Some(id) } \ No newline at end of file diff --git a/src/api/openaev/connector/post_logs.rs b/src/api/openaev/connector/post_logs.rs index c614bd4..d664bf4 100644 --- a/src/api/openaev/connector/post_logs.rs +++ b/src/api/openaev/connector/post_logs.rs @@ -8,7 +8,7 @@ struct InstanceConnectorLogsInput { connector_instance_logs: Vec, } -pub async fn add_logs(id: String, logs: Vec, api: &ApiOpenAEV)-> Option { +pub async fn add_logs(id: String, logs: Vec, api: &ApiOpenAEV)-> Option { let logs_input = InstanceConnectorLogsInput{ connector_instance_logs: logs }; @@ -24,5 +24,5 @@ pub async fn add_logs(id: String, logs: Vec, api: &ApiOpenAEV)-> Option< "push logs for connector instance" ).await; - Some(cynic::Id::new(id)) + Some(id) } \ No newline at end of file diff --git a/src/api/openaev/manager/mod.rs b/src/api/openaev/manager/mod.rs index d960bbb..ff91b55 100644 --- a/src/api/openaev/manager/mod.rs +++ b/src/api/openaev/manager/mod.rs @@ -6,6 +6,6 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct ConnectorManager { - pub xtm_composer_id: cynic::Id, + pub xtm_composer_id: String, pub xtm_composer_version: String, } \ No newline at end of file diff --git a/src/api/openaev/mod.rs b/src/api/openaev/mod.rs index 159c0f1..65dcc96 100644 --- a/src/api/openaev/mod.rs +++ b/src/api/openaev/mod.rs @@ -37,8 +37,8 @@ impl ApiOpenAEV { let connect_timeout = settings.openaev.connect_timeout; let http_client = reqwest::Client::builder() - .connect_timeout(Duration::from_secs(2)) - .timeout(Duration::from_secs(5)) + .connect_timeout(Duration::from_secs(connect_timeout)) + .timeout(Duration::from_secs(request_timeout)) .build() .unwrap(); // or handle the error appropriately @@ -70,6 +70,7 @@ impl ApiOpenAEV { self.http_client .put(&api_route) + .header("Content-Type", "application/json") .header(AUTHORIZATION_HEADER, self.bearer.as_str()) } @@ -113,11 +114,11 @@ impl ComposerApi for ApiOpenAEV { connector::patch_status::update_status(id, status, self).await } - async fn patch_logs(&self, id: String, logs: Vec) -> Option { + async fn patch_logs(&self, id: String, logs: Vec) -> Option { connector::post_logs::add_logs(id, logs, self).await } - async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { + async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { connector::patch_health::update_health(id, restart_count, started_at, is_in_reboot_loop, self).await } } diff --git a/src/api/opencti/connector/mod.rs b/src/api/opencti/connector/mod.rs index fb5b54c..df36c02 100644 --- a/src/api/opencti/connector/mod.rs +++ b/src/api/opencti/connector/mod.rs @@ -11,7 +11,7 @@ pub mod post_health; use cynic; use crate::api::opencti::opencti as schema; -use crate::api::decrypt_value::parse_aes_encrypted_value_deprecated_fct; +use crate::api::decrypt_value::parse_aes_encrypted_value; #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] pub struct ConnectorContractConfiguration { @@ -48,7 +48,7 @@ impl ManagedConnector { let is_sensitive = c.encrypted.unwrap_or_default(); if is_sensitive { let encrypted_value = c.value.unwrap_or_default(); - let decoded_value_result = parse_aes_encrypted_value_deprecated_fct(private_key, encrypted_value); + let decoded_value_result = parse_aes_encrypted_value(private_key, encrypted_value); match decoded_value_result { Ok(decoded_value) => ApiContractConfig { key: c.key, diff --git a/src/api/opencti/connector/post_health.rs b/src/api/opencti/connector/post_health.rs index c3ce5b9..f8cbbe9 100644 --- a/src/api/opencti/connector/post_health.rs +++ b/src/api/opencti/connector/post_health.rs @@ -34,7 +34,7 @@ pub async fn health( started_at: String, is_in_reboot_loop: bool, api: &ApiOpenCTI, -) -> Option { +) -> Option { use cynic::MutationBuilder; let vars = UpdateConnectorHealthVariables { @@ -53,7 +53,7 @@ pub async fn health( response, "update_connector_health", "OpenCTI backend does not support XTM composer health updates. The connector will continue to run but health metrics won't be sent to OpenCTI." - ).map(|data| data.update_connector_health) + ).map(|data| data.update_connector_health.inner().to_string()) } Err(e) => { error!(error = e.to_string(), "Fail to push health metrics"); diff --git a/src/api/opencti/connector/post_logs.rs b/src/api/opencti/connector/post_logs.rs index 51a193c..4d3540d 100644 --- a/src/api/opencti/connector/post_logs.rs +++ b/src/api/opencti/connector/post_logs.rs @@ -25,7 +25,7 @@ pub struct LogsConnectorStatusInput<'a> { } // endregion -pub async fn logs(id: String, logs: Vec, api: &ApiOpenCTI) -> Option { +pub async fn logs(id: String, logs: Vec, api: &ApiOpenCTI) -> Option { use cynic::MutationBuilder; let str_logs = logs.iter().map(|c| c.as_str()).collect(); let vars = ReportConnectorLogsVariables { @@ -42,7 +42,7 @@ pub async fn logs(id: String, logs: Vec, api: &ApiOpenCTI) -> Option { error!(error = e.to_string(), "Fail to push logs"); diff --git a/src/api/opencti/mod.rs b/src/api/opencti/mod.rs index 520f98d..80f7c99 100644 --- a/src/api/opencti/mod.rs +++ b/src/api/opencti/mod.rs @@ -101,11 +101,11 @@ impl ComposerApi for ApiOpenCTI { connector::post_status::status(id, status, self).await } - async fn patch_logs(&self, id: String, logs: Vec) -> Option { + async fn patch_logs(&self, id: String, logs: Vec) -> Option { connector::post_logs::logs(id, logs, self).await } - async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { + async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { connector::post_health::health(id, restart_count, started_at, is_in_reboot_loop, self).await } } From 58127ffcd06b6ac84f29e57a8ee7e754b10629a5 Mon Sep 17 00:00:00 2001 From: Marine LM Date: Mon, 29 Dec 2025 17:11:01 +0100 Subject: [PATCH 8/9] [composer] notify API when container has been successfully removed --- src/api/mod.rs | 2 ++ src/api/openaev/connector/mod.rs | 1 + .../openaev/connector/notify_container_removed.rs | 14 ++++++++++++++ src/api/openaev/mod.rs | 14 +++++++++++++- src/api/opencti/mod.rs | 4 ++++ src/orchestrator/composer.rs | 4 +++- src/orchestrator/docker/docker.rs | 4 +++- src/orchestrator/kubernetes/kubernetes.rs | 15 +++++++++------ src/orchestrator/mod.rs | 2 +- src/orchestrator/portainer/docker/portainer.rs | 4 +++- 10 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 src/api/openaev/connector/notify_container_removed.rs diff --git a/src/api/mod.rs b/src/api/mod.rs index f65fcb1..d4952b3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -184,4 +184,6 @@ pub trait ComposerApi { async fn patch_logs(&self, id: String, logs: Vec) -> Option; async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option; + + async fn container_removed_successfully(&self, id: String) -> (); } diff --git a/src/api/openaev/connector/mod.rs b/src/api/openaev/connector/mod.rs index 25a9b6e..ac2f664 100644 --- a/src/api/openaev/connector/mod.rs +++ b/src/api/openaev/connector/mod.rs @@ -8,6 +8,7 @@ pub mod get_connector_instances; pub mod patch_health; pub mod patch_status; pub mod post_logs; +pub mod notify_container_removed; #[derive(Debug, Deserialize)] pub struct ConnectorContractConfiguration { diff --git a/src/api/openaev/connector/notify_container_removed.rs b/src/api/openaev/connector/notify_container_removed.rs new file mode 100644 index 0000000..18d529d --- /dev/null +++ b/src/api/openaev/connector/notify_container_removed.rs @@ -0,0 +1,14 @@ +use crate::api::openaev::api_handler::{handle_api_text_response}; +use crate::api::openaev::ApiOpenAEV; + +pub async fn notify_container_removed(id: String, api: &ApiOpenAEV) { + let settings = crate::settings(); + let response = api.delete(&format!("/xtm-composer/{}/connector-instances/{}", settings.clone().manager.id, id.clone())) + .send() + .await; + + let _ = handle_api_text_response( + response, + "Notify OpenAEV that the container has been successfully removed" + ).await; +} \ No newline at end of file diff --git a/src/api/openaev/mod.rs b/src/api/openaev/mod.rs index 65dcc96..b75e4f0 100644 --- a/src/api/openaev/mod.rs +++ b/src/api/openaev/mod.rs @@ -82,6 +82,14 @@ impl ApiOpenAEV { .header(AUTHORIZATION_HEADER, self.bearer.as_str()) } + pub fn delete(&self, route: &str) -> reqwest::RequestBuilder { + let api_route = format!("{}{}", self.api_uri.clone(), route); + + self.http_client + .delete(&api_route) + .header(AUTHORIZATION_HEADER, self.bearer.as_str()) + } + } #[async_trait] @@ -118,7 +126,11 @@ impl ComposerApi for ApiOpenAEV { connector::post_logs::add_logs(id, logs, self).await } - async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { + async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool ) -> Option { connector::patch_health::update_health(id, restart_count, started_at, is_in_reboot_loop, self).await } + + async fn container_removed_successfully(&self, id: String) -> () { + connector::notify_container_removed::notify_container_removed(id, self).await + } } diff --git a/src/api/opencti/mod.rs b/src/api/opencti/mod.rs index 80f7c99..07911ca 100644 --- a/src/api/opencti/mod.rs +++ b/src/api/opencti/mod.rs @@ -108,4 +108,8 @@ impl ComposerApi for ApiOpenCTI { async fn patch_health(&self, id: String, restart_count: u32, started_at: String, is_in_reboot_loop: bool) -> Option { connector::post_health::health(id, restart_count, started_at, is_in_reboot_loop, self).await } + + async fn container_removed_successfully(&self, id: String) -> () { + // Do nothing + } } diff --git a/src/orchestrator/composer.rs b/src/orchestrator/composer.rs index f39708b..d807ed2 100644 --- a/src/orchestrator/composer.rs +++ b/src/orchestrator/composer.rs @@ -165,7 +165,9 @@ pub async fn orchestrate( for container in existing_containers { let connector_id = container.extract_opencti_id(); if !connectors_by_id.contains_key(&connector_id) { - orchestrator.remove(&container).await; + if orchestrator.remove(&container).await { + api.container_removed_successfully(connector_id).await; + } } } } diff --git a/src/orchestrator/docker/docker.rs b/src/orchestrator/docker/docker.rs index f0ba0a5..9c1c910 100644 --- a/src/orchestrator/docker/docker.rs +++ b/src/orchestrator/docker/docker.rs @@ -127,7 +127,7 @@ impl Orchestrator for DockerOrchestrator { .await; } - async fn remove(&self, container: &OrchestratorContainer) -> () { + async fn remove(&self, container: &OrchestratorContainer) -> bool { let container_name = container.name.as_str(); let remove_response = self .docker @@ -143,6 +143,7 @@ impl Orchestrator for DockerOrchestrator { match remove_response { Ok(_) => { info!(name = container_name, "Removed container"); + true } Err(err) => { error!( @@ -150,6 +151,7 @@ impl Orchestrator for DockerOrchestrator { error = err.to_string(), "Could not remove container" ); + false } } } diff --git a/src/orchestrator/kubernetes/kubernetes.rs b/src/orchestrator/kubernetes/kubernetes.rs index 1f0d27d..48ead4b 100644 --- a/src/orchestrator/kubernetes/kubernetes.rs +++ b/src/orchestrator/kubernetes/kubernetes.rs @@ -252,7 +252,7 @@ impl Orchestrator for KubeOrchestrator { self.set_deployment_scale(connector, 0).await; } - async fn remove(&self, container: &OrchestratorContainer) -> () { + async fn remove(&self, container: &OrchestratorContainer) -> bool { let lp = &ListParams::default().labels(&format!( "opencti-connector-id={}", container.extract_opencti_id() @@ -260,11 +260,14 @@ impl Orchestrator for KubeOrchestrator { let dp = &DeleteParams::default(); let delete_response = self.deployments.delete_collection(dp, lp).await; match delete_response { - Ok(_) => info!( - id = container.extract_opencti_id(), - "Deployment successfully deleted" - ), - Err(err) => error!(error = err.to_string(), "Fail removing the deployments"), + Ok(_) => { + info!(id = container.extract_opencti_id(),"Deployment successfully deleted"); + true + } + Err(err) => { + error!(error = err.to_string(), "Fail removing the deployments"); + false + }, } } diff --git a/src/orchestrator/mod.rs b/src/orchestrator/mod.rs index 081db34..41928b1 100644 --- a/src/orchestrator/mod.rs +++ b/src/orchestrator/mod.rs @@ -65,7 +65,7 @@ pub trait Orchestrator { async fn stop(&self, container: &OrchestratorContainer, connector: &ApiConnector) -> (); - async fn remove(&self, container: &OrchestratorContainer) -> (); + async fn remove(&self, container: &OrchestratorContainer) -> bool; async fn refresh(&self, connector: &ApiConnector) -> Option; diff --git a/src/orchestrator/portainer/docker/portainer.rs b/src/orchestrator/portainer/docker/portainer.rs index b3c1eea..443e288 100644 --- a/src/orchestrator/portainer/docker/portainer.rs +++ b/src/orchestrator/portainer/docker/portainer.rs @@ -146,7 +146,7 @@ impl Orchestrator for PortainerDockerOrchestrator { self.client.post(start_container_uri).send().await.unwrap(); } - async fn remove(&self, container: &OrchestratorContainer) -> () { + async fn remove(&self, container: &OrchestratorContainer) -> bool { let container_name = container.name.as_str(); let delete_container_uri = format!("{}/{}?v=0&force=true", self.container_uri, container.id); @@ -154,6 +154,7 @@ impl Orchestrator for PortainerDockerOrchestrator { match remove_response { Ok(_) => { info!(name = container_name, "Removed container"); + true } Err(err) => { error!( @@ -161,6 +162,7 @@ impl Orchestrator for PortainerDockerOrchestrator { error = err.to_string(), "Could not remove container" ); + false } } } From 6e6a56390d2121c6f14c227c8673a8dbfb394a76 Mon Sep 17 00:00:00 2001 From: Marine LM Date: Tue, 6 Jan 2026 09:46:25 +0100 Subject: [PATCH 9/9] [composer] improve code --- Cargo.toml | 1 - src/api/decrypt_value.rs | 51 +++++++++---------- .../connector/get_connector_instances.rs | 2 +- src/api/openaev/connector/mod.rs | 4 +- .../connector/notify_container_removed.rs | 2 +- src/api/openaev/connector/patch_health.rs | 2 +- src/api/openaev/connector/patch_status.rs | 2 +- src/api/openaev/connector/post_logs.rs | 2 +- src/api/openaev/manager/ping_alive.rs | 2 +- src/api/openaev/manager/post_register.rs | 4 +- src/api/openaev/mod.rs | 8 +-- 11 files changed, 39 insertions(+), 41 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 84d091c..74c24f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ k8s-openapi = { version = "0.24.0", features = ["latest"] } base64 = "0.22.1" aes-gcm = "0.10.3" sha2 = "0.10.8" -digest = "0.10.7" [build-dependencies] cynic-codegen = { version = "3" } diff --git a/src/api/decrypt_value.rs b/src/api/decrypt_value.rs index 0de2db8..237dfc8 100644 --- a/src/api/decrypt_value.rs +++ b/src/api/decrypt_value.rs @@ -13,34 +13,33 @@ pub fn parse_aes_encrypted_value( ) -> Result> { let encrypted_bytes = general_purpose::STANDARD.decode(encrypted_value)?; - let version = encrypted_bytes[0]; - if !matches!(version, 1 | 2) { - warn!(version, "Encryption version not handled"); - Ok(String::from("")) + let version = *encrypted_bytes.get(0) + .ok_or("Encrypted value is empty")?; - } else { - let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; - let aes_key_iv_decrypted_bytes = match version { - 1 => private_key.decrypt(Pkcs1v15Encrypt, aes_key_iv_encrypted_bytes)?, - 2 => private_key.decrypt(Oaep::new::(), aes_key_iv_encrypted_bytes)?, - _ => unreachable!(), // Already checked above - }; - let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; - let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; - let encrypted_value_bytes = &encrypted_bytes[513..]; + let aes_key_iv_encrypted_bytes = &encrypted_bytes[1..=512]; + let aes_key_iv_decrypted_bytes = match version { + 1 => private_key.decrypt(Pkcs1v15Encrypt, aes_key_iv_encrypted_bytes)?, + 2 => private_key.decrypt(Oaep::new::(), aes_key_iv_encrypted_bytes)?, + _ => { + warn!(version, "Encryption version not handled"); + return Ok(String::new()); + } + }; + let aes_key_bytes = &aes_key_iv_decrypted_bytes[0..32]; + let aes_iv_bytes = &aes_key_iv_decrypted_bytes[32..44]; + let encrypted_value_bytes = &encrypted_bytes[513..]; - let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; - let nonce = Nonce::from_slice(&aes_iv_bytes); - let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); - match plaintext_result { - Ok(plaintext) => { - let decoded_value = str::from_utf8(&plaintext)?.to_string(); // Fixed: use ? instead of unwrap - Ok(decoded_value) - }, - Err(e) => { - warn!(error = e.to_string(), "Fail to decode value"); - Ok(String::from("")) - } + let cipher = Aes256Gcm::new_from_slice(&aes_key_bytes)?; + let nonce = Nonce::from_slice(&aes_iv_bytes); + let plaintext_result = cipher.decrypt(&nonce, encrypted_value_bytes.as_ref()); + match plaintext_result { + Ok(plaintext) => { + let decoded_value = str::from_utf8(&plaintext)?.to_string(); + Ok(decoded_value) + }, + Err(e) => { + warn!(error = e.to_string(), "Fail to decode value"); + Ok(String::from("")) } } } \ No newline at end of file diff --git a/src/api/openaev/connector/get_connector_instances.rs b/src/api/openaev/connector/get_connector_instances.rs index f3e462e..4c59cd8 100644 --- a/src/api/openaev/connector/get_connector_instances.rs +++ b/src/api/openaev/connector/get_connector_instances.rs @@ -4,7 +4,7 @@ use crate::api::openaev::connector::ConnectorInstances; pub async fn get_connector_instances(api: &crate::api::openaev::ApiOpenAEV) -> Option> { let settings = crate::settings(); - let get_connectors = api.get(&format!("/xtm-composer/{}/connector-instances", settings.clone().manager.id)) + let get_connectors = api.get(&format!("/xtm-composer/{}/connector-instances", settings.manager.id)) .send() .await; diff --git a/src/api/openaev/connector/mod.rs b/src/api/openaev/connector/mod.rs index ac2f664..74ff8c0 100644 --- a/src/api/openaev/connector/mod.rs +++ b/src/api/openaev/connector/mod.rs @@ -37,7 +37,7 @@ impl ConnectorInstances { .unwrap() .into_iter() .map(|c| { - let is_sensitive = c.configuration_is_encrypted.clone(); + let is_sensitive = c.configuration_is_encrypted; if is_sensitive { let encrypted_value = c.configuration_value.clone().unwrap_or_default(); let decoded_value_result = parse_aes_encrypted_value(private_key, encrypted_value); @@ -51,7 +51,7 @@ impl ConnectorInstances { warn!(error = e.to_string(), "Fail to decode value"); ApiContractConfig { key: c.configuration_key.clone(), - value: String::from(""), + value: String::new(), is_sensitive: true, } } diff --git a/src/api/openaev/connector/notify_container_removed.rs b/src/api/openaev/connector/notify_container_removed.rs index 18d529d..6423743 100644 --- a/src/api/openaev/connector/notify_container_removed.rs +++ b/src/api/openaev/connector/notify_container_removed.rs @@ -3,7 +3,7 @@ use crate::api::openaev::ApiOpenAEV; pub async fn notify_container_removed(id: String, api: &ApiOpenAEV) { let settings = crate::settings(); - let response = api.delete(&format!("/xtm-composer/{}/connector-instances/{}", settings.clone().manager.id, id.clone())) + let response = api.delete(&format!("/xtm-composer/{}/connector-instances/{}", settings.manager.id, id)) .send() .await; diff --git a/src/api/openaev/connector/patch_health.rs b/src/api/openaev/connector/patch_health.rs index 5217410..ad92ece 100644 --- a/src/api/openaev/connector/patch_health.rs +++ b/src/api/openaev/connector/patch_health.rs @@ -24,7 +24,7 @@ pub async fn update_health( connector_instance_is_in_reboot_loop: is_in_reboot_loop }; - let health_check_response = api.put(&format!("/xtm-composer/{}/connector-instances/{}/health-check", settings.clone().manager.id, id.clone())) + let health_check_response = api.put(&format!("/xtm-composer/{}/connector-instances/{}/health-check", settings.manager.id, id)) .json(&health_check_input) .send() .await; diff --git a/src/api/openaev/connector/patch_status.rs b/src/api/openaev/connector/patch_status.rs index 3e785a7..1680251 100644 --- a/src/api/openaev/connector/patch_status.rs +++ b/src/api/openaev/connector/patch_status.rs @@ -21,7 +21,7 @@ pub async fn update_status(id: String, status: ConnectorStatus, api: &ApiOpenAEV }; let settings = crate::settings(); - let update_status_response = api.put(&format!("/xtm-composer/{}/connector-instances/{}/status", settings.clone().manager.id, id)) + let update_status_response = api.put(&format!("/xtm-composer/{}/connector-instances/{}/status", settings.manager.id, id)) .json(&status_input) .send() .await; diff --git a/src/api/openaev/connector/post_logs.rs b/src/api/openaev/connector/post_logs.rs index d664bf4..ada3b34 100644 --- a/src/api/openaev/connector/post_logs.rs +++ b/src/api/openaev/connector/post_logs.rs @@ -13,7 +13,7 @@ pub async fn add_logs(id: String, logs: Vec, api: &ApiOpenAEV)-> Option< connector_instance_logs: logs }; let settings = crate::settings(); - let add_logs_response = api.post(&format!("/xtm-composer/{}/connector-instances/{}/logs",settings.clone().manager.id, id)) + let add_logs_response = api.post(&format!("/xtm-composer/{}/connector-instances/{}/logs",settings.manager.id, id)) .json(&logs_input) .send() .await; diff --git a/src/api/openaev/manager/ping_alive.rs b/src/api/openaev/manager/ping_alive.rs index 7e1a954..243dbf2 100644 --- a/src/api/openaev/manager/ping_alive.rs +++ b/src/api/openaev/manager/ping_alive.rs @@ -4,7 +4,7 @@ use crate::api::openaev::manager::ConnectorManager; pub async fn ping_alive(api: &ApiOpenAEV) -> Option { let settings = crate::settings(); - let response = api.put(&format!("/xtm-composer/{}/refresh-connectivity", settings.clone().manager.id)) + let response = api.put(&format!("/xtm-composer/{}/refresh-connectivity", settings.manager.id)) .send() .await; diff --git a/src/api/openaev/manager/post_register.rs b/src/api/openaev/manager/post_register.rs index df1e269..4014013 100644 --- a/src/api/openaev/manager/post_register.rs +++ b/src/api/openaev/manager/post_register.rs @@ -22,9 +22,9 @@ pub async fn register(api: &ApiOpenAEV) { .expect("Failed to encode public key as PKCS#8"); let register_input = RegisterInput { - id: settings.clone().manager.id, + id: settings.manager.id.clone(), name: settings.manager.name.clone(), - public_key: public_key.clone(), + public_key, }; let register_response = api.post("/xtm-composer/register") diff --git a/src/api/openaev/mod.rs b/src/api/openaev/mod.rs index b75e4f0..a6ff316 100644 --- a/src/api/openaev/mod.rs +++ b/src/api/openaev/mod.rs @@ -57,7 +57,7 @@ impl ApiOpenAEV { } pub fn post(&self, route: &str) -> reqwest::RequestBuilder { - let api_route = format!("{}{}", self.api_uri.clone(), route); + let api_route = format!("{}{}", self.api_uri, route); self.http_client .post(&api_route) @@ -66,7 +66,7 @@ impl ApiOpenAEV { } pub fn put(&self, route: &str) -> reqwest::RequestBuilder { - let api_route = format!("{}{}", self.api_uri.clone(), route); + let api_route = format!("{}{}", self.api_uri, route); self.http_client .put(&api_route) @@ -75,7 +75,7 @@ impl ApiOpenAEV { } pub fn get(&self, route: &str) -> reqwest::RequestBuilder { - let api_route = format!("{}{}", self.api_uri.clone(), route); + let api_route = format!("{}{}", self.api_uri, route); self.http_client .get(&api_route) @@ -83,7 +83,7 @@ impl ApiOpenAEV { } pub fn delete(&self, route: &str) -> reqwest::RequestBuilder { - let api_route = format!("{}{}", self.api_uri.clone(), route); + let api_route = format!("{}{}", self.api_uri, route); self.http_client .delete(&api_route)