Building a Small Rust eBPF EDR 7: Running, Packaging, Health, and Roadmap

마지막 글에서는 rand-guard를 어떻게 빌드하고 실행하고 패키징하고 검증하는지 정리한다. eBPF EDR은 코드만 작성해서 끝나는 프로젝트가 아니다. 실제로 build artifact가 만들어져야 하고, root 권한으로 load되어야 하며, output이 검증 가능해야 한다.

rand-guard는 이 반복 작업을 xtask로 묶어 둔다.

xtask workflow

저장소 root에서 다음 명령을 사용할 수 있다.

cargo run -p xtask -- ci-format
cargo run -p xtask -- check
cargo run -p xtask -- clippy
cargo run -p xtask -- test
cargo run -p xtask -- build

Cargo alias도 있다.

cargo xf
cargo xc
cargo xl
cargo xt
cargo xb

eBPF program build에는 nightly Rust와 rust-src, bpf-linker가 필요하다. userspace crate는 stable Rust로 빌드된다. 이 분리는 eBPF target의 특수성을 반영한다.

실행은 다음과 같이 할 수 있다.

cargo run -p xtask -- run

이 명령은 eBPF object를 load하므로 root 또는 적절한 capability가 필요하다. 일반적인 Linux 환경에서는 sudo가 필요할 수 있다.

package workflow

release artifact는 package command로 만들 수 있다.

cargo run -p xtask -- package

package에는 다음 항목이 포함된다.

  • userspace binary
  • eBPF object
  • default config
  • sample rules
  • systemd unit
  • installer script

quickstart 문서 기준으로 설치 경로는 다음과 같다.

/usr/local/bin/rand-guard
/usr/local/lib/rand-guard/edr-ebpf
/etc/rand-guard/config.toml
/etc/rand-guard/rules.d/sample-rules.toml
/etc/systemd/system/rand-guard.service

systemd unit은 EDR_CONFIGEDR_EBPF_OBJECT environment variable을 설정한다. 이 구조 덕분에 runtime binary와 eBPF object 위치를 명확히 분리할 수 있다.

quick verification

agent를 실행한 뒤 process event는 간단히 만들 수 있다.

/bin/true

file event는 demo service file을 만들고 지우는 방식으로 확인할 수 있다.

sudo sh -c 'printf "# rand-guard quickstart\n" > /etc/rand-guard-quickstart.service'
sudo rm -f /etc/rand-guard-quickstart.service

network telemetry는 기본 비활성화이므로 config에서 켠 뒤 확인해야 한다.

[events]
network = true

[network]
enabled = true
hooks = ["connect", "bind", "listen"]
collect_dns = false
collect_payload = false

이후 loopback listener demo를 실행할 수 있다.

nc -l 127.0.0.1 4444
printf 'demo\n' | nc 127.0.0.1 4444

health records

runtime은 주기적으로 health record를 출력한다. health record는 detection이 아니라 runtime 상태를 이해하기 위한 telemetry다.

대표 field는 다음과 같다.

  • raw events read
  • normalized events output
  • alerts output
  • userspace filtered count
  • userspace rate limited count
  • invalid schema count
  • unsupported kind count
  • output failure count
  • process table size
  • pending exec source size
  • eviction count
  • uptime
  • RSS

이 정보는 성능 문제나 event loss를 볼 때 중요하다. 예를 들어 raw event는 많이 들어오는데 normalized output이 적다면 filtering이나 rate limiting이 영향을 주고 있을 수 있다. process table eviction이 많다면 cache limit을 조정하거나 workload를 다시 봐야 한다.

throughput workflow

rand-guard에는 local throughput workflow도 있다.

cargo run -p xtask -- throughput

이 benchmark는 controlled file-write workload를 만들고, health record를 파싱해서 .local/throughput/ 아래에 summary를 남긴다.

중요한 점은 benchmark 숫자를 일반적인 성능 보장처럼 쓰면 안 된다는 것이다. kernel version, CPU, filesystem, background load, enabled hook, build mode에 따라 결과가 달라진다. 이 workflow는 같은 host에서 코드 변경 전후를 비교하기 위한 local comparison data다.

threat model

rand-guard는 root attacker를 막는 agent가 아니다. root 권한을 가진 공격자는 service를 중지하거나 config를 바꾸거나 binary를 교체하거나 output을 조작할 수 있다.

현재 threat model은 다음 범위에 더 가깝다.

  • local user-space command가 telemetry를 발생시킨다.
  • eBPF program은 verifier-friendly하게 작게 유지한다.
  • output record는 민감한 host telemetry로 취급한다.
  • detection은 selected behavior를 모델링하는 investigation signal이다.

이런 제한을 문서에 명시하는 것은 중요하다. 보안 프로젝트에서 하지 않는 것을 분명히 쓰는 것은 하는 것을 쓰는 것만큼 중요하다.

roadmap

현재 MVP는 다음을 구현했다.

  • process lifecycle collection
  • file collection and filtering
  • optional network syscall telemetry
  • shared event ABI
  • ring buffer delivery
  • userspace normalization and enrichment
  • built-in persistence and suspicious port detection
  • MVP [[rules]]
  • stable alert records
  • packaging and smoke workflows

near-term work는 rule evaluation test, alert output stability, demo scenario 개선, benchmark 문서화, config validation 개선, false positive 문서화에 가깝다.

longer-term research로는 reverse shell, web shell execution, credential access, systemd persistence, drop-and-execute 같은 scenario-based detection이 있다. 하지만 이것들은 현재 구현된 기능으로 과장해서 쓰면 안 된다. 연구 방향이지 완성된 coverage가 아니다.

시리즈 정리

이 시리즈는 작은 Rust eBPF EDR을 end-to-end로 구성하는 과정을 다뤘다.

  • 1편에서는 전체 architecture를 잡았다.
  • 2편에서는 eBPF와 userspace 사이의 ABI를 다뤘다.
  • 3편에서는 process lifecycle과 process table enrichment를 봤다.
  • 4편에서는 file telemetry와 persistence-sensitive detection을 봤다.
  • 5편에서는 opt-in network telemetry를 봤다.
  • 6편에서는 MVP rule engine과 alert record를 다뤘다.
  • 7편에서는 실행, 패키징, health, benchmark, roadmap을 정리했다.

rand-guard의 핵심은 기능을 많이 붙이는 것이 아니라, 작고 설명 가능한 EDR pipeline을 실제 코드로 완성하는 것이다. eBPF program은 커널에서 안전하게 수집하고, userspace는 그 event를 이해 가능한 telemetry와 alert로 바꾼다. 이 경계를 선명하게 유지하는 것이 프로젝트의 가장 중요한 설계 원칙이다.