Skip to content

Latest commit

 

History

History
192 lines (162 loc) · 19.6 KB

File metadata and controls

192 lines (162 loc) · 19.6 KB

Changelog

All notable changes to AegisBPF will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Added — Operator Tooling

  • Pre-install hook capability probe (aegisbpf probe, src/hook_capabilities.{hpp,cpp}, docs/HOOK_CAPABILITY_PROBE.md) — closes Honest Limitation #3. Operators can now run aegisbpf probe before installing or rolling out AegisBPF on a fleet to find out, for each of the 14 LSM hooks AegisBPF wants to attach, whether the target kernel will let it. The probe loads vmlinux BTF (/sys/kernel/btf/vmlinux) and asks libbpf whether each bpf_lsm_<hook> trampoline is present as a BTF_KIND_FUNC — the exact symbol BPF-LSM attach needs. Output JSON gains a hook_probe.hooks.<name> block per hook with kernel_supported, required, btf_symbol, and description fields, plus a hook_probe.btf_available summary so callers can distinguish "BTF was unavailable" from "BTF was there but symbol was missing". Catalog covers lsm_file_open, lsm_inode_permission (required), and lsm_bprm_check_security, lsm_bprm_ima_check, lsm_file_mmap, lsm_socket_{connect,bind,listen,accept,sendmsg,recvmsg}, lsm_ptrace_access_check, lsm_locked_down, lsm_inode_copy_up (optional). Names mirror the keys in the daemon's runtime /var/lib/aegisbpf/capabilities.json so consumers can join "predicted attachable" against "actually attached". Probe needs no privileges beyond reading /sys/kernel/btf/vmlinux and loads no BPF programs. New 5-test GTest suite (tests/test_hook_capabilities.cpp) pins the catalog shape (size + name set), enforces the bpf_lsm_* BTF-symbol prefix invariant, asserts only lsm_file_open/lsm_inode_permission are required, exercises the no-BTF path on hosts without /sys/kernel/btf/vmlinux (GTEST_SKIP() otherwise), and verifies the two required hooks resolve in vmlinux BTF on hosts that have it. Updates README Limitation #3 from "Runtime probing today; a machine-readable capability report is on the roadmap" to point at aegisbpf probe and the new doc.

Added — Supply Chain

  • Bit-for-bit reproducible builds (cmake/Reproducibility.cmake, AEGIS_REPRODUCIBLE_BUILD=ON by default) — aegisbpf is now byte-identical across builds from differing absolute source paths, hostnames, users, and wall-clock times, given the same compiler version and SOURCE_DATE_EPOCH. Implemented via -ffile-prefix-map=<src>=. -ffile-prefix-map=<build>=. -fdebug-prefix-map=<src>=. -fdebug-prefix-map=<build>=. (strips absolute paths from __FILE__, DWARF, and assertion macros), -Wl,--build-id=sha1 (content-addressed build-id replacing the default uuid/random), and ar -D / ranlib -D (zeroed mtime/uid/gid/mode on .a archives, so libaegisbpf_lib.a is reproducible too). SOURCE_DATE_EPOCH is honoured and propagated. The flags module is included from the top-level CMakeLists.txt after all sanitizer/coverage/hardening flags so prefix-map applies to every TU. scripts/check_reproducible_build.sh was rewritten to do a real test: it stages two source-tree copies at distinctly different absolute paths (/tmp/aegis-repro-XXX/aaaaaa/src vs …/bbbbbbbbbbbbbb/src), builds each with SKIP_BPF_BUILD=ON BUILD_TESTING=OFF, and compares the full aegisbpf ELF with sha256sum — no objcopy --strip-debug, no section extraction. On failure it runs diffoscope and retains the scratch trees (KEEP_TMP=1) for inspection. The previous workaround that compared only .text/.rodata/.data.rel.ro payloads is gone. CI runs the same script via .github/workflows/reproducibility.yml. Documented end-to-end in docs/REPRODUCIBLE_BUILDS.md (knob table, what's covered, what isn't, release-binary verification recipe). Adds a "Supply chain: Bit-for-bit reproducible builds" row to the README Standards Alignment matrix.

Added — Portability

  • BTFhub fallback resolver (src/btf_loader.{hpp,cpp}, AEGIS_BTF_PATH env var) — explicit multi-tier lookup for the BTF blob handed to libbpf at BPF object load time, so kernels without /sys/kernel/btf/vmlinux (RHEL 7, very old embedded, stripped-down kernels) can still run aegisbpfd. Resolution order: AEGIS_BTF_PATH override → /sys/kernel/btf/vmlinux (kernel built-in) → /lib/modules/<release>/btf/vmlinux (Debian/Ubuntu linux-image-extra location) → /var/lib/aegisbpf/btfs/<release>.btf (runtime cache) → /usr/lib/aegisbpf/btfs/<release>.btf (package-shipped) → /etc/aegisbpf/btfs/<release>.btf (operator-managed). An override that points at an unreadable file fails fast with BpfLoadFailed rather than silently falling back to the kernel BTF — a typo is much more likely than a deliberate mid-run swap, and a mismatched BTF would cause subtle CO-RE field-offset drift. The "no BTF found" path logs every searched location so operators can see exactly where to drop the blob. Pulled the inline lookup out of bpf_ops.cpp into a pure function resolve_btf_path(kernel_release, override) -> BtfResolution{path, source, searched} so it's testable without spinning up libbpf. New 7-test GTest suite (tests/test_btf_loader.cpp) covers env-var pickup, readable/unreadable override semantics, kernel-built-in preference, empty-kernel-release safety (no /lib/modules//btf traversal), and searched list population. Documented end-to-end in docs/BTF_FALLBACK.md (resolution table, override semantics, scripts/btfgen.sh + BTFhub-archive workflow, log examples). Flips the "Portability: BTFhub fallback for kernels without /sys/kernel/btf/vmlinux" row in the Standards Alignment matrix from Roadmap to shipped, and rewrites the matching Honest Limitation #6 from "unsupported" to "requires per-kernel blobs (here's how)".

Added — Event Output

  • OCSF 1.1.0 event format (--event-format=ocsf, src/ocsf_formatter.{hpp,cpp}) — opt-in OCSF JSON output for two highest-volume event types: BlockEvent reshaped to OCSF File Activity (class_uid 1001, activity_id 14 Open) and NetBlockEvent reshaped to OCSF Network Activity (class_uid 4001, activity_id 1 Open for connect/bind/listen/accept, activity_id 6 Traffic for sendmsg/recvmsg). Audit-mode events emit action_id=1 (Allowed) with no disposition_id; enforce-mode events emit action_id=2 (Denied) + disposition_id=2 (Blocked). Severity scales (Low for audit, High for enforce). AegisBPF-specific forensic fields (inode/dev/cgroup id/exec id) are preserved under the OCSF unmapped extension so SIEM parsers see standard fields without losing evidence. Hostname cached at startup via gethostname(). New EventFormat enum + set_event_format() / current_event_format() helpers; CLI flag accepts aegis (default), ocsf, OCSF, ocsf-1.1, ocsf-1.1.0. Format dispatch is global and orthogonal to the sink (--log=stdout|journald|both); journald path stores the OCSF payload in MESSAGE= while preserving the existing AEGIS_* field set on the journal entry. New 9-test GTest suite (tests/test_ocsf_formatter.cpp) covers required fields per OCSF class, audit-vs-enforce semantics, file path resolution (raw vs resolved), root-file path handling, all six network direction codes, and CLI keyword acceptance. Out of scope (still emitted in AegisBPF-native shape today): ExecEvent, ExecArgvEvent, ForensicEvent, KernelBlockEvent, OverlayCopyUpEvent, state_change, control_change. Closes docs/POSITIONING.md §3.3 OCSF row from "Roadmap" to "shipped for File + Network Activity"; flips the matching README Standards Alignment row.

