From c9eb032bd6339bddba8d47df3625dd889cc4a246 Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Thu, 10 Oct 2024 12:33:10 +0200 Subject: [PATCH] firewall rules persistence --- Cargo.lock | 35 ++++++++++++++++ Justfile | 2 +- oryx-tui/Cargo.toml | 4 +- oryx-tui/src/filter/direction.rs | 3 +- oryx-tui/src/handler.rs | 44 +++++++++++++-------- oryx-tui/src/section/firewall.rs | 68 ++++++++++++++++++++++++++++++-- 6 files changed, 133 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 183fe81..e90a42a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -672,6 +672,8 @@ dependencies = [ "network-types", "oryx-common", "ratatui", + "serde", + "serde_json", "tui-big-text", "tui-input", "uuid", @@ -825,6 +827,38 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1006,6 +1040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", + "serde", ] [[package]] diff --git a/Justfile b/Justfile index f36f9f3..a46f882 100644 --- a/Justfile +++ b/Justfile @@ -17,7 +17,7 @@ show interface: # Run oryx debug run-debug: echo "" > log-file - RUST_LOG=info cargo xtask run 2> log-file + RUST_LOG=info RUST_BACKTRACE=1 cargo xtask run 2> log-file run: cargo xtask run diff --git a/oryx-tui/Cargo.toml b/oryx-tui/Cargo.toml index 8466043..7a95465 100644 --- a/oryx-tui/Cargo.toml +++ b/oryx-tui/Cargo.toml @@ -24,9 +24,11 @@ kanal = "0.1.0-pre8" mimalloc = "0.1" clap = { version = "4", features = ["derive", "cargo"] } network-types = "0.0.7" -uuid = { version = "1", default-features = false, features = ["v4"] } +uuid = { version = "1", default-features = false, features = ["v4","serde"] } log = "0.4" env_logger = "0.11" +serde_json = "1.0.128" +serde = {version = "1.0", features = ["derive"] } [[bin]] name = "oryx" diff --git a/oryx-tui/src/filter/direction.rs b/oryx-tui/src/filter/direction.rs index 0d27105..2e5cb2d 100644 --- a/oryx-tui/src/filter/direction.rs +++ b/oryx-tui/src/filter/direction.rs @@ -12,6 +12,7 @@ use ratatui::{ widgets::{Block, BorderType, Borders, Row, Table, TableState}, Frame, }; +use serde::{Deserialize, Serialize}; #[derive(Debug)] pub struct TrafficDirectionFilter { @@ -22,7 +23,7 @@ pub struct TrafficDirectionFilter { pub terminate_egress: Arc, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum TrafficDirection { Ingress, Egress, diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 68a9fe6..3d5d636 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -191,24 +191,34 @@ pub fn handle_key_events( } KeyCode::Char('s') => { - let app_packets = app.packets.lock().unwrap(); - if app_packets.is_empty() { - Notification::send( - "There is no packets".to_string(), - NotificationLevel::Info, - event_sender, - )?; + if app.section.focused_section == FocusedSection::Firewall { + app.section + .firewall + .handle_keys(key_event, event_sender.clone())? } else { - match export(&app_packets) { - Ok(_) => { - Notification::send( - "Packets exported to ~/oryx/capture file".to_string(), - NotificationLevel::Info, - event_sender, - )?; - } - Err(e) => { - Notification::send(e.to_string(), NotificationLevel::Error, event_sender)?; + let app_packets = app.packets.lock().unwrap(); + if app_packets.is_empty() { + Notification::send( + "There is no packets".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } else { + match export(&app_packets) { + Ok(_) => { + Notification::send( + "Packets exported to ~/oryx/capture file".to_string(), + NotificationLevel::Info, + event_sender, + )?; + } + Err(e) => { + Notification::send( + e.to_string(), + NotificationLevel::Error, + event_sender, + )?; + } } } } diff --git a/oryx-tui/src/section/firewall.rs b/oryx-tui/src/section/firewall.rs index f6b3b43..a1f1fb3 100644 --- a/oryx-tui/src/section/firewall.rs +++ b/oryx-tui/src/section/firewall.rs @@ -1,5 +1,6 @@ use core::fmt::Display; use crossterm::event::{Event, KeyCode, KeyEvent}; +use log::info; use oryx_common::MAX_FIREWALL_RULES; use ratatui::{ layout::{Constraint, Direction, Flex, Layout, Margin, Rect}, @@ -8,7 +9,9 @@ use ratatui::{ widgets::{Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table, TableState}, Frame, }; -use std::{net::IpAddr, num::ParseIntError, str::FromStr}; +use serde::{Deserialize, Serialize}; +use serde_json; +use std::{fs, net::IpAddr, num::ParseIntError, os::unix::fs::chown, str::FromStr}; use tui_input::{backend::crossterm::EventHandler, Input}; use uuid; @@ -20,7 +23,7 @@ pub enum FirewallSignal { Kill, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct FirewallRule { id: uuid::Uuid, name: String, @@ -30,7 +33,7 @@ pub struct FirewallRule { direction: TrafficDirection, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum BlockedPort { Single(u16), All, @@ -314,6 +317,42 @@ impl Firewall { self.user_input = Some(UserInput::new()); } + pub fn save_rules(&self) -> AppResult<()> { + info!("saving rules"); + info!("{:#?}", self.rules); + + let json = serde_json::to_string(&self.rules).unwrap(); + + let uid = unsafe { libc::geteuid() }; + + let oryx_export_dir = dirs::home_dir().unwrap().join("oryx"); + + if !oryx_export_dir.exists() { + fs::create_dir(&oryx_export_dir)?; + chown(&oryx_export_dir, Some(uid), Some(uid))?; + } + + let oryx_export_file = oryx_export_dir.join("firewall.json"); + fs::write(oryx_export_file, json).expect("Could not save Firewall Rules"); + info!("rules saved"); + Ok(()) + } + + pub fn load_rules(&mut self) -> AppResult<()> { + info!("loading rules"); + let oryx_export_file = dirs::home_dir().unwrap().join("oryx").join("firewall.json"); + if oryx_export_file.exists() { + info!("EXISTS"); + let json_string = + fs::read_to_string(oryx_export_file).expect("Could not load Firewall Rules"); + let parsed_rules: Vec = + serde_json::from_str(&json_string).expect("Could not load Firewall Rules"); + info!("rules loaded"); + self.rules = parsed_rules; + } + + Ok(()) + } fn validate_duplicate_rules(rules: &[FirewallRule], user_input: &UserInput) -> AppResult<()> { if let Some(exiting_rule_with_same_ip) = rules.iter().find(|rule| { rule.ip == IpAddr::from_str(user_input.ip.field.value()).unwrap() @@ -410,6 +449,29 @@ impl Firewall { key_event: KeyEvent, sender: kanal::Sender, ) -> AppResult<()> { + match key_event.code { + KeyCode::Char('s') => { + if let Err(e) = self.save_rules() { + Notification::send( + "Error saving rules", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err(e); + } + } + KeyCode::Char('l') => { + if let Err(e) = self.load_rules() { + Notification::send( + "Error loading rules", + crate::notification::NotificationLevel::Warning, + sender.clone(), + )?; + return Err(e); + } + } + _ => {} + } if let Some(user_input) = &mut self.user_input { match key_event.code { KeyCode::Esc => {