mirror of
https://github.com/ParrotXray/Mantis.git
synced 2026-08-24 18:50:28 +09:00
feat: Add egress eBPF access control and fix Suricata HTTP port detection
This commit is contained in:
parent
e916b54a5b
commit
75c82691fc
18
CLAUDE.md
18
CLAUDE.md
@ -161,14 +161,18 @@ Default admin: username=`admin`, password=`admin` (seeded on first boot).
|
||||
|
||||
| Method | Path | Body | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/ipv4/{direction}/{list_type}` | — | Get IPv4 list |
|
||||
| PUT | `/ipv4/{direction}/{list_type}` | `SocketAddrV4` JSON | Add entry |
|
||||
| DELETE | `/ipv4/{direction}/{list_type}` | `SocketAddrV4` JSON | Remove entry |
|
||||
| GET | `/ipv6/{direction}/{list_type}` | — | Get IPv6 list |
|
||||
| PUT | `/ipv6/{direction}/{list_type}` | `SocketAddrV6` JSON | Add entry |
|
||||
| DELETE | `/ipv6/{direction}/{list_type}` | `SocketAddrV6` JSON | Remove entry |
|
||||
| GET | `/{nic}/ipv4/{flow}/{list_type}` | — | Get IPv4 list |
|
||||
| PUT | `/{nic}/ipv4/{flow}/{list_type}` | `SocketAddrV4` JSON | Add entry |
|
||||
| DELETE | `/{nic}/ipv4/{flow}/{list_type}` | `SocketAddrV4` JSON | Remove entry |
|
||||
| GET | `/{nic}/ipv6/{flow}/{list_type}` | — | Get IPv6 list |
|
||||
| PUT | `/{nic}/ipv6/{flow}/{list_type}` | `SocketAddrV6` JSON | Add entry |
|
||||
| DELETE | `/{nic}/ipv6/{flow}/{list_type}` | `SocketAddrV6` JSON | Remove entry |
|
||||
|
||||
`direction`: `ingress` | `egress` — `list_type`: `whitelist` | `blacklist`
|
||||
`nic`: `ingress` | `egress` — `flow`: `source` | `destination` — `list_type`: `whitelist` | `blacklist`
|
||||
|
||||
Ingress access control targets the ingress eBPF (inbound packets from external network).
|
||||
Egress access control targets the egress eBPF (outbound packets from internal network).
|
||||
`source` matches the packet's src IP/port; `destination` matches dst IP/port.
|
||||
|
||||
### eBPF Service Control — `/ebpf/service`
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ pub mod ingress {
|
||||
}
|
||||
|
||||
pub mod egress {
|
||||
pub const STATISTICS: u32 = 0;
|
||||
pub const TRANSMISSION: u32 = 1;
|
||||
pub const ACCESS_CONTROL: u32 = 0;
|
||||
pub const STATISTICS: u32 = 1;
|
||||
pub const TRANSMISSION: u32 = 2;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ default_admin_password = "admin"
|
||||
|
||||
# Suricata rule engine. Remove this entire section to disable.
|
||||
[Config.suricata]
|
||||
home_net = "140.130.34.0/24"
|
||||
home_net = ["140.130.34.0/24"]
|
||||
worker_cpu_set = [4, 6]
|
||||
management_cpu = 0
|
||||
af_packet_threads = "auto"
|
||||
|
||||
110
egress-ebpf/src/action/access_control.rs
Normal file
110
egress-ebpf/src/action/access_control.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use aya_ebpf::macros::map;
|
||||
use aya_ebpf::maps::HashMap;
|
||||
use common::define::setting::{MAX_RULES, MAX_RULES_PORT};
|
||||
use common::model::event::{IPv4Event, IPv6Event};
|
||||
use common::model::ip_address::{IPv4, IPv6, Port};
|
||||
|
||||
#[map]
|
||||
static EGRESS_IPV4_SRC_WHITELIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV6_SRC_WHITELIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV4_DST_WHITELIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV6_DST_WHITELIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV4_SRC_BLACKLIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV6_SRC_BLACKLIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV4_DST_BLACKLIST: HashMap<IPv4, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static EGRESS_IPV6_DST_BLACKLIST: HashMap<IPv6, [Port; MAX_RULES_PORT]> =
|
||||
HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
|
||||
pub fn ipv4_is_whitelisted(event: &IPv4Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = EGRESS_IPV4_SRC_WHITELIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = EGRESS_IPV4_DST_WHITELIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn ipv6_is_whitelisted(event: &IPv6Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = EGRESS_IPV6_SRC_WHITELIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = EGRESS_IPV6_DST_WHITELIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn ipv4_is_blacklisted(event: &IPv4Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = EGRESS_IPV4_SRC_BLACKLIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = EGRESS_IPV4_DST_BLACKLIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn ipv6_is_blacklisted(event: &IPv6Event) -> bool {
|
||||
unsafe {
|
||||
if let Some(ports) = EGRESS_IPV6_SRC_BLACKLIST.get(&event.src_ip) {
|
||||
if is_port_exist(ports, event.src_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(ports) = EGRESS_IPV6_DST_BLACKLIST.get(&event.dst_ip) {
|
||||
if is_port_exist(ports, event.dst_port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_port_exist(ports: &[Port; MAX_RULES_PORT], target_port: Port) -> bool {
|
||||
if ports.get(0) == Some(&0) {
|
||||
return true;
|
||||
}
|
||||
for &port in ports.iter() {
|
||||
if port == 0 {
|
||||
break;
|
||||
}
|
||||
if port == target_port {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
pub mod access_control;
|
||||
pub mod statistics;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#![no_main]
|
||||
mod action;
|
||||
|
||||
use action::statistics;
|
||||
use action::{access_control, statistics};
|
||||
use aya_ebpf::bindings::xdp_action;
|
||||
use aya_ebpf::macros::{map, xdp};
|
||||
use aya_ebpf::maps::{PerCpuArray, ProgramArray, XskMap};
|
||||
@ -33,7 +33,50 @@ unsafe fn packet_intake(ctx: XdpContext) -> Result<u32, ()> {
|
||||
let end = ctx.data_end();
|
||||
let ptr = PARSED_PACKET.get_ptr_mut(0).ok_or(())?;
|
||||
parsing::parse_packet(start, end, ptr)?;
|
||||
let _ = PROGRAM_ARRAY.tail_call(&ctx, STATISTICS);
|
||||
let _ = PROGRAM_ARRAY.tail_call(&ctx, ACCESS_CONTROL);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[xdp]
|
||||
pub fn access_control(ctx: XdpContext) -> u32 {
|
||||
unsafe {
|
||||
match try_access_control(&ctx) {
|
||||
Ok(action) => action,
|
||||
Err(_) => {
|
||||
let _ = PROGRAM_ARRAY.tail_call(&ctx, TRANSMISSION);
|
||||
xdp_action::XDP_PASS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
unsafe {
|
||||
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
|
||||
let parsed_packet = &*ptr;
|
||||
match parsed_packet {
|
||||
Event::IPv4(event) => {
|
||||
if access_control::ipv4_is_whitelisted(event) {
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||
return Err(());
|
||||
}
|
||||
if access_control::ipv4_is_blacklisted(event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
}
|
||||
Event::IPv6(event) => {
|
||||
if access_control::ipv6_is_whitelisted(event) {
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||
return Err(());
|
||||
}
|
||||
if access_control::ipv6_is_blacklisted(event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use common::define::setting::MAX_RULES_PORT;
|
||||
use common::model::ip_address::{IPv4, IPv6, Port};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::direction::{Direction, FlowDirection};
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::ip_address::NativeConvert;
|
||||
@ -15,117 +15,266 @@ use crate::model::list_type::ListType;
|
||||
use crate::utils::ip_address::convert_ports_to_vec;
|
||||
|
||||
pub struct AccessControl {
|
||||
ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
ipv4_src_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||
ipv4_dst_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
ipv4_dst_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||
ipv6_src_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||
ipv6_src_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||
ipv6_dst_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||
ipv6_dst_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||
ingress_ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
ingress_ipv4_src_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||
ingress_ipv4_dst_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
ingress_ipv4_dst_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||
ingress_ipv6_src_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||
ingress_ipv6_src_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||
ingress_ipv6_dst_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||
ingress_ipv6_dst_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||
egress_ipv4_src_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
egress_ipv4_src_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||
egress_ipv4_dst_whitelist: RwLock<MapWrapper<IPv4>>,
|
||||
egress_ipv4_dst_blacklist: RwLock<MapWrapper<IPv4>>,
|
||||
egress_ipv6_src_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||
egress_ipv6_src_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||
egress_ipv6_dst_whitelist: RwLock<MapWrapper<IPv6>>,
|
||||
egress_ipv6_dst_blacklist: RwLock<MapWrapper<IPv6>>,
|
||||
}
|
||||
|
||||
impl AccessControl {
|
||||
pub fn new(ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||
let access_control = Self {
|
||||
ipv4_src_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV4_SRC_WHITELIST")?),
|
||||
ipv4_src_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV4_SRC_BLACKLIST")?),
|
||||
ipv4_dst_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV4_DST_WHITELIST")?),
|
||||
ipv4_dst_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV4_DST_BLACKLIST")?),
|
||||
ipv6_src_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV6_SRC_WHITELIST")?),
|
||||
ipv6_src_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV6_SRC_BLACKLIST")?),
|
||||
ipv6_dst_whitelist: RwLock::new(MapWrapper::new(ebpf, "IPV6_DST_WHITELIST")?),
|
||||
ipv6_dst_blacklist: RwLock::new(MapWrapper::new(ebpf, "IPV6_DST_BLACKLIST")?),
|
||||
};
|
||||
Ok(access_control)
|
||||
pub fn new(ingress_ebpf: &mut Ebpf, egress_ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
ingress_ipv4_src_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_SRC_WHITELIST")?),
|
||||
ingress_ipv4_src_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_SRC_BLACKLIST")?),
|
||||
ingress_ipv4_dst_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_DST_WHITELIST")?),
|
||||
ingress_ipv4_dst_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV4_DST_BLACKLIST")?),
|
||||
ingress_ipv6_src_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_SRC_WHITELIST")?),
|
||||
ingress_ipv6_src_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_SRC_BLACKLIST")?),
|
||||
ingress_ipv6_dst_whitelist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_DST_WHITELIST")?),
|
||||
ingress_ipv6_dst_blacklist: RwLock::new(MapWrapper::new(ingress_ebpf, "IPV6_DST_BLACKLIST")?),
|
||||
egress_ipv4_src_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_SRC_WHITELIST")?),
|
||||
egress_ipv4_src_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_SRC_BLACKLIST")?),
|
||||
egress_ipv4_dst_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_DST_WHITELIST")?),
|
||||
egress_ipv4_dst_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV4_DST_BLACKLIST")?),
|
||||
egress_ipv6_src_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_SRC_WHITELIST")?),
|
||||
egress_ipv6_src_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_SRC_BLACKLIST")?),
|
||||
egress_ipv6_dst_whitelist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_DST_WHITELIST")?),
|
||||
egress_ipv6_dst_blacklist: RwLock::new(MapWrapper::new(egress_ebpf, "EGRESS_IPV6_DST_BLACKLIST")?),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_ipv4_list(&self, direction: FlowDirection, list_type: ListType) -> HashMap<Ipv4Addr, Vec<Port>> {
|
||||
let map_wrapper = match (direction, list_type) {
|
||||
(FlowDirection::Source, ListType::White) => self.ipv4_src_whitelist.read().await,
|
||||
(FlowDirection::Source, ListType::Black) => self.ipv4_src_blacklist.read().await,
|
||||
(FlowDirection::Destination, ListType::White) => self.ipv4_dst_whitelist.read().await,
|
||||
(FlowDirection::Destination, ListType::Black) => self.ipv4_dst_blacklist.read().await,
|
||||
pub async fn get_ipv4_list(
|
||||
&self,
|
||||
nic: Direction,
|
||||
flow: FlowDirection,
|
||||
list_type: ListType,
|
||||
) -> HashMap<Ipv4Addr, Vec<Port>> {
|
||||
let guard = match (nic, flow, list_type) {
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||
self.ingress_ipv4_src_whitelist.read().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||
self.ingress_ipv4_src_blacklist.read().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||
self.ingress_ipv4_dst_whitelist.read().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.ingress_ipv4_dst_blacklist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::White) => {
|
||||
self.egress_ipv4_src_whitelist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::Black) => {
|
||||
self.egress_ipv4_src_blacklist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||
self.egress_ipv4_dst_whitelist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.egress_ipv4_dst_blacklist.read().await
|
||||
}
|
||||
};
|
||||
map_wrapper.get_list()
|
||||
guard.get_list()
|
||||
}
|
||||
|
||||
pub async fn get_ipv6_list(&self, direction: FlowDirection, list_type: ListType) -> HashMap<Ipv6Addr, Vec<Port>> {
|
||||
let map_wrapper = match (direction, list_type) {
|
||||
(FlowDirection::Source, ListType::White) => self.ipv6_src_whitelist.read().await,
|
||||
(FlowDirection::Source, ListType::Black) => self.ipv6_src_blacklist.read().await,
|
||||
(FlowDirection::Destination, ListType::White) => self.ipv6_dst_whitelist.read().await,
|
||||
(FlowDirection::Destination, ListType::Black) => self.ipv6_dst_blacklist.read().await,
|
||||
pub async fn get_ipv6_list(
|
||||
&self,
|
||||
nic: Direction,
|
||||
flow: FlowDirection,
|
||||
list_type: ListType,
|
||||
) -> HashMap<Ipv6Addr, Vec<Port>> {
|
||||
let guard = match (nic, flow, list_type) {
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||
self.ingress_ipv6_src_whitelist.read().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||
self.ingress_ipv6_src_blacklist.read().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||
self.ingress_ipv6_dst_whitelist.read().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.ingress_ipv6_dst_blacklist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::White) => {
|
||||
self.egress_ipv6_src_whitelist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::Black) => {
|
||||
self.egress_ipv6_src_blacklist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||
self.egress_ipv6_dst_whitelist.read().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.egress_ipv6_dst_blacklist.read().await
|
||||
}
|
||||
};
|
||||
map_wrapper.get_list()
|
||||
guard.get_list()
|
||||
}
|
||||
|
||||
pub async fn add_ipv4_list(
|
||||
&self,
|
||||
direction: FlowDirection,
|
||||
nic: Direction,
|
||||
flow: FlowDirection,
|
||||
list_type: ListType,
|
||||
address: SocketAddrV4,
|
||||
) -> Result<(), Error> {
|
||||
let ip: u32 = (*address.ip()).to_bits().to_be();
|
||||
let port = address.port();
|
||||
let mut map_wrapper = match (direction, list_type) {
|
||||
(FlowDirection::Source, ListType::White) => self.ipv4_src_whitelist.write().await,
|
||||
(FlowDirection::Source, ListType::Black) => self.ipv4_src_blacklist.write().await,
|
||||
(FlowDirection::Destination, ListType::White) => self.ipv4_dst_whitelist.write().await,
|
||||
(FlowDirection::Destination, ListType::Black) => self.ipv4_dst_blacklist.write().await,
|
||||
let mut guard = match (nic, flow, list_type) {
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||
self.ingress_ipv4_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||
self.ingress_ipv4_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||
self.ingress_ipv4_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.ingress_ipv4_dst_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::White) => {
|
||||
self.egress_ipv4_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::Black) => {
|
||||
self.egress_ipv4_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||
self.egress_ipv4_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.egress_ipv4_dst_blacklist.write().await
|
||||
}
|
||||
};
|
||||
map_wrapper.add(ip, port)
|
||||
guard.add(ip, port)
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_list(
|
||||
&self,
|
||||
direction: FlowDirection,
|
||||
nic: Direction,
|
||||
flow: FlowDirection,
|
||||
list_type: ListType,
|
||||
address: SocketAddrV6,
|
||||
) -> Result<(), Error> {
|
||||
let ip: u128 = (*address.ip()).to_bits().to_be();
|
||||
let port = address.port();
|
||||
let mut map_wrapper = match (direction, list_type) {
|
||||
(FlowDirection::Source, ListType::White) => self.ipv6_src_whitelist.write().await,
|
||||
(FlowDirection::Source, ListType::Black) => self.ipv6_src_blacklist.write().await,
|
||||
(FlowDirection::Destination, ListType::White) => self.ipv6_dst_whitelist.write().await,
|
||||
(FlowDirection::Destination, ListType::Black) => self.ipv6_dst_blacklist.write().await,
|
||||
let mut guard = match (nic, flow, list_type) {
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||
self.ingress_ipv6_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||
self.ingress_ipv6_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||
self.ingress_ipv6_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.ingress_ipv6_dst_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::White) => {
|
||||
self.egress_ipv6_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::Black) => {
|
||||
self.egress_ipv6_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||
self.egress_ipv6_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.egress_ipv6_dst_blacklist.write().await
|
||||
}
|
||||
};
|
||||
map_wrapper.add(ip, port)
|
||||
guard.add(ip, port)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv4_list(
|
||||
&self,
|
||||
direction: FlowDirection,
|
||||
nic: Direction,
|
||||
flow: FlowDirection,
|
||||
list_type: ListType,
|
||||
address: SocketAddrV4,
|
||||
) -> Result<(), Error> {
|
||||
let ip: u32 = (*address.ip()).to_bits().to_be();
|
||||
let port = address.port();
|
||||
let mut map_wrapper = match (direction, list_type) {
|
||||
(FlowDirection::Source, ListType::White) => self.ipv4_src_whitelist.write().await,
|
||||
(FlowDirection::Source, ListType::Black) => self.ipv4_src_blacklist.write().await,
|
||||
(FlowDirection::Destination, ListType::White) => self.ipv4_dst_whitelist.write().await,
|
||||
(FlowDirection::Destination, ListType::Black) => self.ipv4_dst_blacklist.write().await,
|
||||
let mut guard = match (nic, flow, list_type) {
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||
self.ingress_ipv4_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||
self.ingress_ipv4_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||
self.ingress_ipv4_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.ingress_ipv4_dst_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::White) => {
|
||||
self.egress_ipv4_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::Black) => {
|
||||
self.egress_ipv4_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||
self.egress_ipv4_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.egress_ipv4_dst_blacklist.write().await
|
||||
}
|
||||
};
|
||||
map_wrapper.remove(ip, port)
|
||||
guard.remove(ip, port)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6_list(
|
||||
&self,
|
||||
direction: FlowDirection,
|
||||
nic: Direction,
|
||||
flow: FlowDirection,
|
||||
list_type: ListType,
|
||||
address: SocketAddrV6,
|
||||
) -> Result<(), Error> {
|
||||
let ip: u128 = (*address.ip()).to_bits().to_be();
|
||||
let port = address.port();
|
||||
let mut map_wrapper = match (direction, list_type) {
|
||||
(FlowDirection::Source, ListType::White) => self.ipv6_src_whitelist.write().await,
|
||||
(FlowDirection::Source, ListType::Black) => self.ipv6_src_blacklist.write().await,
|
||||
(FlowDirection::Destination, ListType::White) => self.ipv6_dst_whitelist.write().await,
|
||||
(FlowDirection::Destination, ListType::Black) => self.ipv6_dst_blacklist.write().await,
|
||||
let mut guard = match (nic, flow, list_type) {
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::White) => {
|
||||
self.ingress_ipv6_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Source, ListType::Black) => {
|
||||
self.ingress_ipv6_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::White) => {
|
||||
self.ingress_ipv6_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Ingress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.ingress_ipv6_dst_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::White) => {
|
||||
self.egress_ipv6_src_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Source, ListType::Black) => {
|
||||
self.egress_ipv6_src_blacklist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::White) => {
|
||||
self.egress_ipv6_dst_whitelist.write().await
|
||||
}
|
||||
(Direction::Egress, FlowDirection::Destination, ListType::Black) => {
|
||||
self.egress_ipv6_dst_blacklist.write().await
|
||||
}
|
||||
};
|
||||
map_wrapper.remove(ip, port)
|
||||
guard.remove(ip, port)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ pub struct EbpfServices {
|
||||
impl EbpfServices {
|
||||
pub fn new(app_config: Arc<AppConfig>, ingress_ebpf: &mut Ebpf, egress_ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||
let xsk_manager = XskManager::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||
let access_control = AccessControl::new(ingress_ebpf)?;
|
||||
let access_control = AccessControl::new(ingress_ebpf, egress_ebpf)?;
|
||||
let service = Service::new(ingress_ebpf)?;
|
||||
let statistics = Statistics::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||
let ebpf_services = Self {
|
||||
|
||||
@ -25,10 +25,6 @@ impl AppDB {
|
||||
pub fn open(path: impl AsRef<Path>, key: &str) -> Result<Self, Error> {
|
||||
let path = path.as_ref();
|
||||
|
||||
if let Some(parent) = Path::new(path).parent() {
|
||||
std::fs::create_dir_all(parent).map_err(|e| AuthError::DBError { msg: e.to_string() })?;
|
||||
}
|
||||
|
||||
let conn = Connection::open(path).map_err(|e| AuthError::DBError { msg: e.to_string() })?;
|
||||
|
||||
// Must be the first statement on the connection to unlock the encrypted DB.
|
||||
|
||||
@ -40,6 +40,10 @@ pub struct System {
|
||||
|
||||
impl System {
|
||||
pub async fn new() -> Result<Self, Error> {
|
||||
Logging::initialize()?;
|
||||
|
||||
log!(SystemLog::Initializing);
|
||||
|
||||
let (mut ingress_ebpf, ingress_program_array) = System::get_ingress_ebpf()?;
|
||||
let (mut egress_ebpf, egress_program_array) = System::get_egress_ebpf()?;
|
||||
let app_config = Arc::new(AppConfig::new()?);
|
||||
@ -73,8 +77,6 @@ impl System {
|
||||
pub async fn run(&mut self) -> Result<(), Error> {
|
||||
let ebpf_services = self.ebpf_services.clone();
|
||||
let app_services = self.app_services.clone();
|
||||
Logging::initialize()?;
|
||||
log!(SystemLog::Initializing);
|
||||
|
||||
log!(MLLog::ModelsLoaded(
|
||||
self.app_services.ml_models.get_model_info("deep_autoencoder")
|
||||
@ -85,7 +87,6 @@ impl System {
|
||||
});
|
||||
|
||||
self.aya_log_init()?;
|
||||
log!(SystemLog::InitializeComplete);
|
||||
self.attach_ebpf()?;
|
||||
|
||||
ebpf_services
|
||||
@ -93,6 +94,8 @@ impl System {
|
||||
.await?;
|
||||
app_services.run().await?;
|
||||
self.run_http_server().await?;
|
||||
|
||||
log!(SystemLog::InitializeComplete);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -168,6 +171,8 @@ impl System {
|
||||
.await
|
||||
.map_err(HttpError::BindPortError)?;
|
||||
|
||||
log!(SystemLog::HttpServerListening { port });
|
||||
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(async {
|
||||
tokio::signal::ctrl_c().await.ok();
|
||||
@ -205,6 +210,12 @@ impl System {
|
||||
Ebpf::load(aya::include_bytes_aligned!(env!("EGRESS_PATH"))).map_err(EbpfError::EbpfNotFound)?;
|
||||
let program_array = egress_ebpf.take_map("PROGRAM_ARRAY").ok_or(EbpfError::MapNotFound)?;
|
||||
let mut program_array = ProgramArray::try_from(program_array).map_err(EbpfError::MapOperationError)?;
|
||||
Self::load_program(
|
||||
&mut egress_ebpf,
|
||||
&mut program_array,
|
||||
"access_control",
|
||||
egress::ACCESS_CONTROL,
|
||||
)?;
|
||||
Self::load_program(&mut egress_ebpf, &mut program_array, "statistics", egress::STATISTICS)?;
|
||||
Self::load_program(
|
||||
&mut egress_ebpf,
|
||||
@ -243,4 +254,4 @@ impl System {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,7 +242,7 @@ vars:
|
||||
ENIP_CLIENT: "$HOME_NET"
|
||||
ENIP_SERVER: "$HOME_NET"
|
||||
port-groups:
|
||||
HTTP_PORTS: "80"
|
||||
HTTP_PORTS: "80,8080,8000,8008,8443,8888"
|
||||
SHELLCODE_PORTS: "!80"
|
||||
ORACLE_PORTS: 1521
|
||||
SSH_PORTS: 22
|
||||
@ -293,6 +293,8 @@ app-layer:
|
||||
enabled: yes
|
||||
http:
|
||||
enabled: yes
|
||||
detection-ports:
|
||||
dp: "any"
|
||||
dns:
|
||||
enabled: yes
|
||||
smtp:
|
||||
@ -315,7 +317,7 @@ legacy:
|
||||
|
||||
host-mode: sniffer-only
|
||||
"#,
|
||||
home_net = config.home_net,
|
||||
home_net = config.home_net.join(","),
|
||||
rule_path = rule_path,
|
||||
eve_socket = eve_socket,
|
||||
suppress_path = suppress_path,
|
||||
|
||||
@ -8,7 +8,7 @@ pub struct ConfigTable {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SuricataConfig {
|
||||
pub home_net: String,
|
||||
pub home_net: Vec<String>,
|
||||
pub worker_cpu_set: Option<[u32; 2]>,
|
||||
pub management_cpu: Option<u32>,
|
||||
#[serde(default = "default_af_threads")]
|
||||
|
||||
@ -27,5 +27,7 @@ loggable! {
|
||||
#[error("Traffic logging mode enabled — writing packets to: {path}")]
|
||||
TrafficLoggingEnabled { path: String } => tracing::Level::INFO,
|
||||
|
||||
#[error("HTTP server listening on 0.0.0.0:{port}")]
|
||||
HttpServerListening { port: u16 } => tracing::Level::INFO,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,65 +7,65 @@ use axum::routing::{delete, get, put};
|
||||
use axum::{Json, Router};
|
||||
|
||||
use crate::core::app_state::AppState;
|
||||
use crate::model::direction::FlowDirection;
|
||||
use crate::model::direction::{Direction, FlowDirection};
|
||||
use crate::model::list_type::ListType;
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/ipv4/{direction}/{list_type}",
|
||||
"/{nic_direction}/ipv4/{flow_direction}/{list_type}",
|
||||
get(get_ipv4_list).put(add_ipv4_list).delete(remove_ipv4_list),
|
||||
)
|
||||
.route(
|
||||
"/ipv6/{direction}/{list_type}",
|
||||
"/{nic_direction}/ipv6/{flow_direction}/{list_type}",
|
||||
get(get_ipv6_list).put(add_ipv6_list).delete(remove_ipv6_list),
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_ipv4_list(
|
||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
||||
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||
State(state): State<AppState>,
|
||||
) -> impl IntoResponse {
|
||||
Json(state.access_control.get_ipv4_list(direction, list_type).await)
|
||||
Json(state.access_control.get_ipv4_list(nic, flow, list_type).await)
|
||||
}
|
||||
|
||||
async fn get_ipv6_list(
|
||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
||||
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||
State(state): State<AppState>,
|
||||
) -> impl IntoResponse {
|
||||
Json(state.access_control.get_ipv6_list(direction, list_type).await)
|
||||
Json(state.access_control.get_ipv6_list(nic, flow, list_type).await)
|
||||
}
|
||||
|
||||
async fn add_ipv4_list(
|
||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
||||
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||
State(state): State<AppState>,
|
||||
Json(address): Json<SocketAddrV4>,
|
||||
) -> impl IntoResponse {
|
||||
match state.access_control.add_ipv4_list(direction, list_type, address).await {
|
||||
match state.access_control.add_ipv4_list(nic, flow, list_type, address).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_ipv6_list(
|
||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
||||
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||
State(state): State<AppState>,
|
||||
Json(address): Json<SocketAddrV6>,
|
||||
) -> impl IntoResponse {
|
||||
match state.access_control.add_ipv6_list(direction, list_type, address).await {
|
||||
match state.access_control.add_ipv6_list(nic, flow, list_type, address).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv4_list(
|
||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
||||
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||
State(state): State<AppState>,
|
||||
Json(address): Json<SocketAddrV4>,
|
||||
) -> impl IntoResponse {
|
||||
match state
|
||||
.access_control
|
||||
.remove_ipv4_list(direction, list_type, address)
|
||||
.remove_ipv4_list(nic, flow, list_type, address)
|
||||
.await
|
||||
{
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
@ -74,13 +74,13 @@ async fn remove_ipv4_list(
|
||||
}
|
||||
|
||||
async fn remove_ipv6_list(
|
||||
Path((direction, list_type)): Path<(FlowDirection, ListType)>,
|
||||
Path((nic, flow, list_type)): Path<(Direction, FlowDirection, ListType)>,
|
||||
State(state): State<AppState>,
|
||||
Json(address): Json<SocketAddrV6>,
|
||||
) -> impl IntoResponse {
|
||||
match state
|
||||
.access_control
|
||||
.remove_ipv6_list(direction, list_type, address)
|
||||
.remove_ipv6_list(nic, flow, list_type, address)
|
||||
.await
|
||||
{
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user