diff --git a/README.md b/README.md index d9768e2..1af6c19 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ > [!WARNING] > Ignore this README. It may change as I develop and adjust configurations. > The entire tool is under development, while I am learning Rust and eBPF. +> I am not a Rust developer, so I am learning as I go. I am open to any suggestions or improvements. +> Code is not optimized, and I am not following best practices. I am learning and improving as I go.

nflux

@@ -10,21 +12,15 @@ **Table of Contents** *generated with [mtoc](https://github.com/containerscrew/mtoc)* -- [Intro](#intro) +- [Nflux architecture](#nflux-architecture) - [Features](#features) -- [Requirements](#requirements) - [Installation](#installation) - - [Debian/Ubuntu installation](#debianubuntu-installation) -- [Configuration file](#configuration-file) -- [Logging](#logging) -- [Testing firewall](#testing-firewall) -- [Debugging](#debugging) - [Contribution](#contribution) - [License](#license) -# Intro +# Nflux architecture Look at what level it works XDP: @@ -34,134 +30,36 @@ Powerful, right? Same for traffic control (TC). ![tc](./tc.png) +> [!NOTE] +> nflux uses XDP for incoming packet processing (only works with physical interfaces). For outgoing packets, it uses TC. If you want to use it with a virtual interface, you need to use the `tc` mode which is not implemented yet. + # Features Basic XDP firewall: -* Block TCP SYN incoming packets -* Allow incoming SYN-ACK incoming packets (for example, you are using your browser) -* Block ICMP incoming packets -* User can allow traffic for specific incoming ports -* User can allow traffic for specific IP addresses - - -# Requirements - -`nflux` is not available in any package manager yet. You need to build it from source. - -1. Install rust: -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -2. Install nightly toolchain: -``` -rustup install stable && rustup toolchain install nightly --component rust-src -``` - -3. Optional, if using mac or other linux: -```bash -LLVM_SYS_180_PREFIX=$(brew --prefix llvm) cargo install --no-default-features bpf-linker -``` - -3. **MANDATORY:** -```bash -cargo install bpf-linker -``` - -> [!CAUTION] -> nflux uses XDP for packet processing. Only works with physical interfaces. If you want to use it with a virtual interface, you need to use the `tc` mode which is not implemented yet. -> For example, you want to monitor incoming traffic using a virtual interface like `tun0` (VPN), you need to use the `tc` mode. - +* Block incoming ipv4/ipv6-tcp/udp packets. +* Allow incoming ipv4/ipv6-tcp/udp packets. +* Block incoming ICMP packets. +* Filter outgoing packets. # Installation -By the moment, I only have the setup for `dpkg` (Debian/Ubuntu). - -```shell -git clone https://github.com/containerscrew/nflux.git -``` - -## Debian/Ubuntu installation - -```shell -make install-dpkg -``` - -# Configuration file - -Edit the main configuration [`nflux.toml`](./nflux.toml) file if needed: - -```shell -sudo nvim /etc/nflux/nflux.toml -sudo systemctl restart nflux.service -``` - -# Logging - -```shell -make journal-logs -``` - -# Testing firewall - -Now you can try to map some services using docker. For example, let's expose an nginx server (tcp) and bind9 (udp): - -```shell -make compose-up -``` - -Test the exposed services works. (Recommended) test the services from other device in the same network: - -* Nginx server: +By the moment, the quickest way to install **`nflux`** is using containers. Let's see how to run `nflux` with `docker-compose`. ```bash -curl http://ip:8081 # Welcome to nginx! +git clone https://github.com/containerscrew/nflux.git +make compose-build ``` -* Bind9 server: +Before running the container, you need to edit the configuration file [`nflux.toml`](./nflux.toml). The most important configuration is the `interface` name. ```bash -dig @ip -p 5053 mycompany.org A -``` - -> Change `ip` to the ip of the machine where you are running the docker-compose and where you will run the firewall. - -Now, since the exposed port of `nginx` for example is `8081`, let's run the firewall without any allowed port: - -For example, in `nflux.toml`: - -```toml -[log] -log_level = "info" # trace, debug, info, warn or error. Defaults to info if not set -log_type = "text" # text or json. Defaults to text if not set - -[nflux] -interface_name = "wlp2s0" - -[firewall] -# All incoming connections will be blocked by default -# You can specify allowed IP addresses and ports -# This will allow both, udp and tcp connections - -# Specify IP addresses you want to allow. Will be able to access full ports tcp/udp -allowed_ipv4 = ["192.168.0.30"] -# Specify ports you want to allow. Everyone will be able to access these ports. No ip filtering -allowed_ports = ["8080"] -# Allow ICMP packets (ping) -allow_icmp = false +ip link show # get the name of your PHISICAL interface +# Once is changes in the conf file, lets run nflux +make compose-up ``` -Try again `curl http://ip:8081` and you will see that the connection is blocked. - -Change the `interface_name` to your physical interface name, also you can play changing the `allowed_ipv4` and `allowed_ports` to allow some traffic. - -# Debugging - -```bash -sudo bpftool prog list # show ebpf running programs -ip link show dev wlo1 # xdp attached to your interface -``` +> By default, nflux will allow SSH (22) connections from any IP. Avoid blocking your SSH connection if testing in remote servers (VPS). # Contribution diff --git a/docker/compose.yml b/docker/compose.yml index e58e69d..abd836b 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -6,6 +6,10 @@ services: restart: unless-stopped network_mode: host cap_add: + # Required caps: + # - CAP_NET_ADMIN + # - CAP_BPF + # - CAP_PERFMON # Insecure, by the moment give all capabilities - ALL container_name: nflux diff --git a/docs/local_dev.md b/docs/local_dev.md new file mode 100644 index 0000000..6dc37e3 --- /dev/null +++ b/docs/local_dev.md @@ -0,0 +1,38 @@ + +**Table of Contents** *generated with [mtoc](https://github.com/containerscrew/mtoc)* +- [Local development](#local-development) + - [Requirements](#requirements) +- [Debugging](#debugging) + +# Local development + +For more information, please visit [official documentation](https://aya-rs.dev/book/). + +## Requirements + +1. Install rust: +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +2. Install nightly toolchain: +``` +rustup install stable && rustup toolchain install nightly --component rust-src +``` + +3. Optional, if using mac or other linux: +```bash +LLVM_SYS_180_PREFIX=$(brew --prefix llvm) cargo install --no-default-features bpf-linker +``` + +3. **MANDATORY:** +```bash +cargo install bpf-linker +``` + +# Debugging + +```bash +sudo bpftool prog list # show ebpf running programs +ip link show dev wlo1 # xdp attached to your interface +``` diff --git a/nflux.toml b/nflux.toml index 76978bd..c19186a 100644 --- a/nflux.toml +++ b/nflux.toml @@ -12,10 +12,11 @@ log_type = "text" # text or json. Defaults to text if not set [ip_rules] # The /32 CIDR block is used to represent a single IP address rather than a range -"192.168.0.0/24" = { priority = 1, action = "allow", ports = [22, 8000, 80], protocol = "tcp", log = false, description = "Allow SSH for entire local net" } +"0.0.0.0/0" = { priority = 1, action = "allow", ports = [22], protocol = "tcp", log = true, description = "Allow SSH from anywhere" } +"192.168.0.0/24" = { priority = 2, action = "allow", ports = [8000, 80], protocol = "tcp", log = false, description = "Allow SSH for entire local net" } # curl -6 -v http://\[::ffff:192.168.0.26\]:80 -"fe80::5bc2:662b:ac2f:7e8b/128" = { priority = 2, action = "allow", ports = [80], protocol = "tcp", log = false, description = "Deny HTTP for specific IPv6 address" } +"fe80::5bc2:662b:ac2f:7e8b/128" = { priority = 3, action = "allow", ports = [80], protocol = "tcp", log = false, description = "Deny HTTP for specific IPv6 address" } #[mac_rules] # todo: MAC address filtering diff --git a/stuff/test.rs b/stuff/test.rs deleted file mode 100644 index 2e780b4..0000000 --- a/stuff/test.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Read events from ring buffer -// let cpus = online_cpus().unwrap(); -// let num_cpus = cpus.len(); -// let mut events = AsyncPerfEventArray::try_from(bpf.map_mut("CONNECTION_EVENTS").unwrap())?; - -// for cpu in cpus { -// let mut buf = events.open(cpu, None)?; -// -// tokio::spawn(async move { -// let mut buffers = (0..num_cpus) -// .map(|_| BytesMut::with_capacity(9000)) -// .collect::>(); -// -// loop { -// // Attempt to read events from the perf buffer into the prepared buffers. -// let events = match buf.read_events(&mut buffers).await { -// Ok(events) => events, -// Err(e) => { -// warn!("Error reading events: {}", e); -// continue; -// } -// }; -// -// // Iterate over the number of events read. `events.read` indicates how many events were read. -// for i in 0..events.read { -// let buf = &mut buffers[i]; -// let data = buf.as_ptr() as *const ConnectionEvent; // Cast the buffer pointer to a Data pointer. -// info!("{:?}", unsafe { *data }); -// } -// } -// }); -// } - -// Log connection events for TCP and UDP -// fn log_connection_event( -// ctx: &XdpContext, -// src_addr: u32, -// dst_addr: u32, -// src_port: u16, -// dst_port: u16, -// protocol: u8, -// ) { -// let event = ConnectionEvent { -// src_addr, -// dst_addr, -// src_port, -// dst_port, -// protocol, -// }; -// let _ = CONNECTION_EVENTS.output(ctx, &event, 0); -// }