Added — Daemon Hardening

  • Landlock LSM filesystem self-sandbox (--landlock, src/landlock.{hpp,cpp}) — opt-in post-init confinement of the daemon's own filesystem access to a fixed allowlist (RO /etc/aegisbpf, /usr/lib/aegisbpf, /proc, /sys/kernel/btf, $AEGIS_KEYS_DIR, dirname($AEGIS_BPF_OBJ); RW /var/lib/aegisbpf, /sys/fs/bpf). Direct landlock_create_ruleset / landlock_add_rule / landlock_restrict_self syscalls (no glibc wrapper dependency); raw __NR_* fallbacks for older libc. ABI version probed via LANDLOCK_CREATE_RULESET_VERSION — ABI 2 picks up LANDLOCK_ACCESS_FS_REFER, ABI 3 adds LANDLOCK_ACCESS_FS_TRUNCATE. Sets NO_NEW_PRIVS unconditionally before landlock_restrict_self (idempotent with the seccomp path). Missing allowlist entries are logged and skipped, not fatal. Kernels without Landlock log a WARN and continue — --landlock never fails startup on unsupported hosts. New 6-test GTest suite (tests/test_landlock_sandbox.cpp) covers ABI probe, default config shape, AEGIS_KEYS_DIR pickup, and a fork+restrict EACCES verification; self-skips via GTEST_SKIP() when the kernel lacks Landlock. Closes the "daemon hardening: Landlock self-sandbox" row in the Standards Alignment matrix and docs/HARDENING.md.

Added — Operator Policy Model (v0.5.0)

  • Per-rule action field on FileRule and NetworkRule (Allow or Block, default Block) so a single policy can express both deny and allow semantics. Allow rules lower into the daemon's existing [allow_*] sections; no daemon change required.
  • Allow > Block merge precedence in MergePolicies: any literal that appears in an [allow_*] section is removed from the corresponding [deny_*] section across the merged ConfigMap, mirroring Tetragon and KubeArmor behaviour. Sections that are emptied by the sweep are dropped from the final output.
  • spec.workloadSelector with full Kubernetes LabelSelector support (matchLabels + matchExpressions: In/NotIn/Exists/DoesNotExist), plus a separate namespaceSelector and matchNamespaceNames shortcut. Replaces the v0.4.x PolicySelector for new policies.
  • internal/selector package that evaluates workloadSelector against the live cluster (resolving namespaces, then matching pods inside each), with fallback to the legacy spec.selector only when workloadSelector is unset.
  • Admission webhook validation for the new fields: rejects Action=Allow on inode-based or protect file rules, detects in-spec Allow/Block collisions on the same target (path / IP / CIDR / port / ip:port / binary hash), validates LabelSelector parseability, validates matchNamespaceNames as DNS-1123 labels, and rejects cross-namespace selection from a namespaced AegisPolicy.
  • Deprecated status condition raised on policies that still use spec.selector, with reason LegacySelectorInUse. The policy continues to reconcile normally; the condition is informational.
  • Pinned controller-gen Makefile workflow (make controller-gen / manifests / deepcopy / generate / verify-generated, controller-gen v0.14.0). verify-generated fails CI when CRD YAML or zz_generated.deepcopy.go drift from the markers in api/.
  • CRD schema regenerated for aegispolicies.aegisbpf.io and aegisclusterpolicies.aegisbpf.io: adds workloadSelector (podSelector, namespaceSelector, matchNamespaceNames) and the per-rule action enum defaulted to Block.
  • New example operator/examples/allow-override.yaml demonstrating the cross-policy Allow override flow (a global block + a namespaced allow carve-out).

Backwards compatibility (v0.5.0)

  • v0.4.x policies that use spec.selector continue to admit, reconcile, and translate to byte-identical INI output. They simply gain a Deprecated=True condition.
  • The per-rule action field defaults to Block, so existing rule lists keep their original semantics with no edits.
  • The CRD remains v1alpha1. No v1alpha2 bump.
  • Daemon (policy_parse.cpp and the BPF maps) is unchanged in v0.5.0; the operator translates per-rule Action into the existing [allow_*] and [deny_*] INI sections.

