mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
Project freshly minted installer command intent onto the stable host before returning remote config, and consume that intent once so later admin policy changes remain authoritative.
236 lines
14 KiB
Markdown
236 lines
14 KiB
Markdown
# Agent Security
|
|
|
|
Pulse agents incorporate several security mechanisms to ensure that the code running on your infrastructure is authentic and untampered with.
|
|
|
|
**Start with the least privilege that answers your monitoring question.** For
|
|
Proxmox VE, PBS, and PMG, that is usually no agent at all: API-only monitoring
|
|
with a read-only token covers inventory, status, and metrics, and the
|
|
generated setup script creates a privilege-separated monitoring user for it
|
|
(see [Proxmox Deployment Choices](#proxmox-deployment-choices)). Install a
|
|
host agent only where you want data the platform API cannot provide, and on
|
|
Linux consider the supported
|
|
[least-privilege profile](#least-privilege-agent-profile) before the root
|
|
default.
|
|
|
|
## Agent Privilege Model
|
|
|
|
Pulse's Linux/systemd installer runs the unified agent as `root` by default.
|
|
That is intentional for full host telemetry: disk SMART data, mdadm/RAID state,
|
|
temperature sensors, Docker or Podman socket reads, Proxmox host-local details
|
|
that are not available through the API, and some NAS/platform integrations
|
|
commonly require root or equivalent local privileges. On Linux/systemd hosts,
|
|
the supported alternative is the least-privilege profile documented below; it
|
|
trades the root-only collectors it has not been granted for a dedicated
|
|
non-root service user.
|
|
|
|
Treat a host agent like other infrastructure monitoring software with local
|
|
root read access:
|
|
|
|
- install it only on hosts you trust Pulse to monitor;
|
|
- keep the agent token scoped to that Pulse server;
|
|
- keep command execution disabled unless you explicitly need governed
|
|
remediation;
|
|
- update from signed release assets rather than arbitrary branch snapshots.
|
|
|
|
The agent is primarily an outbound reporter to your Pulse server. By default it
|
|
binds the health and Prometheus endpoints to `127.0.0.1:9191`, so a root agent
|
|
does not expose that HTTP surface to the network unless you explicitly opt in.
|
|
Set `--health-addr :9191` only when you intentionally scrape the agent from
|
|
another host. Use `--health-addr ""` or `PULSE_HEALTH_ADDR=off` to disable the
|
|
listener.
|
|
|
|
Generated Linux/systemd units also include conservative sandboxing such as
|
|
`NoNewPrivileges=true`, `PrivateTmp=true`, kernel/control-group write
|
|
protection, a private umask, and setuid/personality restrictions. Those
|
|
directives reduce service blast radius while keeping the filesystem and device
|
|
access needed for full host telemetry, Proxmox token setup, SMART, Docker, and
|
|
NAS integrations.
|
|
|
|
Disk monitoring normally filters pseudo-filesystems such as `tmpfs`. The local
|
|
`--disk-include` option can opt a specific device or mount point back into
|
|
capacity monitoring, for example a log2ram `/var/log` mount. This reports
|
|
filesystem capacity and usage metadata. It does not read or transmit file
|
|
contents. Local `--disk-exclude` rules still take precedence.
|
|
|
|
Command execution is disabled by default. It can be enabled with
|
|
`--enable-commands`, `PULSE_ENABLE_COMMANDS=true`, or the centralized agent
|
|
command setting after enrollment. Leave it disabled for read-only monitoring.
|
|
When enabled, commands still flow through Pulse's command policy and approval
|
|
surfaces instead of silently turning every agent into an unrestricted remote
|
|
shell.
|
|
|
|
Custom numeric sensors are a separate, local configuration boundary. Enabling
|
|
them with `--custom-sensors-file` does not enable remote commands and
|
|
`--enable-commands` is not required. The server cannot add or alter a custom
|
|
sensor command. The agent accepts only absolute executable paths with no
|
|
arguments or shell interpretation, bounds concurrency, time, and output, and
|
|
revalidates the command before every run. On POSIX systems the configuration,
|
|
commands, and immediate command directories must pass ownership, symlink, and
|
|
write-permission checks. Treat the configured executables as trusted agent
|
|
code: the service commonly runs as root, so only administrators should be able
|
|
to replace them.
|
|
|
|
Agent command tokens must be bound to a host or agent identity before command
|
|
registration is accepted. Pulse-minted install-command tokens for the generic
|
|
host and Proxmox flows are the only first-use exception: because the server
|
|
mints them before the installer knows the final hostname, Pulse binds them to
|
|
the first command agent that registers with that token. Those tokens also carry
|
|
the operator's command-policy choice into the first accepted report so a stale
|
|
policy from an earlier installation cannot immediately disable the replacement
|
|
agent. Generic unbound `agent:exec` tokens still fail closed.
|
|
|
|
## Proxmox Deployment Choices
|
|
|
|
You do not need a Pulse agent on every Proxmox-related host just to see basic
|
|
cluster inventory and utilization. Start with the least-privilege path that
|
|
answers your monitoring question:
|
|
|
|
| Goal | Recommended path | Root agent needed? |
|
|
|---|---|---|
|
|
| PVE/PBS/PMG inventory, node status, VM/container status, storage usage, and normal Proxmox API metrics | Add the Proxmox connection with a read-only or narrowly scoped API token | No |
|
|
| VM guest disk and memory details through QEMU Guest Agent | Use Proxmox API permissions such as `VM.GuestAgent.Audit` and `VM.GuestAgent.FileRead` where supported | No host agent for the Proxmox node |
|
|
| All mounted LXC filesystem capacities and usage | Install the Unified Agent on the owning PVE node; it automatically uses bounded `pct list` and `pct df` reads for running LXCs | Yes, on the PVE node |
|
|
| Docker/Podman containers inside a VM or LXC through guest-local reporting | Install the agent inside that VM/LXC with Docker/Podman monitoring enabled, or use another explicit guest access/reporting path | Usually requires root or Docker socket-equivalent access |
|
|
| Docker containers inside an LXC from a Proxmox host agent | Turn on **Discover Docker in LXC guests** in Settings → System → General (admin only), or start Pulse with `PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY=true` to lock it on; optionally limit guests with `PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS=101,102` | Requires a root/equivalent Pulse agent on the Proxmox node and explicit server opt-in |
|
|
| Host SMART, temperatures, local ZFS/Ceph/mdadm detail, arbitrary mount reads, and full host telemetry | Install the agent on that host | Yes, for the supported full-telemetry profile |
|
|
| Kubernetes node/pod monitoring from a cluster | Use the Kubernetes agent/DaemonSet profile | Depends on whether host metrics are enabled |
|
|
|
|
Inside-guest runtime visibility is explicit. Installing the agent inside a VM or
|
|
LXC authorizes that guest-local agent to report Docker/Podman monitoring data
|
|
according to its local module flags. A Proxmox node agent does not look inside
|
|
LXCs by default. Its automatic LXC filesystem collector is a node-local
|
|
capacity query only: it runs `pct list`, then `pct df <vmid>` for guests already
|
|
reported running, and reports mount keys, volume labels, mount paths, and
|
|
capacity/usage numbers. It does not run a command inside the guest or read
|
|
guest files, processes, environment, or container-runtime metadata. It skips
|
|
guests reported stopped and bounds command time, output, guest count, and disk
|
|
count.
|
|
|
|
The node agent can collect Docker container inventory from LXC guests
|
|
through `pct exec`, but only after an explicit server-side opt-in: the
|
|
admin-only **Discover Docker in LXC guests** toggle in Settings → System →
|
|
General, or starting the server with
|
|
`PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY=true` (which locks the toggle).
|
|
Inventory collection is disabled by default, can be VMID-allowlisted, and is
|
|
limited to the Docker page summary path: Docker host/runtime version, container
|
|
ID, name, image, state/status, ports, and aggregate `docker stats` counters.
|
|
It does not run `docker inspect` and does not collect guest environment values,
|
|
mount sources, container commands, files, or process details. The lighter
|
|
socket-presence hint remains separately available through
|
|
`PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION=true`.
|
|
|
|
For VMs, a Proxmox host agent still cannot see Docker/Podman inventory without
|
|
guest cooperation such as a guest-local Pulse agent, QEMU guest-agent mediated
|
|
integration, SSH, or an explicitly exposed Docker/Podman reporting endpoint.
|
|
|
|
If Proxmox API data is enough for your use case, prefer API-only monitoring and
|
|
do not install a host agent just because the installer exists. Install agents
|
|
where you need data that Proxmox cannot provide through its API, or where the
|
|
data lives inside a guest/container rather than at the Proxmox node layer.
|
|
The Settings Proxmox setup flow uses this API inventory path as the default;
|
|
the host telemetry agent path is for the full-telemetry cases above.
|
|
Generated API Inventory setup still needs a one-time privileged shell on the
|
|
Proxmox host so it can create the `pulse-monitor` account, token, and ACLs, but
|
|
steady-state monitoring uses the Proxmox API rather than a root Pulse agent.
|
|
For PVE, the generated script creates a privilege-separated API token and
|
|
mirrors the generated read/monitoring ACLs onto both the service user and the
|
|
token. For PBS, the generated script grants the `Audit` ACL to both the service
|
|
user and token.
|
|
|
|
## Least-Privilege Agent Profile
|
|
|
|
On standard Linux systemd hosts, `install.sh --least-privilege` is a supported
|
|
alternative to the root profile. It runs the service as a dedicated
|
|
`pulse-agent` system user (nologin shell, owning only its state directory and
|
|
binary), joins the `docker` group when Docker monitoring is enabled so socket
|
|
reads keep working, and keeps every hardening directive of the root unit while
|
|
dropping the LXC-attach ambient capability grant entirely.
|
|
|
|
Two optional flags restore the collectors that genuinely need elevation, each
|
|
through an exact-command sudoers grant validated with `visudo` and a
|
|
root-owned wrapper the agent is pointed at via an absolute-path-only
|
|
environment override. Because `NoNewPrivileges` blocks `sudo` entirely, a
|
|
unit with an active grant sets `NoNewPrivileges=false` while keeping the
|
|
remaining hardening; a grantless least-privilege install keeps
|
|
`NoNewPrivileges=true`. Choose grants deliberately: each one is a scoped,
|
|
auditable widening of the profile.
|
|
|
|
- `--grant-smart` allows exactly `smartctl`, restoring SMART disk health.
|
|
- `--grant-pct` allows exactly `pct list` and `pct df`, restoring Proxmox LXC
|
|
filesystem capacity. The grant deliberately excludes `pct exec`, `start`,
|
|
`stop`, and `enter`, so guest Docker inventory stays a root-profile feature.
|
|
|
|
What the profile gives up: command execution (`--enable-commands` is refused
|
|
and a later server-side enable requires reinstalling the root profile),
|
|
`pct exec` guest Docker inventory, and any platform integration that needs
|
|
device or socket access you have not granted. Core metrics, mounts, `/proc`
|
|
RAID state, hwmon temperatures, and Docker socket reads work without root.
|
|
Ungranted collectors fail soft, and the agent reports its privilege profile so
|
|
**Settings → Infrastructure → Agent Doctor** shows the service user and active
|
|
helpers instead of presenting missing collectors as a fault. Appliance
|
|
platforms (TrueNAS, Synology, QNAP, Unraid) and non-systemd init systems keep
|
|
the root profile; the installer refuses `--least-privilege` there rather than
|
|
silently falling back to root.
|
|
|
|
`--update` preserves an existing least-privilege profile and its grants
|
|
without the flags being repeated. Uninstall removes the sudoers file and
|
|
helpers; the inert system user is left behind deliberately.
|
|
|
|
## Supply-Chain Boundary
|
|
|
|
The agent self-update path is not just "download the latest binary and run it".
|
|
Release builds require checksum validation, and when trusted update keys are
|
|
embedded they also require an Ed25519 release signature before replacing the
|
|
running binary.
|
|
|
|
The initial installer is different: if you paste and run a shell command as
|
|
root, you are granting root to that installer at that moment. Prefer the
|
|
release-pinned, signature-verified server installer flow documented in
|
|
[README.md](../README.md) and [INSTALL.md](INSTALL.md), then use the agent
|
|
install command generated by your own Pulse server.
|
|
|
|
For the server installer, avoid `latest` when you want a tighter change-control
|
|
boundary. Download a specific release tag, verify the `install.sh.sshsig`
|
|
signature, and pass that same tag to `bash install.sh --version`. Agent
|
|
self-updates still verify checksum headers, and release builds require
|
|
signatures when a trusted update key is embedded.
|
|
|
|
The first automatic hop from an already-installed v5 `pulse-agent` to v6 is
|
|
performed by the v5 updater. That updater verifies TLS by default, requires the
|
|
server-provided SHA-256 checksum, validates executable magic, enforces the size
|
|
limit, and swaps atomically, but it does not yet have the v6 Ed25519 signature
|
|
requirement or downloaded-binary `--self-test`. For that migration hop, use
|
|
HTTPS or a trusted local network. In high-assurance environments, reinstall the
|
|
v6 `pulse-agent` through the signed installer path instead of relying on the
|
|
automatic v5-to-v6 first hop over plain HTTP.
|
|
|
|
## Self-Update Security
|
|
|
|
The agent's self-update mechanism is critical for security and stability. To prevent supply chain attacks or compromised update servers from distributing malicious or broken agents, Pulse employs a rigorous verification process.
|
|
|
|
### 1. Checksum Verification
|
|
The agent verifies a SHA-256 checksum of the downloaded binary. The server must provide
|
|
`X-Checksum-Sha256`; updates are rejected if the header is missing or mismatched.
|
|
|
|
### 2. Signature Verification
|
|
Release builds embed trusted Ed25519 update public keys and require
|
|
`X-Signature-Ed25519` in addition to the checksum header. Updates are rejected
|
|
when the signature is missing or does not verify against the embedded trust
|
|
root.
|
|
|
|
### 3. Pre-Flight Checks
|
|
To prevent "brick-updates"—bad updates that crash immediately and require manual recovery—agents perform pre-flight validation before replacing the running executable.
|
|
|
|
Unified agent (`pulse-agent`):
|
|
1. Download new binary.
|
|
2. Verify checksum (required).
|
|
3. Verify the Ed25519 release signature when trusted update keys are embedded.
|
|
4. Validate binary magic (ELF/Mach-O/PE) and size limits (100MB max).
|
|
5. Run the downloaded binary with `--self-test`, passing any live token through a short-lived `0600` token file rather than argv.
|
|
6. Make executable and swap atomically.
|
|
|
|
## API Security
|
|
|
|
- **Token Authentication**: All agent-to-server communication requires a valid API token.
|
|
- **TLS**: Encrypted by default (unless specifically disabled).
|
|
- **Network Isolation (optional)**: The agent control plane can be served on a dedicated, separately firewalled port. It exposes the bounded report/config, command WebSocket, version, and bootstrap routes needed for the full agent lifecycle, but not the web UI or management API. See [Split-Port Agent Ingest](CONFIGURATION.md#split-port-agent-ingest-network-isolation).
|