mirror of
https://github.com/ParrotXray/Mantis.git
synced 2026-08-24 18:50:28 +09:00
feat: Remove SERVICE stage from ingress eBPF pipeline
This commit is contained in:
parent
487f8c4913
commit
1ead3c9e0d
@ -1,8 +1,7 @@
|
||||
pub mod ingress {
|
||||
pub const ACCESS_CONTROL: u32 = 0;
|
||||
pub const SERVICE: u32 = 1;
|
||||
pub const STATISTICS: u32 = 2;
|
||||
pub const TRANSMISSION: u32 = 3;
|
||||
pub const STATISTICS: u32 = 1;
|
||||
pub const TRANSMISSION: u32 = 2;
|
||||
}
|
||||
|
||||
pub mod egress {
|
||||
|
||||
@ -1,3 +1,2 @@
|
||||
pub mod access_control;
|
||||
pub mod service;
|
||||
pub mod statistics;
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
use aya_ebpf::macros::map;
|
||||
use aya_ebpf::maps::{Array, HashMap};
|
||||
use common::define::offset::*;
|
||||
use common::define::setting::MAX_RULES;
|
||||
use common::model::event::{IPv4Event, IPv6Event};
|
||||
use common::model::http_method::HttpMethodBitmap;
|
||||
use common::model::ip_address::*;
|
||||
use common::model::placeholder::PlaceHolder;
|
||||
use network_types::ip::IpProto;
|
||||
use network_types::tcp::TcpHdr;
|
||||
|
||||
#[map]
|
||||
static IPV4_HTTP_SERVICE: HashMap<AddrPortV4, HttpMethodBitmap> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static IPV6_HTTP_SERVICE: HashMap<AddrPortV6, HttpMethodBitmap> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static SSH_WHITE_LIST_ENABLE: Array<PlaceHolder> = Array::with_max_entries(1, 0);
|
||||
#[map]
|
||||
static IPV4_SSH_SERVICE: HashMap<AddrPortV4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static IPV6_SSH_SERVICE: HashMap<AddrPortV6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static IPV4_SSH_WHITE_LIST: HashMap<IPv4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static IPV6_SSH_WHITE_LIST: HashMap<IPv6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static IPV4_SSH_BLACK_LIST: HashMap<IPv4, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
#[map]
|
||||
static IPV6_SSH_BLACK_LIST: HashMap<IPv6, PlaceHolder> = HashMap::with_max_entries(MAX_RULES as u32, 0);
|
||||
|
||||
pub fn ipv4_service_rule_violation(start: usize, end: usize, event: &IPv4Event) -> bool {
|
||||
let protocol = event.protocol;
|
||||
let source = event.source_addr();
|
||||
let destination = event.destination_addr();
|
||||
ipv4_http_service_violation(start, end, &protocol, &destination)
|
||||
|| ipv4_ssh_service_violation(&source, &destination)
|
||||
}
|
||||
|
||||
pub fn ipv6_service_rule_violation(start: usize, end: usize, event: &IPv6Event) -> bool {
|
||||
let protocol = event.protocol;
|
||||
let source = event.source_addr();
|
||||
let destination = event.destination_addr();
|
||||
ipv6_http_service_violation(start, end, &protocol, &destination)
|
||||
|| ipv6_ssh_service_violation(&source, &destination)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ipv4_http_service_violation(start: usize, end: usize, protocol: &IpProto, destination: &AddrPortV4) -> bool {
|
||||
match IPV4_HTTP_SERVICE.get_ptr_mut(destination) {
|
||||
Some(allow_method) => {
|
||||
if !matches!(protocol, IpProto::Tcp) {
|
||||
return false;
|
||||
}
|
||||
unsafe {
|
||||
if start + IPV4_TCP_HEADER_END > end {
|
||||
return false;
|
||||
}
|
||||
let tcp_header = &*((start + IPV4_TCP_HEADER_START) as *const TcpHdr);
|
||||
if tcp_header.syn() != 0 || tcp_header.rst() != 0 || tcp_header.fin() != 0 {
|
||||
return false;
|
||||
}
|
||||
if tcp_header.psh() == 0 || tcp_header.ack() == 0 {
|
||||
return false;
|
||||
}
|
||||
let doff = tcp_header.doff();
|
||||
if doff < 5 || doff > 15 {
|
||||
return false;
|
||||
}
|
||||
let tcp_header_len = (doff * 4) as usize;
|
||||
let tcp_payload_start = IPV4_TCP_HEADER_END + tcp_header_len;
|
||||
match get_http_request_method(start, end, tcp_payload_start) {
|
||||
Some(http_method) => *allow_method & http_method == 0,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ipv6_http_service_violation(start: usize, end: usize, protocol: &IpProto, destination: &AddrPortV6) -> bool {
|
||||
match IPV6_HTTP_SERVICE.get_ptr_mut(destination) {
|
||||
Some(allow_method) => {
|
||||
if !matches!(protocol, IpProto::Tcp) {
|
||||
return false;
|
||||
}
|
||||
unsafe {
|
||||
if start + IPV6_TCP_HEADER_END > end {
|
||||
return false;
|
||||
}
|
||||
let tcp_header = &*((start + IPV6_TCP_HEADER_START) as *const TcpHdr);
|
||||
if tcp_header.syn() != 0 || tcp_header.rst() != 0 || tcp_header.fin() != 0 {
|
||||
return false;
|
||||
}
|
||||
if tcp_header.psh() == 0 || tcp_header.ack() == 0 {
|
||||
return false;
|
||||
}
|
||||
let doff = tcp_header.doff();
|
||||
if doff < 5 || doff > 15 {
|
||||
return false;
|
||||
}
|
||||
let tcp_header_len = (doff * 4) as usize;
|
||||
let tcp_payload_start = IPV6_TCP_HEADER_END + tcp_header_len;
|
||||
match get_http_request_method(start, end, tcp_payload_start) {
|
||||
Some(http_method) => *allow_method & http_method == 0,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_http_request_method(start: usize, end: usize, offset: usize) -> Option<HttpMethodBitmap> {
|
||||
if start + offset + 8 > end {
|
||||
return None;
|
||||
}
|
||||
let data = unsafe { core::slice::from_raw_parts((start + offset) as *const u8, 8) };
|
||||
match &data[..4] {
|
||||
b"GET " => Some(1 << 0),
|
||||
b"POST" if &data[4..5] == b" " => Some(1 << 1),
|
||||
b"PUT " => Some(1 << 2),
|
||||
b"DELE" if &data[4..7] == b"TE " => Some(1 << 3),
|
||||
b"HEAD" if &data[4..5] == b" " => Some(1 << 4),
|
||||
b"OPTI" if &data[4..8] == b"ONS " => Some(1 << 5),
|
||||
b"PATC" if &data[4..6] == b"H " => Some(1 << 6),
|
||||
b"TRAC" if &data[4..6] == b"E " => Some(1 << 7),
|
||||
b"CONN" if &data[4..8] == b"ECT " => Some(1 << 8),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ipv4_ssh_service_violation(source: &AddrPortV4, destination: &AddrPortV4) -> bool {
|
||||
unsafe {
|
||||
if IPV4_SSH_SERVICE.get(destination).is_some() {
|
||||
if SSH_WHITE_LIST_ENABLE.get(0).is_some() {
|
||||
IPV4_SSH_WHITE_LIST.get(&source.ip()).is_none()
|
||||
} else {
|
||||
IPV4_SSH_BLACK_LIST.get(&source.ip()).is_some()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ipv6_ssh_service_violation(source_ip: &AddrPortV6, destination: &AddrPortV6) -> bool {
|
||||
unsafe {
|
||||
if IPV6_SSH_SERVICE.get(destination).is_some() {
|
||||
if SSH_WHITE_LIST_ENABLE.get(0).is_some() {
|
||||
IPV6_SSH_WHITE_LIST.get(&source_ip.ip()).is_none()
|
||||
} else {
|
||||
IPV6_SSH_BLACK_LIST.get(&source_ip.ip()).is_some()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ use common::define::program_array::ingress::*;
|
||||
use common::ebpf::parsing;
|
||||
use common::model::event::Event;
|
||||
|
||||
use crate::action::{access_control, service, statistics};
|
||||
use crate::action::{access_control, statistics};
|
||||
|
||||
#[map]
|
||||
static PROGRAM_ARRAY: ProgramArray = ProgramArray::with_max_entries(8, 0);
|
||||
@ -80,43 +80,6 @@ unsafe fn try_access_control(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, SERVICE);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[xdp]
|
||||
pub fn service(ctx: XdpContext) -> u32 {
|
||||
unsafe {
|
||||
match try_service(&ctx) {
|
||||
Ok(action) => action,
|
||||
Err(_) => {
|
||||
let _ = PROGRAM_ARRAY.tail_call(&ctx, TRANSMISSION);
|
||||
xdp_action::XDP_PASS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn try_service(ctx: &XdpContext) -> Result<u32, ()> {
|
||||
unsafe {
|
||||
let start = ctx.data();
|
||||
let end = ctx.data_end();
|
||||
let ptr = PARSED_PACKET.get_ptr(0).ok_or(())?;
|
||||
let parsed_packet = &*ptr;
|
||||
match parsed_packet {
|
||||
Event::IPv4(event) => {
|
||||
if service::ipv4_service_rule_violation(start, end, event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
}
|
||||
Event::IPv6(event) => {
|
||||
if service::ipv6_service_rule_violation(start, end, event) {
|
||||
return Ok(xdp_action::XDP_DROP);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = PROGRAM_ARRAY.tail_call(ctx, STATISTICS);
|
||||
Err(())
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::core::ebpf::access_control::AccessControl;
|
||||
use crate::core::ebpf::service::Service;
|
||||
use crate::core::ebpf::statistics::Statistics;
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
use crate::core::infrastructure::app_db::AppDB;
|
||||
@ -14,7 +13,6 @@ pub struct AppState {
|
||||
pub app_config: Arc<AppConfig>,
|
||||
pub inference_config: Arc<InferenceConfig>,
|
||||
pub access_control: Arc<AccessControl>,
|
||||
pub service: Arc<Service>,
|
||||
pub statistics: Arc<Statistics>,
|
||||
pub health: Arc<SystemHealth>,
|
||||
pub detection_alert: Arc<DetectionAlert>,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
pub mod access_control;
|
||||
pub mod service;
|
||||
pub mod statistics;
|
||||
pub mod xsk_manager;
|
||||
|
||||
@ -11,7 +10,6 @@ use macros::log;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::core::ebpf::access_control::AccessControl;
|
||||
use crate::core::ebpf::service::Service;
|
||||
use crate::core::ebpf::statistics::Statistics;
|
||||
use crate::core::ebpf::xsk_manager::XskManager;
|
||||
use crate::core::infrastructure::app_config::AppConfig;
|
||||
@ -23,7 +21,6 @@ use crate::model::error::system::SystemError;
|
||||
pub struct EbpfServices {
|
||||
pub xsk_manager: Arc<XskManager>,
|
||||
pub access_control: Arc<AccessControl>,
|
||||
pub service: Arc<Service>,
|
||||
pub statistics: Arc<Statistics>,
|
||||
pub shutdowns: SegQueue<oneshot::Sender<()>>,
|
||||
}
|
||||
@ -32,12 +29,10 @@ 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, egress_ebpf)?;
|
||||
let service = Service::new(ingress_ebpf)?;
|
||||
let statistics = Statistics::new(app_config.clone(), ingress_ebpf, egress_ebpf)?;
|
||||
let ebpf_services = Self {
|
||||
xsk_manager: Arc::new(xsk_manager),
|
||||
access_control: Arc::new(access_control),
|
||||
service: Arc::new(service),
|
||||
statistics: Arc::new(statistics),
|
||||
shutdowns: SegQueue::new(),
|
||||
};
|
||||
|
||||
@ -1,332 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
||||
|
||||
use aya::maps::{Array as AyaArray, HashMap as AyaHashMap, MapData};
|
||||
use aya::{Ebpf, Pod};
|
||||
use common::model::http_method::{HttpMethod, HttpMethodBitmap};
|
||||
use common::model::ip_address::{AddrPortV4, AddrPortV6, IPv4, IPv6};
|
||||
use common::model::placeholder::PlaceHolder;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::model::error::Error;
|
||||
use crate::model::error::ebpf::EbpfError;
|
||||
use crate::model::ip_address::NativeConvert;
|
||||
|
||||
pub struct Service {
|
||||
ipv4_http_service: RwLock<HttpServiceWrapper<AddrPortV4>>,
|
||||
ipv6_http_service: RwLock<HttpServiceWrapper<AddrPortV6>>,
|
||||
ssh_white_list_enable: RwLock<WhiteListControl>,
|
||||
ipv4_ssh_service: RwLock<SshServiceWrapper<AddrPortV4>>,
|
||||
ipv6_ssh_service: RwLock<SshServiceWrapper<AddrPortV6>>,
|
||||
ipv4_ssh_white_list: RwLock<SshListWrapper<IPv4>>,
|
||||
ipv6_ssh_white_list: RwLock<SshListWrapper<IPv6>>,
|
||||
ipv4_ssh_black_list: RwLock<SshListWrapper<IPv4>>,
|
||||
ipv6_ssh_black_list: RwLock<SshListWrapper<IPv6>>,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
pub fn new(ebpf: &mut Ebpf) -> Result<Self, Error> {
|
||||
let service = Self {
|
||||
ipv4_http_service: RwLock::new(HttpServiceWrapper::new(ebpf, "IPV4_HTTP_SERVICE")?),
|
||||
ipv6_http_service: RwLock::new(HttpServiceWrapper::new(ebpf, "IPV6_HTTP_SERVICE")?),
|
||||
ssh_white_list_enable: RwLock::new(WhiteListControl::new(ebpf, "SSH_WHITE_LIST_ENABLE")?),
|
||||
ipv4_ssh_service: RwLock::new(SshServiceWrapper::new(ebpf, "IPV4_SSH_SERVICE")?),
|
||||
ipv6_ssh_service: RwLock::new(SshServiceWrapper::new(ebpf, "IPV6_SSH_SERVICE")?),
|
||||
ipv4_ssh_white_list: RwLock::new(SshListWrapper::new(ebpf, "IPV4_SSH_WHITE_LIST")?),
|
||||
ipv6_ssh_white_list: RwLock::new(SshListWrapper::new(ebpf, "IPV6_SSH_WHITE_LIST")?),
|
||||
ipv4_ssh_black_list: RwLock::new(SshListWrapper::new(ebpf, "IPV4_SSH_BLACK_LIST")?),
|
||||
ipv6_ssh_black_list: RwLock::new(SshListWrapper::new(ebpf, "IPV6_SSH_BLACK_LIST")?),
|
||||
};
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
pub async fn get_ipv4_http_service(&self) -> HashMap<SocketAddrV4, Vec<HttpMethod>> {
|
||||
self.ipv4_http_service.read().await.get_http_method()
|
||||
}
|
||||
|
||||
pub async fn get_ipv6_http_service(&self) -> HashMap<SocketAddrV6, Vec<HttpMethod>> {
|
||||
self.ipv6_http_service.read().await.get_http_method()
|
||||
}
|
||||
|
||||
pub async fn add_ipv4_http_service(
|
||||
&self,
|
||||
address: SocketAddrV4,
|
||||
http_method: Vec<HttpMethod>,
|
||||
) -> Result<(), Error> {
|
||||
self.ipv4_http_service
|
||||
.write()
|
||||
.await
|
||||
.add_http_service(address, http_method)
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_http_service(
|
||||
&self,
|
||||
address: SocketAddrV6,
|
||||
http_method: Vec<HttpMethod>,
|
||||
) -> Result<(), Error> {
|
||||
self.ipv6_http_service
|
||||
.write()
|
||||
.await
|
||||
.add_http_service(address, http_method)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv4_http_service(
|
||||
&self,
|
||||
address: SocketAddrV4,
|
||||
removed_http_method: Vec<HttpMethod>,
|
||||
) -> Result<(), Error> {
|
||||
self.ipv4_http_service
|
||||
.write()
|
||||
.await
|
||||
.remove_http_service(address, removed_http_method)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6_http_service(
|
||||
&self,
|
||||
address: SocketAddrV6,
|
||||
removed_http_method: Vec<HttpMethod>,
|
||||
) -> Result<(), Error> {
|
||||
self.ipv6_http_service
|
||||
.write()
|
||||
.await
|
||||
.remove_http_service(address, removed_http_method)
|
||||
}
|
||||
|
||||
pub async fn is_ssh_white_list_enable(&self) -> bool {
|
||||
self.ssh_white_list_enable.read().await.is_white_list_enable()
|
||||
}
|
||||
|
||||
pub async fn enable_ssh_white_list(&self) -> Result<(), Error> {
|
||||
self.ssh_white_list_enable.write().await.enable_white_list()
|
||||
}
|
||||
|
||||
pub async fn disable_ssh_white_list(&self) -> Result<(), Error> {
|
||||
self.ssh_white_list_enable.write().await.disable_white_list()
|
||||
}
|
||||
|
||||
pub async fn get_ipv4_ssh_service(&self) -> Vec<SocketAddrV4> {
|
||||
self.ipv4_ssh_service.read().await.get_ssh_service()
|
||||
}
|
||||
|
||||
pub async fn get_ipv6_ssh_service(&self) -> Vec<SocketAddrV6> {
|
||||
self.ipv6_ssh_service.read().await.get_ssh_service()
|
||||
}
|
||||
|
||||
pub async fn add_ipv4_ssh_service(&self, address: SocketAddrV4) -> Result<(), Error> {
|
||||
self.ipv4_ssh_service.write().await.add_ssh_service(address)
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_ssh_service(&self, address: SocketAddrV6) -> Result<(), Error> {
|
||||
self.ipv6_ssh_service.write().await.add_ssh_service(address)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv4_ssh_service(&self, address: SocketAddrV4) -> Result<(), Error> {
|
||||
self.ipv4_ssh_service.write().await.remove_ssh_service(address)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6_ssh_service(&self, address: SocketAddrV6) -> Result<(), Error> {
|
||||
self.ipv6_ssh_service.write().await.remove_ssh_service(address)
|
||||
}
|
||||
|
||||
pub async fn get_ipv4_ssh_white_list(&self) -> Vec<Ipv4Addr> {
|
||||
self.ipv4_ssh_white_list.read().await.get_list()
|
||||
}
|
||||
|
||||
pub async fn get_ipv6_ssh_white_list(&self) -> Vec<Ipv6Addr> {
|
||||
self.ipv6_ssh_white_list.read().await.get_list()
|
||||
}
|
||||
|
||||
pub async fn add_ipv4_ssh_white_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
||||
self.ipv4_ssh_white_list.write().await.add_list(ip)
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_ssh_white_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
||||
self.ipv6_ssh_white_list.write().await.add_list(ip)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv4_ssh_white_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
||||
self.ipv4_ssh_white_list.write().await.remove_list(ip)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6_ssh_white_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
||||
self.ipv6_ssh_white_list.write().await.remove_list(ip)
|
||||
}
|
||||
|
||||
pub async fn get_ipv4_ssh_black_list(&self) -> Vec<Ipv4Addr> {
|
||||
self.ipv4_ssh_black_list.read().await.get_list()
|
||||
}
|
||||
|
||||
pub async fn get_ipv6_ssh_black_list(&self) -> Vec<Ipv6Addr> {
|
||||
self.ipv6_ssh_black_list.read().await.get_list()
|
||||
}
|
||||
|
||||
pub async fn add_ipv4_ssh_black_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
||||
self.ipv4_ssh_black_list.write().await.add_list(ip)
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_ssh_black_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
||||
self.ipv6_ssh_black_list.write().await.add_list(ip)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv4_ssh_black_list(&self, ip: Ipv4Addr) -> Result<(), Error> {
|
||||
self.ipv4_ssh_black_list.write().await.remove_list(ip)
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6_ssh_black_list(&self, ip: Ipv6Addr) -> Result<(), Error> {
|
||||
self.ipv6_ssh_black_list.write().await.remove_list(ip)
|
||||
}
|
||||
}
|
||||
|
||||
struct WhiteListControl {
|
||||
map: AyaArray<MapData, PlaceHolder>,
|
||||
}
|
||||
|
||||
impl WhiteListControl {
|
||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
||||
let map = AyaArray::try_from(map).map_err(EbpfError::MapOperationError)?;
|
||||
Ok(Self { map })
|
||||
}
|
||||
|
||||
fn is_white_list_enable(&self) -> bool {
|
||||
match self.map.get(&0, 0) {
|
||||
Ok(status) => {
|
||||
if status == 0 {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_white_list(&mut self) -> Result<(), Error> {
|
||||
self.map.set(0, 1_u8, 0).map_err(EbpfError::MapOperationError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_white_list(&mut self) -> Result<(), Error> {
|
||||
self.map.set(0, 0_u8, 0).map_err(EbpfError::MapOperationError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct HttpServiceWrapper<T> {
|
||||
map: AyaHashMap<MapData, T, HttpMethodBitmap>,
|
||||
}
|
||||
|
||||
impl<T: NativeConvert + Pod> HttpServiceWrapper<T> {
|
||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
||||
let map = AyaHashMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
||||
Ok(Self { map })
|
||||
}
|
||||
|
||||
fn get_http_method(&self) -> HashMap<T::Native, Vec<HttpMethod>> {
|
||||
self.map
|
||||
.iter()
|
||||
.filter_map(Result::ok)
|
||||
.map(|(key, value)| {
|
||||
let address = key.into_native();
|
||||
(address, HttpMethod::convert_from_bitmap(value))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn add_http_service(&mut self, address: T::Native, http_method: Vec<HttpMethod>) -> Result<(), Error> {
|
||||
let address = T::from_native(address);
|
||||
let ebpf_method = HttpMethod::convert_to_bitmap(http_method);
|
||||
self.map
|
||||
.insert(address, ebpf_method, 0)
|
||||
.map_err(|_| EbpfError::RuleReachLimit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_http_service(&mut self, address: T::Native, removed_http_method: Vec<HttpMethod>) -> Result<(), Error> {
|
||||
let address = T::from_native(address);
|
||||
if let Ok(current_http_method) = self.map.get(&address, 0) {
|
||||
let mut http_method = HttpMethod::convert_from_bitmap(current_http_method);
|
||||
http_method.retain(|method| !removed_http_method.contains(method));
|
||||
if http_method.is_empty() {
|
||||
self.map.remove(&address).map_err(EbpfError::MapOperationError)?;
|
||||
} else {
|
||||
let new_http_method = HttpMethod::convert_to_bitmap(http_method);
|
||||
self.map
|
||||
.insert(&address, new_http_method, 0)
|
||||
.map_err(EbpfError::MapOperationError)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EbpfError::IpDoesNotExist)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SshServiceWrapper<T> {
|
||||
map: AyaHashMap<MapData, T, PlaceHolder>,
|
||||
}
|
||||
|
||||
impl<T: NativeConvert + Pod> SshServiceWrapper<T> {
|
||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
||||
let map = AyaHashMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
||||
Ok(Self { map })
|
||||
}
|
||||
|
||||
fn get_ssh_service(&self) -> Vec<T::Native> {
|
||||
self.map
|
||||
.keys()
|
||||
.filter_map(Result::ok)
|
||||
.map(|key| key.into_native())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn add_ssh_service(&mut self, address: T::Native) -> Result<(), Error> {
|
||||
let address = T::from_native(address);
|
||||
self.map
|
||||
.insert(address, 0_u8, 0)
|
||||
.map_err(|_| EbpfError::RuleReachLimit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_ssh_service(&mut self, address: T::Native) -> Result<(), Error> {
|
||||
let address = T::from_native(address);
|
||||
self.map.remove(&address).map_err(|_| EbpfError::IpDoesNotExist)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct SshListWrapper<T> {
|
||||
map: AyaHashMap<MapData, T, PlaceHolder>,
|
||||
}
|
||||
|
||||
impl<T: NativeConvert + Pod> SshListWrapper<T> {
|
||||
fn new(ebpf: &mut Ebpf, map_name: &str) -> Result<Self, Error> {
|
||||
let map = ebpf.take_map(map_name).ok_or(EbpfError::MapNotFound)?;
|
||||
let map = AyaHashMap::try_from(map).map_err(EbpfError::MapOperationError)?;
|
||||
Ok(Self { map })
|
||||
}
|
||||
|
||||
fn get_list(&self) -> Vec<T::Native> {
|
||||
self.map
|
||||
.keys()
|
||||
.filter_map(Result::ok)
|
||||
.map(|key| key.into_native())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn add_list(&mut self, address: T::Native) -> Result<(), Error> {
|
||||
let address = T::from_native(address);
|
||||
self.map
|
||||
.insert(address, 0_u8, 0)
|
||||
.map_err(|_| EbpfError::RuleReachLimit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_list(&mut self, address: T::Native) -> Result<(), Error> {
|
||||
let address = T::from_native(address);
|
||||
self.map.remove(&address).map_err(|_| EbpfError::IpDoesNotExist)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -149,7 +149,6 @@ impl System {
|
||||
app_config: self.app_config.clone(),
|
||||
inference_config: self.inference_config.clone(),
|
||||
access_control: self.ebpf_services.access_control.clone(),
|
||||
service: self.ebpf_services.service.clone(),
|
||||
statistics: self.ebpf_services.statistics.clone(),
|
||||
health: self.app_services.health.clone(),
|
||||
detection_alert: self.app_services.detection_alert.clone(),
|
||||
@ -194,7 +193,6 @@ impl System {
|
||||
"access_control",
|
||||
ingress::ACCESS_CONTROL,
|
||||
)?;
|
||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "service", ingress::SERVICE)?;
|
||||
Self::load_program(&mut ingress_ebpf, &mut program_array, "statistics", ingress::STATISTICS)?;
|
||||
Self::load_program(
|
||||
&mut ingress_ebpf,
|
||||
|
||||
@ -33,6 +33,7 @@ impl SuricataEngine {
|
||||
eve_socket: &Path,
|
||||
fusion: Arc<FusionEngine>,
|
||||
) -> Result<Arc<Self>, SuricataError> {
|
||||
Self::kill_existing();
|
||||
Self::setup_veth()?;
|
||||
|
||||
let ifindex = Self::get_ifindex(MIRROR_IFACE)?;
|
||||
@ -305,6 +306,7 @@ app-layer:
|
||||
af-packet:
|
||||
- interface: {iface}
|
||||
threads: {threads}
|
||||
cluster-id: 99
|
||||
use-mmap: yes
|
||||
tpacket-v3: yes
|
||||
ring-size: {ring_size}
|
||||
@ -351,6 +353,11 @@ host-mode: sniffer-only
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
fn kill_existing() {
|
||||
let _ = Command::new("pkill").args(["-x", "suricata"]).output();
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
|
||||
fn setup_veth() -> Result<(), SuricataError> {
|
||||
let _ = Command::new("ip").args(["link", "del", MIRROR_IFACE]).output();
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
pub mod access_control;
|
||||
pub mod service;
|
||||
pub mod statistics;
|
||||
|
||||
use axum::Router;
|
||||
@ -9,6 +8,5 @@ use crate::core::app_state::AppState;
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.nest("/access_control", access_control::router())
|
||||
.nest("/service", service::router())
|
||||
.nest("/statistics", statistics::router())
|
||||
}
|
||||
|
||||
@ -1,243 +0,0 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
||||
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::{delete, get, post, put};
|
||||
use axum::{Json, Router};
|
||||
use common::model::http_method::HttpMethod;
|
||||
|
||||
use crate::core::app_state::AppState;
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route(
|
||||
"/ipv4/http_service",
|
||||
get(get_ipv4_http_service)
|
||||
.put(add_ipv4_http_service)
|
||||
.delete(remove_ipv4_http_service),
|
||||
)
|
||||
.route(
|
||||
"/ipv6/http_service",
|
||||
get(get_ipv6_http_service)
|
||||
.put(add_ipv6_http_service)
|
||||
.delete(remove_ipv6_http_service),
|
||||
)
|
||||
.route("/ssh_white_list", get(is_ssh_white_list_enable))
|
||||
.route("/ssh_white_list/enable", post(enable_ssh_white_list))
|
||||
.route("/ssh_white_list/disable", post(disable_ssh_white_list))
|
||||
.route(
|
||||
"/ipv4/ssh_service",
|
||||
get(get_ipv4_ssh_service)
|
||||
.put(add_ipv4_ssh_service)
|
||||
.delete(remove_ipv4_ssh_service),
|
||||
)
|
||||
.route(
|
||||
"/ipv6/ssh_service",
|
||||
get(get_ipv6_ssh_service)
|
||||
.put(add_ipv6_ssh_service)
|
||||
.delete(remove_ipv6_ssh_service),
|
||||
)
|
||||
.route(
|
||||
"/ipv4/ssh_white_list",
|
||||
get(get_ipv4_ssh_white_list)
|
||||
.put(add_ipv4_ssh_white_list)
|
||||
.delete(remove_ipv4_ssh_white_list),
|
||||
)
|
||||
.route(
|
||||
"/ipv6/ssh_white_list",
|
||||
get(get_ipv6_ssh_white_list)
|
||||
.put(add_ipv6_ssh_white_list)
|
||||
.delete(remove_ipv6_ssh_white_list),
|
||||
)
|
||||
.route(
|
||||
"/ipv4/ssh_black_list",
|
||||
get(get_ipv4_ssh_black_list)
|
||||
.put(add_ipv4_ssh_black_list)
|
||||
.delete(remove_ipv4_ssh_black_list),
|
||||
)
|
||||
.route(
|
||||
"/ipv6/ssh_black_list",
|
||||
get(get_ipv6_ssh_black_list)
|
||||
.put(add_ipv6_ssh_black_list)
|
||||
.delete(remove_ipv6_ssh_black_list),
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_ipv4_http_service(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv4_http_service().await)
|
||||
}
|
||||
|
||||
async fn get_ipv6_http_service(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv6_http_service().await)
|
||||
}
|
||||
|
||||
async fn add_ipv4_http_service(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<(SocketAddrV4, Vec<HttpMethod>)>,
|
||||
) -> impl IntoResponse {
|
||||
let (addr, methods) = payload;
|
||||
match state.service.add_ipv4_http_service(addr, methods).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_ipv6_http_service(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<(SocketAddrV6, Vec<HttpMethod>)>,
|
||||
) -> impl IntoResponse {
|
||||
let (addr, methods) = payload;
|
||||
match state.service.add_ipv6_http_service(addr, methods).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv4_http_service(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<(SocketAddrV4, Vec<HttpMethod>)>,
|
||||
) -> impl IntoResponse {
|
||||
let (addr, methods) = payload;
|
||||
match state.service.remove_ipv4_http_service(addr, methods).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv6_http_service(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<(SocketAddrV6, Vec<HttpMethod>)>,
|
||||
) -> impl IntoResponse {
|
||||
let (addr, methods) = payload;
|
||||
match state.service.remove_ipv6_http_service(addr, methods).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_ssh_white_list_enable(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.is_ssh_white_list_enable().await)
|
||||
}
|
||||
|
||||
async fn enable_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
||||
match state.service.enable_ssh_white_list().await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn disable_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
||||
match state.service.disable_ssh_white_list().await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_ipv4_ssh_service(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv4_ssh_service().await)
|
||||
}
|
||||
|
||||
async fn get_ipv6_ssh_service(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv6_ssh_service().await)
|
||||
}
|
||||
|
||||
async fn add_ipv4_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV4>) -> impl IntoResponse {
|
||||
match state.service.add_ipv4_ssh_service(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_ipv6_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV6>) -> impl IntoResponse {
|
||||
match state.service.add_ipv6_ssh_service(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv4_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV4>) -> impl IntoResponse {
|
||||
match state.service.remove_ipv4_ssh_service(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv6_ssh_service(State(state): State<AppState>, Json(addr): Json<SocketAddrV6>) -> impl IntoResponse {
|
||||
match state.service.remove_ipv6_ssh_service(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_ipv4_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv4_ssh_white_list().await)
|
||||
}
|
||||
|
||||
async fn get_ipv6_ssh_white_list(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv6_ssh_white_list().await)
|
||||
}
|
||||
|
||||
async fn add_ipv4_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
||||
match state.service.add_ipv4_ssh_white_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_ipv6_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
||||
match state.service.add_ipv6_ssh_white_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv4_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
||||
match state.service.remove_ipv4_ssh_white_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv6_ssh_white_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
||||
match state.service.remove_ipv6_ssh_white_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_ipv4_ssh_black_list(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv4_ssh_black_list().await)
|
||||
}
|
||||
|
||||
async fn get_ipv6_ssh_black_list(State(state): State<AppState>) -> impl IntoResponse {
|
||||
Json(state.service.get_ipv6_ssh_black_list().await)
|
||||
}
|
||||
|
||||
async fn add_ipv4_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
||||
match state.service.add_ipv4_ssh_black_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_ipv6_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
||||
match state.service.add_ipv6_ssh_black_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv4_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv4Addr>) -> impl IntoResponse {
|
||||
match state.service.remove_ipv4_ssh_black_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_ipv6_ssh_black_list(State(state): State<AppState>, Json(addr): Json<Ipv6Addr>) -> impl IntoResponse {
|
||||
match state.service.remove_ipv6_ssh_black_list(addr).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user