Added — Quality & Observability

  • Per-hook latency tracking (hook_latency PERCPU_ARRAY map) — records total, count, min, and max nanoseconds per LSM/tracepoint hook invocation for overhead benchmarking
  • In-kernel event pre-filtering (event_approver_inode, event_approver_path maps) — Datadog-style approver/discarder pattern to suppress noisy events in-kernel, reducing ring buffer pressure
  • Priority ring buffer (priority_events, 4 MB) — dedicated ring buffer for security-critical forensic events, isolated from the main events ring buffer to prevent drops
  • Forensic event capture (ForensicEvent / forensic_block) — enriched block events with UID/GID, exec identity stage, verified_exec flag, and process context, emitted via the priority ring buffer
  • Startup self-tests (src/selftest.{hpp,cpp}) — Datadog-pattern startup validation: map accessibility, ring buffer FD, config readability, and process_tree write/read/delete cycle
  • Map capacity monitoring (src/map_monitor.{hpp,cpp}) — iterates BPF map entries to compute usage ratios and log warnings when thresholds are exceeded
  • Process cache /proc reconciliation (src/proc_scan.{hpp,cpp}) — scans /proc at startup to populate process_tree with pre-existing processes
  • BPF program signing preparation (src/bpf_signing.{hpp,cpp}) — SHA-256 hash verification of BPF object files with Ed25519 signature placeholder, break-glass override via AEGIS_ALLOW_UNSIGNED_BPF
  • Binary hash verification (src/binary_hash.{hpp,cpp}) — SHA-256 integrity verification for binary allow-lists with recursive directory scanning
  • Hot-loadable detection rules (src/rule_engine.{hpp,cpp}) — JSON-based detection rule engine with comm/path matching, severity levels, and thread-safe hot-reload
  • Plugin/extension system (src/plugin.{hpp,cpp}) — abstract plugin interface with virtual event handlers, lifecycle management, and break-on-consume dispatch; ships with built-in JsonLoggerPlugin

Added — CI Quality Gates

  • Real kernel BPF testing (.github/workflows/kernel-bpf-test.yml) — virtme-ng boots a real kernel in CI to test BPF object loading and map creation
  • BPF code coverage analysis (.github/workflows/bpf-coverage.yml) — llvm-objdump instruction counting per BPF program with JSON summary artifact

Changed

  • BPF hook functions instrumented with record_hook_latency() calls at every return point across aegis_exec.bpf.h, aegis_file.bpf.h, and aegis_net.bpf.h
  • handle_event() now processes EVENT_FORENSIC_BLOCK events from the priority ring buffer
  • BpfState extended with hook_latency, event_approver_inode, event_approver_path, and priority_events map pointers
  • Daemon startup now runs self-tests, reconciles /proc, and checks map capacity after ring buffer creation
  • Event union extended with ForensicEvent forensic member
  • Event schema (config/event-schema.json) extended with ForensicBlockEvent definition
  • BPF map schema (docs/BPF_MAP_SCHEMA.md) updated with 4 new maps and memory budget
  • Feature surface contract updated to validate new components
  • ForensicEvent static_assert corrected to 104 bytes (was 112)

Testing

  • Test suite: 210/210 passing
  • Feature surface contract: passing
  • Build: zero errors, zero warnings

[0.1.1] - 2026-02-07

Security

  • CRITICAL FIX: Eliminated TweetNaCl memory exhaustion vulnerability
    • Replaced unbounded heap allocation with fixed 4KB stack-based buffers
    • Added size validation to prevent memory exhaustion DoS attacks
    • Implemented secure buffer zeroing with volatile pointers
    • See docs/SECURITY_FIX_TWEETNACL_MEMORY.md for full details

