initial commit
This commit is contained in:
commit
78c6150d59
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,72 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "nfq"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf3fd1239e9f2acc4d80da3ecb17f95c8714fb07cddbf64abb8bcc339ac7a4b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pdu"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0091978bc0658e5ea8bfd61339429ddb0489e91160bfb9a9c5b7726ccba4551"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "zunft"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"nfq",
|
||||
"pdu",
|
||||
"regex",
|
||||
]
|
|
@ -0,0 +1,14 @@
|
|||
cargo-features = ["edition2021"]
|
||||
[package]
|
||||
name = "zunft"
|
||||
version = "0.1.0"
|
||||
authors = ["Felix Kehrer <felix.kehrer@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
nfq = "0.2.4"
|
||||
pdu = "1.4.1"
|
||||
regex = "1.5.4"
|
|
@ -0,0 +1,16 @@
|
|||
# README
|
||||
|
||||
Zunft - Zauberei und nftables
|
||||
|
||||
Now using the new Rust 2021 edition :)
|
||||
|
||||
## Useful commands
|
||||
|
||||
`cargo +nightly build --release && sudo setcap cap_net_admin+ep ./target/release/zunft && cargo +nightly run --release`
|
||||
`sudo iptables -t filter -A INPUT -j NFQUEUE --queue-num 0 --queue-bypass`
|
||||
`sudo iptables -L INPUT --line-numbers`
|
||||
`sudo iptables -D INPUT 1`
|
||||
|
||||
## Testing the filter
|
||||
|
||||
While you can run whatever you want, the test case I made is for you to serve the assets folder with something like `python -m http.server 8000`, and then try to have a lookg at the files. Normally you should be able to get both files, but once the firewall runs, trying to access `/secret`should not work anymore.
|
|
@ -0,0 +1,4 @@
|
|||
you should be able to read this.
|
||||
if not, you probably filtered too much.
|
||||
but then again, if that happens you won't be able to read this.
|
||||
so, erm, good luck, i guess.
|
|
@ -0,0 +1,2 @@
|
|||
lol, you should not be able to see this.
|
||||
is the firewall actually running?
|
|
@ -0,0 +1,233 @@
|
|||
#[allow(unused_imports)]
|
||||
use lazy_static::lazy_static;
|
||||
use nfq::{Queue, Verdict};
|
||||
use pdu::{Gre, GrePdu, Icmp, IcmpPdu, Ip, Ipv4, Ipv4Pdu, Ipv6Pdu, Tcp, TcpPdu, Udp, UdpPdu};
|
||||
#[allow(unused_imports)]
|
||||
use regex::bytes::Regex;
|
||||
|
||||
fn main() {
|
||||
let mut queue = Queue::open().expect("Could not open queue!");
|
||||
queue.bind(0).expect("Could not bind to queue 0!");
|
||||
loop {
|
||||
let mut msg = queue.recv().expect("Could not receive message from queue!");
|
||||
let packet = msg.get_payload();
|
||||
|
||||
let verdict = match Ip::new(packet) {
|
||||
Ok(Ip::Ipv4(ipv4_pdu)) => {
|
||||
print!("IPv4 -> ");
|
||||
//print_ipv4(ipv4_pdu);
|
||||
match ipv4_pdu.inner() {
|
||||
#[allow(unused_variables)]
|
||||
Ok(Ipv4::Raw(raw)) => {
|
||||
print!("RAW");
|
||||
Verdict::Accept
|
||||
}
|
||||
Ok(Ipv4::Tcp(tcp_pdu)) => {
|
||||
print!("TCP -> ");
|
||||
//print_tcp(tcp_pdu);
|
||||
match tcp_pdu.inner() {
|
||||
Ok(Tcp::Raw(raw)) => {
|
||||
print!("RAW");
|
||||
filter_ipv4_tcp_raw(ipv4_pdu, tcp_pdu, raw)
|
||||
}
|
||||
Err(e) => panic!("Could not get inner payload of TCP PDU: {}", e),
|
||||
}
|
||||
}
|
||||
Ok(Ipv4::Udp(udp_pdu)) => {
|
||||
print!("UDP -> ");
|
||||
//print_udp(udp_pdu);
|
||||
match udp_pdu.inner() {
|
||||
#[allow(unused_variables)]
|
||||
Ok(Udp::Raw(raw)) => {
|
||||
print!("RAW");
|
||||
}
|
||||
Err(e) => panic!("Could not get inner payload of UDP PDU: {}", e),
|
||||
}
|
||||
Verdict::Accept
|
||||
}
|
||||
Ok(Ipv4::Icmp(icmp_pdu)) => {
|
||||
print!("ICMP -> ");
|
||||
//print_icmp(icmp_pdu);
|
||||
match icmp_pdu.inner() {
|
||||
#[allow(unused_variables)]
|
||||
Ok(Icmp::Raw(raw)) => {
|
||||
print!("RAW");
|
||||
}
|
||||
Err(e) => panic!("Could not get inner payload of ICMP PDU: {}", e),
|
||||
}
|
||||
Verdict::Accept
|
||||
}
|
||||
Ok(Ipv4::Gre(gre_pdu)) => {
|
||||
print!("GRE -> ");
|
||||
//print_gre(gre_pdu);
|
||||
match gre_pdu.inner() {
|
||||
#[allow(unused_variables)]
|
||||
Ok(Gre::Raw(raw)) => {
|
||||
print!("RAW");
|
||||
}
|
||||
Ok(_) => print!("Eth/Ipv4/Ipv6"),
|
||||
Err(e) => panic!("Could not get inner payload of GRE PDU: {}", e),
|
||||
}
|
||||
Verdict::Accept
|
||||
}
|
||||
Err(e) => panic!("Could not decode inner packet: {}", e),
|
||||
}
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
Ok(Ip::Ipv6(ipv6_pdu)) => {
|
||||
println!("IPv6!");
|
||||
// TODO same as above
|
||||
Verdict::Accept
|
||||
}
|
||||
Err(e) => panic!("Could not decode IP packet: {}", e),
|
||||
};
|
||||
println!();
|
||||
|
||||
msg.set_verdict(verdict);
|
||||
queue
|
||||
.verdict(msg)
|
||||
.expect("Could not set verdict for message!");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_gre(gre_pdu: GrePdu) {
|
||||
print!(
|
||||
"GRE {:10} {:3} {:5} {:5} {:5} {:5} {:?} {:?} {:?} {:?} ",
|
||||
gre_pdu.computed_ihl(),
|
||||
gre_pdu.version(),
|
||||
gre_pdu.ethertype(),
|
||||
gre_pdu.has_checksum(),
|
||||
gre_pdu.has_key(),
|
||||
gre_pdu.has_sequence_number(),
|
||||
gre_pdu.checksum(),
|
||||
gre_pdu.computed_checksum(),
|
||||
gre_pdu.key(),
|
||||
gre_pdu.sequence_number()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_icmp(icmp_pdu: IcmpPdu) {
|
||||
print!(
|
||||
"ICMP {:3} {:3} {:5} {:10}",
|
||||
icmp_pdu.message_type(),
|
||||
icmp_pdu.message_code(),
|
||||
icmp_pdu.checksum(),
|
||||
//icmp_pdu.computed_checksum(ip: &crate::Ip)
|
||||
icmp_pdu.computed_data_offset()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_ipv4(ipv4_pdu: Ipv4Pdu) {
|
||||
let sa = ipv4_pdu.source_address();
|
||||
let da = ipv4_pdu.destination_address();
|
||||
print!(
|
||||
"IPv4 {} {} {} {:2} {} {:4} {:5} {:5} {:5} {} {} {:3} {:2} {:5} {:5} {:03}:{:03}:{:03}:{:03} {:03}:{:03}:{:03}:{:03} ",
|
||||
ipv4_pdu.version(),
|
||||
ipv4_pdu.ihl(),
|
||||
ipv4_pdu.computed_ihl(),
|
||||
ipv4_pdu.dscp(),
|
||||
ipv4_pdu.ecn(),
|
||||
ipv4_pdu.total_length(),
|
||||
ipv4_pdu.identification(),
|
||||
ipv4_pdu.dont_fragment(),
|
||||
ipv4_pdu.more_fragments(),
|
||||
ipv4_pdu.fragment_offset(),
|
||||
ipv4_pdu.computed_fragment_offset(),
|
||||
ipv4_pdu.ttl(),
|
||||
ipv4_pdu.protocol(),
|
||||
ipv4_pdu.checksum(),
|
||||
ipv4_pdu.computed_checksum(),
|
||||
//ipv4_pdu.source_address(),
|
||||
sa[0],
|
||||
sa[1],
|
||||
sa[2],
|
||||
sa[3],
|
||||
//ipv4_pdu.destination_address(),
|
||||
da[0],
|
||||
da[1],
|
||||
da[2],
|
||||
da[3]
|
||||
//ipv4_pdu.options()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_ipv6(ipv6_pdu: Ipv6Pdu) {
|
||||
print!(
|
||||
"IPv {:3} {:3} {:3} {:10} {:5} {:3} {:10} {:3} {:?} {:?} {:?} {:3} {:?} {:?}",
|
||||
// the {:10} could maybe be shorter
|
||||
// {:?} should be replaced with "proper" hexdecoding, aka a lib to do the displaying
|
||||
ipv6_pdu.version(),
|
||||
ipv6_pdu.dscp(),
|
||||
ipv6_pdu.ecn(),
|
||||
ipv6_pdu.flow_label(),
|
||||
ipv6_pdu.payload_length(),
|
||||
ipv6_pdu.next_header(),
|
||||
ipv6_pdu.computed_ihl(),
|
||||
ipv6_pdu.computed_protocol(),
|
||||
ipv6_pdu.computed_identification(),
|
||||
ipv6_pdu.computed_more_fragments(),
|
||||
ipv6_pdu.computed_fragment_offset(),
|
||||
ipv6_pdu.hop_limit(),
|
||||
ipv6_pdu.source_address(),
|
||||
ipv6_pdu.destination_address(),
|
||||
//ipv6_pdu.extension_headers()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_tcp(tcp_pdu: TcpPdu) {
|
||||
print!(
|
||||
"TCP {:5} {:5} {:10} {:10} {:2} {:2} {:2} {:5} {:5} {:5} {:5} {:5} {:5} {:5} {:5} {:5} {} {} ",
|
||||
tcp_pdu.source_port(),
|
||||
tcp_pdu.destination_port(),
|
||||
tcp_pdu.sequence_number(),
|
||||
tcp_pdu.acknowledgement_number(),
|
||||
tcp_pdu.data_offset(),
|
||||
tcp_pdu.computed_data_offset(),
|
||||
tcp_pdu.flags(),
|
||||
tcp_pdu.fin(),
|
||||
tcp_pdu.syn(),
|
||||
tcp_pdu.rst(),
|
||||
tcp_pdu.psh(),
|
||||
tcp_pdu.ack(),
|
||||
tcp_pdu.urg(),
|
||||
tcp_pdu.ecn(),
|
||||
tcp_pdu.cwr(),
|
||||
tcp_pdu.window_size(),
|
||||
//tcp_pdu.computed_window_size(shift: u8),
|
||||
tcp_pdu.checksum(),
|
||||
//tcp_pdu.computed_checksum(ip: &crate::Ip),
|
||||
tcp_pdu.urgent_pointer(),
|
||||
//tcp_pdu.options(),
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_udp(udp_pdu: UdpPdu) {
|
||||
print!(
|
||||
"UDP {:5} {:5} {:5} {:5} {} ",
|
||||
udp_pdu.source_port(),
|
||||
udp_pdu.destination_port(),
|
||||
udp_pdu.length(),
|
||||
udp_pdu.checksum(),
|
||||
//udp_pdu.computed_checksum(ip: &crate::Ip),
|
||||
udp_pdu.computed_data_offset()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn filter_ipv4_tcp_raw(ipv4_pdu: Ipv4Pdu, tcp_pdu: TcpPdu, raw: &[u8]) -> Verdict {
|
||||
// example filter that drops GET requests to /secret
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new("^GET /secret ").expect("Could not compile Regex!");
|
||||
}
|
||||
if RE.is_match(raw) {
|
||||
Verdict::Drop
|
||||
} else {
|
||||
Verdict::Accept
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue