feat: gate remote command execution behind allow_remote_command

CMD packets arrive as unauthenticated UDP datagrams, yet every hbc client
executed the shell command they carry without any opt-in. Add an
allow_remote_command config key, default false: when off, the command is
logged and refused with "Refused: allow_remote_command is false" (visible in
the server event log under the command service), and subprocess is never
reached. When on, the client warns at startup that it will execute CMD
packets.

Applied to all four clients that handle CMD — hbc, hbc_windows.py,
hbc_mini.py, and the C hbc_mini — since gating only one leaves the others
wide open. The C client reads the same key from ~/.hbc.json and needs a
rebuild to pick it up. UPD (self-update) is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Andreas Wrede
2026-07-23 13:10:41 -07:00
co-authored by Claude Opus 4.8
parent 4414967bdc
commit ae3f2fc70f
7 changed files with 195 additions and 8 deletions
+25 -1
View File
@@ -212,6 +212,9 @@ hb_port: 50003 # Server UDP port
interval: 10 # Heartbeat interval (seconds)
owner: alice # Optional: claim ownership of this host
allow_remote_command: false # Execute shell commands sent by the server in CMD packets.
# Default false — see Remote command execution below.
plugins:
cpu_monitor:
interval: 300 # Override collection interval
@@ -242,6 +245,27 @@ plugins:
- If a connection fails to open at startup, IPv6 connections are dropped after 3 consecutive failures. IPv4 connections retry indefinitely.
- In daemon mode (`-d`), all log output goes to syslog (`LOG_DAEMON` facility).
### Remote command execution
The server can queue a shell command for a host, delivered in a `CMD` packet. Because
heartbeat packets are unauthenticated UDP, the client only runs those commands when the
host opts in:
```yaml
allow_remote_command: true
```
With the default `false`, the command is logged and refused, and the client replies
`Refused: allow_remote_command is false` — visible in the server's event log under the
`command` service, so a queued command never fails silently. When enabled, `hbc` logs a
warning at startup naming the risk. `SIGHUP` re-execs the client, so a config change takes
effect on reload.
All four clients enforce this: `hbc`, `hbc_windows.py`, `hbc_mini.py`, and the C
`hbc_mini` (which reads the same key from `~/.hbc.json` and needs a rebuild to pick the
change up). It does not gate `UPD` (self-update via `hb_install.sh`), which remains
ungated.
---
## UDP Protocol
@@ -259,7 +283,7 @@ Payload format: `key=value;key=value;...`
| `HTB` | client → server | Heartbeat (name, timestamp, RTT, acks, interval) |
| `PLG` | client → server | Plugin data (plugin name + metrics) |
| `ACK` | server → client | Acknowledgment |
| `CMD` | server → client | Execute a shell command on the client |
| `CMD` | server → client | Execute a shell command on the client (requires `allow_remote_command`) |
| `UPD` | server → client | Trigger self-update via `hb_install.sh` |
Value encoding: