723 lines
81 KiB
Markdown
723 lines
81 KiB
Markdown
# Runtime Agents Architecture
|
|
|
|
## Overview
|
|
|
|
This document describes the runtime observation layer in StellaOps, including eBPF-based kernel instrumentation, container lifecycle monitoring, and signal aggregation. Runtime evidence provides the highest-confidence reachability determination for policy decisions.
|
|
|
|
---
|
|
|
|
## Runtime Observation Stack
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ RUNTIME OBSERVATION STACK │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ APPLICATION LAYER │
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ Container 1 │ │ Container 2 │ │ Container 3 │ │ Container N │ │
|
|
│ │ (app:v1) │ │ (api:v2) │ │ (worker) │ │ (...) │ │
|
|
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
|
│ │ │ │ │ │
|
|
│ └────────────────┴────────────────┴────────────────┘ │
|
|
│ │ │
|
|
│ [Function Calls] │
|
|
│ [System Calls] │
|
|
│ [Network I/O] │
|
|
│ │ │
|
|
└────────────────────────────────────┼────────────────────────────────────────────────┘
|
|
│
|
|
┌────────────────────────────────────┼────────────────────────────────────────────────┐
|
|
│ KERNEL SPACE │
|
|
│ │ │
|
|
│ ┌─────────────────┐ ┌────────▼────────┐ ┌─────────────────┐ │
|
|
│ │ kprobes │ │ eBPF VM │ │ tracepoints │ │
|
|
│ │ (syscalls) │◄───┤ (verified │───►│ (scheduler, │ │
|
|
│ │ │ │ bytecode) │ │ network) │ │
|
|
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
|
|
│ │ │ │ │
|
|
│ └──────────────────────┼──────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────▼───────────────┐ │
|
|
│ │ eBPF Ring Buffer │ │
|
|
│ │ (per-CPU, lock-free) │ │
|
|
│ └───────────────┬───────────────┘ │
|
|
│ │ │
|
|
└────────────────────────────────────┼────────────────────────────────────────────────┘
|
|
│
|
|
│ [perf events]
|
|
│
|
|
┌────────────────────────────────────┼────────────────────────────────────────────────┐
|
|
│ USER SPACE │
|
|
│ │ │
|
|
│ ┌─────────────────────▼─────────────────────┐ │
|
|
│ │ RuntimeSignalCollector │ │
|
|
│ │ (StellaOps.Signals) │ │
|
|
│ ├───────────────────────────────────────────┤ │
|
|
│ │ • Perf event polling │ │
|
|
│ │ • Symbol resolution (DWARF/kallsyms) │ │
|
|
│ │ • Stack unwinding │ │
|
|
│ │ • Container ID correlation (cgroup) │ │
|
|
│ │ • Event batching and compression │ │
|
|
│ └─────────────────────┬─────────────────────┘ │
|
|
│ │ │
|
|
│ ┌─────────────────────▼─────────────────────┐ │
|
|
│ │ ZastavaObserver │ │
|
|
│ │ (Container Lifecycle) │ │
|
|
│ ├───────────────────────────────────────────┤ │
|
|
│ │ • Container create/start/stop events │ │
|
|
│ │ • Image digest extraction │ │
|
|
│ │ • Runtime posture evaluation │ │
|
|
│ │ • Label/annotation parsing │ │
|
|
│ └─────────────────────┬─────────────────────┘ │
|
|
│ │ │
|
|
│ ┌─────────────────────▼─────────────────────┐ │
|
|
│ │ RuntimeSignalNormalizer │ │
|
|
│ │ (Event Processing) │ │
|
|
│ ├───────────────────────────────────────────┤ │
|
|
│ │ • Deduplicate events │ │
|
|
│ │ • Aggregate call counts │ │
|
|
│ │ • Map to package PURLs │ │
|
|
│ │ • Enrich with SBOM context │ │
|
|
│ └─────────────────────┬─────────────────────┘ │
|
|
│ │ │
|
|
└────────────────────────────────────┼────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ STORAGE LAYER │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Hot Symbol Index │ │
|
|
│ │ (PostgreSQL: signals.hot_symbols) │ │
|
|
│ │ │ │
|
|
│ │ image_digest │ function_name │ purl │ invocation_count │ last_observed │ │
|
|
│ │ ─────────────┼───────────────┼──────┼──────────────────┼───────────────── │ │
|
|
│ │ sha256:abc.. │ lodash.merge │ npm/..│ 1,247 │ 2024-12-29T10:00 │ │
|
|
│ │ sha256:abc.. │ lodash.get │ npm/..│ 8,923 │ 2024-12-29T10:01 │ │
|
|
│ │ sha256:def.. │ axios.request │ npm/..│ 456 │ 2024-12-29T09:55 │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## eBPF Agent Architecture
|
|
|
|
### Probe Types
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ eBPF PROBE TYPES │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ KPROBES / KRETPROBES │ │
|
|
│ │ │ │
|
|
│ │ Purpose: Trace kernel function entry/exit │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Syscall Tracing │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ kprobe/sys_execve → New process execution │ │ │
|
|
│ │ │ kprobe/sys_openat → File access │ │ │
|
|
│ │ │ kprobe/sys_connect → Network connection │ │ │
|
|
│ │ │ kprobe/sys_socket → Socket creation │ │ │
|
|
│ │ │ kprobe/sys_read → File/socket reads │ │ │
|
|
│ │ │ kprobe/sys_write → File/socket writes │ │ │
|
|
│ │ │ kprobe/sys_mmap → Memory mapping │ │ │
|
|
│ │ │ kprobe/sys_clone → Process/thread creation │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ TRACEPOINTS │ │
|
|
│ │ │ │
|
|
│ │ Purpose: Stable kernel instrumentation points │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Category │ Tracepoint │ Data │ │ │
|
|
│ │ │ ──────────────────┼───────────────────────────┼──────────────────│ │ │
|
|
│ │ │ Scheduler │ sched:sched_process_exec │ New execution │ │ │
|
|
│ │ │ Scheduler │ sched:sched_switch │ Context switch │ │ │
|
|
│ │ │ Network │ net:net_dev_xmit │ Packet TX │ │ │
|
|
│ │ │ Network │ sock:inet_sock_set_state │ TCP state │ │ │
|
|
│ │ │ Filesystem │ ext4:ext4_da_write_begin │ Write start │ │ │
|
|
│ │ │ Memory │ kmem:kmalloc │ Allocation │ │ │
|
|
│ │ │ Security │ security:security_* │ LSM hooks │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ UPROBES │ │
|
|
│ │ │ │
|
|
│ │ Purpose: Trace userspace function entry/exit │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Library/Function Tracing │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc │ │ │
|
|
│ │ │ uprobe:/usr/lib/libssl.so:SSL_read │ │ │
|
|
│ │ │ uprobe:/usr/lib/libcrypto.so:EVP_EncryptFinal │ │ │
|
|
│ │ │ uprobe:/path/to/app:vulnerable_function │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Dynamic attachment based on: │ │ │
|
|
│ │ │ • SBOM analysis (known vulnerable functions) │ │ │
|
|
│ │ │ • Static call graph (entry points to vulnerable code) │ │ │
|
|
│ │ │ • Symbol resolution from DWARF/debug info │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ USDT PROBES │ │
|
|
│ │ │ │
|
|
│ │ Purpose: User Statically Defined Tracing (application-level) │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Runtime-Specific Probes │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Node.js: │ │ │
|
|
│ │ │ usdt:/usr/bin/node:http__server__request │ │ │
|
|
│ │ │ usdt:/usr/bin/node:gc__start │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Python: │ │ │
|
|
│ │ │ usdt:/usr/bin/python3:function__entry │ │ │
|
|
│ │ │ usdt:/usr/bin/python3:function__return │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ JVM: │ │ │
|
|
│ │ │ usdt:libjvm.so:method__entry │ │ │
|
|
│ │ │ usdt:libjvm.so:gc__begin │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Event Data Structures
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ eBPF EVENT DATA STRUCTURES │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ FunctionCallEvent │ │
|
|
│ │ │ │
|
|
│ │ struct function_call_event { │ │
|
|
│ │ u64 timestamp_ns; // Kernel monotonic timestamp │ │
|
|
│ │ u32 pid; // Process ID │ │
|
|
│ │ u32 tid; // Thread ID │ │
|
|
│ │ u64 cgroup_id; // Container cgroup (for correlation) │ │
|
|
│ │ u32 uid; // User ID │ │
|
|
│ │ char comm[16]; // Process name │ │
|
|
│ │ char func_name[64]; // Function name (if resolved) │ │
|
|
│ │ u64 func_addr; // Function address (for offline resolution) │ │
|
|
│ │ u64 caller_addr; // Return address (call site) │ │
|
|
│ │ u64 stack_id; // Stack trace ID (BPF_MAP_TYPE_STACK_TRACE) │ │
|
|
│ │ u64 latency_ns; // Function execution time (kretprobe) │ │
|
|
│ │ u8 event_type; // ENTRY(0), EXIT(1), ERROR(2) │ │
|
|
│ │ }; │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ SyscallEvent │ │
|
|
│ │ │ │
|
|
│ │ struct syscall_event { │ │
|
|
│ │ u64 timestamp_ns; // Kernel monotonic timestamp │ │
|
|
│ │ u32 pid; // Process ID │ │
|
|
│ │ u32 tid; // Thread ID │ │
|
|
│ │ u64 cgroup_id; // Container cgroup │ │
|
|
│ │ u32 syscall_nr; // Syscall number │ │
|
|
│ │ u64 args[6]; // Syscall arguments │ │
|
|
│ │ i64 ret; // Return value │ │
|
|
│ │ u64 latency_ns; // Syscall duration │ │
|
|
│ │ u64 stack_id; // Stack trace ID │ │
|
|
│ │ }; │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ NetworkEvent │ │
|
|
│ │ │ │
|
|
│ │ struct network_event { │ │
|
|
│ │ u64 timestamp_ns; │ │
|
|
│ │ u32 pid; │ │
|
|
│ │ u64 cgroup_id; │ │
|
|
│ │ u8 protocol; // TCP(6), UDP(17) │ │
|
|
│ │ u8 direction; // INBOUND(0), OUTBOUND(1) │ │
|
|
│ │ u32 src_addr; // Source IPv4 (or first 4 bytes of IPv6) │ │
|
|
│ │ u32 dst_addr; // Destination IPv4 │ │
|
|
│ │ u16 src_port; │ │
|
|
│ │ u16 dst_port; │ │
|
|
│ │ u64 bytes; // Data transferred │ │
|
|
│ │ u8 state; // TCP state (ESTABLISHED, SYN_SENT, etc.) │ │
|
|
│ │ }; │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Zastava Container Observer
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ ZASTAVA CONTAINER OBSERVER │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ ARCHITECTURE │ │
|
|
│ │ │ │
|
|
│ │ ┌─────────────────────────────────┐ │ │
|
|
│ │ │ Container Runtime │ │ │
|
|
│ │ │ ┌─────────┐ ┌─────────────────┐│ │ │
|
|
│ │ │ │ Docker │ │ containerd ││ │ │
|
|
│ │ │ │ Engine │ │ ││ │ │
|
|
│ │ │ └────┬────┘ └────────┬────────┘│ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ └───────┼───────────────┼─────────┘ │ │
|
|
│ │ │ │ │ │
|
|
│ │ ┌──────────▼───────────────▼──────────┐ │ │
|
|
│ │ │ ZastavaObserver │ │ │
|
|
│ │ │ (Event Subscriber) │ │ │
|
|
│ │ ├─────────────────────────────────────┤ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Connects to: │ │ │
|
|
│ │ │ • /var/run/docker.sock │ │ │
|
|
│ │ │ • /run/containerd/containerd.sock │ │ │
|
|
│ │ │ • /run/crio/crio.sock │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Listens for: │ │ │
|
|
│ │ │ • container.create │ │ │
|
|
│ │ │ • container.start │ │ │
|
|
│ │ │ • container.stop │ │ │
|
|
│ │ │ • container.die │ │ │
|
|
│ │ │ • image.pull │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────────────┬────────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌─────────────────────────────────────┐ │ │
|
|
│ │ │ ContainerLifecycleEvent │ │ │
|
|
│ │ ├─────────────────────────────────────┤ │ │
|
|
│ │ │ event_type: CREATE|START|STOP|DIE │ │ │
|
|
│ │ │ container_id: string │ │ │
|
|
│ │ │ image_ref: string │ │ │
|
|
│ │ │ image_digest: sha256:... │ │ │
|
|
│ │ │ labels: map<string,string> │ │ │
|
|
│ │ │ annotations: map<string,string> │ │ │
|
|
│ │ │ env_vars: string[] (filtered) │ │ │
|
|
│ │ │ pid: u32 (on start) │ │ │
|
|
│ │ │ cgroup_path: string │ │ │
|
|
│ │ │ started_at: timestamp │ │ │
|
|
│ │ │ stopped_at: timestamp │ │ │
|
|
│ │ │ exit_code: i32 │ │ │
|
|
│ │ │ oom_killed: bool │ │ │
|
|
│ │ └─────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ RUNTIME POSTURE EVALUATION │ │
|
|
│ │ │ │
|
|
│ │ On container START, Zastava evaluates the runtime observation posture: │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Posture │ Observation Level │ Prerequisites │ │ │
|
|
│ │ ├───────────────────────┼──────────────────────┼─────────────────────┤ │ │
|
|
│ │ │ None │ No observation │ eBPF disabled │ │ │
|
|
│ │ │ Passive │ Lifecycle only │ Docker socket only │ │ │
|
|
│ │ │ ActiveTracing │ Syscalls + network │ eBPF + kprobes │ │ │
|
|
│ │ │ EbpfDeep │ + function calls │ + uprobes enabled │ │ │
|
|
│ │ │ FullInstrumentation │ + USDT + coverage │ + debug symbols │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ Posture is determined by: │ │
|
|
│ │ 1. Kernel capabilities (CAP_BPF, CAP_SYS_ADMIN) │ │
|
|
│ │ 2. Available probe types │ │
|
|
│ │ 3. Container annotations (stellaops.io/observe-level) │ │
|
|
│ │ 4. Image debug symbol availability │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Signal Processing Pipeline
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ SIGNAL PROCESSING PIPELINE │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Raw Events │
|
|
│ (millions/sec) │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ STAGE 1: FILTERING │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ In-Kernel Filtering (eBPF maps) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ • Filter by cgroup_id (only monitored containers) │ │ │
|
|
│ │ │ • Filter by syscall type (only security-relevant) │ │ │
|
|
│ │ │ • Rate limiting per-container (prevent flood) │ │ │
|
|
│ │ │ • Sampling for high-frequency events │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────┬──────────────┘ │
|
|
│ │ │
|
|
│ Filtered Events │ │
|
|
│ (thousands/sec) │ │
|
|
│ │◄────────────────────────────────────────────────────────┘ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ STAGE 2: ENRICHMENT │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Symbol Resolution │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ func_addr: 0x7f3a4b5c6d7e │ │ │
|
|
│ │ │ ↓ │ │ │
|
|
│ │ │ Resolve via: │ │ │
|
|
│ │ │ 1. /proc/<pid>/maps (memory mappings) │ │ │
|
|
│ │ │ 2. Symbol table cache (from SBOM binaries) │ │ │
|
|
│ │ │ 3. DWARF debug info (if available) │ │ │
|
|
│ │ │ 4. kallsyms (for kernel symbols) │ │ │
|
|
│ │ │ ↓ │ │ │
|
|
│ │ │ func_name: "lodash.template" │ │ │
|
|
│ │ │ module: "/app/node_modules/lodash/lodash.js" │ │ │
|
|
│ │ │ purl: "pkg:npm/lodash@4.17.20" │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Container Correlation │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ cgroup_id: 12345678 │ │ │
|
|
│ │ │ ↓ │ │ │
|
|
│ │ │ Lookup in Zastava registry: │ │ │
|
|
│ │ │ ↓ │ │ │
|
|
│ │ │ container_id: "abc123def456" │ │ │
|
|
│ │ │ image_digest: "sha256:789..." │ │ │
|
|
│ │ │ scan_id: "scan-xyz" (from label) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────┬──────────────────┘ │
|
|
│ │ │
|
|
│ Enriched Events │ │
|
|
│ (hundreds/sec) │ │
|
|
│ │◄─────────────────────────────────────────────────────┘ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ STAGE 3: AGGREGATION │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Time-Window Aggregation (1-minute windows) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Key: (image_digest, purl, function_name) │ │ │
|
|
│ │ │ Value: AggregatedMetrics { │ │ │
|
|
│ │ │ invocation_count: u64, │ │ │
|
|
│ │ │ unique_callers: HashSet<caller_addr>, │ │ │
|
|
│ │ │ total_latency_ns: u64, │ │ │
|
|
│ │ │ max_latency_ns: u64, │ │ │
|
|
│ │ │ sample_stack: Option<StackTrace>, │ │ │
|
|
│ │ │ first_seen: Timestamp, │ │ │
|
|
│ │ │ last_seen: Timestamp, │ │ │
|
|
│ │ │ } │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────┬──────────────────┘ │
|
|
│ │ │
|
|
│ Aggregated Records │ │
|
|
│ (per function per minute) │ │
|
|
│ │◄─────────────────────────────────────────────────────┘ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ STAGE 4: PERSISTENCE │ │
|
|
│ │ │ │
|
|
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ PostgreSQL: signals.hot_symbols │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ INSERT INTO signals.hot_symbols │ │ │
|
|
│ │ │ (image_digest, function_name, purl, invocation_count, ...) │ │ │
|
|
│ │ │ ON CONFLICT (image_digest, function_name) │ │ │
|
|
│ │ │ DO UPDATE SET │ │ │
|
|
│ │ │ invocation_count = hot_symbols.invocation_count + EXCLUDED.count,│ │ │
|
|
│ │ │ last_observed = EXCLUDED.last_observed, │ │ │
|
|
│ │ │ sample_stack = COALESCE(EXCLUDED.sample_stack, hot_symbols....) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Hot Symbol Index Schema
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ HOT SYMBOL INDEX SCHEMA │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ TABLE: signals.hot_symbols │ │
|
|
│ │ │ │
|
|
│ │ CREATE TABLE signals.hot_symbols ( │ │
|
|
│ │ -- Identity │ │
|
|
│ │ id BIGSERIAL PRIMARY KEY, │ │
|
|
│ │ image_digest TEXT NOT NULL, │ │
|
|
│ │ function_name TEXT NOT NULL, │ │
|
|
│ │ module TEXT, -- Library/file containing func │ │
|
|
│ │ purl TEXT, -- Package URL for module │ │
|
|
│ │ │ │
|
|
│ │ -- Observation metrics │ │
|
|
│ │ invocation_count BIGINT DEFAULT 0, │ │
|
|
│ │ unique_callers INTEGER DEFAULT 0, │ │
|
|
│ │ total_latency_ns BIGINT DEFAULT 0, │ │
|
|
│ │ max_latency_ns BIGINT DEFAULT 0, │ │
|
|
│ │ │ │
|
|
│ │ -- Timestamps │ │
|
|
│ │ first_observed TIMESTAMPTZ NOT NULL DEFAULT NOW(), │ │
|
|
│ │ last_observed TIMESTAMPTZ NOT NULL DEFAULT NOW(), │ │
|
|
│ │ │ │
|
|
│ │ -- Sample data │ │
|
|
│ │ sample_stack JSONB, -- Representative stack trace │ │
|
|
│ │ sample_args JSONB, -- Sample arguments (if captured) │ │
|
|
│ │ │ │
|
|
│ │ -- Constraints │ │
|
|
│ │ CONSTRAINT hot_symbols_unique UNIQUE (image_digest, function_name) │ │
|
|
│ │ ); │ │
|
|
│ │ │ │
|
|
│ │ -- Indexes │ │
|
|
│ │ CREATE INDEX idx_hot_symbols_purl ON signals.hot_symbols(purl); │ │
|
|
│ │ CREATE INDEX idx_hot_symbols_count ON signals.hot_symbols(invocation_count);│ │
|
|
│ │ CREATE INDEX idx_hot_symbols_last ON signals.hot_symbols(last_observed); │ │
|
|
│ │ CREATE INDEX idx_hot_symbols_image ON signals.hot_symbols(image_digest); │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ QUERY EXAMPLES │ │
|
|
│ │ │ │
|
|
│ │ -- Is vulnerable function observed at runtime? │ │
|
|
│ │ SELECT EXISTS ( │ │
|
|
│ │ SELECT 1 FROM signals.hot_symbols │ │
|
|
│ │ WHERE image_digest = $1 │ │
|
|
│ │ AND purl = $2 │ │
|
|
│ │ AND function_name = $3 │ │
|
|
│ │ AND invocation_count > 0 │ │
|
|
│ │ ); │ │
|
|
│ │ │ │
|
|
│ │ -- Get all observed functions for a vulnerable package │ │
|
|
│ │ SELECT function_name, invocation_count, last_observed, sample_stack │ │
|
|
│ │ FROM signals.hot_symbols │ │
|
|
│ │ WHERE image_digest = $1 AND purl = $2 │ │
|
|
│ │ ORDER BY invocation_count DESC; │ │
|
|
│ │ │ │
|
|
│ │ -- Hot path analysis (most called functions) │ │
|
|
│ │ SELECT purl, function_name, SUM(invocation_count) as total_calls │ │
|
|
│ │ FROM signals.hot_symbols │ │
|
|
│ │ WHERE image_digest = $1 │ │
|
|
│ │ GROUP BY purl, function_name │ │
|
|
│ │ ORDER BY total_calls DESC │ │
|
|
│ │ LIMIT 100; │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Runtime Evidence Integration
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ RUNTIME EVIDENCE → POLICY ENGINE │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ REACHABILITY STATE MAPPING │ │
|
|
│ │ │ │
|
|
│ │ Runtime Evidence → Reachability State │ │
|
|
│ │ ───────────────────────────────────────────────────────────────────────── │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Function observed at runtime → DynamicReachable │ │ │
|
|
│ │ │ (invocation_count > 0) (confidence: 0.90) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Function observed + exploit path → LiveExploitPath │ │ │
|
|
│ │ │ (confirmed vulnerable flow) (confidence: 1.00) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ Package imported but function → PotentiallyReachable │ │ │
|
|
│ │ │ not observed (long window) (confidence: 0.50) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ No runtime observation → (defer to static analysis) │ │ │
|
|
│ │ │ (posture = None) │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ CONFIDENCE FACTOR CALCULATION │ │
|
|
│ │ │ │
|
|
│ │ The Runtime factor (RTS) in confidence scoring: │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Scenario │ RTS Score │ Weight: 0.25 │ │ │
|
|
│ │ │ ──────────────────────────────────┼───────────┼───────────────── │ │ │
|
|
│ │ │ High frequency observation │ 1.00 │ Strong evidence │ │ │
|
|
│ │ │ (invocation_count > 1000/day) │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ Moderate observation │ 0.80 │ Confirmed used │ │ │
|
|
│ │ │ (invocation_count > 0) │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ No observation (neutral) │ 0.50 │ Inconclusive │ │ │
|
|
│ │ │ (posture active, no calls) │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ No observation (long window) │ 0.20 │ Likely unused │ │ │
|
|
│ │ │ (>30 days, no calls) │ │ │ │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ │ No runtime posture │ 0.50 │ Cannot determine │ │ │
|
|
│ │ │ (eBPF not available) │ │ │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ EVIDENCE CHAIN │ │
|
|
│ │ │ │
|
|
│ │ When runtime observation is available: │ │
|
|
│ │ │ │
|
|
│ │ Evidence { │ │
|
|
│ │ source: "signals/hot_symbols", │ │
|
|
│ │ evidence_type: "runtime_observation", │ │
|
|
│ │ data: { │ │
|
|
│ │ image_digest: "sha256:abc...", │ │
|
|
│ │ purl: "pkg:npm/lodash@4.17.20", │ │
|
|
│ │ function: "lodash.template", │ │
|
|
│ │ invocation_count: 1247, │ │
|
|
│ │ observation_window: "2024-12-01T00:00:00Z/2024-12-29T00:00:00Z", │ │
|
|
│ │ sample_stack: [ ... ] │ │
|
|
│ │ }, │ │
|
|
│ │ confidence: 0.90, │ │
|
|
│ │ timestamp: "2024-12-29T10:30:00Z" │ │
|
|
│ │ } │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Deployment Considerations
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ DEPLOYMENT CONSIDERATIONS │
|
|
├─────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ KERNEL REQUIREMENTS │ │
|
|
│ │ │ │
|
|
│ │ Minimum: Linux 5.8+ (BPF_LINK, ring buffer support) │ │
|
|
│ │ Recommended: Linux 5.15+ (BTF, CO-RE) │ │
|
|
│ │ │ │
|
|
│ │ Required kernel config: │ │
|
|
│ │ • CONFIG_BPF=y │ │
|
|
│ │ • CONFIG_BPF_SYSCALL=y │ │
|
|
│ │ • CONFIG_BPF_JIT=y │ │
|
|
│ │ • CONFIG_HAVE_EBPF_JIT=y │ │
|
|
│ │ • CONFIG_DEBUG_INFO_BTF=y (for CO-RE) │ │
|
|
│ │ • CONFIG_UPROBE_EVENTS=y (for uprobes) │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ CONTAINER PRIVILEGES │ │
|
|
│ │ │ │
|
|
│ │ Agent container requires: │ │
|
|
│ │ │ │
|
|
│ │ securityContext: │ │
|
|
│ │ privileged: true # For eBPF loading │ │
|
|
│ │ # OR use capabilities: │ │
|
|
│ │ capabilities: │ │
|
|
│ │ add: │ │
|
|
│ │ - SYS_ADMIN # BPF program loading │ │
|
|
│ │ - SYS_PTRACE # Stack unwinding │ │
|
|
│ │ - SYS_RESOURCE # RLIMIT_MEMLOCK │ │
|
|
│ │ - NET_ADMIN # Network probes │ │
|
|
│ │ - BPF # BPF operations (5.8+) │ │
|
|
│ │ - PERFMON # Performance monitoring (5.8+) │ │
|
|
│ │ │ │
|
|
│ │ Volume mounts: │ │
|
|
│ │ - /sys/kernel/debug (debugfs, for kprobes) │ │
|
|
│ │ - /var/run/docker.sock (container events) │ │
|
|
│ │ - /proc (process info) │ │
|
|
│ │ - /sys/fs/bpf (BPF filesystem, optional) │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ PERFORMANCE IMPACT │ │
|
|
│ │ │ │
|
|
│ │ Overhead by probe type: │ │
|
|
│ │ │ │
|
|
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
|
|
│ │ │ Probe Type │ Latency Added │ CPU Overhead │ Memory │ │ │
|
|
│ │ │ ────────────────┼──────────────────┼──────────────┼──────────────│ │ │
|
|
│ │ │ kprobe │ ~100-500ns │ <0.5% │ ~1MB/probe │ │ │
|
|
│ │ │ tracepoint │ ~50-100ns │ <0.2% │ ~500KB/tp │ │ │
|
|
│ │ │ uprobe │ ~1-5us │ 1-3%* │ ~1MB/probe │ │ │
|
|
│ │ │ USDT │ ~500ns-2us │ <1% │ ~1MB/probe │ │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ * uprobe overhead depends on call frequency │ │ │
|
|
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
|
|
│ │ │ │
|
|
│ │ Mitigation strategies: │ │
|
|
│ │ • Selective probe attachment (only observed containers) │ │
|
|
│ │ • In-kernel filtering (reduce userspace events) │ │
|
|
│ │ • Sampling for high-frequency events │ │
|
|
│ │ • Per-container rate limiting │ │
|
|
│ │ │ │
|
|
│ └──────────────────────────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [Policy Engine Data Pipeline](policy-engine-data-pipeline.md) - How runtime feeds policy
|
|
- [Reachability Drift Alert Flow](../../flows/19-reachability-drift-alert-flow.md) - Runtime-triggered alerts
|
|
- [Signals Module Architecture](../../modules/signals/architecture.md) - Signals module dossier
|
|
- [Zastava Architecture](../../modules/zastava/architecture.md) - Container observer dossier
|