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 <noreply@anthropic.com>
This commit is contained in:
parent
1de5e86fed
commit
6ff2087329
11 changed files with 930 additions and 26 deletions
226
docs/COMMAND_REFERENCE.md
Normal file
226
docs/COMMAND_REFERENCE.md
Normal file
|
|
@ -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 <command>
|
||||
```
|
||||
|
||||
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 <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 <incident-id>
|
||||
enodia-sentinel incident export <incident-id>
|
||||
enodia-sentinel posture check
|
||||
enodia-sentinel respond plan <incident-id>
|
||||
enodia-sentinel respond apply <plan-id>
|
||||
```
|
||||
|
||||
These should be introduced with stable JSON schemas and tests before they are
|
||||
documented as supported.
|
||||
143
docs/OPERATIONS.md
Normal file
143
docs/OPERATIONS.md
Normal file
|
|
@ -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.
|
||||
174
docs/ROADMAP.md
Normal file
174
docs/ROADMAP.md
Normal file
|
|
@ -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 <incident-id>`.
|
||||
- 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.
|
||||
206
docs/SPECIFICATION.md
Normal file
206
docs/SPECIFICATION.md
Normal file
|
|
@ -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 <id>` and `incident export <id>` 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.
|
||||
119
docs/THREAT_MODEL.md
Normal file
119
docs/THREAT_MODEL.md
Normal file
|
|
@ -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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue