From 6ff2087329ad0b9efc420f67024b2a241e4151b7 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 10 Jun 2026 04:03:06 -0700 Subject: [PATCH] Add platform docs; reposition as host security platform Add docs/ (SPECIFICATION, ROADMAP, OPERATIONS, COMMAND_REFERENCE, THREAT_MODEL) describing the current v0.7 agent and the phased path from local HIDS to a host security platform. Reframe README, package metadata, and CLI/module descriptions from "intrusion-detection daemon" to "Linux host security platform", and surface the v0.7 signed-package verify (pkgdb_pkgverify*) and anti-rootkit (rootcheck_*) knobs in the sample config. Also wrap the pacman.conf read in pkgdb.siglevel_alert in a context manager to avoid a leaked file handle. Co-Authored-By: Claude Opus 4.8 --- README.md | 59 +++++++--- config/enodia-sentinel.toml | 14 +++ docs/COMMAND_REFERENCE.md | 226 ++++++++++++++++++++++++++++++++++++ docs/OPERATIONS.md | 143 +++++++++++++++++++++++ docs/ROADMAP.md | 174 +++++++++++++++++++++++++++ docs/SPECIFICATION.md | 206 ++++++++++++++++++++++++++++++++ docs/THREAT_MODEL.md | 119 +++++++++++++++++++ enodia_sentinel/__init__.py | 8 +- enodia_sentinel/cli.py | 2 +- enodia_sentinel/pkgdb.py | 3 +- pyproject.toml | 2 +- 11 files changed, 930 insertions(+), 26 deletions(-) create mode 100644 docs/COMMAND_REFERENCE.md create mode 100644 docs/OPERATIONS.md create mode 100644 docs/ROADMAP.md create mode 100644 docs/SPECIFICATION.md create mode 100644 docs/THREAT_MODEL.md diff --git a/README.md b/README.md index 88fa5a2..33c2754 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,41 @@ # Enodia Sentinel -A host intrusion-detection daemon for Linux. It continuously runs a set of -detectors over live system state — processes, sockets, file descriptors, the -SUID inventory, and sensitive files — and writes a detailed forensic snapshot -(text **and** JSON) with incident-response guidance the moment a known attack -signature appears. +A Linux host security platform: intrusion detection, file/package integrity, +anti-rootkit cross-checks, tamper-evidence, forensic snapshots, push alerts, and +a read-only operator console. + +The current daemon continuously runs detectors over live system state — +processes, sockets, file descriptors, the SUID inventory, sensitive files, the +package database, and rootkit-hiding artifacts — then writes a detailed forensic +snapshot (text **and** JSON) with incident-response guidance the moment a known +attack signature appears. Think of it as the security counterpart to a performance watchdog: instead of "I/O pressure spiked, here's the kernel state," it's *"a shell just wired itself -to a socket — here's the process tree, the peer, and what to do about it."* +to a socket — here's the process tree, the peer, what changed on disk, whether +the package database was tampered with, and what to do next."* + +## Product direction + +Enodia is becoming more than an IDS. The local detector is the first layer of a +broader host security platform: + +| Function | Current capability | Direction | +|---|---|---| +| Detect | Poll detectors + eBPF exec rules | More event sources and correlation | +| Verify | FIM, package DB anchor, signed-package checks | External anchors and signed evidence | +| Investigate | Text/JSON snapshots, dashboard, triage | Incident timelines and evidence export | +| Respond | Human guidance today | Dry-run response plans and audited containment | +| Assure | Heartbeat, watchdog, self-integrity, rootcheck | Fleet health and attestation-ready anchors | + +Project docs: + +- [Specification](docs/SPECIFICATION.md) — product model, current scope, target + platform shape, data model, and acceptance criteria. +- [Roadmap](docs/ROADMAP.md) — phased work from local sensor to incident, + response, fleet, and assurance layers. +- [Operations guide](docs/OPERATIONS.md) — install checks, health checks, alert + workflow, baseline hygiene, and evidence export. > **Two implementations, on purpose.** The project began as a bash prototype > (`src/sentinel.sh`, kept as the regression **oracle**) and was re-architected @@ -131,9 +158,8 @@ sudo tail -f /var/log/enodia-sentinel/events.log sentinel-redteam # safe, self-cleaning attack simulations ``` -You'll watch the drills trip `reverse_shell`, `ld_preload`, `deleted_exe`, -`new_listener`, and `new_suid` in real time, each producing a `.log` + `.json` -snapshot with response guidance. +You'll watch the drills trip the core poll signatures in real time, each +producing a `.log` + `.json` snapshot with response guidance. Output lives in `/var/log/enodia-sentinel/`: - `events.log` — one line per alert @@ -170,7 +196,7 @@ drill only sets the env var on a throwaway process. ## Testing ```bash -make test # 25 unit tests, stdlib unittest, no deps +make test # stdlib unittest suite, no runtime deps ``` Detectors are pure functions over an injectable `SystemState`, so tests build @@ -442,16 +468,11 @@ conscious tradeoff documented in the file itself. ## Roadmap -1. **bpftrace tracepoints (optional)** — `capture_execve_bpftrace = true` adds a - live `execve` trace to each snapshot. -2. ✅ **Event-driven `execve` detection (done)** — a real eBPF probe via bcc, - feeding a Snort-style rule engine, so a short-lived process can't slip - between sweeps. -3. **More event sources** — `tcp_connect` (event-driven egress) and - `security_bprm_check` (LSM) probes, plus per-process lineage tracking. -4. **A libbpf + CO-RE agent (Go or Rust)** — the production EDR core: ring-buffer - event streaming, tamper resistance, no runtime compiler. +The short version: Enodia grows from local HIDS into a host security platform: +incident grouping, posture checks, response planning, richer eBPF telemetry, +fleet health, external anchors, and eventually attestation-ready assurance. +See [docs/ROADMAP.md](docs/ROADMAP.md) for the release tracks and phased plan. The polling daemon isn't throwaway — it's the **oracle**: every signature is a test case the event layer must reproduce, and `sentinel-redteam` is the shared regression suite for both. diff --git a/config/enodia-sentinel.toml b/config/enodia-sentinel.toml index de97bab..c2615ce 100644 --- a/config/enodia-sentinel.toml +++ b/config/enodia-sentinel.toml @@ -74,10 +74,24 @@ fim_pkg_verify_interval = 21600 # seconds between package verifications pkgdb_verify = true pkgdb_interval = 600 # seconds between DB integrity checks heartbeat_max_age = 120 # daemon heartbeat older than this = stale/silent +# Layer 2: compare on-disk files to hashes in cached signed packages. This +# survives a rewritten local package DB, but needs a populated package cache and +# costs untar+hash work, so it samples packages on a slow cadence. +pkgdb_pkgverify = false +pkgdb_pkgverify_interval = 21600 # seconds between signed-package passes +pkgdb_pkgverify_sample = 40 # packages verified per pass (rotates) # Sentinel's own binaries/config/units/hook are always FIM-watched (self- # integrity). For real teeth, make them immutable: chattr +i # and run an external `enodia-sentinel watchdog` against this host's dashboard. +# --- anti-rootkit cross-view -------------------------------------------- +# Ask the same question two ways and diff the answers: kill(0) vs /proc for +# hidden processes, /sys/module vs /proc/modules for hidden LKMs, /proc/net/tcp +# vs ss for hidden listeners, and interface flags for promiscuous sniffing. +rootcheck_enabled = true +rootcheck_interval = 300 # seconds between cross-view sweeps +rootcheck_pid_cap = 65536 # upper PID to brute-force via kill(0) + # --- eBPF event layer ---------------------------------------------------- # Event-driven execve monitor: catches short-lived processes the poll loop # misses, matched against the Snort-style rule engine. Requires python-bpfcc diff --git a/docs/COMMAND_REFERENCE.md b/docs/COMMAND_REFERENCE.md new file mode 100644 index 0000000..5be394e --- /dev/null +++ b/docs/COMMAND_REFERENCE.md @@ -0,0 +1,226 @@ +# Enodia Sentinel Command Reference + +This is the operator-facing command contract for the current local agent. Commands +are intentionally plain: they either inspect state, manage baselines, run the +daemon, or expose existing evidence. + +Global options: + +```bash +enodia-sentinel --version +enodia-sentinel -c /path/to/config.toml +``` + +If no command is provided, `run` is used. + +## Service Commands + +### `run` + +```bash +enodia-sentinel run +``` + +Starts the daemon loop. This is the command used by the systemd service. It +builds or loads baselines, starts optional event monitoring, writes heartbeats, +runs detector sweeps, captures snapshots, and sends notifications. + +Expected use: systemd, not an interactive shell. + +### `check` + +```bash +enodia-sentinel check +``` + +Runs the enabled detector set once and prints alerts. This forces the SUID scan +and arms baseline-gated detectors immediately, so it is useful for health checks +and debugging. + +Exit code: + +- `0`: no alerts. +- `0`: alerts may still print today; this command is currently human-oriented, + not a strict CI gate. + +### `baseline` + +```bash +enodia-sentinel baseline +``` + +Rebuilds listener and SUID baselines under `log_dir`. Use after installing on a +known-good host or after intentionally adding persistent services or privileged +helpers. + +Do not run this to silence unexplained findings. + +## Detection and Integrity Commands + +### `list-detectors` + +```bash +enodia-sentinel list-detectors +``` + +Prints the poll detectors and whether each is enabled by config. + +### `rootcheck` + +```bash +enodia-sentinel rootcheck +``` + +Runs anti-rootkit cross-view checks once: + +- PIDs alive via `kill(pid, 0)` but missing from `/proc`. +- Live modules in `/sys/module` but missing from `/proc/modules`. +- Listening TCP ports in `/proc/net/tcp*` but missing from `ss`. +- Network interfaces in promiscuous mode. + +Exit code: + +- `0`: no rootcheck findings. +- `1`: one or more rootcheck alerts. + +### `fim-baseline` + +```bash +enodia-sentinel fim-baseline +``` + +Builds the SHA-256 file integrity baseline for configured critical paths plus +Sentinel's own footprint. + +Use on a known-good host. The package hook also calls `fim-update` after normal +package transactions. + +### `fim-update` + +```bash +enodia-sentinel fim-update +``` + +Refreshes the FIM baseline. This exists primarily for the package manager hook. +Manual use should be treated as an acknowledgement that the current file state +is legitimate. + +### `fim-check` + +```bash +enodia-sentinel fim-check +enodia-sentinel fim-check --packages +``` + +Diffs current file state against the FIM baseline. With `--packages`, also runs +package-owned file verification through the package manager (`pacman -Qkk` on +Arch). + +Exit code: + +- `0`: no baseline changes. +- `1`: one or more added, removed, or modified files. + +### `pkgdb-check` + +```bash +enodia-sentinel pkgdb-check +``` + +Checks whether the local package database fingerprint changed outside a logged +package transaction. This catches an attacker rewriting stored package checksums +to hide a modified binary. + +Exit code: + +- `0`: package DB matches the anchor or was legitimately re-anchored. +- `1`: out-of-band DB tampering suspected. + +### `pkgdb-verify` + +```bash +enodia-sentinel pkgdb-verify +enodia-sentinel pkgdb-verify --sample 200 +``` + +Compares on-disk package-owned files to SHA-256 hashes from `.MTREE` manifests +inside cached signed packages. This is independent of the mutable local package +database. + +The command also flags insecure global `SigLevel` settings and warns if the +pacman keyring is missing. + +Exit code: + +- `0`: sampled files match and signature policy is not downgraded. +- `1`: a signed-package mismatch or insecure signature setting was found. + +## Evidence and Operator Commands + +### `web` + +```bash +enodia-sentinel web +``` + +Starts the read-only dashboard. By default it binds to the Tailscale interface +when available and uses bearer-token authentication for non-loopback binds. + +Expected use: `enodia-sentinel-web.service`. + +### `triage` + +```bash +enodia-sentinel triage +``` + +Reads captured alert JSON snapshots and classifies distinct findings as likely +false positive or review-needed. It suggests config snippets for known benign +noise but never edits config automatically. + +### `watchdog` + +```bash +enodia-sentinel watchdog --url http://100.x.y.z:8787 --token --max-age 120 +``` + +Polls a remote dashboard and alerts if the sensor is down, unreachable, or has a +stale heartbeat. Run this from a separate host. A watchdog on the same machine +does not prove the protected host is alive. + +Exit code: + +- `0`: remote sensor is reachable and fresh. +- `1`: remote sensor is stale, down, or unreachable. + +## Development Shortcuts + +The Makefile wraps common local tasks: + +```bash +make test +make check +make baseline +make web +make drill +``` + +`make install` installs a plain package directory plus a launcher wrapper. It +does not require pip or a virtualenv. + +## Future Command Contracts + +The roadmap reserves these command shapes: + +```bash +enodia-sentinel status --json +enodia-sentinel incident list +enodia-sentinel incident show +enodia-sentinel incident export +enodia-sentinel posture check +enodia-sentinel respond plan +enodia-sentinel respond apply +``` + +These should be introduced with stable JSON schemas and tests before they are +documented as supported. diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md new file mode 100644 index 0000000..112f5dc --- /dev/null +++ b/docs/OPERATIONS.md @@ -0,0 +1,143 @@ +# Enodia Sentinel Operations Guide + +This guide turns the current toolset into repeatable operator workflows. It is +deliberately practical: what to run, what to expect, and what to check next. + +## First Install Checklist + +1. Install and enable the service. + + ```bash + sudo make install + sudo make enable + ``` + +2. Build the listener and SUID baselines. + + ```bash + sudo enodia-sentinel baseline + ``` + +3. Build the FIM baseline. + + ```bash + sudo enodia-sentinel fim-baseline + ``` + +4. Run a one-shot check. + + ```bash + sudo enodia-sentinel check + sudo enodia-sentinel rootcheck + sudo enodia-sentinel pkgdb-check + ``` + +5. Start the dashboard if desired. + + ```bash + sudo systemctl enable --now enodia-sentinel-web + ``` + +6. Configure one off-box notification or watchdog path. A local-only alerting + path is not enough if the whole host goes silent. + +## Routine Health Checks + +Run these after upgrades, config edits, or suspicious behavior: + +```bash +enodia-sentinel check +enodia-sentinel fim-check +enodia-sentinel pkgdb-check +enodia-sentinel pkgdb-verify --sample 100 +enodia-sentinel rootcheck +enodia-sentinel triage +``` + +Expected healthy output: + +- `check`: no alerts, or only expected tuned findings. +- `fim-check`: no changes against baseline after legitimate updates have been + acknowledged. +- `pkgdb-check`: package DB consistent with anchor. +- `pkgdb-verify`: sampled files match signed cache packages. +- `rootcheck`: no hidden processes, modules, ports, or sniffers. + +## Alert Workflow + +When Sentinel fires: + +1. Open the newest JSON and text snapshots in `/var/log/enodia-sentinel`. +2. Identify the signature, `sid`, affected PIDs, executable paths, remote peers, + and parent process. +3. Run `enodia-sentinel triage` to separate known benign listener noise from + findings that need review. +4. If the alert involves a binary, run: + + ```bash + enodia-sentinel fim-check --packages + enodia-sentinel pkgdb-verify --sample 200 + ``` + +5. If the alert involves hiding or tampering, run: + + ```bash + enodia-sentinel rootcheck + enodia-sentinel pkgdb-check + ``` + +6. Preserve evidence before changing state: + + ```bash + cp -a /var/log/enodia-sentinel /tmp/enodia-sentinel-evidence + ``` + +7. Contain manually for now. Future response commands should produce a dry-run + plan first. + +## Common Findings + +| Finding | First checks | +|---|---| +| `reverse_shell` | Inspect parent process, fd table, remote peer, shell argv, and user. | +| `egress` | Confirm interpreter, destination IP, command line, and parent process. | +| `new_listener` | Identify owning process and whether the binary is package-owned. | +| `new_suid` | Check path, owner, package provenance, and whether it lives in a writable directory. | +| `persistence` | Diff the changed file and identify the modifying package or process if possible. | +| `fim_modified` | Verify package ownership, package checksum, and whether the change followed an upgrade. | +| `pkgdb_tamper` | Treat as high-confidence tampering until a legitimate package transaction explains it. | +| `pkg_signature_mismatch` | Treat as a potentially trojaned package-owned file. | +| `hidden_*` rootcheck alerts | Compare live tools, preserve evidence, and consider offline analysis. | + +## Baseline Hygiene + +Baselines are useful only if they represent a known-good state. + +- Build baselines after installing Sentinel on a clean host. +- Rebuild listener/SUID baselines after intentionally adding long-running + services or privileged binaries. +- Use `fim-update` only after confirming changes are legitimate. Package hooks + already run it for normal package transactions. +- Keep a copy of important anchors off-box when possible. + +## Suggested Hardening + +- Keep the dashboard bound to Tailscale or loopback, not a public interface. +- Set `web_token` explicitly for fully read-only service operation. +- Enable at least one push backend. +- Run `watchdog` from another machine. +- Consider `chattr +i` for Sentinel binaries, systemd units, config, baselines, + and the pacman hook after setup. +- Keep the package cache populated if using signed-package verification. +- Do not disable package signature verification. + +## Evidence Export + +Until the incident command set exists, an evidence bundle is simply: + +```bash +tar -C /var/log -czf /tmp/enodia-sentinel-evidence.tgz enodia-sentinel +``` + +That bundle contains events, snapshots, baselines, heartbeat state, package DB +anchor, and dashboard-readable JSON. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..efea9d7 --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,174 @@ +# Enodia Sentinel Roadmap + +This roadmap moves Enodia Sentinel from a host IDS into a host security platform: +detect, verify, investigate, respond, and assure. Dates are intentionally not +promised here; the sequence matters more than calendar precision. + +## Release Tracks + +| Track | Goal | +|---|---| +| Sensor | Better host telemetry, event capture, and detection coverage. | +| Integrity | Stronger proof that files, packages, baselines, and Sentinel itself were not silently modified. | +| Incident | Group alerts, build timelines, and export evidence. | +| Response | Add safe, auditable containment and recovery workflows. | +| Fleet | Let multiple hosts report to a small controller without making the local agent dependent on it. | +| Assurance | Move trust anchors off-box or below user space. | + +## v0.8: Documentation, Posture, and Incident Shape + +Purpose: make the current agent easier to operate and create the data model for +work that is bigger than single alerts. + +- Add `incident_id` grouping around related alerts while preserving existing + alert JSON. +- Add an incident timeline generator: + alert time, process ancestry, sockets, persistence writes, FIM diffs, package + transactions, and rootcheck findings. +- Add `enodia-sentinel incident list/show/export`. +- Add host posture checks: + SSH password login, root SSH login, permissive sudoers entries, unexpected + enabled services, world-writable PATH components, disabled package signatures, + and unsafe systemd unit settings. +- Add a machine-readable `status --json` command for automation. +- Update the red-team harness so every current detection has a named drill and + expected `sid`. +- Document incident response runbooks for reverse shells, persistence, trojaned + binaries, hidden listeners, and sensor tampering. + +Exit criteria: + +- One alert can become one incident artifact. +- Operators can answer "what changed around this alert?" without reading every + raw snapshot manually. +- Posture checks report reviewable findings but do not block daemon startup. + +## v0.9: Response and Recovery + +Purpose: move from "tell me" to "help me act" without unsafe automation. + +- Add response plan generation: + `enodia-sentinel respond plan `. +- Add dry-run first actions: + kill process, stop/disable service, block remote IP, quarantine file, restore + package-owned file by reinstalling its package, and freeze evidence. +- Require explicit `--apply` for changes; default to read-only plans. +- Write response audit logs under the normal log directory. +- Add baseline reconciliation: + accept legitimate FIM/package/listener/SUID changes with a recorded reason. +- Add richer false-positive suppression suggestions that can emit TOML snippets + but never modify config automatically. +- Add recovery checks: + "verify package", "re-run rootcheck", "confirm no persistence diff", and + "confirm heartbeat/dashboard visible from watchdog". + +Exit criteria: + +- An operator can go from alert to reviewed response plan with one command. +- Any state-changing action is explicit, logged, reversible where possible, and + testable in a safe fixture. + +## v1.0: Stable Local Platform + +Purpose: define a stable single-host product. + +- Commit to stable JSON schemas for alerts, incidents, status, and response + audits. +- Add compatibility tests for schema evolution. +- Add documentation versioning and manpage-style command reference. +- Harden packaging for Arch first, then document Debian/RPM install paths. +- Add signed release artifacts and checksums. +- Improve dashboard from alert browser to local console: + incidents, posture, integrity state, response plans, and watchdog status. +- Keep the dashboard read-only unless a separate authenticated write path is + designed and reviewed. + +Exit criteria: + +- The local agent can be installed, operated, upgraded, and integrated without + reading source code. +- JSON and command contracts are stable enough for downstream automation. + +## v1.1: Event Coverage and Correlation + +Purpose: reduce blind spots and connect related signals. + +- Add eBPF event sources: + `tcp_connect`, `bind`, `accept`, module load, privilege transitions, and + writes to watched persistence paths. +- Build process lineage across polling and event sources. +- Correlate multi-stage behavior: + web service spawns shell, shell downloads payload, payload adds persistence, + listener appears, and FIM changes. +- Add configurable correlation windows and severity escalation rules. +- Keep polling as the oracle and fallback. + +Exit criteria: + +- Short-lived network and persistence actions are visible. +- Related alerts collapse into one incident with a readable timeline. + +## v1.2: Fleet Mode + +Purpose: support several personal or small-business Linux hosts without turning +the agent into a heavy enterprise platform. + +- Add an optional collector service. +- Agents push signed JSON events and heartbeats over HTTPS or a tailnet. +- Collector stores host status, alerts, incidents, and package/integrity state. +- Add host enrollment with per-host tokens. +- Add fleet-wide views: + stale sensors, repeated signatures, hosts missing package signatures, and + integrity drift. +- Preserve local autonomy: detection and evidence capture must continue if the + collector is down. + +Exit criteria: + +- A small fleet can answer "which host is compromised or silent?" from one + console. +- The agent still works as a standalone local tool. + +## v1.3+: Assurance and Hardening + +Purpose: make tampering harder to hide from an attacker with high privileges. + +- External baseline anchor: + mirror FIM and package DB fingerprints off-box with append-only semantics. +- Hash-chain `events.log` and snapshots. +- Optional snapshot signing with a host key. +- Investigate Linux IMA/EVM and TPM-backed attestation for supported machines. +- Replace bcc with a libbpf + CO-RE agent if the event layer becomes core to + the product. +- Explore minimal kernel-side enforcement hooks for response actions, while + keeping the default product inspect-only. + +Exit criteria: + +- A local root attacker has to tamper with both the host and an external or + hardware-backed trust anchor to hide compromise. + +## Backlog + +Useful ideas that need design before implementation: + +- Policy-as-code for host posture. +- YARA-compatible scanning for selected paths. +- Sigma-like host event rules. +- SBOM and package provenance reporting. +- Offline evidence bundle for outside analysis. +- Dashboard import of exported incident bundles. +- Support for non-pacman package managers in signed-package verification. +- Dedicated Debian/RPM packaging. +- Installer preflight checks and health diagnostics. +- Rule documentation generated from source defaults. + +## Roadmap Rules + +- Do not add dependencies to the base daemon without a specific security or + operator value that cannot be achieved with stdlib. +- Prefer read-only detection and dry-run response until the command contract is + tested and documented. +- Every new detector or response action needs a safe drill or fixture. +- Do not build stealth or self-hiding behavior. +- Keep the local agent useful without a dashboard, collector, or network. diff --git a/docs/SPECIFICATION.md b/docs/SPECIFICATION.md new file mode 100644 index 0000000..75e60bb --- /dev/null +++ b/docs/SPECIFICATION.md @@ -0,0 +1,206 @@ +# Enodia Sentinel Specification + +This document defines Enodia Sentinel as a host security platform, not only a +signature IDS. The current implementation is a local Linux sensor with evidence +capture, integrity monitoring, anti-rootkit checks, a dashboard, and alert +delivery. The intended product is a defensive control plane for a host or small +fleet: detect compromise, prove what changed, preserve useful evidence, guide +response, and make sensor tampering visible. + +## Product Definition + +Enodia Sentinel is a zero-runtime-dependency Linux host security agent. It runs +as a local daemon, observes high-signal host behavior, records forensic context +when something suspicious happens, and exposes the result through local files, +JSON, a read-only dashboard, push notifications, and command-line tools. + +The long-term product should combine five functions: + +| Function | Purpose | +|---|---| +| Detection | Find active compromise behavior: reverse shells, fileless execution, new listeners, suspicious egress, persistence edits, and privilege-escalation artifacts. | +| Integrity | Prove whether important files, packages, baselines, and Sentinel's own footprint changed. | +| Evidence | Capture enough context for incident response without forcing the operator to reproduce a live state after the attacker is gone. | +| Response | Provide safe, explicit operator workflows for triage, containment, and recovery. | +| Assurance | Make the agent itself measurable: heartbeat, dead-man's switch, tamper-evidence, and future remote attestation. | + +## Design Principles + +1. **High-signal first.** Prefer detections that are cheap to observe and costly + for attackers to avoid, even if that means a smaller rule set. +2. **Evidence over noise.** Every alert should carry enough context to answer + "what happened, where, and what should I inspect next?" +3. **Zero dependency core.** The base daemon must run with the Python standard + library only. Optional layers such as bcc/eBPF may degrade cleanly. +4. **One local trust boundary is not enough.** On-box checks are useful but + root can tamper with them. Important anchors should move to signed package + metadata, immutable filesystem controls, external watchers, or future + remote attestation. +5. **Operator control.** Sentinel should recommend response actions before it + takes them. Destructive containment must be opt-in and auditable. +6. **Bash prototype remains the oracle.** The original shell implementation and + red-team harness preserve behavioral compatibility for core signatures. + +## Current Scope: v0.7 + +### Poll Detectors + +The daemon builds one `SystemState` per sweep and runs pure detector functions +against cached process and socket data: + +| Detector | Signal | +|---|---| +| `reverse_shell` | Interpreter with a network socket on fd 0/1/2. | +| `ld_preload` | Non-empty `/etc/ld.so.preload` or writable-path `LD_PRELOAD`. | +| `deleted_exe` | Process executing from a deleted file or `memfd:` image. | +| `new_listener` | Listening socket absent from the startup baseline. | +| `new_suid` | New SUID/SGID binary, critical in writable locations. | +| `persistence` | Changes to cron, systemd units, SSH keys, shell rc files, and similar persistence locations. | +| `egress` | Interpreter with an established connection to a public IP. | + +### Event Detection + +An optional bcc eBPF `execve` monitor feeds a declarative rule engine. This +closes the polling gap for short-lived commands and supports custom TOML rules +without code changes. Failure is fail-safe: if the probe cannot load, polling +continues. + +### Integrity and Tamper-Evidence + +Sentinel includes several integrity layers: + +| Layer | Current behavior | +|---|---| +| FIM baseline | SHA-256 baseline for critical files and Sentinel's own footprint. | +| Package verification | Optional `pacman -Qkk` verification for package-owned files. | +| Package DB anchor | Detects out-of-band edits to `/var/lib/pacman/local`. | +| Signed-package anchor | Compares on-disk files to `.MTREE` hashes from cached signed packages. | +| Heartbeat | Writes daemon liveness for local dashboard and external watchdog use. | +| Rootcheck | Cross-view checks for hidden processes, hidden modules, hidden ports, and promiscuous interfaces. | + +### Evidence Capture + +When a fresh alert survives cooldown, Sentinel writes: + +- A human-readable snapshot. +- A JSON snapshot suitable for automation and SIEM import. +- Relevant process tree, sockets, environment hints, file descriptors, and + response guidance. +- Event log entries and dashboard-visible status. + +### Operator Interfaces + +| Interface | Role | +|---|---| +| CLI | Run daemon, one-shot checks, baseline management, FIM checks, package DB checks, rootcheck, triage, watchdog. | +| Logs | Durable local evidence under `/var/log/enodia-sentinel`. | +| Dashboard | Read-only web view for status, alert browsing, and snapshot inspection. | +| Push | ntfy, Pushover, and generic webhook notifications. | +| Red-team harness | Safe drills for testing signatures and demos. | + +## Target Scope: Host Security Platform + +The next product shape should make Enodia useful before, during, and after an +incident. + +### Prevent + +- Configuration posture checks for risky SSH, sudo, systemd, cron, kernel, + package manager, firewall, and service settings. +- Baseline drift reports for newly exposed services, new SUID files, new + autostarts, new users/groups, and sensitive permission changes. +- A policy mode that can fail CI or package build checks for unsafe host images. + +### Detect + +- More event sources: `tcp_connect`, `bind`, `accept`, file-write events for + persistence paths, module loading, privilege transitions, and LSM hooks. +- Correlation across signals: "web service spawned shell" + "new listener" + + "persistence write" should become one incident, not three unrelated alerts. +- Optional YARA-style and Sigma-like local rules if they can stay dependency + light and operator-readable. + +### Investigate + +- Incident timeline files derived from event log, snapshots, FIM diffs, + package transactions, process lineage, and network observations. +- Stable incident IDs that group related alerts over time. +- `enodia-sentinel incident show ` and `incident export ` workflows. + +### Respond + +- Safe containment actions behind explicit flags: + stop a process, disable a systemd unit, remove a persistence entry, block an + IP with nftables, quarantine a file, or freeze evidence. +- Dry-run by default, with JSON plans that can be reviewed before execution. +- Response playbooks that are code-reviewed data files, not shell snippets + pasted into documentation. + +### Recover + +- Package restore guidance: identify owning package, verify cache signature, + reinstall package, and re-run integrity checks. +- Baseline reconciliation workflow after confirmed legitimate changes. +- Post-incident report generation from captured evidence. + +### Assure + +- External anchor storage for baselines and package DB fingerprints. +- Signed snapshots or hash-chained event logs. +- Remote watchdog improvements and eventually fleet controller support. +- Optional TPM/IMA integration for machines that can support measured boot. + +## Data Model + +The current `Alert` object is the public detection unit: + +| Field | Meaning | +|---|---| +| `sid` | Stable signature ID for tuning, documentation, and downstream rules. | +| `severity` | `MEDIUM`, `HIGH`, or `CRITICAL`. | +| `signature` | Stable machine-readable detection name. | +| `classtype` | Snort/Suricata-style category. | +| `key` | Cooldown and deduplication identity. | +| `detail` | Human-readable explanation. | +| `pids` | Process IDs for snapshot deep dives. | + +Future incident grouping should add an `incident_id` around one or more alerts, +without breaking the alert JSON schema. + +## Configuration Model + +Configuration is a TOML file loaded at startup. Unknown keys are ignored so +newer config templates remain tolerable on older agents, but documentation +should clearly mark version-specific fields. Existing defaults should prefer +detection correctness over suppression. Convenience suppression knobs should +remain explicit and reviewable. + +## Security Model + +Sentinel runs as root because it must inspect root-owned files, process state, +file descriptors, and sockets. The systemd unit should continue to keep the +root process narrow: read-mostly, constrained writable paths, no new privileges, +restricted namespaces, and only the capabilities needed for the enabled layers. + +Local root can eventually defeat any purely local sensor. Sentinel's answer is +layering: signed package metadata, immutable files, off-box watchdogs, external +anchors, and future kernel/attestation features. + +## Non-Goals + +- Replacing a full enterprise SIEM. +- Silent self-hiding or rootkit-like persistence. +- Automatic destructive remediation without explicit operator approval. +- Shipping a broad dependency tree in the default agent. +- Claiming complete protection against a custom kernel implant. + +## Acceptance Criteria For New Platform Features + +A feature is ready when it has: + +- A stable `sid`/signature or documented command contract. +- Unit tests for pure logic and a safe integration path where practical. +- JSON output or a durable artifact suitable for automation. +- Configuration documented in the sample TOML and README or relevant docs. +- Failure behavior that does not take down the daemon. +- Clear operator guidance for false positives, limitations, and response. diff --git a/docs/THREAT_MODEL.md b/docs/THREAT_MODEL.md new file mode 100644 index 0000000..4cad961 --- /dev/null +++ b/docs/THREAT_MODEL.md @@ -0,0 +1,119 @@ +# Enodia Sentinel Threat Model + +This document describes what Enodia Sentinel is meant to catch, what it can only +make evident, and what it does not claim to solve. + +## Assets + +Sentinel protects and records evidence about: + +- Running processes and process ancestry. +- Network sockets and remote peers. +- File descriptors and executable paths. +- SUID/SGID inventory and privilege-escalation artifacts. +- Persistence locations: cron, systemd units, SSH keys, shell startup files, and + sensitive account/sudo configuration. +- Package-owned binaries and local package database integrity. +- Sentinel's own binaries, config, systemd units, hook, baselines, logs, and + heartbeat. + +## Adversary Model + +Sentinel is designed to help against: + +| Adversary | Examples | Expected Sentinel value | +|---|---|---| +| Opportunistic remote shell | Webshell, exposed service RCE, stolen SSH key | Detect reverse shell, suspicious egress, new listeners, persistence writes. | +| Local privilege escalation | New SUID helper, dropped setuid shell, writable path abuse | Detect new SUID/SGID and critical writable-directory placement. | +| Fileless or short-lived execution | Deleted executable, memfd payload, fast `curl|sh` | Detect deleted executables and eBPF exec rules where available. | +| Package/file tampering | Trojaned binary, rewritten package DB checksums | Detect FIM drift, package DB tamper, and signed-package mismatches. | +| Common rootkit hiding | LD_PRELOAD tricks, `/proc` hiding, module-list hiding, hidden listener | Detect LD_PRELOAD and cross-view inconsistencies. | +| Sensor tampering | Stop daemon, edit config, remove hook, modify baseline | Detect self-integrity changes and stale heartbeat via external watchdog. | + +## Trust Boundaries + +### Local Root Is Not Fully Trusted + +The daemon runs as root so it can inspect the host. If an attacker gains root, +they share that privilege. Sentinel therefore does not claim that local files, +local baselines, or local process views are unforgeable. + +The design goal is layered tamper-evidence: + +- Package signatures are harder for a local attacker to forge than the local + package DB. +- Immutable bits force visible state changes before local files can be edited. +- External watchdogs notice silence. +- Future external anchors and signed logs should make retroactive rewriting + harder. + +### Optional eBPF Expands Privilege + +The default service is hardened and read-mostly. Enabling the bcc eBPF monitor +requires additional capabilities and memory permissions. That tradeoff is +explicit: stronger event visibility for a wider runtime permission set. + +## Detection Assumptions + +Sentinel assumes: + +- Attackers need a process, socket, file, package, persistence location, or + privilege transition somewhere on the host. +- High-confidence host behaviors are more useful than broad noisy rules. +- Many attacks are visible from more than one view: `/proc`, `/sys`, `ss`, + package metadata, logs, and filesystem hashes. +- Snapshots captured immediately are more useful than asking an operator to + reconstruct state after the process exits. + +## Important Limitations + +- A custom kernel implant that hooks every view consistently can evade + user-space cross-view checks. +- A local root attacker can delete or rewrite local logs unless external anchors + or immutable controls are in place. +- Signed-package verification depends on a populated package cache and valid + package signature policy. +- `pacman -Qkk` trusts the local package database; it is useful but not an + independent root of trust. +- The dashboard is read-only by design; it is not a remote response console. +- The current product does not perform automatic containment. + +## Security Controls Already Present + +| Control | Purpose | +|---|---| +| Hardened systemd unit | Limits daemon write surface and runtime privilege where possible. | +| Cooldown deduplication | Prevents alert storms from one persistent condition. | +| Snapshot retention | Preserves evidence while bounding local disk growth. | +| FIM self-watch | Detects changes to Sentinel's own files and service definitions. | +| Package DB anchor | Flags package checksum DB edits outside package transactions. | +| Signed-package verification | Checks files against package manifests independent of the local DB. | +| Rootcheck | Finds common hiding artifacts by comparing independent views. | +| Heartbeat + watchdog | Makes a silent sensor observable from another machine. | +| Read-only dashboard | Exposes evidence without adding a remote write path. | + +## Response Safety + +Sentinel should remain conservative: + +- Detection may be automatic. +- Evidence capture may be automatic. +- Notification may be automatic. +- Triage suggestions may be automatic. +- Destructive response should require an explicit operator action. + +Future response features should produce dry-run plans first, log all changes, +and avoid shell-string execution where structured APIs exist. + +## Abuse Considerations + +Some security tools can become dual-use. Enodia should avoid: + +- Stealth, hiding, or rootkit-like self-defense. +- Persistence mechanisms that are hard for the owner to inspect. +- Automated deletion of suspicious files without review. +- Covert exfiltration of host data. +- Cloud dependence for local detection. + +The correct defensive posture is visibility, evidence, and explicit operator +control. diff --git a/enodia_sentinel/__init__.py b/enodia_sentinel/__init__.py index 7b95879..7886812 100644 --- a/enodia_sentinel/__init__.py +++ b/enodia_sentinel/__init__.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later -"""Enodia Sentinel — a host intrusion-detection daemon. +"""Enodia Sentinel — a Linux host security platform. -A poll-based HIDS that runs a set of detectors over live system state and -captures a forensic snapshot with incident-response guidance whenever a known -attack signature appears. The Python re-architecture of the bash v0 prototype. +Detection, file/package integrity, anti-rootkit checks, tamper-evidence, +forensic snapshots, and operator workflows. The Python re-architecture of the +bash v0 prototype. """ __version__ = "0.7.0" diff --git a/enodia_sentinel/cli.py b/enodia_sentinel/cli.py index 19bc262..8063a7a 100644 --- a/enodia_sentinel/cli.py +++ b/enodia_sentinel/cli.py @@ -51,7 +51,7 @@ def _cmd_check(cfg: Config) -> int: def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( prog="enodia-sentinel", - description="Host intrusion-detection daemon.", + description="Linux host security platform.", ) parser.add_argument("--version", action="version", version=f"enodia-sentinel {__version__}") diff --git a/enodia_sentinel/pkgdb.py b/enodia_sentinel/pkgdb.py index 14187c2..2d2b570 100644 --- a/enodia_sentinel/pkgdb.py +++ b/enodia_sentinel/pkgdb.py @@ -169,7 +169,8 @@ def siglevel_insecure(value: str) -> bool: def siglevel_alert(conf_path: str = PACMAN_CONF) -> Alert | None: try: - text = open(conf_path, encoding="utf-8", errors="replace").read() + with open(conf_path, encoding="utf-8", errors="replace") as fh: + text = fh.read() except OSError: return None value = parse_global_siglevel(text) diff --git a/pyproject.toml b/pyproject.toml index 56074e8..f24c9e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "enodia-sentinel" version = "0.7.0" -description = "Host intrusion-detection daemon — signature-based detection of reverse shells, LD_PRELOAD rootkits, fileless malware, and persistence tampering" +description = "Linux host security platform — detection, integrity, anti-rootkit checks, tamper-evidence, and forensic snapshots" readme = "README.md" requires-python = ">=3.11" license = { text = "GPL-3.0-or-later" }