Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist firewall rules #27

Merged
merged 12 commits into from
Oct 10, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -122,6 +122,8 @@ sudo oryx

`e`: Edit a firewall rule.

`s`: Save firewall rules to `~/oryx/firewall.json`

`Enter`: Create or Save a firewall rule.

## ⚖️ License
1 change: 1 addition & 0 deletions Release.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
### Added

- Firewall
- Save and Load firewall rules.

## v0.3 - 2024-09-25

4 changes: 3 additions & 1 deletion oryx-tui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
serde = { version = "1", features = ["derive"] }

[[bin]]
name = "oryx"
5 changes: 5 additions & 0 deletions oryx-tui/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use log::error;
use oryx_common::RawPacket;
use ratatui::{
layout::{Constraint, Direction, Layout},
@@ -134,6 +135,10 @@ impl App {
}

pub fn quit(&mut self) {
if let Err(e) = self.section.firewall.save_rules() {
error!("{}", e)
}

self.running = false;
}
}
3 changes: 2 additions & 1 deletion oryx-tui/src/filter/direction.rs
Original file line number Diff line number Diff line change
@@ -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<AtomicBool>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum TrafficDirection {
Ingress,
Egress,
25 changes: 0 additions & 25 deletions oryx-tui/src/handler.rs
Original file line number Diff line number Diff line change
@@ -3,9 +3,7 @@ use std::{thread, time::Duration};
use crate::{
app::{ActivePopup, App, AppResult},
event::Event,
export::export,
filter::FocusedBlock,
notification::{Notification, NotificationLevel},
section::FocusedSection,
};
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
@@ -190,29 +188,6 @@ 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,
)?;
} 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)?;
}
}
}
}
_ => {
app.section.handle_keys(key_event, event_sender.clone())?;
}
4 changes: 4 additions & 0 deletions oryx-tui/src/help.rs
Original file line number Diff line number Diff line change
@@ -57,6 +57,10 @@ impl Help {
(Cell::from("## Firewall").bold().yellow(), ""),
(Cell::from("n").bold(), "Add new firewall rule"),
(Cell::from("e").bold(), "Edit a firewall rule"),
(
Cell::from("s").bold(),
"Save firewall rules to ~/oryx/firewall.json ",
),
(Cell::from("Space").bold(), "Toggle firewall rule status"),
(Cell::from("Enter").bold(), "Create or Save a firewall rule"),
],
10 changes: 9 additions & 1 deletion oryx-tui/src/section.rs
Original file line number Diff line number Diff line change
@@ -151,6 +151,9 @@ impl Section {
Span::from("i").bold(),
Span::from(" Infos").bold(),
Span::from(" | ").bold(),
Span::from("s").bold(),
Span::from(" Save").bold(),
Span::from(" | ").bold(),
Span::from("f").bold(),
Span::from(" Filters").bold(),
Span::from(" | ").bold(),
@@ -176,6 +179,9 @@ impl Section {
Span::from("e").bold(),
Span::from(" Edit").bold(),
Span::from(" | ").bold(),
Span::from("s").bold(),
Span::from(" Save").bold(),
Span::from(" | ").bold(),
Span::from("󱁐 ").bold(),
Span::from(" Toggle").bold(),
Span::from(" | ").bold(),
@@ -275,7 +281,9 @@ impl Section {
},

_ => match self.focused_section {
FocusedSection::Inspection => self.inspection.handle_keys(key_event),
FocusedSection::Inspection => self
.inspection
.handle_keys(key_event, notification_sender.clone())?,
FocusedSection::Firewall => self
.firewall
.handle_keys(key_event, notification_sender.clone())?,
80 changes: 76 additions & 4 deletions oryx-tui/src/section/firewall.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::fmt::Display;
use crossterm::event::{Event, KeyCode, KeyEvent};
use log::{error, 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,
@@ -302,8 +305,16 @@ impl Firewall {
ingress_sender: kanal::Sender<FirewallSignal>,
egress_sender: kanal::Sender<FirewallSignal>,
) -> Self {
let rules_list: Vec<FirewallRule> = match Self::load_saved_rules() {
Ok(saved_rules) => saved_rules,

Err(err) => {
error!("{}", err.to_string());
Vec::new()
}
};
Self {
rules: Vec::new(),
rules: rules_list,
state: TableState::default(),
user_input: None,
ingress_sender,
@@ -315,6 +326,49 @@ impl Firewall {
self.user_input = Some(UserInput::new());
}

pub fn save_rules(&self) -> AppResult<()> {
info!("Saving Firewall Rules");

let json = serde_json::to_string(&self.rules)?;

let user_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(user_uid), Some(user_uid))?;
}

let oryx_export_file = oryx_export_dir.join("firewall.json");
fs::write(oryx_export_file, json)?;
info!("Firewall Rules saved");

Ok(())
}

fn load_saved_rules() -> AppResult<Vec<FirewallRule>> {
let oryx_export_file = dirs::home_dir().unwrap().join("oryx").join("firewall.json");
if oryx_export_file.exists() {
info!("Loading Firewall Rules");

let json_string = fs::read_to_string(oryx_export_file)?;

let mut parsed_rules: Vec<FirewallRule> = serde_json::from_str(&json_string)?;

// as we don't know if ingress/egress programs are loaded we have to disable all rules
parsed_rules
.iter_mut()
.for_each(|rule| rule.enabled = false);

info!("Firewall Rules loaded");
Ok(parsed_rules)
} else {
info!("Firewall Rules file not found");
Ok(Vec::new())
}
}

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()
@@ -500,6 +554,24 @@ impl Firewall {
self.add_rule();
}

KeyCode::Char('s') => match self.save_rules() {
Ok(_) => {
Notification::send(
"Firewall rules saved to ~/oryx/firewall.json file",
crate::notification::NotificationLevel::Info,
sender.clone(),
)?;
}
Err(e) => {
Notification::send(
"Error while saving firewall rules.",
crate::notification::NotificationLevel::Error,
sender.clone(),
)?;
error!("Error while saving firewall rules. {}", e);
}
},

KeyCode::Char('e') => {
if let Some(index) = self.state.selected() {
let rule = self.rules[index].clone();
38 changes: 37 additions & 1 deletion oryx-tui/src/section/inspection.rs
Original file line number Diff line number Diff line change
@@ -14,7 +14,10 @@ use ratatui::{
use tui_input::backend::crossterm::EventHandler;

use crate::{
app::AppResult,
export,
filter::fuzzy::{self, Fuzzy},
notification::{Notification, NotificationLevel},
packet::{
network::{IpPacket, IpProto},
AppPacket,
@@ -56,7 +59,11 @@ impl Inspection {
}
}

pub fn handle_keys(&mut self, key_event: KeyEvent) {
pub fn handle_keys(
&mut self,
key_event: KeyEvent,
event_sender: kanal::Sender<crate::event::Event>,
) -> AppResult<()> {
let fuzzy_is_enabled = { self.fuzzy.lock().unwrap().is_enabled() };

if fuzzy_is_enabled {
@@ -126,9 +133,38 @@ impl Inspection {
self.scroll_up();
}

KeyCode::Char('s') => {
let app_packets = self.packets.lock().unwrap();
if app_packets.is_empty() {
Notification::send(
"There is no packets".to_string(),
NotificationLevel::Info,
event_sender,
)?;
} else {
match export::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,
)?;
}
}
}
}

_ => {}
}
}
Ok(())
}

pub fn scroll_up(&mut self) {