From 465106438a1b5747b163472b07ef7005aeb1edd5 Mon Sep 17 00:00:00 2001 From: jeangab Date: Fri, 27 Sep 2024 16:51:49 -0400 Subject: [PATCH 01/19] wip: Actual implementation of opnsense dhcp --- harmony-rs/harmony/src/infra/opnsense/mod.rs | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs index 92f1826..0f81d40 100644 --- a/harmony-rs/harmony/src/infra/opnsense/mod.rs +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -1,10 +1,10 @@ mod management; pub use management::*; -use crate::topology::{ +use crate::{executors::ExecutorError, topology::{ Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallError, FirewallRule, Frontend, IpAddress, LoadBalancer, LoadBalancerError, LogicalHost, -}; +}}; use derive_new::new; #[derive(new, Clone)] @@ -16,6 +16,10 @@ impl OPNSenseFirewall { pub fn get_ip(&self) -> IpAddress { self.host.ip } + + fn save_xml_to_config(&self, xml_entry: &str) -> Result<(), ExecutorError> { + todo!("Save XML Entry in opnsense /conf/config.xml {xml_entry}"); + } } impl Firewall for OPNSenseFirewall { @@ -77,6 +81,20 @@ impl DhcpServer for OPNSenseFirewall { &self, entry: &DHCPStaticEntry, ) -> Result<(), crate::topology::DhcpError> { + let mac = &entry.mac; + let name = &entry.name; + let ip = &entry.ip; + + let xml_entry = format!(" +> {mac} +> {ip} +> {name} +> {name} +> +> +> +> "); + self.save_xml_to_config(&xml_entry)?; todo!("Register {:?}", entry) } From 407bdbc032597e424c0bc29e85431c8b2edcd693 Mon Sep 17 00:00:00 2001 From: jeangab Date: Mon, 30 Sep 2024 16:20:11 -0400 Subject: [PATCH 02/19] wip: OPNSense XML config editor coming along --- harmony-rs/Cargo.lock | 74 ++++++++++++++++--- harmony-rs/Cargo.toml | 1 + harmony-rs/harmony/Cargo.toml | 1 + harmony-rs/harmony/src/domain/hardware/mod.rs | 1 + .../harmony/src/domain/inventory/mod.rs | 7 +- .../src/domain/topology/load_balancer.rs | 11 ++- .../harmony/src/domain/topology/network.rs | 27 ++++--- .../harmony/src/infra/opnsense/config.rs | 43 +++++++++++ harmony-rs/harmony/src/infra/opnsense/mod.rs | 70 ++++++++++-------- harmony-rs/harmony/src/modules/dhcp.rs | 16 ++-- 10 files changed, 181 insertions(+), 70 deletions(-) create mode 100644 harmony-rs/harmony/src/infra/opnsense/config.rs diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index a0a529d..6e0abb7 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -58,17 +58,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "affilium" -version = "0.1.0" -dependencies = [ - "cidr", - "env_logger", - "harmony", - "log", - "tokio", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -258,6 +247,15 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cbc" version = "0.1.2" @@ -315,6 +313,19 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -817,6 +828,7 @@ dependencies = [ "env_logger", "libredfish", "log", + "minidom", "reqwest", "russh", "rust-ipmi", @@ -1069,6 +1081,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minidom" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e394a0e3c7ccc2daea3dffabe82f09857b6b510cb25af87d54bf3e910ac1642d" +dependencies = [ + "rxml", +] + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1732,6 +1753,31 @@ dependencies = [ "base64", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "rxml" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc94b580d0f5a6b7a2d604e597513d3c673154b52ddeccd1d5c32360d945ee" +dependencies = [ + "bytes", + "rxml_validation", +] + +[[package]] +name = "rxml_validation" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4" +dependencies = [ + "compact_str", +] + [[package]] name = "ryu" version = "1.0.18" @@ -1993,6 +2039,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.6.1" diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 9e5cc19..954f000 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -17,3 +17,4 @@ derive-new = "0.7.0" async-trait = "0.1.82" tokio = { version = "1.40.0", features = ["io-std"] } cidr = "0.2.3" +minidom = "0.16.0" diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index 17b07da..b017d54 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -17,3 +17,4 @@ log = { workspace = true } env_logger = { workspace = true } async-trait = { workspace = true } cidr = { workspace = true } +minidom = { workspace = true } diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index 985e16e..8ec4f81 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -7,6 +7,7 @@ use crate::topology::MacAddress; pub type HostGroup = Vec; pub type SwitchGroup = Vec; pub type FirewallGroup = Vec; + #[derive(Debug, Clone)] pub struct PhysicalHost { pub category: HostCategory, diff --git a/harmony-rs/harmony/src/domain/inventory/mod.rs b/harmony-rs/harmony/src/domain/inventory/mod.rs index af4a00c..930ee02 100644 --- a/harmony-rs/harmony/src/domain/inventory/mod.rs +++ b/harmony-rs/harmony/src/domain/inventory/mod.rs @@ -23,6 +23,9 @@ use super::{ pub struct Inventory { pub location: Location, pub switch: SwitchGroup, + // Firewall is really just a host but with somewhat specialized hardware + // I'm not entirely sure it belongs to its own category but it helps make things easier and + // clearer for now so let's try it this way. pub firewall: FirewallGroup, pub worker_host: HostGroup, pub storage_host: HostGroup, @@ -34,9 +37,11 @@ impl Inventory { pub fn empty_inventory() -> Self { Self { location: Location::test_building(), - host: HostGroup::new(), switch: SwitchGroup::new(), firewall: FirewallGroup::new(), + worker_host: HostGroup::new(), + storage_host: HostGroup::new(), + control_plane_host: HostGroup::new(), } } } diff --git a/harmony-rs/harmony/src/domain/topology/load_balancer.rs b/harmony-rs/harmony/src/domain/topology/load_balancer.rs index cf79ac3..9f5226a 100644 --- a/harmony-rs/harmony/src/domain/topology/load_balancer.rs +++ b/harmony-rs/harmony/src/domain/topology/load_balancer.rs @@ -1,10 +1,11 @@ +use crate::executors::ExecutorError; use super::{IpAddress, LogicalHost}; pub trait LoadBalancer: Send + Sync { - fn add_backend(&mut self, backend: Backend) -> Result<(), LoadBalancerError>; - fn remove_backend(&mut self, backend_id: &str) -> Result<(), LoadBalancerError>; - fn add_frontend(&mut self, frontend: Frontend) -> Result<(), LoadBalancerError>; - fn remove_frontend(&mut self, frontend_id: &str) -> Result<(), LoadBalancerError>; + fn add_backend(&mut self, backend: Backend) -> Result<(), ExecutorError>; + fn remove_backend(&mut self, backend_id: &str) -> Result<(), ExecutorError>; + fn add_frontend(&mut self, frontend: Frontend) -> Result<(), ExecutorError>; + fn remove_frontend(&mut self, frontend_id: &str) -> Result<(), ExecutorError>; fn list_backends(&self) -> Vec; fn list_frontends(&self) -> Vec; fn get_ip(&self) -> IpAddress; @@ -17,8 +18,6 @@ impl std::fmt::Debug for dyn LoadBalancer { } } -pub struct LoadBalancerError; - #[derive(Clone, Debug)] pub struct Backend { pub id: String, diff --git a/harmony-rs/harmony/src/domain/topology/network.rs b/harmony-rs/harmony/src/domain/topology/network.rs index 00189e4..8096af9 100644 --- a/harmony-rs/harmony/src/domain/topology/network.rs +++ b/harmony-rs/harmony/src/domain/topology/network.rs @@ -1,3 +1,7 @@ +use async_trait::async_trait; + +use crate::executors::ExecutorError; + use super::{IpAddress, LogicalHost}; #[derive(Debug)] @@ -14,8 +18,8 @@ impl std::fmt::Display for DHCPStaticEntry { } pub trait Firewall: Send + Sync { - fn add_rule(&mut self, rule: FirewallRule) -> Result<(), FirewallError>; - fn remove_rule(&mut self, rule_id: &str) -> Result<(), FirewallError>; + fn add_rule(&mut self, rule: FirewallRule) -> Result<(), ExecutorError>; + fn remove_rule(&mut self, rule_id: &str) -> Result<(), ExecutorError>; fn list_rules(&self) -> Vec; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; @@ -31,10 +35,11 @@ pub struct NetworkDomain { pub name: String, } +#[async_trait] pub trait DhcpServer: Send + Sync { - fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), DhcpError>; - fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), DhcpError>; - fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; + async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>; + async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>; + async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; } @@ -51,8 +56,8 @@ pub trait DnsServer: Send + Sync { name: &str, record_type: DnsRecordType, value: &str, - ) -> Result<(), DnsError>; - fn remove_record(&mut self, name: &str, record_type: DnsRecordType) -> Result<(), DnsError>; + ) -> Result<(), ExecutorError>; + fn remove_record(&mut self, name: &str, record_type: DnsRecordType) -> Result<(), ExecutorError>; fn list_records(&self) -> Vec; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; @@ -89,6 +94,9 @@ pub enum Action { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct MacAddress(pub [u8; 6]); +// TODO create a small macro to provide a nice API to initiate a MacAddress +// MacAddress::from!("00:90:7f:df:2c:23"), + impl MacAddress { pub fn dummy() -> Self { Self([0, 0, 0, 0, 0, 0]) @@ -119,8 +127,3 @@ pub struct DnsRecord { pub record_type: DnsRecordType, pub value: String, } - -// Error types -pub struct FirewallError; -pub struct DhcpError; -pub struct DnsError; diff --git a/harmony-rs/harmony/src/infra/opnsense/config.rs b/harmony-rs/harmony/src/infra/opnsense/config.rs new file mode 100644 index 0000000..5ed2f40 --- /dev/null +++ b/harmony-rs/harmony/src/infra/opnsense/config.rs @@ -0,0 +1,43 @@ +use crate::executors::ExecutorError; + +pub struct OPNSenseXmlConfigEditor; + +impl OPNSenseXmlConfigEditor { + pub(crate) async fn add(vec: Vec<&str>, xml_entry: &str) -> Result<(), ExecutorError>{ + todo!() + } +} + +#[cfg(test)] +mod test { + use minidom::Element; + use std::fs; + use std::io::{BufReader, Read}; + use std::process::Command; + + // #[test] + // fn should_not_alter_config() { + // let path = "./private_repos/affilium_mcd/private/config.xml"; + // // TODO + // // Load file to string + // // Parse with minidom (ex: `let root: Element = file_str.parse().unwrap()`) + // // save file with suffix name + // // Verify that file is still identical with md5sum after removing indentation + // } + + #[test] + fn should_not_alter_config() { + let path = "/home/jeangab/work/nationtech/harmony/harmony-rs/affilium_mcd/private/config.xml"; + let output_path = format!("{}.test_output", path); + + // Load file to string + let file_str = fs::read_to_string(path).expect("Failed to read file"); + + // Parse with minidom + let root: Element = file_str.parse().unwrap(); + + // Save file with suffix name + fs::write(&output_path, String::from(&root)).expect("Failed to write output file"); + } + +} diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs index 0f81d40..fd7bd6c 100644 --- a/harmony-rs/harmony/src/infra/opnsense/mod.rs +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -1,15 +1,20 @@ mod management; +mod config; +use async_trait::async_trait; pub use management::*; -use crate::{executors::ExecutorError, topology::{ - Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallError, FirewallRule, - Frontend, IpAddress, LoadBalancer, LoadBalancerError, LogicalHost, -}}; +use crate::{ + executors::ExecutorError, infra::opnsense::config::OPNSenseXmlConfigEditor, topology::{ + Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallRule, Frontend, + IpAddress, LoadBalancer, LogicalHost, + } +}; use derive_new::new; #[derive(new, Clone)] pub struct OPNSenseFirewall { host: LogicalHost, + cluster_nic_name: String, } impl OPNSenseFirewall { @@ -17,17 +22,14 @@ impl OPNSenseFirewall { self.host.ip } - fn save_xml_to_config(&self, xml_entry: &str) -> Result<(), ExecutorError> { - todo!("Save XML Entry in opnsense /conf/config.xml {xml_entry}"); - } } impl Firewall for OPNSenseFirewall { - fn add_rule(&mut self, _rule: FirewallRule) -> Result<(), FirewallError> { + fn add_rule(&mut self, _rule: FirewallRule) -> Result<(), ExecutorError> { todo!() } - fn remove_rule(&mut self, _rule_id: &str) -> Result<(), FirewallError> { + fn remove_rule(&mut self, _rule_id: &str) -> Result<(), ExecutorError> { todo!() } @@ -44,19 +46,19 @@ impl Firewall for OPNSenseFirewall { } impl LoadBalancer for OPNSenseFirewall { - fn add_backend(&mut self, _backend: Backend) -> Result<(), LoadBalancerError> { + fn add_backend(&mut self, _backend: Backend) -> Result<(), ExecutorError> { todo!() } - fn remove_backend(&mut self, _backend_id: &str) -> Result<(), LoadBalancerError> { + fn remove_backend(&mut self, _backend_id: &str) -> Result<(), ExecutorError> { todo!() } - fn add_frontend(&mut self, _frontend: Frontend) -> Result<(), LoadBalancerError> { + fn add_frontend(&mut self, _frontend: Frontend) -> Result<(), ExecutorError> { todo!() } - fn remove_frontend(&mut self, _frontend_id: &str) -> Result<(), LoadBalancerError> { + fn remove_frontend(&mut self, _frontend_id: &str) -> Result<(), ExecutorError> { todo!() } @@ -76,36 +78,40 @@ impl LoadBalancer for OPNSenseFirewall { } } +#[async_trait] impl DhcpServer for OPNSenseFirewall { - fn add_static_mapping( - &self, - entry: &DHCPStaticEntry, - ) -> Result<(), crate::topology::DhcpError> { + async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> { let mac = &entry.mac; let name = &entry.name; let ip = &entry.ip; - let xml_entry = format!(" -> {mac} -> {ip} -> {name} -> {name} -> -> -> -> "); - self.save_xml_to_config(&xml_entry)?; + let xml_entry = format!( + " + {mac} + {ip} + {name} + {name} + + + + " + ); + // XML Path : + OPNSenseXmlConfigEditor::add( + vec!["opnsense", "dhcpd", &self.cluster_nic_name, "staticmap"], + &xml_entry, + ).await?; todo!("Register {:?}", entry) } - fn remove_static_mapping( + async fn remove_static_mapping( &self, _mac: &crate::topology::MacAddress, - ) -> Result<(), crate::topology::DhcpError> { + ) -> Result<(), ExecutorError> { todo!() } - fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> { + async fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> { todo!() } @@ -122,7 +128,7 @@ impl DnsServer for OPNSenseFirewall { _name: &str, _record_type: crate::topology::DnsRecordType, _value: &str, - ) -> Result<(), crate::topology::DnsError> { + ) -> Result<(), ExecutorError> { todo!() } @@ -130,7 +136,7 @@ impl DnsServer for OPNSenseFirewall { &mut self, _name: &str, _record_type: crate::topology::DnsRecordType, - ) -> Result<(), crate::topology::DnsError> { + ) -> Result<(), ExecutorError> { todo!() } diff --git a/harmony-rs/harmony/src/modules/dhcp.rs b/harmony-rs/harmony/src/modules/dhcp.rs index ec100ea..246576b 100644 --- a/harmony-rs/harmony/src/modules/dhcp.rs +++ b/harmony-rs/harmony/src/modules/dhcp.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use async_trait::async_trait; use derive_new::new; use log::info; @@ -7,7 +9,6 @@ use crate::{ data::{Id, Version}, interpret::InterpretStatus, }, - infra::executors::russh::RusshClient, interpret::{Interpret, InterpretError, InterpretName, Outcome}, inventory::Inventory, topology::{DHCPStaticEntry, HAClusterTopology, HostBinding}, @@ -114,9 +115,8 @@ impl Interpret for DhcpInterpret { topology: &HAClusterTopology, ) -> Result { info!("Executing {} on inventory {inventory:?}", self.get_name()); - let ssh_client = RusshClient {}; - let entries: Vec = self + let dhcp_entries: Vec = self .score .host_binding .iter() @@ -126,16 +126,16 @@ impl Interpret for DhcpInterpret { ip: binding.logical_host.ip, }) .collect(); - info!("DHCPStaticEntry : {:?}", entries); + info!("DHCPStaticEntry : {:?}", dhcp_entries); - let dhcp = topology.dhcp_server.clone(); + let dhcp = Arc::new(Box::new(topology.dhcp_server.clone())); info!("DHCP server : {:?}", dhcp); - entries.iter().for_each(|entry| { - match dhcp.add_static_mapping(&entry) { + for entry in dhcp_entries.into_iter() { + match dhcp.add_static_mapping(&entry).await { Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry), Err(_) => todo!(), } - }); + } todo!("Configure DHCPServer"); Ok(Outcome::new( From 6a5ebdbac7c8308d06610c29fc72d1edfd956b5c Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Fri, 4 Oct 2024 11:26:55 -0400 Subject: [PATCH 03/19] wip: xml parser --- harmony-rs/Cargo.lock | 97 +++++++------------ harmony-rs/Cargo.toml | 2 +- harmony-rs/harmony/Cargo.toml | 2 +- .../harmony/src/infra/opnsense/config.rs | 8 +- 4 files changed, 40 insertions(+), 69 deletions(-) diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index 6e0abb7..ff15101 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -247,15 +247,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" -[[package]] -name = "castaway" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" -dependencies = [ - "rustversion", -] - [[package]] name = "cbc" version = "0.1.2" @@ -313,19 +304,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" -[[package]] -name = "compact_str" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "ryu", - "static_assertions", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -828,7 +806,6 @@ dependencies = [ "env_logger", "libredfish", "log", - "minidom", "reqwest", "russh", "rust-ipmi", @@ -836,6 +813,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "xml_dom", ] [[package]] @@ -1081,15 +1059,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minidom" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e394a0e3c7ccc2daea3dffabe82f09857b6b510cb25af87d54bf3e910ac1642d" -dependencies = [ - "rxml", -] - [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1452,6 +1421,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.37" @@ -1753,31 +1731,6 @@ dependencies = [ "base64", ] -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "rxml" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc94b580d0f5a6b7a2d604e597513d3c673154b52ddeccd1d5c32360d945ee" -dependencies = [ - "bytes", - "rxml_validation", -] - -[[package]] -name = "rxml_validation" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4" -dependencies = [ - "compact_str", -] - [[package]] name = "ryu" version = "1.0.18" @@ -2039,12 +1992,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "subtle" version = "2.6.1" @@ -2218,9 +2165,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -2583,6 +2542,18 @@ dependencies = [ "tap", ] +[[package]] +name = "xml_dom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836bd445caf6b9e969199f2a9667d58b433b286ddb515764303ab75a6d17e51f" +dependencies = [ + "log", + "quick-xml", + "regex", + "tracing", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 954f000..0263f23 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -17,4 +17,4 @@ derive-new = "0.7.0" async-trait = "0.1.82" tokio = { version = "1.40.0", features = ["io-std"] } cidr = "0.2.3" -minidom = "0.16.0" +xml_dom = "0.2.8" diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index b017d54..5917bdf 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -17,4 +17,4 @@ log = { workspace = true } env_logger = { workspace = true } async-trait = { workspace = true } cidr = { workspace = true } -minidom = { workspace = true } +xml_dom = { workspace = true } diff --git a/harmony-rs/harmony/src/infra/opnsense/config.rs b/harmony-rs/harmony/src/infra/opnsense/config.rs index 5ed2f40..309e59d 100644 --- a/harmony-rs/harmony/src/infra/opnsense/config.rs +++ b/harmony-rs/harmony/src/infra/opnsense/config.rs @@ -10,11 +10,12 @@ impl OPNSenseXmlConfigEditor { #[cfg(test)] mod test { - use minidom::Element; use std::fs; use std::io::{BufReader, Read}; use std::process::Command; + use xml_dom::parser::read_xml; + // #[test] // fn should_not_alter_config() { // let path = "./private_repos/affilium_mcd/private/config.xml"; @@ -34,10 +35,9 @@ mod test { let file_str = fs::read_to_string(path).expect("Failed to read file"); // Parse with minidom - let root: Element = file_str.parse().unwrap(); + let root = read_xml(&file_str).unwrap(); - // Save file with suffix name - fs::write(&output_path, String::from(&root)).expect("Failed to write output file"); + assert_eq!(&root.to_string(), &file_str); } } From 32cea6c3ff460e1ae87e4a78a1756884c35dbb25 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Sun, 13 Oct 2024 08:48:56 -0400 Subject: [PATCH 04/19] wip: New crate opnsense-config --- harmony-rs/Cargo.lock | 85 ++++++++++++++++--- harmony-rs/Cargo.toml | 3 + harmony-rs/opnsense-config/Cargo.toml | 14 +++ harmony-rs/opnsense-config/adr/001-yaserde.md | 38 +++++++++ harmony-rs/opnsense-config/src/config.rs | 55 ++++++++++++ harmony-rs/opnsense-config/src/error.rs | 13 +++ harmony-rs/opnsense-config/src/lib.rs | 6 ++ .../opnsense-config/src/modules/dhcp.rs | 24 ++++++ harmony-rs/opnsense-config/src/modules/mod.rs | 2 + .../opnsense-config/src/modules/opnsense.rs | 44 ++++++++++ 10 files changed, 272 insertions(+), 12 deletions(-) create mode 100644 harmony-rs/opnsense-config/Cargo.toml create mode 100644 harmony-rs/opnsense-config/adr/001-yaserde.md create mode 100644 harmony-rs/opnsense-config/src/config.rs create mode 100644 harmony-rs/opnsense-config/src/error.rs create mode 100644 harmony-rs/opnsense-config/src/lib.rs create mode 100644 harmony-rs/opnsense-config/src/modules/dhcp.rs create mode 100644 harmony-rs/opnsense-config/src/modules/mod.rs create mode 100644 harmony-rs/opnsense-config/src/modules/opnsense.rs diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index ff15101..db0ca21 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -124,7 +124,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -400,7 +400,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -428,7 +428,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -695,7 +695,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -822,6 +822,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1208,7 +1214,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1229,6 +1235,20 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opnsense-config" +version = "0.1.0" +dependencies = [ + "async-trait", + "russh", + "russh-keys", + "thiserror", + "tokio", + "xml-rs", + "yaserde", + "yaserde_derive", +] + [[package]] name = "p256" version = "0.13.2" @@ -1826,7 +1846,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1998,6 +2018,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.77" @@ -2072,7 +2103,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -2115,7 +2146,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -2177,7 +2208,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -2298,7 +2329,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -2332,7 +2363,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2542,6 +2573,12 @@ dependencies = [ "tap", ] +[[package]] +name = "xml-rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" + [[package]] name = "xml_dom" version = "0.2.8" @@ -2554,6 +2591,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "yaserde" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8198a8ee4113411b7be1086e10b654f83653c01e4bd176fb98fe9d11951af5e" +dependencies = [ + "log", + "xml-rs", +] + +[[package]] +name = "yaserde_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82eaa312529cc56b0df120253c804a8c8d593d2b5fe8deb5402714f485f62d79" +dependencies = [ + "heck", + "log", + "proc-macro2", + "quote", + "syn 1.0.109", + "xml-rs", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -2572,7 +2633,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 0263f23..89dbb19 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "private_repos/*", "harmony", + "opnsense-config", ] [workspace.package] @@ -18,3 +19,5 @@ async-trait = "0.1.82" tokio = { version = "1.40.0", features = ["io-std"] } cidr = "0.2.3" xml_dom = "0.2.8" +russh = "0.45.0" +russh-keys = "0.45.0" diff --git a/harmony-rs/opnsense-config/Cargo.toml b/harmony-rs/opnsense-config/Cargo.toml new file mode 100644 index 0000000..8499311 --- /dev/null +++ b/harmony-rs/opnsense-config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "opnsense-config" +version = "0.1.0" +edition = "2021" + +[dependencies] +russh = { workspace = true } +russh-keys = { workspace = true } +yaserde = "0.11.1" +yaserde_derive = "0.11.1" +xml-rs = "0.8" +thiserror = "1.0" +async-trait = { workspace = true } +tokio = { workspace = true } diff --git a/harmony-rs/opnsense-config/adr/001-yaserde.md b/harmony-rs/opnsense-config/adr/001-yaserde.md new file mode 100644 index 0000000..69e61df --- /dev/null +++ b/harmony-rs/opnsense-config/adr/001-yaserde.md @@ -0,0 +1,38 @@ +# Architecture Decision Record: Using yaserde for OPNsense Config Parsing + +- Status : Proposed +- Author : Jean-Gabriel Gill-Couture + +## Context + +We need to parse and manipulate the OPNsense config.xml file in our Rust crate. We considered several XML parsing libraries, including quick-xml, xml-dom, minidom and yaserde. Each library has its own strengths and trade-offs in terms of performance, ease of use, and robustness. + +## Decision + +We have decided to use yaserde for parsing and manipulating the OPNsense config.xml file. + +## Rationale + +1. Type Safety: yaserde allows us to define a complete Rust representation of the config.xml structure. This provides strong type safety and makes it easier to catch errors at compile-time rather than runtime. + +2. Robustness: By mapping the entire config structure to Rust types, we ensure that our code interacts with the config in a well-defined manner. This reduces the risk of runtime errors due to unexpected XML structures. + +3. Ease of Use: Working with native Rust types is more intuitive and less error-prone than manipulating XML directly. This can lead to more maintainable and readable code. + +4. Memory Usage: While yaserde may use more memory than streaming parsers like quick-xml, the OPNsense config files are typically not large enough for this to be a significant concern. We prioritize robustness and ease of use over minimal memory usage in this context. + +5. Serialization/Deserialization: yaserde provides both deserialization (XML to Rust structs) and serialization (Rust structs to XML) out of the box, which simplifies our implementation. + +## Consequences + +Positive: +- Increased type safety and robustness in handling the config.xml structure. +- More intuitive API for developers working with the config. +- Easier to extend and maintain the code that interacts with different parts of the config. + +Negative: +- It will be harder to maintain when there are breaking changes in the config.xml format. Any structural changes in the XML will require corresponding updates to our Rust struct definitions. +- Slightly higher memory usage compared to streaming parsers. +- Initial development time may be longer due to the need to define the entire config structure upfront. + +We accept the trade-off of potentially more difficult maintenance in the face of breaking config.xml changes, as we believe the benefits of increased robustness and type safety outweigh this drawback. When OPNsense releases updates that change the config.xml structure, we will need to update our Rust struct definitions accordingly. diff --git a/harmony-rs/opnsense-config/src/config.rs b/harmony-rs/opnsense-config/src/config.rs new file mode 100644 index 0000000..99c8a45 --- /dev/null +++ b/harmony-rs/opnsense-config/src/config.rs @@ -0,0 +1,55 @@ +use crate::error::Error; +use crate::modules::opnsense::OPNsense; +use russh::client::{Config as SshConfig, Handler}; +use std::sync::Arc; +use russh_keys::key; + +pub struct Config { + opnsense: OPNsense, + ssh_config: Arc, + host: String, + username: String, +} + +impl Config { + pub async fn new(host: &str, username: &str, key_path: &str) -> Result { + let key = russh_keys::load_secret_key(key_path, None).expect("Secret key failed loading"); + let config = SshConfig::default(); + let config = Arc::new(config); + + let mut ssh = russh::client::connect(config.clone(), host, Handler).await?; + ssh.authenticate_publickey(username, key).await?; + + let (xml, _) = ssh.exec(true, "cat /conf/config.xml").await?; + let xml = String::from_utf8(xml).map_err(|e| Error::Config(e.to_string()))?; + + let opnsense = yaserde::de::from_str(&xml).map_err(|e| Error::Xml(e.to_string()))?; + + Ok(Self { + opnsense, + ssh_config: config, + host: host.to_string(), + username: username.to_string(), + }) + } + + pub fn get_opnsense(&self) -> &OPNsense { + &self.opnsense + } + + pub fn get_opnsense_mut(&mut self) -> &mut OPNsense { + &mut self.opnsense + } + + pub async fn save(&self) -> Result<(), Error> { + let xml = yaserde::ser::to_string(&self.opnsense).map_err(|e| Error::Xml(e.to_string()))?; + + let mut ssh = russh::client::connect(self.ssh_config.clone(), &self.host, Handler).await?; + ssh.authenticate_publickey(&self.username, key).await?; + + ssh.exec(true, &format!("echo '{}' > /conf/config.xml", xml)) + .await?; + + Ok(()) + } +} diff --git a/harmony-rs/opnsense-config/src/error.rs b/harmony-rs/opnsense-config/src/error.rs new file mode 100644 index 0000000..733746b --- /dev/null +++ b/harmony-rs/opnsense-config/src/error.rs @@ -0,0 +1,13 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("XML error: {0}")] + Xml(String), + #[error("SSH error: {0}")] + Ssh(#[from] russh::Error), + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), + #[error("Config error: {0}")] + Config(String), +} diff --git a/harmony-rs/opnsense-config/src/lib.rs b/harmony-rs/opnsense-config/src/lib.rs new file mode 100644 index 0000000..7673ffc --- /dev/null +++ b/harmony-rs/opnsense-config/src/lib.rs @@ -0,0 +1,6 @@ +pub mod config; +pub mod modules; +pub mod error; + +pub use config::Config; +pub use error::Error; diff --git a/harmony-rs/opnsense-config/src/modules/dhcp.rs b/harmony-rs/opnsense-config/src/modules/dhcp.rs new file mode 100644 index 0000000..2bb7362 --- /dev/null +++ b/harmony-rs/opnsense-config/src/modules/dhcp.rs @@ -0,0 +1,24 @@ +use super::opnsense::{OPNsense, StaticMap}; + +pub struct DhcpConfig<'a> { + opnsense: &'a mut OPNsense, +} + +impl<'a> DhcpConfig<'a> { + pub fn new(opnsense: &'a mut OPNsense) -> Self { + Self { opnsense } + } + + pub fn add_static_mapping(&mut self, mac: String, ipaddr: String, hostname: String) { + let static_map = StaticMap { + mac, + ipaddr, + hostname, + }; + self.opnsense.dhcpd.lan.staticmaps.push(static_map); + } + + pub fn get_static_mappings(&self) -> &[StaticMap] { + &self.opnsense.dhcpd.lan.staticmaps + } +} diff --git a/harmony-rs/opnsense-config/src/modules/mod.rs b/harmony-rs/opnsense-config/src/modules/mod.rs new file mode 100644 index 0000000..d847dcb --- /dev/null +++ b/harmony-rs/opnsense-config/src/modules/mod.rs @@ -0,0 +1,2 @@ +pub mod opnsense; +pub mod dhcp; diff --git a/harmony-rs/opnsense-config/src/modules/opnsense.rs b/harmony-rs/opnsense-config/src/modules/opnsense.rs new file mode 100644 index 0000000..2ca04c0 --- /dev/null +++ b/harmony-rs/opnsense-config/src/modules/opnsense.rs @@ -0,0 +1,44 @@ +use yaserde_derive::{YaDeserialize, YaSerialize}; + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +#[yaserde(rename = "opnsense")] +pub struct OPNsense { + #[yaserde(rename = "dhcpd")] + pub dhcpd: Dhcpd, + // Add other top-level elements as needed +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Dhcpd { + #[yaserde(rename = "lan")] + pub lan: DhcpInterface, + // Add other interfaces as needed +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct DhcpInterface { + #[yaserde(rename = "enable")] + pub enable: bool, + #[yaserde(rename = "range")] + pub range: DhcpRange, + #[yaserde(rename = "staticmap")] + pub staticmaps: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct DhcpRange { + #[yaserde(rename = "from")] + pub from: String, + #[yaserde(rename = "to")] + pub to: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct StaticMap { + #[yaserde(rename = "mac")] + pub mac: String, + #[yaserde(rename = "ipaddr")] + pub ipaddr: String, + #[yaserde(rename = "hostname")] + pub hostname: String, +} From 8459c38499e751669d0373735e47d1a44b770410 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Mon, 14 Oct 2024 07:53:02 -0400 Subject: [PATCH 05/19] wip(opnsense-config): It compiles now, still have to test it --- harmony-rs/opnsense-config/src/config.rs | 64 ++++++++++++++++++++---- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/harmony-rs/opnsense-config/src/config.rs b/harmony-rs/opnsense-config/src/config.rs index 99c8a45..205b7d3 100644 --- a/harmony-rs/opnsense-config/src/config.rs +++ b/harmony-rs/opnsense-config/src/config.rs @@ -1,27 +1,68 @@ use crate::error::Error; use crate::modules::opnsense::OPNsense; +use async_trait::async_trait; use russh::client::{Config as SshConfig, Handler}; -use std::sync::Arc; use russh_keys::key; +use std::{fmt::Write as _, sync::Arc}; +use tokio::io::AsyncWriteExt; + +struct Client {} + +// More SSH event handlers +// can be defined in this trait +// In this example, we're only using Channel, so these aren't needed. +#[async_trait] +impl Handler for Client { + type Error = Error; + + async fn check_server_key( + &mut self, + _server_public_key: &key::PublicKey, + ) -> Result { + Ok(true) + } +} pub struct Config { opnsense: OPNsense, ssh_config: Arc, host: String, username: String, + key: Arc, } impl Config { pub async fn new(host: &str, username: &str, key_path: &str) -> Result { let key = russh_keys::load_secret_key(key_path, None).expect("Secret key failed loading"); + let key = Arc::new(key); let config = SshConfig::default(); let config = Arc::new(config); - let mut ssh = russh::client::connect(config.clone(), host, Handler).await?; - ssh.authenticate_publickey(username, key).await?; + let mut ssh = russh::client::connect(config.clone(), host, Client {}).await?; + ssh.authenticate_publickey(username, key.clone()).await?; - let (xml, _) = ssh.exec(true, "cat /conf/config.xml").await?; - let xml = String::from_utf8(xml).map_err(|e| Error::Config(e.to_string()))?; + let mut channel = ssh.channel_open_session().await?; + + channel.exec(true, "cat /conf/config.xml").await?; + let mut code; + let mut output = String::new(); + loop { + let Some(msg) = channel.wait().await else { + break; + }; + + match msg { + russh::ChannelMsg::Data { ref data } => { + write!(&mut output, "{:?}", data); + println!("Got data {output}"); + } + russh::ChannelMsg::ExitStatus { exit_status } => { + code = Some(exit_status); + } + _ => todo!(), + } + } + let xml = output; let opnsense = yaserde::de::from_str(&xml).map_err(|e| Error::Xml(e.to_string()))?; @@ -30,6 +71,7 @@ impl Config { ssh_config: config, host: host.to_string(), username: username.to_string(), + key, }) } @@ -44,12 +86,14 @@ impl Config { pub async fn save(&self) -> Result<(), Error> { let xml = yaserde::ser::to_string(&self.opnsense).map_err(|e| Error::Xml(e.to_string()))?; - let mut ssh = russh::client::connect(self.ssh_config.clone(), &self.host, Handler).await?; - ssh.authenticate_publickey(&self.username, key).await?; + let mut ssh = + russh::client::connect(self.ssh_config.clone(), &self.host, Client {}).await?; + ssh.authenticate_publickey(&self.username, self.key.clone()).await?; + todo!("Writing config file to remote host {xml}"); - ssh.exec(true, &format!("echo '{}' > /conf/config.xml", xml)) - .await?; + // ssh.exec(true, &format!("echo '{}' > /conf/config.xml", xml)) + // .await?; - Ok(()) + // Ok(()) } } From b332723431f581db7254fd5da9e79b950e8b36f4 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Mon, 14 Oct 2024 16:13:20 -0400 Subject: [PATCH 06/19] feat(opnsense-config): Refactor config to use a repository trait, implement file based and ssh, save a full config file --- harmony-rs/Cargo.lock | 8 +- harmony-rs/opnsense-config/Cargo.toml | 6 +- harmony-rs/opnsense-config/src/config.rs | 170 +- .../src/tests/data/config-full-1.xml | 2778 +++++++++++++++++ 4 files changed, 2917 insertions(+), 45 deletions(-) create mode 100644 harmony-rs/opnsense-config/src/tests/data/config-full-1.xml diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index db0ca21..6fc9ac4 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -1240,8 +1240,10 @@ name = "opnsense-config" version = "0.1.0" dependencies = [ "async-trait", + "log", "russh", "russh-keys", + "serde", "thiserror", "tokio", "xml-rs", @@ -2594,8 +2596,7 @@ dependencies = [ [[package]] name = "yaserde" version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8198a8ee4113411b7be1086e10b654f83653c01e4bd176fb98fe9d11951af5e" +source = "git+https://git.nationtech.io/NationTech/yaserde#353558737f3ef73e93164c596ff920d4344f30a3" dependencies = [ "log", "xml-rs", @@ -2604,8 +2605,7 @@ dependencies = [ [[package]] name = "yaserde_derive" version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82eaa312529cc56b0df120253c804a8c8d593d2b5fe8deb5402714f485f62d79" +source = "git+https://git.nationtech.io/NationTech/yaserde#353558737f3ef73e93164c596ff920d4344f30a3" dependencies = [ "heck", "log", diff --git a/harmony-rs/opnsense-config/Cargo.toml b/harmony-rs/opnsense-config/Cargo.toml index 8499311..d79b74e 100644 --- a/harmony-rs/opnsense-config/Cargo.toml +++ b/harmony-rs/opnsense-config/Cargo.toml @@ -4,10 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] +serde = { version = "1.0.123", features = [ "derive" ] } +log = { workspace = true } russh = { workspace = true } russh-keys = { workspace = true } -yaserde = "0.11.1" -yaserde_derive = "0.11.1" +yaserde = { git = "https://git.nationtech.io/NationTech/yaserde" } +yaserde_derive = { git = "https://git.nationtech.io/NationTech/yaserde" } xml-rs = "0.8" thiserror = "1.0" async-trait = { workspace = true } diff --git a/harmony-rs/opnsense-config/src/config.rs b/harmony-rs/opnsense-config/src/config.rs index 205b7d3..41f2ee3 100644 --- a/harmony-rs/opnsense-config/src/config.rs +++ b/harmony-rs/opnsense-config/src/config.rs @@ -1,16 +1,19 @@ use crate::error::Error; use crate::modules::opnsense::OPNsense; use async_trait::async_trait; +use log::info; use russh::client::{Config as SshConfig, Handler}; use russh_keys::key; -use std::{fmt::Write as _, sync::Arc}; -use tokio::io::AsyncWriteExt; +use std::{fs, net::Ipv4Addr, path::Path, sync::Arc}; + +#[async_trait] +pub trait ConfigRepository: std::fmt::Debug { + async fn load(&self) -> Result; + async fn save(&self, content: &str) -> Result<(), Error>; +} struct Client {} -// More SSH event handlers -// can be defined in this trait -// In this example, we're only using Channel, so these aren't needed. #[async_trait] impl Handler for Client { type Error = Error; @@ -23,55 +26,131 @@ impl Handler for Client { } } -pub struct Config { - opnsense: OPNsense, +#[derive(Debug)] +pub struct SshConfigRepository { ssh_config: Arc, - host: String, username: String, key: Arc, + host: (Ipv4Addr, u16), } -impl Config { - pub async fn new(host: &str, username: &str, key_path: &str) -> Result { - let key = russh_keys::load_secret_key(key_path, None).expect("Secret key failed loading"); - let key = Arc::new(key); - let config = SshConfig::default(); - let config = Arc::new(config); +impl SshConfigRepository { + pub fn new( + host: (Ipv4Addr, u16), + username: String, + key: Arc, + ssh_config: Arc, + ) -> Self { + Self { + ssh_config, + username, + key, + host, + } + } +} - let mut ssh = russh::client::connect(config.clone(), host, Client {}).await?; - ssh.authenticate_publickey(username, key.clone()).await?; +#[async_trait] +impl ConfigRepository for SshConfigRepository { + async fn load(&self) -> Result { + let mut ssh = russh::client::connect(self.ssh_config.clone(), self.host, Client {}).await?; + ssh.authenticate_publickey(&self.username, self.key.clone()) + .await?; let mut channel = ssh.channel_open_session().await?; channel.exec(true, "cat /conf/config.xml").await?; - let mut code; - let mut output = String::new(); + let mut output: Vec = vec![]; + loop { + let Some(msg) = channel.wait().await else { + break; + }; + + info!("got msg {:?}", msg); + match msg { + russh::ChannelMsg::Data { ref data } => { + output.append(&mut data.to_vec()); + } + russh::ChannelMsg::ExitStatus { .. } => {} + russh::ChannelMsg::WindowAdjusted { .. } => {} + russh::ChannelMsg::Success { .. } => {} + russh::ChannelMsg::Eof { .. } => {} + _ => todo!(), + } + } + Ok(String::from_utf8(output).expect("Valid utf-8 bytes")) + } + + async fn save(&self, content: &str) -> Result<(), Error> { + let mut ssh = russh::client::connect(self.ssh_config.clone(), self.host, Client {}).await?; + ssh.authenticate_publickey(&self.username, self.key.clone()) + .await?; + + let mut channel = ssh.channel_open_session().await?; + + let command = format!( + "echo '{}' > /conf/config.xml", + content.replace("'", "'\"'\"'") + ); + channel.exec(true, command.as_bytes()).await?; + loop { let Some(msg) = channel.wait().await else { break; }; match msg { - russh::ChannelMsg::Data { ref data } => { - write!(&mut output, "{:?}", data); - println!("Got data {output}"); - } russh::ChannelMsg::ExitStatus { exit_status } => { - code = Some(exit_status); + if exit_status != 0 { + return Err(Error::Ssh(russh::Error::Disconnect)); + } } - _ => todo!(), + _ => {} } } - let xml = output; + + Ok(()) + } +} + +#[derive(Debug)] +pub struct LocalFileConfigRepository { + file_path: String, +} + +impl LocalFileConfigRepository { + pub fn new(file_path: String) -> Self { + Self { file_path } + } +} + +#[async_trait] +impl ConfigRepository for LocalFileConfigRepository { + async fn load(&self) -> Result { + Ok(fs::read_to_string(&self.file_path)?) + } + + async fn save(&self, content: &str) -> Result<(), Error> { + Ok(fs::write(&self.file_path, content)?) + } +} + +#[derive(Debug)] +pub struct Config { + opnsense: OPNsense, + repository: Box, +} + +impl Config { + pub async fn new(repository: Box) -> Result { + let xml = repository.load().await?; + info!("xml {}", xml); let opnsense = yaserde::de::from_str(&xml).map_err(|e| Error::Xml(e.to_string()))?; Ok(Self { opnsense, - ssh_config: config, - host: host.to_string(), - username: username.to_string(), - key, + repository, }) } @@ -85,15 +164,28 @@ impl Config { pub async fn save(&self) -> Result<(), Error> { let xml = yaserde::ser::to_string(&self.opnsense).map_err(|e| Error::Xml(e.to_string()))?; - - let mut ssh = - russh::client::connect(self.ssh_config.clone(), &self.host, Client {}).await?; - ssh.authenticate_publickey(&self.username, self.key.clone()).await?; - todo!("Writing config file to remote host {xml}"); - - // ssh.exec(true, &format!("echo '{}' > /conf/config.xml", xml)) - // .await?; - - // Ok(()) + self.repository.save(&xml).await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[tokio::test] + async fn test_load_config_from_local_file() { + let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + test_file_path.push("src/tests/data/config-full-1.xml"); + + let config_file_path = test_file_path.to_str().unwrap().to_string(); + println!("File path {config_file_path}"); + let repository = Box::new(LocalFileConfigRepository::new(config_file_path)); + let config = Config::new(repository) + .await + .expect("Failed to load config"); + + println!("Config {:?}", config); + assert!(false); } } diff --git a/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml b/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml new file mode 100644 index 0000000..1feaa2e --- /dev/null +++ b/harmony-rs/opnsense-config/src/tests/data/config-full-1.xml @@ -0,0 +1,2778 @@ + + + opnsense + + + Increase UFS read-ahead speeds to match the state of hard drives and NCQ. + vfs.read_max + default + + + Set the ephemeral port range to be lower. + net.inet.ip.portrange.first + default + + + Drop packets to closed TCP ports without returning a RST + net.inet.tcp.blackhole + default + + + Do not send ICMP port unreachable messages for closed UDP ports + net.inet.udp.blackhole + default + + + Randomize the ID field in IP packets + net.inet.ip.random_id + default + + + + Source routing is another way for an attacker to try to reach non-routable addresses behind your box. + It can also be used to probe for information about your internal networks. These functions come enabled + as part of the standard FreeBSD core system. + + net.inet.ip.sourceroute + default + + + + Source routing is another way for an attacker to try to reach non-routable addresses behind your box. + It can also be used to probe for information about your internal networks. These functions come enabled + as part of the standard FreeBSD core system. + + net.inet.ip.accept_sourceroute + default + + + + This option turns off the logging of redirect packets because there is no limit and this could fill + up your logs consuming your whole hard drive. + + net.inet.icmp.log_redirect + default + + + Drop SYN-FIN packets (breaks RFC1379, but nobody uses it anyway) + net.inet.tcp.drop_synfin + default + + + Enable sending IPv6 redirects + net.inet6.ip6.redirect + default + + + Enable privacy settings for IPv6 (RFC 4941) + net.inet6.ip6.use_tempaddr + default + + + Prefer privacy addresses and use them over the normal addresses + net.inet6.ip6.prefer_tempaddr + default + + + Generate SYN cookies for outbound SYN-ACK packets + net.inet.tcp.syncookies + default + + + Maximum incoming/outgoing TCP datagram size (receive) + net.inet.tcp.recvspace + default + + + Maximum incoming/outgoing TCP datagram size (send) + net.inet.tcp.sendspace + default + + + Do not delay ACK to try and piggyback it onto a data packet + net.inet.tcp.delayed_ack + default + + + Maximum outgoing UDP datagram size + net.inet.udp.maxdgram + default + + + Handling of non-IP packets which are not passed to pfil (see if_bridge(4)) + net.link.bridge.pfil_onlyip + default + + + Set to 1 to additionally filter on the physical interface for locally destined packets + net.link.bridge.pfil_local_phys + default + + + Set to 0 to disable filtering on the incoming and outgoing member interfaces. + net.link.bridge.pfil_member + default + + + Set to 1 to enable filtering on the bridge interface + net.link.bridge.pfil_bridge + default + + + Allow unprivileged access to tap(4) device nodes + net.link.tap.user_open + default + + + Randomize PID's (see src/sys/kern/kern_fork.c: sysctl_kern_randompid()) + kern.randompid + default + + + Maximum size of the IP input queue + net.inet.ip.intr_queue_maxlen + default + + + Disable CTRL+ALT+Delete reboot from keyboard. + hw.syscons.kbd_reboot + default + + + Hint at default settings for serial console in case the autodetect is not working + hw.uart.console + default + + + Enable TCP extended debugging + net.inet.tcp.log_debug + default + + + Set ICMP Limits + net.inet.icmp.icmplim + default + + + TCP Offload Engine + net.inet.tcp.tso + default + + + UDP Checksums + net.inet.udp.checksum + default + + + Maximum socket buffer size + kern.ipc.maxsockbuf + default + + + Page Table Isolation (Meltdown mitigation, requires reboot.) + vm.pmap.pti + default + + + Disable Indirect Branch Restricted Speculation (Spectre V2 mitigation) + hw.ibrs_disable + default + + + Hide processes running as other groups + security.bsd.see_other_gids + default + + + Hide processes running as other users + security.bsd.see_other_uids + default + + + Enable/disable sending of ICMP redirects in response to IP packets for which a better, + and for the sender directly reachable, route and next hop is known. + + net.inet.ip.redirect + 0 + + + + Redirect attacks are the purposeful mass-issuing of ICMP type 5 packets. In a normal network, redirects + to the end stations should not be required. This option enables the NIC to drop all inbound ICMP redirect + packets without returning a response. + + net.inet.icmp.drop_redirect + 1 + + + Maximum outgoing UDP datagram size + net.local.dgram.maxdgram + default + + + + + + 115200 + serial + video + normal + OPN1 + somedomain.yourlocal.mcd + + admins + System Administrators + system + 1999 + 0 + 2000 + page-all + + + root + System Administrator + system + admins + $2y$10$5555555555o8dj21980j1doiOIJDIOASJOID!jidjeue19812y + 0 + + + + + + + $2y$11$55555555556D8198uOASIDJaiojdjd1oijdijosaoijdaoidOIASJDoijdoiadOASdoiK + user + someuser + + + + + + /bin/sh + 2000 + + 2001 + 2000 + Etc/UTC + 0.opnsense.pool.ntp.org 1.opnsense.pool.ntp.org 2.opnsense.pool.ntp.org 3.opnsense.pool.ntp.org + + https + 6155aba4c9375 + + + + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + hadp + hadp + hadp + + monthly + + aesni + 1 + 1 + 1 + + admins + 1 + + + + + + enabled + 1 + + 1 + + + + + os-ddclient,os-dyndns,os-haproxy,os-wireguard + + + + 1 + admins + yes + basic + + + + + en_US + + none + none + none + none + none + none + none + none + 1 + + + + + pppoe0 + WAN + 1 + 1 + + 1 + 1 + pppoe + + + em1 + LAN + 1 + + 192.168.20.1 + 24 + track6 + + 0 + + + 1 + Loopback + 1 + lo0 + 127.0.0.1 + ::1 + 8 + 128 + none + 1 + + + em5 + backup_sync + 1 + 1 + + 10.10.5.1 + 24 + + + 1 + WireGuard (Group) + wireguard + 1 + 1 + group + + + + 1 + 1 + openvpn + OpenVPN + group + 1 + + + + + + 1 + 192.168.20.1 + somedomain.yourlocal.mcd + hmac-md5 + + + + + 192.168.20.50 + 192.168.20.200 + + + 192.168.20.1 + + + 55:55:55:55:55:1c + 192.168.20.160 + somehost983 + someservire8 + + + + + + 55:55:55:55:55:1c + 192.168.20.155 + somehost893 + + + + + + 55:55:55:55:55:1c + 192.168.20.52 + somehost545 + + + + + + 55:55:55:55:55:1c + 192.168.20.51 + sw1 + someswitch2 + + + + + + 55:55:55:55:55:1c + 192.168.20.50 + hostswitch2 + switch-2 (bottom) + + + + + + + + + + + public + + + + 3 + + + + automatic + + + tcp + wan + + inet + + + + + nat_618812d37b8193.31302503 + host_3 + 22 + + 1 + + + wanip + 55555 + + + root@192.168.1.118 + + /firewall_nat_edit.php made changes + + + root@192.168.1.118 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_64fa19f4acba11.80049900 + 192.168.20.150 + 7860 + + 1 + + + wanip + 7860 + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_64fb1fbba71e29.76190279 + 192.168.20.150 + 7861 + + 1 + + + wanip + 7861 + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_64fb1fcea6d8b7.62653343 + 192.168.20.150 + 7862 + + 1 + + + wanip + 7862 + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_64fb1fdb48ff18.28912920 + 192.168.20.150 + 7863 + + 1 + + + wanip + 7863 + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_65aed5a66c4f65.25454286 + 192.168.20.140 + 8081 + + 1 + + + wanip + 10100 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_65aed67d497580.58958916 + 192.168.20.160 + 8080 + + 1 + + + wanip + 10101 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_65aed6961c4ea7.81903986 + 192.168.20.160 + 4000 + + 1 + + + wanip + 10110 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_65f4a5b928c9a7.52477383 + 192.168.20.115 + 5000 + + 1 + + + wanip + 10111 + + + root@192.168.20.147 + + /firewall_nat_edit.php made changes + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + Redirecting to someservice1 on somehost9 + + + + nat_662bb59baf7573.98640354 + 192.168.20.115 + 11434 + + 1 + + + wanip + 11000 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + port forwarding for virtual ip for someservice2 servers + + + + pass + someservice2_vip + 55555 + + 1 + + + wanip + 55555 + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + port forwarding for virtual ip for someservice2 servers + + + + nat_670979b3279551.73601303 + 1 + 192.168.20.1 + 55555 + + 1 + + + wanip + 55555 + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + port forwarding for reconfig of someservice2 somehost3 + + + + nat_6709763b6a6748.85579760 + 1 + 192.168.20.132 + 55555 + + 1 + + + wanip + 5533 + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + Redirecting to someservice81 on somehost9 + + + + nat_663c3458b7b5e4.19986620 + 192.168.20.115 + 27017 + + 1 + + + wanip + 27057 + + + root@172.12.0.8 + + /firewall_nat_edit.php made changes + + + root@172.12.0.8 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + Redirecting to someservice858 on somehost545 + + + + nat_663d85b2e3b364.53108170 + 192.168.20.163 + 8888 + + 1 + + + wanip + 8888 + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + Redirecting to someservice858 on somehost545 + + + + nat_667cd97504d870.57128970 + 192.168.20.163 + 8889 + + 1 + + + wanip + 9888 + + + root@172.12.0.8 + + /firewall_nat_edit.php made changes + + + root@172.12.0.8 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + Redirecting to someservice858 on somehost9 + + + + nat_666c8932142ed6.34062700 + 192.168.20.115 + 8888 + + 1 + + + wanip + 8889 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + + + + tcp + wan + + inet + + + + + nat_651ffc35e573d9.09092618 + 192.168.20.140 + 22 + + 1 + + + wanip + 30140 + + + root@172.12.0.11 + + /firewall_nat_edit.php made changes + + + root@172.12.0.11 + + /firewall_nat_edit.php made changes + + + + + + pass + wan + inet + keep state + allow public connections to vpn + in + wireguard + 1 + udp + + 1 + + + wanip + 51820 + + + root@192.168.1.118 + + /firewall_rules_edit.php made changes + + + root@192.168.1.118 + + /firewall_rules_edit.php made changes + + + + pass + wan + inet + keep state + in + 1 + icmp + echoreq + + 1 + + + wanip + + + root@192.168.1.136 + + /firewall_rules_edit.php made changes + + + root@192.168.1.118 + + /firewall_rules_edit.php made changes + + + + + 1 + + wan + keep state + tcp + inet + +
x3690_3
+ 22 +
+ + + nat_618812d37b8193.31302503 + + root@192.168.1.118 + + /firewall_nat_edit.php made changes + +
+ + + 1 + + wan + keep state + tcp + inet + +
192.168.20.150
+ 7860 +
+ + + nat_64fa19f4acba11.80049900 + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + +
+ + + 1 + + wan + keep state + tcp + inet + +
192.168.20.150
+ 7861 +
+ + + nat_64fb1fbba71e29.76190279 + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + +
+ + + 1 + + wan + keep state + tcp + inet + +
192.168.20.150
+ 7862 +
+ + + nat_64fb1fcea6d8b7.62653343 + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + +
+ + + 1 + + wan + keep state + tcp + inet + +
192.168.20.150
+ 7863 +
+ + + nat_64fb1fdb48ff18.28912920 + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + +
+ + + 1 + + wan + keep state + tcp + inet + +
192.168.20.140
+ 22 +
+ + + nat_651ffc35e573d9.09092618 + + root@172.12.0.11 + + /firewall_nat_edit.php made changes + +
+ + nat_65aed5a66c4f65.25454286 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.140
+ 8081 +
+ + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + +
+ + nat_65aed67d497580.58958916 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.160
+ 8080 +
+ + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + +
+ + nat_65aed6961c4ea7.81903986 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.160
+ 4000 +
+ + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + +
+ + nat_65f4a5b928c9a7.52477383 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.115
+ 5000 +
+ + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + +
+ + nat_662bb59baf7573.98640354 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.115
+ 11434 +
+ Redirecting to someservice1 on somehost9 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + +
+ + nat_663c3458b7b5e4.19986620 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.115
+ 27017 +
+ Redirecting to someservice81 on somehost9 + + + root@172.12.0.8 + + /firewall_nat_edit.php made changes + +
+ + nat_663d85b2e3b364.53108170 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.163
+ 8888 +
+ Redirecting to someservice858 on somehost545 + + + root@172.12.0.10 + + /firewall_nat_edit.php made changes + +
+ + nat_666c8932142ed6.34062700 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.115
+ 8888 +
+ Redirecting to someservice858 on somehost9 + + + root@192.168.20.100 + + /firewall_nat_edit.php made changes + +
+ + nat_667cd97504d870.57128970 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.163
+ 8889 +
+ Redirecting to someservice858 on somehost545 + + + root@172.12.0.8 + + /firewall_nat_edit.php made changes + +
+ + pass + wan + inet + keep state + open virtual ip address for someservice2 + in + 1 + tcp + + 1 + + +
192.168.20.225
+ 55555 +
+ + root@172.12.0.12 + + /firewall_rules_edit.php made changes + + + root@172.12.0.12 + + /firewall_rules_edit.php made changes + +
+ + pass + inet + Default allow LAN to any rule + lan + + lan + + + + + + + pass + inet6 + Default allow LAN IPv6 to any rule + lan + + lan + + + + + + + pass + lan + inet + keep state + in + 1 + carp + + 1 + + + 1 + + + root@192.168.20.100 + + /firewall_rules_edit.php made changes + + + root@192.168.20.100 + + /firewall_rules_edit.php made changes + + + + pass + wireguard + inet + keep state + in + 1 + + 1 + + + 1 + + + root@192.168.20.100 + + /firewall_rules_edit.php made changes + + + root@192.168.1.136 + + /firewall_rules_edit.php made changes + + + + nat_6709763b6a6748.85579760 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.132
+ 55555 +
+ port forwarding for reconfig of someservice2 somehost3 + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + 1 +
+ + nat_670979b3279551.73601303 + + 1 + + wan + keep state + tcp + inet + +
192.168.20.1
+ 55555 +
+ port forwarding for virtual ip for someservice2 servers + + + root@172.12.0.12 + + /firewall_nat_edit.php made changes + + 1 +
+
+ + + ICMP + icmp + ICMP + + + + TCP + tcp + Generic TCP + + + + HTTP + http + Generic HTTP + + / + + 200 + + + + HTTPS + https + Generic HTTPS + + / + + 200 + + + + SMTP + send + Generic SMTP + + + 220 * + + + + + 0.opnsense.pool.ntp.org + + + system_information-container:00000000-col3:show,traffic_graphs-container:00000001-col3:show,thermal_sensors-container:00000002-col3:show,log-container:00000003-col3:show,services_status-container:00000004-col4:show,gateways-container:00000005-col4:show,interface_list-container:00000006-col4:show,carp_status-container:00000007-col4:show,wireguard-container:00000008-col4:show,dyn_dns_status-container:00000009-col4:show,system_log-container:00000010-col4:show + 2 + + + root@172.12.0.12 + + /firewall_nat.php made changes + + + + + + + + + + + + + + v9 + + + + 0 + + 1800 + 15 + + + + + + + + + wireguard + 1 + + + + + + + + + + + 1 + x3690_3 + host + + + 0 + + 192.168.1.136 + + + + + 1 + someservice2_vip + host + + + 0 + + 192.168.20.225 + + alias for someservice2 vip + + + + + + + + + + + + 0 + 0 + 0 + wan + 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 + + + W0D23 + 4 + ac + + medium + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + 0 + 120 + 120 + 127.0.0.1 + 25 + + + 0 + auto + 1 + syslog facility log_daemon + + + + 0 + root + oiujds9889DSIJSDIJSDIjdj + 2812 + + + 5 + 1 + + + 0 + root@localhost.local + 0 + + + 10 + + + + 1 + $HOST + + system + + + + 300 + 30 +
+ + + + da6083fd-852c-44af-9ae7-8c9de443bbc9,4f18b847-c2ab-4707-9686-bf656e187ab8,62ea6632-3554-43be-bb0b-ceceab685338,f543f50a-4e52-4afd-85ce-95fe6d61dc54 + + + + + 1 + RootFs + + filesystem + + + / + 300 + 30 +
+ + + + 58896875-13d3-41ad-bed5-d610e0197d37 + + + + + 0 + carp_status_change + + custom + + + /usr/local/opnsense/scripts/OPNsense/Monit/carp_status + 300 + 30 +
+ + + + 78d20574-60b0-4e63-b6d2-0248e7570293 + + + + + 0 + gateway_alert + + custom + + + /usr/local/opnsense/scripts/OPNsense/Monit/gateway_alert + 300 + 30 +
+ + + + 572a5691-1ea7-4191-be24-d7399845a75b + + + + + Ping + NetworkPing + failed ping + alert + + + + NetworkLink + NetworkInterface + failed link + alert + + + + NetworkSaturation + NetworkInterface + saturation is greater than 75% + alert + + + + MemoryUsage + SystemResource + memory usage is greater than 75% + alert + + + + CPUUsage + SystemResource + cpu usage is greater than 75% + alert + + + + LoadAvg1 + SystemResource + loadavg (1min) is greater than 4 + alert + + + + LoadAvg5 + SystemResource + loadavg (5min) is greater than 3 + alert + + + + LoadAvg15 + SystemResource + loadavg (15min) is greater than 2 + alert + + + + SpaceUsage + SpaceUsage + space usage is greater than 75% + alert + + + + ChangedStatus + ProgramStatus + changed status + alert + + + + NonZeroStatus + ProgramStatus + status != 0 + alert + + + + + + + + + 0 + opnsense + + + + 1 + 1 + + + + + + 0 + on + strip + 1 + 1 + 0 + + admin@localhost.local + + + + 0 + /var/squid/cache + 256 + + + always + 100 + 16 + 256 + 0 + 0 + + + + 0 + 2048 + 1024 + 1024 + 256 + + + 0 + + 0 + username + password + + + + + + + lan + 3128 + 3129 + 0 + 0 + + + 4 + 5 + 0 + 3401 + public + + 2121 + 0 + 1 + 0 + + + + + + + + + + + 80:http,21:ftp,443:https,70:gopher,210:wais,1025-65535:unregistered ports,280:http-mgmt,488:gss-http,591:filemaker,777:multiling http + 443:https + + + + + + + 0 + icap://[::1]:1344/avscan + icap://[::1]:1344/avscan + 1 + 0 + 0 + X-Username + 1 + 1024 + 60 + + + + + + OPNsense proxy authentication + 2 + 5 + + + + +