diff options
Diffstat (limited to 'examples/firewall.rs')
-rw-r--r-- | examples/firewall.rs | 234 |
1 files changed, 110 insertions, 124 deletions
diff --git a/examples/firewall.rs b/examples/firewall.rs index fc25010..3169cdc 100644 --- a/examples/firewall.rs +++ b/examples/firewall.rs @@ -3,138 +3,124 @@ //use rustables::query::{send_batch, Error as QueryError}; //use rustables::expr::{LogGroup, LogPrefix, LogPrefixError}; use ipnetwork::IpNetwork; -use std::ffi::{CString, NulError}; -use std::rc::Rc; -use std::str::Utf8Error; +use rustables::error::{BuilderError, QueryError}; +use rustables::expr::Log; +use rustables::{ + Batch, Chain, ChainPolicy, Hook, HookClass, MsgType, Protocol, ProtocolFamily, Rule, Table, +}; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Unable to open netlink socket to netfilter")] - NetlinkOpenError(#[source] std::io::Error), - #[error("Firewall is already started")] - AlreadyDone, - #[error("Error converting from a C String")] - NulError(#[from] NulError), - //#[error("Error creating match")] - //MatchError(#[from] MatchError), - #[error("Error converting to utf-8 string")] - Utf8Error(#[from] Utf8Error), + #[error("Error building a netlink object")] + BuildError(#[from] BuilderError), #[error("Error applying batch")] - BatchError(#[from] std::io::Error), - //#[error("Error applying batch")] - //QueryError(#[from] QueryError), - //#[error("Error encoding the prefix")] - //LogPrefixError(#[from] LogPrefixError), + QueryError(#[from] QueryError), } const TABLE_NAME: &str = "main-table"; +const INBOUND_CHAIN_NAME: &str = "in-chain"; +const FORWARD_CHAIN_NAME: &str = "forward-chain"; +const OUTBOUND_CHAIN_NAME: &str = "out-chain"; fn main() -> Result<(), Error> { - // let fw = Firewall::new()?; - // fw.start()?; + let fw = Firewall::new()?; + fw.start()?; Ok(()) } -// -// -///// An example firewall. See the source of its `start()` method. -//pub struct Firewall { -// batch: Batch, -// inbound: Rc<Chain>, -// _outbound: Rc<Chain>, -// _forward: Rc<Chain>, -// table: Rc<Table>, -//} -// -//impl Firewall { -// pub fn new() -> Result<Self, Error> { -// let mut batch = Batch::new(); -// let table = Rc::new( -// Table::new(&CString::new(TABLE_NAME)?, ProtoFamily::Inet) -// ); -// batch.add(&table, MsgType::Add); -// -// // Create base chains. Base chains are hooked into a Direction/Hook. -// let inbound = Rc::new( -// Chain::from_hook(Hook::In, Rc::clone(&table)) -// .verdict(Policy::Drop) -// .add_to_batch(&mut batch) -// ); -// let _outbound = Rc::new( -// Chain::from_hook(Hook::Out, Rc::clone(&table)) -// .verdict(Policy::Accept) -// .add_to_batch(&mut batch) -// ); -// let _forward = Rc::new( -// Chain::from_hook(Hook::Forward, Rc::clone(&table)) -// .verdict(Policy::Accept) -// .add_to_batch(&mut batch) -// ); -// -// Ok(Firewall { -// table, -// batch, -// inbound, -// _outbound, -// _forward -// }) -// } -// /// Allow some common-sense exceptions to inbound drop, and accept outbound and forward. -// pub fn start(mut self) -> Result<(), Error> { -// // Allow all established connections to get in. -// Rule::new(Rc::clone(&self.inbound)) -// .established() -// .accept() -// .add_to_batch(&mut self.batch); -// // Allow all traffic on the loopback interface. -// Rule::new(Rc::clone(&self.inbound)) -// .iface("lo")? -// .accept() -// .add_to_batch(&mut self.batch); -// // Allow ssh from anywhere, and log to dmesg with a prefix. -// Rule::new(Rc::clone(&self.inbound)) -// .dport("22", &Protocol::TCP)? -// .accept() -// .log(None, Some(LogPrefix::new("allow ssh connection:")?)) -// .add_to_batch(&mut self.batch); -// -// // Allow http from all IPs in 192.168.1.255/24 . -// let local_net = IpNetwork::new([192, 168, 1, 0].into(), 24).unwrap(); -// Rule::new(Rc::clone(&self.inbound)) -// .dport("80", &Protocol::TCP)? -// .snetwork(local_net) -// .accept() -// .add_to_batch(&mut self.batch); -// -// // Allow ICMP traffic, drop IGMP. -// Rule::new(Rc::clone(&self.inbound)) -// .icmp() -// .accept() -// .add_to_batch(&mut self.batch); -// Rule::new(Rc::clone(&self.inbound)) -// .igmp() -// .drop() -// .add_to_batch(&mut self.batch); -// -// // Log all traffic not accepted to NF_LOG group 1, accessible with ulogd. -// Rule::new(Rc::clone(&self.inbound)) -// .log(Some(LogGroup(1)), None) -// .add_to_batch(&mut self.batch); -// -// let mut finalized_batch = self.batch.finalize().unwrap(); -// send_batch(&mut finalized_batch)?; -// println!("table {} commited", TABLE_NAME); -// Ok(()) -// } -// /// If there is any table with name TABLE_NAME, remove it. -// pub fn stop(mut self) -> Result<(), Error> { -// self.batch.add(&self.table, MsgType::Add); -// self.batch.add(&self.table, MsgType::Del); -// -// let mut finalized_batch = self.batch.finalize().unwrap(); -// send_batch(&mut finalized_batch)?; -// println!("table {} destroyed", TABLE_NAME); -// Ok(()) -// } -//} -// -// + +/// An example firewall. See the source of its `start()` method. +pub struct Firewall { + batch: Batch, + inbound: Chain, + _outbound: Chain, + _forward: Chain, + table: Table, +} + +impl Firewall { + pub fn new() -> Result<Self, Error> { + let mut batch = Batch::new(); + let table = Table::new(ProtocolFamily::Inet).with_name(TABLE_NAME); + batch.add(&table, MsgType::Add); + + // Create base chains. Base chains are hooked into a Direction/Hook. + let inbound = Chain::new(&table) + .with_name(INBOUND_CHAIN_NAME) + .with_hook(Hook::new(HookClass::In, 0)) + .with_policy(ChainPolicy::Drop) + .add_to_batch(&mut batch); + let _outbound = Chain::new(&table) + .with_name(OUTBOUND_CHAIN_NAME) + .with_hook(Hook::new(HookClass::Out, 0)) + .with_policy(ChainPolicy::Accept) + .add_to_batch(&mut batch); + let _forward = Chain::new(&table) + .with_name(FORWARD_CHAIN_NAME) + .with_hook(Hook::new(HookClass::Forward, 0)) + .with_policy(ChainPolicy::Accept) + .add_to_batch(&mut batch); + + Ok(Firewall { + table, + batch, + inbound, + _outbound, + _forward, + }) + } + /// Allow some common-sense exceptions to inbound drop, and accept outbound and forward. + pub fn start(mut self) -> Result<(), Error> { + // Allow all established connections to get in. + Rule::new(&self.inbound)? + .established()? + .accept() + .add_to_batch(&mut self.batch); + // Allow all traffic on the loopback interface. + Rule::new(&self.inbound)? + .iface("lo")? + .accept() + .add_to_batch(&mut self.batch); + // Allow ssh from anywhere, and log to dmesg with a prefix. + Rule::new(&self.inbound)? + .dport(22, Protocol::TCP) + .accept() + .with_expr(Log::new(None, Some("allow ssh connection:"))?) + .add_to_batch(&mut self.batch); + + // Allow http from all IPs in 192.168.1.255/24 . + let local_net = IpNetwork::new([192, 168, 1, 0].into(), 24).unwrap(); + Rule::new(&self.inbound)? + .dport(80, Protocol::TCP) + .snetwork(local_net)? + .accept() + .add_to_batch(&mut self.batch); + + // Allow ICMP traffic, drop IGMP. + Rule::new(&self.inbound)? + .icmp() + .accept() + .add_to_batch(&mut self.batch); + Rule::new(&self.inbound)? + .igmp() + .drop() + .add_to_batch(&mut self.batch); + + // Log all traffic not accepted to NF_LOG group 1, accessible with ulogd. + Rule::new(&self.inbound)? + .with_expr(Log::new(Some(1), None::<String>)?) + .add_to_batch(&mut self.batch); + + self.batch.send()?; + println!("table {} commited", TABLE_NAME); + Ok(()) + } + /// If there is any table with name TABLE_NAME, remove it. + pub fn stop(mut self) -> Result<(), Error> { + self.batch.add(&self.table, MsgType::Add); + self.batch.add(&self.table, MsgType::Del); + + self.batch.send()?; + println!("table {} destroyed", TABLE_NAME); + Ok(()) + } +} |