Added

  • New safe crypto wrapper functions (src/tweetnacl_safe.hpp)
    • crypto_sign_detached_safe() - Stack-based signature generation
    • crypto_sign_verify_detached_safe() - Stack-based signature verification
    • Size limit: 4096 bytes (33× larger than actual usage)
  • Comprehensive test suite for crypto safety (tests/test_crypto_safe.cpp)
    • 13 new tests covering edge cases and security boundaries
    • Tests for empty messages, invalid signatures, size limits
  • Security fix documentation and verification script
    • docs/SECURITY_FIX_TWEETNACL_MEMORY.md - Detailed security analysis
    • SECURITY_FIX_SUMMARY.md - Implementation summary
    • scripts/verify_security_fix.sh - Automated verification

Changed

  • Updated crypto.cpp to use safe crypto wrappers exclusively
  • Enhanced error messages to indicate size limit constraints
  • Updated SECURITY.md with security fixes history section

Performance

  • Neutral to positive impact: stack allocation faster than heap
  • Predictable memory usage with no fragmentation
  • No measurable difference in test suite runtime

Testing

  • Test suite expanded: 153 → 157 tests (all passing)
  • Added edge case tests: empty messages, invalid signatures, boundary conditions
  • Full backward compatibility verified

Compliance

  • OWASP Top 10 2021 compliant
  • CERT Secure Coding Standards compliant
  • CWE/SANS Top 25 compliant
  • Memory safety guaranteed

Migration Notes

  • No breaking changes - fully backward compatible
  • New limitation: Messages > 4096 bytes rejected (no legitimate use cases affected)
  • All existing functionality preserved

0.1.0 - Previous Release

Added

  • Result error handling throughout the codebase
  • Constant-time hash comparison (constant_time_hex_compare()) to prevent timing side-channel attacks
  • Structured logging with text and JSON output formats
  • --log-level and --log-format CLI options
  • --seccomp flag for runtime syscall filtering
  • Thread-safe caching for cgroup and path resolution
  • RAII wrappers for popen (PipeGuard) and ring_buffer (RingBufferGuard)
  • Input validation for CLI path arguments
  • Google Test unit tests for core components
  • Google Benchmark performance tests
  • Sanitizer builds (ASAN, UBSAN, TSAN)
  • Code coverage reporting with gcovr and Codecov
  • Comprehensive CI pipeline with test, sanitizer, and coverage jobs
  • AppArmor profile for runtime confinement
  • SELinux policy module
  • Sigstore/Cosign code signing for releases
  • SBOM generation (SPDX and CycloneDX)
  • Prometheus alert rules
  • Grafana dashboard
  • JSON Schema for event validation
  • Event schema validation tests and sample payloads
  • SIEM integration documentation
  • Dockerfile for containerized deployment
  • Helm chart for Kubernetes deployment
  • Architecture documentation
  • Troubleshooting guide
  • Man page
  • Dev check and environment verification scripts
  • Enforce-mode smoke test script
  • Nightly fuzz workflow, perf regression workflow, and kernel matrix workflow

Changed

  • All functions now return Result instead of int/bool
  • Replaced std::cerr/std::cout with structured logging
  • Improved error messages with context
  • Event schema aligned with emitted JSON fields
  • README/architecture diagrams updated to file-open enforcement

Fixed

  • popen() file descriptor leak in kernel config check
  • Race conditions in cgroup path cache
  • Race conditions in CWD resolution cache
  • Thread-safety issue in journal error reporting

Security

  • Added seccomp-bpf syscall filter
  • Added AppArmor and SELinux policies
  • Added input validation for all user-provided paths
  • Added constant-time comparison for all hash verification (BPF integrity, policy SHA256, bundle verification)
  • Disabled AEGIS_SKIP_BPF_VERIFY bypass in Release builds (only available in Debug builds)
  • Added try-catch exception handling in signed bundle parser to prevent crashes on malformed input
  • Extended json_escape() to handle all control characters, preventing JSON injection in logs

0.1.0 - 2024-01-01

Added

  • Initial release
  • BPF LSM-based execution blocking
  • Tracepoint-based audit mode (fallback)
  • Policy file support with deny_path, deny_inode, allow_cgroup sections
  • SHA256 hash-based blocking
  • Prometheus metrics endpoint
  • Journald integration
  • CLI commands: run, block, allow, policy, stats, metrics, health