Skip to content

Commit ca0944f

Browse files
author
vsilent
committed
multiple updates, eBPF, container API quality is improved
1 parent 83434cf commit ca0944f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4564
-371
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ zstd = "0.13"
5555

5656
# Stream utilities
5757
futures-util = "0.3"
58+
lettre = { version = "0.11", default-features = false, features = ["tokio1", "tokio1-rustls-tls", "builder", "smtp-transport"] }
5859

5960
# eBPF (Linux only)
6061
[target.'cfg(target_os = "linux")'.dependencies]

ebpf/.cargo/config

Lines changed: 0 additions & 5 deletions
This file was deleted.

ebpf/rust-toolchain.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[toolchain]
2+
channel = "nightly"
3+
components = ["rust-src"]

ebpf/src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,10 @@
55
#![no_main]
66
#![no_std]
77

8-
#[no_mangle]
9-
pub fn main() {}
8+
mod maps;
9+
mod syscalls;
10+
11+
#[panic_handler]
12+
fn panic(_info: &core::panic::PanicInfo<'_>) -> ! {
13+
loop {}
14+
}

ebpf/src/maps.rs

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,123 @@
22
//!
33
//! Shared maps for eBPF programs
44
5-
// TODO: Implement eBPF maps in TASK-003
6-
// This will include:
7-
// - Event ring buffer for sending events to userspace
8-
// - Hash maps for tracking state
9-
// - Arrays for configuration
5+
use aya_ebpf::{macros::map, maps::RingBuf};
6+
7+
#[repr(C)]
8+
#[derive(Clone, Copy)]
9+
pub union EbpfEventData {
10+
pub execve: ExecveData,
11+
pub connect: ConnectData,
12+
pub openat: OpenatData,
13+
pub ptrace: PtraceData,
14+
pub raw: [u8; 264],
15+
}
16+
17+
impl EbpfEventData {
18+
pub const fn empty() -> Self {
19+
Self { raw: [0u8; 264] }
20+
}
21+
}
22+
23+
#[repr(C)]
24+
#[derive(Clone, Copy)]
25+
pub struct EbpfSyscallEvent {
26+
pub pid: u32,
27+
pub uid: u32,
28+
pub syscall_id: u32,
29+
pub _pad: u32,
30+
pub timestamp: u64,
31+
pub comm: [u8; 16],
32+
pub data: EbpfEventData,
33+
}
34+
35+
impl EbpfSyscallEvent {
36+
pub const fn empty() -> Self {
37+
Self {
38+
pid: 0,
39+
uid: 0,
40+
syscall_id: 0,
41+
_pad: 0,
42+
timestamp: 0,
43+
comm: [0u8; 16],
44+
data: EbpfEventData::empty(),
45+
}
46+
}
47+
}
48+
49+
#[repr(C)]
50+
#[derive(Clone, Copy)]
51+
pub struct ExecveData {
52+
pub filename_len: u32,
53+
pub filename: [u8; 128],
54+
pub argc: u32,
55+
}
56+
57+
impl ExecveData {
58+
pub const fn empty() -> Self {
59+
Self {
60+
filename_len: 0,
61+
filename: [0u8; 128],
62+
argc: 0,
63+
}
64+
}
65+
}
66+
67+
#[repr(C)]
68+
#[derive(Clone, Copy)]
69+
pub struct ConnectData {
70+
pub dst_ip: [u8; 16],
71+
pub dst_port: u16,
72+
pub family: u16,
73+
}
74+
75+
impl ConnectData {
76+
pub const fn empty() -> Self {
77+
Self {
78+
dst_ip: [0u8; 16],
79+
dst_port: 0,
80+
family: 0,
81+
}
82+
}
83+
}
84+
85+
#[repr(C)]
86+
#[derive(Clone, Copy)]
87+
pub struct OpenatData {
88+
pub path_len: u32,
89+
pub path: [u8; 256],
90+
pub flags: u32,
91+
}
92+
93+
impl OpenatData {
94+
pub const fn empty() -> Self {
95+
Self {
96+
path_len: 0,
97+
path: [0u8; 256],
98+
flags: 0,
99+
}
100+
}
101+
}
102+
103+
#[repr(C)]
104+
#[derive(Clone, Copy)]
105+
pub struct PtraceData {
106+
pub target_pid: u32,
107+
pub request: u32,
108+
pub addr: u64,
109+
pub data: u64,
110+
}
111+
112+
impl PtraceData {
113+
pub const fn empty() -> Self {
114+
Self {
115+
target_pid: 0,
116+
request: 0,
117+
addr: 0,
118+
data: 0,
119+
}
120+
}
121+
}
122+
123+
#[map(name = "EVENTS")]
124+
pub static EVENTS: RingBuf = RingBuf::with_byte_size(256 * 1024, 0);

