feat: signed release manifest + fail-closed binary distribution
No unsigned binary path: build refuses when signing is disabled, downloads return 404 when no signed package resolves. Signed release manifest endpoint, installer verifies manifest signature and pins binary hash, Windows token mandatory, Rust helper verify-binary.
This commit is contained in:
parent
d18b0f8a02
commit
1c18a6b55a
10 changed files with 492 additions and 7022 deletions
33
README.md
33
README.md
|
|
@ -102,8 +102,9 @@ curl -sfL https://your-server.com/install | sudo bash -s -- your-registration-to
|
|||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
iwr https://your-server.com/install.ps1 | iex
|
||||
& ([scriptblock]::Create((iwr -useb https://your-server.com/install.ps1).Content)) -Token your-registration-token
|
||||
```
|
||||
The token is required — the installer will not register without it. (`iwr ... | iex` cannot pass arguments; the scriptblock form is how PowerShell hands the token to the fetched script, the equivalent of Linux's `bash -s -- token`.)
|
||||
|
||||
**macOS (curl):**
|
||||
```bash
|
||||
|
|
@ -277,6 +278,34 @@ Remove-Item "C:\ProgramData\RedFlag\config.json"
|
|||
5. Agent verifies signature + nonce + timestamp before execution
|
||||
6. All updates have checksum verification + rollback on failure
|
||||
|
||||
### Binary Integrity: Closing the Cold-Start Gap
|
||||
|
||||
The update pipeline runs as root and executes downloaded binaries. That makes the
|
||||
binary itself a trust boundary, not just the transport. RedFlag verifies the agent
|
||||
binary at every stage of its life:
|
||||
|
||||
- **Install time (cold start).** The installer fetches a signed *release manifest* —
|
||||
one Ed25519-signed document listing the expected SHA-256 of every released binary
|
||||
per platform/architecture, signed with the same key the agent trusts. Before it
|
||||
executes anything, the installer verifies the manifest signature and confirms the
|
||||
downloaded binary's hash matches the manifest entry. Any mismatch removes the
|
||||
binary and aborts. This closes the gap where a first install would run an
|
||||
unverified binary.
|
||||
- **Upgrade time.** Self-upgrade verifies SHA-256 + Ed25519 signature before the
|
||||
atomic binary swap, with rollback to the previous binary on failure.
|
||||
- **Runtime.** A small, privileged, network-less Rust executor — separate from the Go
|
||||
agent and outside its trust boundary — re-verifies the agent binary's hash. A
|
||||
mismatch stops command issuance and surfaces in the Security Health panel alongside
|
||||
nonce and machine-binding violations.
|
||||
|
||||
**The two-binary payoff.** To run unsigned code on a managed host, an attacker now has
|
||||
to replace *both* the Go agent and the Rust executor — atomically, while the executor
|
||||
is actively watching the agent, before the next check-in reaches the server. That is a
|
||||
different class of problem than swapping a single binary: it takes local root with
|
||||
precise timing, or a compromise of the build-and-signing infrastructure itself — which
|
||||
the capability-token gate already addresses. The defense is layered on purpose, and the
|
||||
layers are independent.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
|
@ -384,7 +413,7 @@ Remove-Item "C:\Program Files\Aggregator\*" -Recurse -ErrorAction SilentlyContin
|
|||
Remove-Item "C:\ProgramData\Aggregator\*" -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
# Then install new agent
|
||||
iwr https://your-server.com/install.ps1 | iex
|
||||
& ([scriptblock]::Create((iwr -useb https://your-server.com/install.ps1).Content)) -Token your-registration-token
|
||||
```
|
||||
|
||||
### Full Fresh Install (Clean State)
|
||||
|
|
|
|||
Loading…
Reference in a new issue