|
| 1 | +# ClawEDR Architecture |
| 2 | + |
| 3 | +## Project Layout |
| 4 | + |
| 5 | +``` |
| 6 | +clawedr/ |
| 7 | +├── builder/ # THE FORGE — private build tools |
| 8 | +│ ├── threat_aggregator.py # Fetches ClawSec advisory feed |
| 9 | +│ ├── compiler.py # Compiles rules into kernel policies |
| 10 | +│ ├── master_rules.yaml # Manual overrides (source of truth, Rule IDs) |
| 11 | +│ ├── config.yaml # Forge environment config (VM host, etc.) |
| 12 | +│ └── tests/ |
| 13 | +│ ├── test_mac_sb.py # macOS Seatbelt enforcement tests |
| 14 | +│ └── test_linux_bpf.py # Linux eBPF tests via OrbStack VM |
| 15 | +│ |
| 16 | +├── deploy/ # THE REGISTRY — public artifacts |
| 17 | +│ ├── install.sh # One-liner OS-detecting dispatcher |
| 18 | +│ ├── compiled_policy.json # Universal policy (Rule IDs for both OSes) |
| 19 | +│ ├── linux/ |
| 20 | +│ │ ├── bpf_hooks.c # eBPF tracepoint hooks (execve interception) |
| 21 | +│ │ ├── monitor.py # Shield daemon — hot-reloads BPF maps |
| 22 | +│ │ └── shield_linux.sh # Linux setup script (systemd integration) |
| 23 | +│ ├── macos/ |
| 24 | +│ │ ├── clawedr.sb # Compiled Seatbelt LISP profile |
| 25 | +│ │ ├── log_tailer.py # Sandbox log monitor + alert dispatcher |
| 26 | +│ │ ├── apply_macos_policy.py # Runtime Seatbelt generator (applies exemptions) |
| 27 | +│ │ └── shield_mac.sh # macOS setup script |
| 28 | +│ ├── shared/ |
| 29 | +│ │ ├── user_rules.py # Reads/writes ~/.clawedr/user_rules.yaml |
| 30 | +│ │ └── alert_dispatcher.py # Pushes alerts to OpenClaw chat |
| 31 | +│ └── dashboard/ |
| 32 | +│ ├── app.py # FastAPI backend (alerts, rules, exemptions) |
| 33 | +│ └── templates/index.html # Browser-based dashboard UI |
| 34 | +│ |
| 35 | +├── main.py # Forge CLI (sync / compile / test / publish) |
| 36 | +├── requirements-dev.txt # Forge-only Python dependencies |
| 37 | +└── .github/workflows/ci.yml # CI validation on push |
| 38 | +``` |
| 39 | + |
| 40 | +## Three-Component Architecture |
| 41 | + |
| 42 | +### 1. The Forge (`builder/` + `main.py`) |
| 43 | + |
| 44 | +The Forge runs on the developer's machine. It fetches threat intelligence, compiles kernel policies, validates them, and publishes to the Registry. |
| 45 | + |
| 46 | +**`sync`** pulls the [ClawSec advisory feed](https://clawsec.prompt.security/advisories/feed.json), extracts `affected_skills`, `malicious_hashes`, and `blocked_domains`, and merges them with manual overrides in `builder/master_rules.yaml`. The result is cached at `builder/.merged_rules.json`. |
| 47 | + |
| 48 | +**`compile`** reads the merged rules and produces: |
| 49 | +- `deploy/compiled_policy.json` — universal policy consumed by both platforms at runtime |
| 50 | +- `deploy/macos/clawedr.sb` — Seatbelt LISP profile with `deny` rules for every blocked path/executable |
| 51 | + |
| 52 | +**`test`** runs `pytest builder/tests/` covering Seatbelt syntax validation, runtime enforcement, and Linux eBPF policy loading. |
| 53 | + |
| 54 | +**`publish`** stages `deploy/`, commits, and pushes to GitHub. |
| 55 | + |
| 56 | +### 2. The Registry (`deploy/`) |
| 57 | + |
| 58 | +Public artifacts served over HTTPS from GitHub. End-users pull these via `install.sh`. |
| 59 | + |
| 60 | +### 3. The Shield (end-user runtime) |
| 61 | + |
| 62 | +Kernel-level enforcement daemon installed on end-user machines. |
| 63 | + |
| 64 | +## Threat Intelligence Pipeline |
| 65 | + |
| 66 | +``` |
| 67 | +ClawSec Feed (community) master_rules.yaml (manual) |
| 68 | + ├── affected_skills + ├── blocked_paths |
| 69 | + ├── malicious_hashes + ├── blocked_domains |
| 70 | + └── blocked_domains + ├── blocked_executables |
| 71 | + └── custom_deny_rules |
| 72 | + ↓ |
| 73 | + .merged_rules.json |
| 74 | + ↓ |
| 75 | + ┌──────────────┴──────────────┐ |
| 76 | + │ │ |
| 77 | + compiled_policy.json clawedr.sb |
| 78 | + (Linux) (macOS) |
| 79 | +``` |
| 80 | + |
| 81 | +Feed entries **add to** manual rules, never replace them. |
| 82 | + |
| 83 | +## Shield: Linux (eBPF) |
| 84 | + |
| 85 | +The Shield runs as a background daemon (`monitor.py`) that: |
| 86 | + |
| 87 | +1. Loads `compiled_policy.json` into eBPF hash maps |
| 88 | +2. Auto-detects OpenClaw processes (gateway, agent, node) and tracks their entire process tree via BPF `fork`/`exit` hooks — only these processes are subject to policy enforcement |
| 89 | +3. Hooks `execve()` via BPF tracepoints — blocked executables spawned by OpenClaw receive `SIGKILL` |
| 90 | +4. Polls for policy file changes and **hot-reloads** BPF maps without restarting |
| 91 | + |
| 92 | +Logs are written to both `/var/log/clawedr_monitor.log` and `journalctl -u clawedr-monitor`. |
| 93 | + |
| 94 | +### Network Blocking |
| 95 | + |
| 96 | +eBPF LSM `socket_connect` blocks connections at the kernel level (returns `-EPERM`). Requires `CONFIG_BPF_LSM=y` and `lsm=...,bpf` in the kernel cmdline. Falls back to tracepoint + SIGKILL if unavailable. |
| 97 | + |
| 98 | +### Process Tracking Scope |
| 99 | + |
| 100 | +ClawEDR only monitors OpenClaw (gateway, agent, node) and their descendants. Non-OpenClaw processes are never tracked. The install script sets `tools.exec.host=gateway` and `agents.defaults.sandbox.mode=off` in `~/.openclaw/openclaw.json` so that agent exec runs on the gateway host and is subject to blocking. Restart the gateway after install for the config to take effect. |
| 101 | + |
| 102 | +### Anti-Tamper |
| 103 | + |
| 104 | +Deny rules (LIN-030, LIN-031) block `kill`/`pkill`/`systemctl stop clawedr` when run by an OpenClaw descendant. They do not affect admin operations from a normal shell. |
| 105 | + |
| 106 | +## Shield: macOS (Seatbelt) |
| 107 | + |
| 108 | +1. `openclaw` wrapper runs the agent under `sandbox-exec -f clawedr.sb` |
| 109 | +2. The `.sb` profile denies file reads/writes to sensitive paths (`~/.ssh`, `~/.gnupg`, `~/.aws`, `~/Library/Keychains`) |
| 110 | +3. `log_tailer.py` monitors sandbox violation events and dispatches alerts |
| 111 | + |
| 112 | +Seatbelt profiles bind at process start and cannot be hot-reloaded. Logs appear in Console.app (subsystem `com.clawedr.shield`) or `/tmp/clawedr_log_tailer.log`. |
| 113 | + |
| 114 | +**macOS note:** Ensure `/usr/local/bin` comes before `/opt/homebrew/bin` in PATH. Verify with: `sandbox-exec -f /usr/local/share/clawedr/clawedr.sb -- nc -h` (should fail with "Operation not permitted"). |
| 115 | + |
| 116 | +## Dashboard |
| 117 | + |
| 118 | +Web UI on port 8477, auto-installed as a system service on both platforms. |
| 119 | + |
| 120 | +### Features |
| 121 | + |
| 122 | +- **Alerts** — Real-time blocked actions with Rule IDs. Click "Exempt" to bypass a rule. |
| 123 | +- **Policy Rules** — Browse all active rules by category. Toggle exemptions inline. Add/edit/delete custom rules. |
| 124 | +- **Platform filtering** — Auto-detects OS, pill bar to switch views. |
| 125 | +- **Sessions** — Active OpenClaw instances being monitored. |
| 126 | +- **Settings** — Auto-update toggle, manual update check. |
| 127 | +- **Rule updates** — Hourly registry checks. Linux: auto-enforced via hot-reload. macOS: banner prompts restart. |
| 128 | + |
| 129 | +### API Reference |
| 130 | + |
| 131 | +| Method | Path | Description | |
| 132 | +|--------|------|-------------| |
| 133 | +| `GET` | `/api/status` | Shield health: OS, policy state, OpenClaw availability | |
| 134 | +| `GET` | `/api/alerts` | Recent blocked actions parsed from logs | |
| 135 | +| `GET` | `/api/rules` | Full compiled policy with Rule IDs | |
| 136 | +| `GET` | `/api/user-rules` | Current user exemptions and custom rules | |
| 137 | +| `POST` | `/api/user-rules` | Update exemptions (preserves custom rules) | |
| 138 | +| `GET` | `/api/custom-rules` | List all user-defined custom rules | |
| 139 | +| `POST` | `/api/custom-rules` | Add a custom rule (`{"type": "domain", "value": "evil.com", "platform": "both"}`) | |
| 140 | +| `PUT` | `/api/custom-rules/{id}` | Update a custom rule | |
| 141 | +| `DELETE` | `/api/custom-rules/{id}` | Delete a custom rule | |
| 142 | +| `GET` | `/api/sessions` | Active OpenClaw sessions | |
| 143 | +| `GET` | `/api/settings` | Dashboard settings | |
| 144 | +| `POST` | `/api/settings` | Update settings | |
| 145 | +| `GET` | `/api/updates` | Check for rule updates from registry | |
| 146 | +| `POST` | `/api/updates/apply` | Download and apply updates | |
| 147 | + |
| 148 | +### Service Management |
| 149 | + |
| 150 | +```sh |
| 151 | +# macOS |
| 152 | +sudo launchctl unload /Library/LaunchDaemons/com.clawedr.dashboard.plist # stop |
| 153 | +sudo launchctl load -w /Library/LaunchDaemons/com.clawedr.dashboard.plist # start |
| 154 | +cat /tmp/clawedr_dashboard.log |
| 155 | + |
| 156 | +# Linux |
| 157 | +sudo systemctl stop clawedr-dashboard |
| 158 | +sudo systemctl start clawedr-dashboard |
| 159 | +sudo journalctl -u clawedr-dashboard -f |
| 160 | +``` |
| 161 | + |
| 162 | +## User Rules |
| 163 | + |
| 164 | +All customizations in `~/.clawedr/user_rules.yaml` survive system updates. |
| 165 | + |
| 166 | +### Exemptions |
| 167 | + |
| 168 | +```yaml |
| 169 | +exempted_rule_ids: |
| 170 | + - "BIN-001" # Allow nc |
| 171 | + - "LIN-004" # Allow stratum+tcp |
| 172 | +``` |
| 173 | +
|
| 174 | +### Custom Rules |
| 175 | +
|
| 176 | +```yaml |
| 177 | +custom_rules: |
| 178 | + - id: USR-BIN-001 |
| 179 | + type: executable |
| 180 | + value: terraform |
| 181 | + - id: USR-DOM-001 |
| 182 | + type: domain |
| 183 | + value: evil.com |
| 184 | + - id: USR-HASH-001 |
| 185 | + type: hash |
| 186 | + value: "sha256:a1b2c3d4e5f6..." |
| 187 | + - id: USR-PATH-001 |
| 188 | + type: path |
| 189 | + value: /var/secrets |
| 190 | + platform: linux |
| 191 | + - id: USR-ARG-001 |
| 192 | + type: argument |
| 193 | + value: "--password" |
| 194 | +``` |
| 195 | +
|
| 196 | +| Type | Blocks | Validation | |
| 197 | +|------|--------|------------| |
| 198 | +| `executable` | Binary by name | No paths, no protected binaries | |
| 199 | +| `domain` | Domain name or IP | RFC-compliant, rejects URLs | |
| 200 | +| `hash` | SHA-256 file hash | 64 hex chars, optional `sha256:` prefix | |
| 201 | +| `path` | File/directory access | Absolute or `~/`, cannot be root | |
| 202 | +| `argument` | Command-line args (regex) | Must be valid regex | |
| 203 | + |
| 204 | +**Linux:** Custom rules merge into BPF maps on every policy reload (hot-reloaded). |
| 205 | +**macOS:** `apply_macos_policy.py` rebuilds `clawedr.sb` including custom rules. Requires agent restart. |
| 206 | + |
| 207 | +## Testing |
| 208 | + |
| 209 | +| Test | Platform | Validates | |
| 210 | +|------|----------|-----------| |
| 211 | +| Profile syntax | macOS | `.sb` file has valid Seatbelt directives | |
| 212 | +| Blocked path enforcement | macOS | `sandbox-exec` denies reads to `~/.ssh` | |
| 213 | +| Allowed commands | macOS | Benign commands work under sandbox | |
| 214 | +| VM SSH connectivity | Linux | SSH to the test VM works | |
| 215 | +| BCC availability | Linux | Python BCC bindings importable | |
| 216 | +| Policy loading | Linux | `compiled_policy.json` parses correctly | |
| 217 | +| Blocked executable | Linux | `nc` appears in blocked list | |
| 218 | + |
| 219 | +Configure the Linux test VM in `builder/config.yaml`: |
| 220 | + |
| 221 | +```yaml |
| 222 | +linux_vm: |
| 223 | + host: "orb" |
| 224 | + user: "ubuntu" |
| 225 | +``` |
| 226 | + |
| 227 | +## CI |
| 228 | + |
| 229 | +GitHub Actions (`.github/workflows/ci.yml`) runs on every push to `main`: |
| 230 | + |
| 231 | +1. Installs Python dependencies |
| 232 | +2. Compiles policies from `master_rules.yaml` |
| 233 | +3. Verifies `deploy/` artifacts exist |
| 234 | +4. Runs unit tests (skips platform-specific enforcement tests) |
0 commit comments