ebpf/src/syscalls.rs

Lines changed: 157 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,160 @@
22
//!
33
//! Tracepoints for monitoring security-relevant syscalls
44
5-
// TODO: Implement eBPF syscall monitoring programs in TASK-003
6-
// This will include:
7-
// - execve/execveat monitoring
8-
// - connect/accept/bind monitoring
9-
// - open/openat monitoring
10-
// - ptrace monitoring
11-
// - mount/umount monitoring
5+
use aya_ebpf::{
6+
helpers::{
7+
bpf_get_current_comm, bpf_probe_read_user, bpf_probe_read_user_buf,
8+
bpf_probe_read_user_str_bytes,
9+
},
10+
macros::tracepoint,
11+
programs::TracePointContext,
12+
EbpfContext,
13+
};
14+
15+
use crate::maps::{
16+
ConnectData, EbpfEventData, EbpfSyscallEvent, ExecveData, OpenatData, PtraceData, EVENTS,
17+
};
18+
19+
const SYSCALL_ARG_START: usize = 16;
20+
const SYSCALL_ARG_SIZE: usize = 8;
21+
22+
const SYS_EXECVE: u32 = 59;
23+
const SYS_CONNECT: u32 = 42;
24+
const SYS_OPENAT: u32 = 257;
25+
const SYS_PTRACE: u32 = 101;
26+
27+
const AF_INET: u16 = 2;
28+
const AF_INET6: u16 = 10;
29+
const MAX_ARGC_SCAN: usize = 16;
30+
31+
#[tracepoint(name = "sys_enter_execve", category = "syscalls")]
32+
pub fn trace_execve(ctx: TracePointContext) -> i32 {
33+
let _ = unsafe { try_trace_execve(&ctx) };
34+
0
35+
}
36+
37+
#[tracepoint(name = "sys_enter_connect", category = "syscalls")]
38+
pub fn trace_connect(ctx: TracePointContext) -> i32 {
39+
let _ = unsafe { try_trace_connect(&ctx) };
40+
0
41+
}
42+
43+
#[tracepoint(name = "sys_enter_openat", category = "syscalls")]
44+
pub fn trace_openat(ctx: TracePointContext) -> i32 {
45+
let _ = unsafe { try_trace_openat(&ctx) };
46+
0
47+
}
48+
49+
#[tracepoint(name = "sys_enter_ptrace", category = "syscalls")]
50+
pub fn trace_ptrace(ctx: TracePointContext) -> i32 {
51+
let _ = unsafe { try_trace_ptrace(&ctx) };
52+
0
53+
}
54+
55+
unsafe fn try_trace_execve(ctx: &TracePointContext) -> Result<(), i64> {
56+
let filename_ptr = read_u64_arg(ctx, 0)? as *const u8;
57+
let argv_ptr = read_u64_arg(ctx, 1)? as *const u64;
58+
let mut event = base_event(ctx, SYS_EXECVE);
59+
let mut data = ExecveData::empty();
60+
61+
if !filename_ptr.is_null() {
62+
if let Ok(bytes) = bpf_probe_read_user_str_bytes(filename_ptr, &mut data.filename) {
63+
data.filename_len = bytes.len() as u32;
64+
}
65+
}
66+
67+
data.argc = count_argv(argv_ptr).unwrap_or(0);
68+
event.data = EbpfEventData { execve: data };
69+
submit_event(&event)
70+
}
71+
72+
unsafe fn try_trace_connect(ctx: &TracePointContext) -> Result<(), i64> {
73+
let sockaddr_ptr = read_u64_arg(ctx, 1)? as *const u8;
74+
if sockaddr_ptr.is_null() {
75+
return Ok(());
76+
}
77+
78+
let family = bpf_probe_read_user(sockaddr_ptr as *const u16)?;
79+
let mut event = base_event(ctx, SYS_CONNECT);
80+
let mut data = ConnectData::empty();
81+
data.family = family;
82+
83+
if family == AF_INET {
84+
data.dst_port = bpf_probe_read_user(sockaddr_ptr.add(2) as *const u16)?;
85+
let mut addr = [0u8; 4];
86+
bpf_probe_read_user_buf(sockaddr_ptr.add(4), &mut addr)?;
87+
data.dst_ip[..4].copy_from_slice(&addr);
88+
} else if family == AF_INET6 {
89+
data.dst_port = bpf_probe_read_user(sockaddr_ptr.add(2) as *const u16)?;
90+
bpf_probe_read_user_buf(sockaddr_ptr.add(8), &mut data.dst_ip)?;
91+
}
92+
93+
event.data = EbpfEventData { connect: data };
94+
submit_event(&event)
95+
}
96+
97+
unsafe fn try_trace_openat(ctx: &TracePointContext) -> Result<(), i64> {
98+
let pathname_ptr = read_u64_arg(ctx, 1)? as *const u8;
99+
let flags = read_u64_arg(ctx, 2)? as u32;
100+
let mut event = base_event(ctx, SYS_OPENAT);
101+
let mut data = OpenatData::empty();
102+
data.flags = flags;
103+
104+
if !pathname_ptr.is_null() {
105+
if let Ok(bytes) = bpf_probe_read_user_str_bytes(pathname_ptr, &mut data.path) {
106+
data.path_len = bytes.len() as u32;
107+
}
108+
}
109+
110+
event.data = EbpfEventData { openat: data };
111+
submit_event(&event)
112+
}
113+
114+
unsafe fn try_trace_ptrace(ctx: &TracePointContext) -> Result<(), i64> {
115+
let mut event = base_event(ctx, SYS_PTRACE);
116+
let data = PtraceData {
117+
request: read_u64_arg(ctx, 0)? as u32,
118+
target_pid: read_u64_arg(ctx, 1)? as u32,
119+
addr: read_u64_arg(ctx, 2)?,
120+
data: read_u64_arg(ctx, 3)?,
121+
};
122+
event.data = EbpfEventData { ptrace: data };
123+
submit_event(&event)
124+
}
125+
126+
fn base_event(ctx: &TracePointContext, syscall_id: u32) -> EbpfSyscallEvent {
127+
let mut event = EbpfSyscallEvent::empty();
128+
event.pid = ctx.tgid();
129+
event.uid = ctx.uid();
130+
event.syscall_id = syscall_id;
131+
event.timestamp = 0;
132+
if let Ok(comm) = bpf_get_current_comm() {
133+
event.comm = comm;
134+
}
135+
event
136+
}
137+
138+
fn submit_event(event: &EbpfSyscallEvent) -> Result<(), i64> {
139+
EVENTS.output(event, 0)
140+
}
141+
142+
fn read_u64_arg(ctx: &TracePointContext, index: usize) -> Result<u64, i64> {
143+
unsafe { ctx.read_at::<u64>(SYSCALL_ARG_START + index * SYSCALL_ARG_SIZE) }
144+
}
145+
146+
unsafe fn count_argv(argv_ptr: *const u64) -> Result<u32, i64> {
147+
if argv_ptr.is_null() {
148+
return Ok(0);
149+
}
150+
151+
let mut argc = 0u32;
152+
while argc < MAX_ARGC_SCAN as u32 {
153+
let arg_ptr = bpf_probe_read_user(argv_ptr.add(argc as usize))?;
154+
if arg_ptr == 0 {
155+
break;
156+
}
157+
argc += 1;
158+
}
159+
160+
Ok(argc)
161+
}

migrations/00000000000000_create_alerts/up.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ CREATE TABLE IF NOT EXISTS alerts (
1414
CREATE INDEX IF NOT EXISTS idx_alerts_status ON alerts(status);
1515
CREATE INDEX IF NOT EXISTS idx_alerts_severity ON alerts(severity);
1616
CREATE INDEX IF NOT EXISTS idx_alerts_timestamp ON alerts(timestamp);
17+
CREATE INDEX IF NOT EXISTS idx_alerts_container_id
18+
ON alerts(json_extract(metadata, '$.container_id'))
19+
WHERE json_valid(metadata);

0 commit comments

Comments
 (0)