Add typed agent privilege helper

This commit is contained in:
Pulse Test 2026-08-29 22:49:28 +01:00
parent 3099085475
commit 6d4ee48000
44 changed files with 3267 additions and 22 deletions

View file

@ -6644,6 +6644,41 @@ server can present the profile descriptively. Uninstall removes the sudoers
file and helpers. `scripts/installtests/install_sh_test.go`
(`TestInstallSHLeastPrivilegeProfile`) pins the profile's invariants.
### Typed local privilege helper boundary
`cmd/pulse-agent-helper/main.go` and `internal/agenthelper/` own the Linux
collector's replacement for command-shaped sudo wrappers. The helper is a
standalone root process with no Pulse URL, API credential, outbound transport,
or generic command/path operation. It accepts exactly one protocol-v1 request
per local Unix-socket connection, uses a four-byte big-endian JSON frame with a
64 KiB request ceiling and 10 MiB response ceiling, rejects unknown fields,
trailing JSON, unsupported versions, and expired deadlines, and returns typed
errors correlated by request ID.
Admission is local and fail closed: Linux peer credentials must resolve to the
configured collector UID before dispatch. The registry exposes only named,
versioned operations whose providers own all executable paths and arguments.
The first collection operations are `smart.snapshot.v1` and
`proxmox.lxc_filesystems.v1`; callers cannot supply a binary path, arbitrary
filesystem path, environment, or command arguments. A health/capabilities
operation reports protocol and operation availability without widening the
allow-list. Provider execution inherits a bounded request context. Audit hooks
receive operation, request ID, peer identity, duration, outcome, and response
size metadata only, never collected payloads or secrets.
The packaged service listens on `/run/pulse-agent/helper.sock` with
`root:pulse-agent` ownership and mode `0660`. Its systemd sandbox is root-owned,
networkless (`PrivateNetwork=true`, `RestrictAddressFamilies=AF_UNIX`), and
retains `NoNewPrivileges=true`, `ProtectSystem=strict`, and `ProtectHome=true`.
It must not use `PrivateDevices=true`, because SMART collection requires access
to host block devices. Helper absence, incompatibility, or an unavailable
operation degrades only the affected telemetry and must never reactivate a sudo
wrapper, Docker-group membership, ambient capability, or root collector.
The rate-limited public `/download/pulse-agent-helper?arch=linux-*` endpoint is
the only server delivery path: it rejects non-Linux targets, serves the exact
separate helper artifact with checksum plus detached signatures, and requires a
published signed release asset when no local binary exists.
### Command dispatch is context-honest and abandoned executions are canceled
The agent command transport now refuses to dispatch work its caller has

View file

@ -20,6 +20,15 @@
## Purpose
The Unified Agent helper artifact has a distinct rate-limited public download
contract at `GET|HEAD /download/pulse-agent-helper?arch=linux-*`. The route
rejects missing and non-Linux architectures, never substitutes the collector
binary, and returns the same checksum, Ed25519 signature, and SSH signature
headers used by the signed agent installer boundary. Development builds fail
with an exact local build command when the helper is absent; published builds
may proxy only the exact versioned `pulse-agent-helper-linux-*` release asset
and its two signature sidecars.
The API runtime is decomposed along production domain boundaries so Go can
compile and execute domain qualification packages concurrently. Shared tenant
identity and scope enforcement live in `internal/api/apicontext/` and

View file

@ -110,6 +110,19 @@ PVE profile; fresh monitoring PVE, PBS/PMG, and least-privilege units retain no
such ambient capabilities. Windows service replacement recovers the same
authority marker from the existing service command line before reinstalling it.
The safe Linux collector profile packages `pulse-agent-helper` as a separate
root-owned binary and activates it through a root-owned systemd socket/service
pair. `/run/pulse-agent/helper.sock` is `root:pulse-agent` mode `0660`; the
service has no Pulse URL or token and is sandboxed with `PrivateNetwork=true`,
`RestrictAddressFamilies=AF_UNIX`, `NoNewPrivileges=true`,
`ProtectSystem=strict`, and `ProtectHome=true`. `PrivateDevices=true` is
forbidden because it would hide the block devices required for SMART telemetry.
The collector binary and unit remain root-owned and non-writable by the service
account, while mutable state is confined to the collector-owned state
directory. Ordinary updates preserve an installed profile and may not migrate a
legacy root/full-trust service implicitly; safe-profile migration, health
verification, and rollback are explicit transactions.
Published exact-version install and rollback guidance must preserve the server
and Unified Agent installer boundary. Supported systemd and Proxmox LXC
deployments use the signed `/bin/update --version vX.Y.Z` server helper;

View file

@ -1295,6 +1295,7 @@
"contract": "docs/release-control/v6/internal/subsystems/agent-lifecycle.md",
"owned_prefixes": [
"internal/agentexec/",
"internal/agenthelper/",
"internal/agenttarget/",
"internal/agentupdate/",
"internal/hostagent/",
@ -1302,6 +1303,7 @@
],
"owned_files": [
".github/workflows/unified-agent-native.yml",
"cmd/pulse-agent-helper/main.go",
"cmd/pulse-agent/main.go",
"cmd/pulse-agent/service_windows.go",
"frontend-modern/src/api/agentProfiles.ts",
@ -1389,6 +1391,23 @@
"exact_files": [],
"require_explicit_path_policy_coverage": true,
"path_policies": [
{
"id": "agent-privilege-helper-runtime",
"label": "typed no-network agent privilege helper proof",
"match_prefixes": [
"internal/agenthelper/"
],
"match_files": [
"cmd/pulse-agent-helper/main.go"
],
"allow_same_subsystem_tests": true,
"test_prefixes": [
"internal/agenthelper/"
],
"exact_files": [
"cmd/pulse-agent-helper/main_test.go"
]
},
{
"id": "patrol-autonomy-colima-real-lab",
"label": "RG-06 server-owned limited-unattended-autonomy Colima proof",
@ -1589,8 +1608,9 @@
"internal/hostagent/docker_lifecycle_test.go",
"internal/hostagent/issue1595_sas_collection_test.go",
"internal/hostagent/observer_delivery_test.go",
"internal/hostagent/package_updates_test.go",
"internal/hostagent/send_report_test.go",
"internal/hostagent/package_updates_test.go",
"internal/hostagent/privilege_helper_client_test.go",
"internal/hostagent/send_report_test.go",
"internal/hostagent/smartctl_standby_guard_test.go",
"internal/hostagent/storage_cleanup_test.go",
"internal/hostagent/unraid_test.go",

View file

@ -36,6 +36,15 @@ independent ceiling: a credential with `agent:exec` cannot promote a service
installed as `monitoring-only`, and Agent Doctor must warn when such excess
scope remains attached to that runtime.
Privileged host telemetry crosses only the local typed helper boundary owned by
`internal/agenthelper/` and `cmd/pulse-agent-helper/main.go`. That root process
has no network or Pulse credential, admits only the configured collector UID
through Linux peer credentials, and exposes named versioned collection
operations rather than command, path, argument, environment, or shell input.
Protocol decoding is bounded and strict, request deadlines are mandatory, and
audit records contain metadata only. SMART and Proxmox LXC filesystem payloads
remain local collector data and must never be copied into helper audit output.
Own Pulse's canonical privacy disclosures, outbound usage-data boundary,
and the security-facing settings surfaces that expose authentication posture,
token-management visibility, and privacy controls to operators. Customer-facing

View file

@ -21,6 +21,11 @@
## Purpose
The shared `internal/api/` helper-binary download route is isolated from
storage and recovery semantics. `/download/pulse-agent-helper` serves a signed
Linux runtime artifact only; it cannot read, mutate, restore, export, or select
storage state, recovery timelines, backup providers, or repository scope.
First-run authentication always writes the canonical `.env` persistence
artifact before runtime state changes. Root systemd installation may also
write a service override, but reload, restart, backup, and recovery remain