Kernel-level entropy-based ransomware detection for Linux servers.
Heimdall uses eBPF probes to intercept filesystem writes at the kernel level, computes Shannon entropy on write buffers in real time, and compares observations against per-process, per-path statistical baselines. When a process starts writing data that looks encrypted — high entropy, abnormal frequency — Heimdall flags it.
No signatures. No cloud. No auto-remediation opinions. Think htop, not antivirus.
Built for hospital server deployments where false positives are a patient safety issue and visibility matters more than magic.
Runs fully offline. No network dependencies at runtime. No telemetry, no cloud, no signature updates.
Quick Start | Installation Guide | Usage Guide | Configuration Reference
Kernel Space User Space
┌──────────────┐ ┌──────────────────────────────┐
│ eBPF Probes │ │ heimdall-daemon │
│ │ ring │ │
│ vfs_write ──┼─buffer──▶ entropy analysis │
│ vfs_writev │ │ EMA baseline comparison │
│ do_renameat2│ │ anomaly scoring │
│ │ │ policy evaluation │──▶ sinks
└──────────────┘ │ enforcement (if enabled) │ (file/syslog/http)
└──────┬───────────────┬───────┘
│ │
unix socket broadcast
│ │
┌──────┴──┐ ┌──────┴──┐
│ CLI │ │ TUI │
└─────────┘ └─────────┘
Detection model:
- eBPF kprobes on
vfs_write,vfs_writev, anddo_renameat2capture write events with the first 4KB of each write buffer - Shannon entropy is computed on the buffer sample (0.0 = identical bytes, 8.0 = perfectly random)
- Each
(process, directory)pair maintains an Exponential Moving Average (EMA) baseline of normal entropy and write frequency - Deviation from baseline is scored:
anomaly = 0.7 * entropy_score + 0.3 * frequency_score - Severity is classified: Low (<0.3), Medium (0.3-0.6), High (0.6-0.85), Critical (>=0.85)
- In enforcing mode, the policy engine maps
(process tag, severity)to an action: alert, freeze (SIGSTOP), or kill (SIGKILL)
Ransomware-encrypted data typically scores >7.0 entropy. Normal text is ~4.0. Normal binaries are ~5.5. The baseline learns what's normal for each process on each machine.
See QUICKSTART.md -- get Heimdall running on your server in 30 minutes. Written for hospital IT staff who know Linux basics but don't need to know Rust, eBPF, or entropy math.
| Crate | Description |
|---|---|
heimdall-daemon |
Core detection engine, eBPF bridge, IPC server, policy enforcement, pluggable sinks |
heimdall-ebpf |
eBPF probes via Aya -- pure Rust, no libbpf-sys |
heimdall-cli |
Command-line interface with plain English + verbose modes |
heimdall-tui |
Real-time ratatui dashboard |
heimdall-common |
Shared event types, IPC protocol, severity/tag/mode enums |
heimdall-test-harness |
Ransomware simulation harness for safe detection validation |
- Linux kernel 5.8+ (BTF support)
- Rust 1.75+ (stable)
- Rust nightly toolchain (for eBPF compilation only)
- Root privileges (for eBPF probe attachment)
# Build userspace components
cargo build --release --workspace --exclude heimdall-ebpf
# Build eBPF probes (requires nightly)
cd heimdall-ebpf
cargo +nightly build -Z build-std=core --target bpfel-unknown-none --release# Copy binaries
sudo cp target/release/heimdalld /usr/local/bin/
sudo cp target/release/heimdall /usr/local/bin/
sudo cp target/release/heimdall-tui /usr/local/bin/
# Create directories
sudo mkdir -p /etc/heimdall /var/lib/heimdall /var/log/heimdall /run/heimdall
# Copy config
sudo cp heimdall.toml /etc/heimdall/heimdall.toml
sudo chmod 640 /etc/heimdall/heimdall.toml
# Run the daemon
sudo heimdalld /etc/heimdall/heimdall.tomlFor production deployments, use the systemd service unit. See INSTALL.md for the full walkthrough including air-gapped builds.
Heimdall has three modes, designed to be used sequentially:
| Mode | What it does | When to use |
|---|---|---|
| Learning | Observes all writes, builds per-process baselines. No alerts, no enforcement. | First 48+ hours after deployment. After major software changes. |
| Monitoring | Compares live writes against baselines. Generates alerts. No enforcement. | After baselines stabilize. Safe for production. |
| Enforcing | Same as monitoring, plus executes policy actions (freeze/kill) on anomalous processes. | After thorough testing. Requires process tagging. |
Start in learning mode. Always. The first 48+ hours build trust -- operators can see what the system would have flagged before any enforcement is turned on.
# Check current mode
heimdall mode
# Switch modes
sudo heimdall mode monitoring
sudo heimdall mode enforcing# Daemon status
heimdall status
heimdall status --verbose
# Recent detection verdicts
heimdall logs
heimdall logs --limit 50 --verbose
# Live event stream
heimdall watch
heimdall watch --verbose
# Process/path tagging
heimdall tag set mysqld critical
heimdall tag set /opt/ehr/ critical
heimdall tag set /tmp/ watched
heimdall tag list
# Custom socket path
heimdall --socket /path/to/heimdall.sock statusDefault output is plain English:
14:23:01 [CRITICAL] java is writing scrambled data to /data/patient_records/db.mdf, this is highly unusual. Confidence: very high.
Action: freeze
Verbose mode shows raw numbers:
14:23:01 [CRITICAL] pid=4821 comm=java path=/data/patient_records/db.mdf
entropy=7.9312 deviation=14.2σ freq_ratio=23.1x anomaly=0.9847 size=65536B
entropy=7.93 baseline_mean=4.12 baseline_stddev=0.27 deviation=14.2σ write_size=65536 freq_ratio=23.1x samples=4821
Action: freeze
heimdall-tuiReal-time terminal dashboard showing:
- Connection status, operating mode, uptime, events/sec
- Live event feed with severity color coding
- Top processes leaderboard sorted by anomaly score
Keybindings:
| Key | Action |
|---|---|
q |
Quit |
v |
Toggle verbose/plain English |
m |
Cycle operating mode |
/ |
Filter events |
Up/Down |
Scroll event feed |
Tags tell Heimdall how to handle anomalies from specific processes or paths. This is where your team's knowledge of what runs on your servers becomes the policy.
| Tag | Meaning | Policy behavior |
|---|---|---|
| critical | Must never be interrupted. EHR, database engines, life-critical services. | Alert only, never freeze or kill. |
| trusted | Known good. Backup software, system services. | Alert only. |
| watched | Under active monitoring. New software, recently updated services. | Freeze on high/critical severity. |
| untrusted | Unknown or suspicious. Default for untagged processes. | Kill on critical, freeze on high. |
# Tag your critical hospital applications
heimdall tag set mysqld critical
heimdall tag set java critical # EHR application server
heimdall tag set /opt/ehr/ critical # All processes writing to EHR data
heimdall tag set /data/pacs/ critical # PACS imaging archive
# Tag known-good infrastructure
heimdall tag set sshd trusted
heimdall tag set systemd-journal trusted
# Watch new or recently updated software
heimdall tag set /opt/newapp/ watchedPath patterns ending with / match as directory prefixes. Process names are matched exactly.
Configuration is TOML at /etc/heimdall/heimdall.toml. See CONFIG_REFERENCE.md for every option with type, range, default, and hospital-specific recommendations.
Key settings:
[daemon]
mode = "learning" # Start here
db_path = "/var/lib/heimdall/baselines.db" # SQLite baseline storage
socket_path = "/run/heimdall/heimdall.sock"
log_level = "info"
[engine]
alpha = 0.05 # EMA smoothing factor (lower = slower adaptation)
entropy_threshold = 3.0 # Stddev threshold for anomaly flagging
frequency_threshold = 5.0 # Write frequency multiplier threshold
min_samples = 100 # Samples before baseline is trusted
sample_size = 4096 # Bytes captured per write event
[watch]
exclude_paths = ["/proc", "/sys", "/dev", "/tmp", "/run"]Configuration validation is strict -- the daemon will refuse to start with invalid values (alpha outside (0,1), zero thresholds, world-writable config file).
Events are dispatched to all configured sinks concurrently. Sink failures are logged but never block detection.
# Local JSON file (recommended minimum)
[[sinks]]
type = "file"
path = "/var/log/heimdall/events.json"
# Syslog (UDP, RFC 5424)
[[sinks]]
type = "syslog"
address = "udp://10.0.1.5:514"
# HTTP POST (Splunk, Elasticsearch, any JSON endpoint)
[[sinks]]
type = "http"
url = "https://splunk.hospital.local:8088/services/collector/event"
[sinks.headers]
Authorization = "Splunk YOUR-HEC-TOKEN-HERE"cargo test --workspace --exclude heimdall-ebpf
# 151 tests across 9 modules: detector, baseline, config, tags, policy,
# dispatcher, protocol, entropy, ebpf bridge5 Kani proofs verify critical mathematical invariants:
- Anomaly score is always in [0.0, 1.0]
- Severity classification has no gaps or overlaps
- Shannon entropy is always in [0.0, 8.0]
- Entropy deviation is always non-negative
- Entropy standard deviation is always non-negative
cargo kani --harness verify_anomaly_score_bounds
cargo kani --harness verify_classify_severity_complete
cargo kani --harness verify_shannon_entropy_bounds
cargo kani --harness verify_entropy_deviation_non_negative
cargo kani --harness verify_entropy_stddev_non_negative5 fuzz targets cover untrusted input surfaces:
cd heimdall-daemon
cargo +nightly fuzz run fuzz_sanitize_event
cargo +nightly fuzz run fuzz_config_toml
cargo +nightly fuzz run fuzz_daemon_request
cargo +nightly fuzz run fuzz_tag_store
cargo +nightly fuzz run fuzz_file_sink_pathThe test harness (heimdall-test-harness) safely simulates ransomware write patterns without any actual encryption or destruction. It generates synthetic write events that trigger Heimdall's detection pipeline, allowing you to validate that the system works before relying on it.
# Build the harness
cargo build --release -p heimdall-test-harness
# Run all simulation scenarios
heimdall-harness allHeimdall is designed to run on hospital servers protecting patient data. Security is not optional.
Hardening measures in the codebase:
- No
.unwrap()on any Mutex or RwLock -- all lock operations return recoverable errors instead of panicking the daemon - PID verification via
/proc/<pid>/commbefore sending SIGKILL/SIGSTOP -- prevents TOCTOU attacks from PID reuse - Path sanitization on all eBPF-provided data -- rejects null bytes, control characters, oversized paths
- Config file permission check -- refuses to start if
heimdall.tomlis world-writable - Config value validation -- rejects out-of-range alpha, zero thresholds, insufficient min_samples
- IPC connection limiting -- max 32 concurrent connections via semaphore
- IPC message size limiting -- max 64KB per message, prevents memory exhaustion
- Concurrent non-blocking sink dispatch -- a hung SIEM endpoint never stalls detection
- Graceful shutdown -- SIGINT + SIGTERM handled, 10-second drain timeout, clean SQLite close
- File sink path traversal rejection -- rejects paths containing
.. - EMA calculation in Rust, not SQL -- avoids mid-update reference bugs in SQLite expressions
- Baseline table bounded -- periodic cleanup of stale entries, 100K row cap with LRU eviction
- Compile-time size assertion on eBPF event struct -- layout mismatch between kernel and userspace is a build error, not a runtime bug
Operational security recommendations:
- Run the daemon as root only for eBPF probe attachment, then consider privilege separation for future versions
- Set config file permissions to
640owned by root - Use HTTPS for HTTP sinks -- API keys are stored in the config file
- Back up the baseline database regularly -- a corrupted baseline means restarting from learning mode
- Monitor
/var/log/heimdall/events.jsonfor disk usage -- consider log rotation
This is v0.1.0. The full detection pipeline is implemented and wired end-to-end.
What works now:
- Full eBPF-to-detector pipeline:
vfs_write,vfs_writev,do_renameat2kprobes attach at daemon startup, events flow through the ring buffer, are converted toWriteEventstructs, and feed into entropy analysis, EMA baseline comparison, anomaly scoring, severity classification, and policy enforcement - Graceful degradation: if eBPF probes fail to load (wrong kernel, missing BTF, permissions), the daemon logs the error and continues running -- it won't crash
- Policy engine with tag-based enforcement (SIGSTOP/SIGKILL with PID verification)
- IPC server with unix socket, connection limiting (32 max), idle timeouts, message size limits (64KB), subscription streaming
- CLI with status, logs, tag, mode, and watch commands
- TUI with live dashboard, event feed, process leaderboard
- Three pluggable sinks: file, syslog, HTTP -- all dispatched concurrently, failures never block detection
- Test harness for safe ransomware behavior simulation
- 151 tests (73 unique) across detector, baseline, config, tags, policy, dispatcher, protocol, entropy, and eBPF bridge modules
- 5 Kani formal verification proofs on mathematical invariants
- 5 libFuzzer fuzz targets on untrusted input surfaces
- systemd service unit with security hardening
What's next:
- Write frequency tracking (currently hardcoded to 1.0 -- frequency ratio is computed but not yet populated from timing data)
- E2E integration test suite
- Packaging (deb/rpm)
- Baseline import/export for fleet deployment
- eBPF-side path filtering (watch.include_paths / watch.exclude_paths are parsed but not yet enforced in the eBPF probes)
GPL-3.0