rmagent-linux — Pull-based remote witness for Linux/macOS hosts
The Linux sibling of rmagent-windows. Same philosophy: pull-only,
allowlisted questions, no agent install, watch-only (Phase 0), capped answers,
holes instead of dumps.
Use when the estate grows beyond Windows — a Mac jump host, a Linux app box —
so the observatory is actually multi-plane instead of Windows-only.
Non-negotiables (identical to rmagent-windows)
- Watch only. No
actuate. No systemctl stop, no usermod, no firewall edits.
- Allowlisted questions only — never an arbitrary shell.
- Capped answers (32 KB) — oversized pulls become holes, never a lake.
- Your estate only. No box you do not administer.
- No tight retry on a silent host.
- Credentials never in the inventory file — env or
~/.rmagent/creds.json.
The five questions
| Question |
Payload |
What you get |
What you must NOT get |
| Alive? (attest) |
attest.sh |
host, utc, uptime, load, failed sudo in window, root logins 5m, users in wheel/sudo group |
full auth.log |
| Anything odd? (sketch) |
sketch.sh |
new users 24h (from /etc/passwd mtime), users added to sudo group, world-writable files in /etc, SUID binaries changed recently |
raw log lists |
| Who did they touch? (edges) |
edges.sh |
accepted SSH logins (time, user, src IP) + sudo escalations + outbound conns by root, capped |
the whole connection table |
| What changed? (explain) |
explain.sh |
user/group changes, new cron entries, new systemd units, package installs in window, auditd rule changes |
the whole journal |
| What persistence exists? (attackmap) |
attackmap.sh |
cron for all users, systemd timers, shell rc files touched recently, authorized_keys mtime, /etc/ld.so.preload |
the whole filesystem |
Every payload runs as ONE non-interactive bash -c over SSH, emits ONE JSON
object, and is capped. journalctl/grep output is trimmed to $Limit.
Setup (one time)
# 1. Key-based SSH from the jump host to each Linux witness
ssh-copy-id user@linux-box
# 2. Inventory — same shape as the Windows skill
cat > estate-linux.yaml <<'YAML'
witnesses:
- id: lx1
name: App server
plane: endpoint
os: linux
door: ssh
address: 10.0.0.20
user: deploy
skills: [attest, sketch, edges, explain, attackmap]
track: [root, deploy]
YAML
# 3. Verify
python3 scripts/census.py --inventory estate-linux.yaml
sudo questions need passwordless sudo for the specific read-only commands
in the payloads (journalctl, grep, stat). If sudo needs a password, the
payload returns a hole saying so — it never prompts.
Scripts
| Job |
Script |
Notes |
| Census |
scripts/census.py |
SSH door; 2 misses = Critical; history in ~/.rmagent |
| Walk |
scripts/hunt.py |
identity grain only; writes ~/.rmagent/cases |
| Drift |
scripts/drift.py |
baseline + diff for sudoers / suid / blind_count |
| Allowlisted payloads |
scripts/questions/linux/*.sh |
attest / sketch / edges / explain / attackmap |
The engine (lib.py) is shared with rmagent-windows — only the door differs
(ssh instead of winrm) and the payload directory (questions/linux/).
What this skill will NOT do
- No
actuate — no service restarts, user changes, firewall edits.
- No journal export. No
tar of /var/log. No packet capture.
- No witness for a box you do not administer.
- No replacement for auditd/EDR — this is the pull-based witness, not the sensor.
Fidelity gaps (documented, not hidden)
- No persistent ring. Like Windows
edges, edges.sh reads the current
state + recent journal — a sub-second connection that closed before the poll
is missed. auditd with auditctl -a always,exit -F arch=b64 -S connect
is the resident answer (an estate change, not Phase 0).
- journald retention varies.
SystemMaxUse may keep hours or weeks;
payloads clamp to $SinceHours and cap at $Limit regardless.
- macOS: works for attest/sketch/edges via
log show/last; attackmap
checks launchd dirs instead of systemd. Untested on this estate — treat as
experimental until live-validated.
1---2name: rmagent-linux3description: Pull-based remote-witness habit for Linux and macOS hosts — the Linux sibling of rmagent-windows. Same philosophy: pull-only, allowlisted questions, no agent install, watch-only (Phase 0), capped answers, holes instead of dumps. Five questions (attest, sketch, edges, explain, attackmap) over SSH reading journald/auth.log/ss/systemd/cron. Use when the estate grows beyond Windows so the security observatory is actually multi-plane. macOS paths documented as experimental.4---56# rmagent-linux — Pull-based remote witness for Linux/macOS hosts78The Linux sibling of `rmagent-windows`. Same philosophy: **pull-only,9allowlisted questions, no agent install, watch-only (Phase 0), capped answers,10holes instead of dumps.**1112Use when the estate grows beyond Windows — a Mac jump host, a Linux app box —13so the observatory is actually multi-plane instead of Windows-only.1415## Non-negotiables (identical to rmagent-windows)1617- **Watch only.** No `actuate`. No `systemctl stop`, no `usermod`, no firewall edits.18- **Allowlisted questions only** — never an arbitrary shell.19- **Capped answers** (32 KB) — oversized pulls become holes, never a lake.20- **Your estate only.** No box you do not administer.21- **No tight retry** on a silent host.22- **Credentials never in the inventory file** — env or `~/.rmagent/creds.json`.2324## The five questions2526| Question | Payload | What you get | What you must NOT get |27|---|---|---|---|28| **Alive?** (attest) | `attest.sh` | host, utc, uptime, load, failed sudo in window, root logins 5m, users in wheel/sudo group | full auth.log |29| **Anything odd?** (sketch) | `sketch.sh` | new users 24h (from /etc/passwd mtime), users added to sudo group, world-writable files in /etc, SUID binaries changed recently | raw log lists |30| **Who did they touch?** (edges) | `edges.sh` | accepted SSH logins (time, user, src IP) + sudo escalations + outbound conns by root, capped | the whole connection table |31| **What changed?** (explain) | `explain.sh` | user/group changes, new cron entries, new systemd units, package installs in window, auditd rule changes | the whole journal |32| **What persistence exists?** (attackmap) | `attackmap.sh` | cron for all users, systemd timers, shell rc files touched recently, authorized_keys mtime, /etc/ld.so.preload | the whole filesystem |3334Every payload runs as ONE non-interactive `bash -c` over SSH, emits ONE JSON35object, and is capped. `journalctl`/`grep` output is trimmed to `$Limit`.3637## Setup (one time)3839```bash40# 1. Key-based SSH from the jump host to each Linux witness41ssh-copy-id user@linux-box4243# 2. Inventory — same shape as the Windows skill44cat > estate-linux.yaml <<'YAML'45witnesses:46 - id: lx147 name: App server48 plane: endpoint49 os: linux50 door: ssh51 address: 10.0.0.2052 user: deploy53 skills: [attest, sketch, edges, explain, attackmap]54 track: [root, deploy]55YAML5657# 3. Verify58python3 scripts/census.py --inventory estate-linux.yaml59```6061`sudo` questions need passwordless sudo for the *specific* read-only commands62in the payloads (`journalctl`, `grep`, `stat`). If sudo needs a password, the63payload returns a hole saying so — it never prompts.6465## Scripts6667| Job | Script | Notes |68|---|---|---|69| Census | `scripts/census.py` | SSH door; 2 misses = Critical; history in `~/.rmagent` |70| Walk | `scripts/hunt.py` | identity grain only; writes `~/.rmagent/cases` |71| Drift | `scripts/drift.py` | baseline + diff for sudoers / suid / blind_count |72| Allowlisted payloads | `scripts/questions/linux/*.sh` | attest / sketch / edges / explain / attackmap |7374The engine (`lib.py`) is shared with rmagent-windows — only the door differs75(`ssh` instead of `winrm`) and the payload directory (`questions/linux/`).7677## What this skill will NOT do7879- No `actuate` — no service restarts, user changes, firewall edits.80- No journal export. No `tar` of `/var/log`. No packet capture.81- No witness for a box you do not administer.82- No replacement for auditd/EDR — this is the pull-based witness, not the sensor.8384## Fidelity gaps (documented, not hidden)8586- **No persistent ring.** Like Windows `edges`, `edges.sh` reads the current87 state + recent journal — a sub-second connection that closed before the poll88 is missed. `auditd` with `auditctl -a always,exit -F arch=b64 -S connect`89 is the resident answer (an estate change, not Phase 0).90- **journald retention varies.** `SystemMaxUse` may keep hours or weeks;91 payloads clamp to `$SinceHours` and cap at `$Limit` regardless.92- **macOS**: works for attest/sketch/edges via `log show`/`last`; `attackmap`93 checks launchd dirs instead of systemd. Untested on this estate — treat as94 experimental until live-validated.