73 lines
3.4 KiB
Markdown
73 lines
3.4 KiB
Markdown
# VPS Agent
|
|
|
|
The agent is a Rust system daemon with two Tokio workers, no database, no metric
|
|
history, and no periodic polling. It exposes authenticated semantic JSON APIs and
|
|
never accepts commands or shell fragments.
|
|
|
|
## Build and test
|
|
|
|
```bash
|
|
cargo fmt --manifest-path Cargo.toml --check
|
|
cargo clippy --manifest-path Cargo.toml --all-targets -- -D warnings
|
|
cargo test --manifest-path Cargo.toml
|
|
cargo build --manifest-path Cargo.toml --release
|
|
```
|
|
|
|
The release profile enables thin LTO, strips symbols, uses one codegen unit, and
|
|
aborts on panic. Output is one binary at `target/release/vps-agent`.
|
|
|
|
## Configuration and permissions
|
|
|
|
Use `config/config.toml` as the reference. Configuration belongs at
|
|
`/etc/vps-agent/config.toml`, owned `root:vps-agent` mode 0640. The application
|
|
token belongs at `/etc/vps-agent/credentials/control-plane.token`, owned by the
|
|
`vps-agent` service user and mode 0600.
|
|
Generate at least 32 random bytes. Do not put credentials in TOML.
|
|
|
|
The default bind is loopback. For remote use, select only the VPS WireGuard IP;
|
|
an unspecified address is rejected. TLS can be enabled with PEM certificate/key
|
|
paths. WireGuard plus the application token is the recommended v1 mode.
|
|
|
|
## API
|
|
|
|
Send `Authorization: Bearer TOKEN` on every `/v1` request. `/healthz` is public
|
|
and returns liveness only. The complete route table is in
|
|
`../docs/architecture.md`. Limits default to 64 KiB bodies, 32 concurrent calls,
|
|
five-second requests, 10 read rps, 2 write rps, and burst 20.
|
|
|
|
System data comes from `/proc` and `/sys`; fixed `ip`, `nft`/`iptables`,
|
|
`fail2ban-client`, `wg`, and `systemctl` invocations augment it. User input is
|
|
never part of a command string. Jail names and IPs are validated, and systemd
|
|
names must exactly match the configuration allow-list.
|
|
|
|
`/v1/logs` is disabled by default. When enabled, it streams bounded,
|
|
cursor-paginated JSON from `journalctl` for `[logs].allowed_units` only. The
|
|
backend polls and retains these rows; Grafana never calls the endpoint. The
|
|
`vps-agent` user normally needs membership in `systemd-journal`. That membership
|
|
widens what the local process can read, so the installer does not add it and an
|
|
administrator must opt in after review.
|
|
|
|
## systemd and privilege
|
|
|
|
Install `packaging/vps-agent.service`. It caps memory at 64 MiB, CPU at 10%,
|
|
tasks at 64, removes all capabilities, sets `NoNewPrivileges`, and enables
|
|
filesystem/kernel/namespace hardening. The agent logs to journald and does not
|
|
write successful polls or metrics to disk.
|
|
|
|
Read APIs work unprivileged on ordinary supported distributions. Process owners
|
|
may be absent due to `/proc` permissions. Fail2Ban changes and service restarts
|
|
will return an error unless the administrator adds narrowly scoped polkit/sudo
|
|
policy; the installer deliberately grants none. Firewall is read-only in v1.
|
|
|
|
## Lifecycle and troubleshooting
|
|
|
|
Use `../scripts/vps-agentctl.sh` for install, update, rollback, and removal. Check
|
|
`systemctl status vps-agent` and `journalctl -u vps-agent --since today`. Validate
|
|
the bind address, token permissions, WireGuard route, and executable availability.
|
|
Rotate a token atomically and restart both ends as described in the security doc.
|
|
|
|
Resource targets are <=30 MiB idle RSS, near-zero idle CPU and no steady disk
|
|
writes. Measure on the target distribution with `systemctl status`,
|
|
`systemd-cgtop`, and `/proc/PID/status`; macOS development builds are not a valid
|
|
resource benchmark.
|