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