source: advance the checked projection
Source-Sha: 194a59adfd6c901635da7a9c18253bb2210e2426 Policy-Sha: b9b0be0ee74e55f561a803b8aef6be3c2134f5a83b46816a069a372f2eb04f4d Tree-Digest: 4697f2e281c1c6ecafff17cf6eeb391a0a1ac0fa6e5fc429f5b473f4579c252c
This commit is contained in:
parent
0401c76c79
commit
ffdccba10f
42 changed files with 2516 additions and 554 deletions
|
|
@ -43,7 +43,7 @@ web/src/
|
|||
│ ├── polling.ts # POLL.* constants — all intervals centralized
|
||||
│ ├── store.ts, queryParser.ts, vulnerabilities.ts
|
||||
│ └── client-logger.ts # Client errors ship to the server log (ETHOS #1)
|
||||
├── desktop/ # Tauri tray-app variant (vite.desktop.config.ts)
|
||||
├── desktop/ # Retained legacy Tauri web shell; not RedFlag Desktop
|
||||
└── types/ # Shared TS types mirroring server models
|
||||
```
|
||||
|
||||
|
|
@ -65,6 +65,10 @@ web/src/
|
|||
- Mobile layout usable, not optimized
|
||||
- Several UI coverage gaps tracked as `UI-*` tasks (not architecture — task tier)
|
||||
|
||||
The native RedFlag Desktop is the Qt/QML component described in
|
||||
[components/05-desktop](05-desktop.md). `web/src/desktop` is retained legacy source, not a
|
||||
second supported Desktop implementation or an architectural authority.
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-14*
|
||||
*Last reviewed: 2026-09-14*
|
||||
|
|
|
|||
|
|
@ -20,6 +20,28 @@ On Linux the agent invokes it through `sudo systemd-run --wait --property=Protec
|
|||
|
||||
### Windows Invocation (SEC-030, decided 2026-07-01)
|
||||
|
||||
**Current source boundary:** the native Windows payload now builds the Rust Helper. It
|
||||
answers its version and supports verification-only binary and `MutationEnvelope` checks.
|
||||
Windows-only source now opens each trust-path component without following reparse points,
|
||||
checks SYSTEM/Administrators ownership and denies write grants to other principals, rejects
|
||||
hard-linked trust files, and creates write-through, one-shot authorization claims. The fixed
|
||||
task dispatcher reads an envelope from its protected exchange, pins the installed authority
|
||||
placement, and uses the shared verifier. The wrapper carrying that envelope is decoded
|
||||
before the envelope itself, so a request the Helper could read but whose envelope it could
|
||||
not is refused under its own validated request id instead of leaving the caller an unknown
|
||||
outcome; a wrapper too malformed to establish an identity still writes no result, because
|
||||
the Helper does not invent one. A shared Go/Rust fixture pins that wrapper on both sides.
|
||||
The Agent reaches this path read-only through `redflag-agent -verify-envelope`, which mints
|
||||
nothing, admits no backend, and passes no command or path to the privileged process.
|
||||
Windows mint and backend execution still return typed refusals.
|
||||
|
||||
Native acceptance of the *installed* rail — ACL enforcement, service identity, task
|
||||
definition, and exchange roots — remains open: the transport is covered by tests, not by a
|
||||
witness on a provisioned host. Replay refusal is likewise open and deliberately unexercised,
|
||||
because verification must not consume an authorization; the claim belongs at an execution
|
||||
boundary that no admitted backend has yet. Low-privilege service migration and WUA / Winget
|
||||
executors remain required before mutation authority is enabled.
|
||||
|
||||
Windows has no `systemd-run` equivalent for spinning up an ad-hoc transient privileged unit, so the helper is invoked through a **Scheduled Task, configured to run once as SYSTEM**. The agent's own service account has no standing right to mutate anything — it only holds a delegated "AllowedToRun" ACE on this one task definition (`schtasks /run /tn RedFlagHelper`), the direct Windows analogue of the Linux sudoers line that grants exactly one `systemd-run` invocation and nothing else.
|
||||
|
||||
Two elevation-model alternatives were considered and rejected:
|
||||
|
|
@ -28,6 +50,54 @@ Two elevation-model alternatives were considered and rejected:
|
|||
|
||||
The task definition itself is provisioned **at agent-install time** by the (already-elevated) install script, the same moment Linux drops its sudoers entry — not self-provisioned by the agent on first gated operation, which would just relocate the "who grants the first elevation" problem rather than solve it.
|
||||
|
||||
`installer/windows/provision-helper.ps1` now prepares that installer-owned boundary. It
|
||||
creates one triggerless `\RedFlag\Helper` task with a fixed Helper path and `run-request`
|
||||
argument, grants the Agent service SID read/run rather than task mutation, and places the
|
||||
Agent-writable request child beneath a non-Agent-writable Helper root beside SYSTEM-owned
|
||||
trust, replay, and result directories. The staged
|
||||
payload validates this description but does not run the provisioning script.
|
||||
|
||||
`run-request` accepts no command-line arguments. Its machine exchange is fixed at
|
||||
`C:\ProgramData\RedFlag\helper`; the installer refuses alternate data roots for this
|
||||
transport. It opens each component relative to a retained directory handle, rejects
|
||||
reparse points and hard-linked files, bounds input size, and denies concurrent write/delete
|
||||
sharing while reading. The RedFlag and Helper roots, authority file, and result directory
|
||||
must pass owner/DACL checks. A handle lock serializes dispatcher instances.
|
||||
|
||||
The Agent writes `requests\pending.json`: `version`, a fresh UUID `request_id`, an action
|
||||
(`verify-envelope` or `execute-envelope`), and the unchanged `envelope`. Unknown envelope
|
||||
fields and duplicate struct fields are refused. Installer-owned `authority.json` contains
|
||||
`version`, `target_id`, `authority_kind` (`standalone` or `fleet-server`), `authority_id`, and
|
||||
`public_keys` (Ed25519 hex). This is local trust configuration. Provisioning accepts it
|
||||
through `-AuthorityFile`, but refuses to replace an existing different authority; enrollment
|
||||
and rotation need their own explicit transaction.
|
||||
|
||||
The result is a create-new `results\<request_id>.json` containing `version`, `request_id`,
|
||||
`envelope_verified`, and the common joined `receipt`. Read-only verification reports
|
||||
`decision=verified`, `executed=false`, and zero verified backend actions. Backend execution
|
||||
currently reports `backend_not_migrated`; neither path consumes replay authorization.
|
||||
Protected results are flushed and closed before the Agent can read them. Results stay as
|
||||
local evidence; retention and timed-out request reconciliation remain follow-up work.
|
||||
|
||||
The Agent's Windows envelope executor triggers only `\RedFlag\Helper`, serializes its one
|
||||
pending slot across processes, validates the full receipt join, and leaves an unresolved
|
||||
slot intact after task-start failure, timeout, cancellation, or an invalid response. Task
|
||||
start never counts as execution success. A later call first reads any pending request and
|
||||
its protected result. A valid joined result retires the slot and is returned when the caller
|
||||
retried the same action, authorization, and manifest; a different call may proceed while the
|
||||
old result stays as evidence. Missing, malformed, or mismatched results keep the slot blocked
|
||||
with its request ID for reconciliation. Windows standalone mint explicitly refuses until
|
||||
fresh StepUp exists. New Agent service installs select the virtual
|
||||
`NT SERVICE\RedFlagAgent` account and enable its service SID. Helper provisioning grants
|
||||
that SID modify access to Agent state/logs, modify access to the untrusted request directory,
|
||||
read access to results, and no access to authority/replay state. Existing SYSTEM services
|
||||
use the explicit `-MigrateAgentService` transaction: enable the service SID, prepare ACL/task
|
||||
ownership while LocalSystem is still available, stop the service, change its account, verify
|
||||
SCM state, and restore its prior running state. Only an observed LocalSystem service is
|
||||
migrated; another operator-owned identity is refused. If the new identity cannot start, the
|
||||
script restores LocalSystem and its prior running state while retaining the prepared narrow
|
||||
ACLs for a later retry. Native transport acceptance remains open.
|
||||
|
||||
The ACL lockdown described here is the v1 cut, not the final word — Casey's call ("that'll do for now"). Revisit if a real gap in the ACE-delegation model surfaces (see `docs/tasks/SEC-030-windows-privileged-mutation-helper.md` Open Questions for what's still unresolved: WUA's COM-driven install path, rollback ownership parity with Linux's `.bak` handling).
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -16,7 +16,12 @@ RedFlag's supply chain gate inverts the traditional defense model: instead of **
|
|||
token. Standalone pacman approval uses a signed `MutationEnvelope`, exact closure archives,
|
||||
detached package signatures, helper custody, and a joined receipt. Docker, Winget, and
|
||||
Windows Update still use the signed-command path. Fleet pacman envelope delivery and kernel
|
||||
enforcement against out-of-band root mutation are not wired.
|
||||
enforcement against out-of-band root mutation are not wired. The native Windows Helper
|
||||
can verify binaries and mutation envelopes but refuses mint and backend execution. A fixed
|
||||
task transport now joins Agent requests to Helper results using handle-relative custody and
|
||||
installer-owned authority selection. It has no admitted Windows mutation backend and lacks
|
||||
native acceptance; service privilege migration and execution remain open. Verification does
|
||||
not consume replay authority. See [Helper invocation](../components/04-helper.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -51,12 +56,12 @@ the unprivileged agent-side consumer. Its *role* as a runtime allow/deny RPC is
|
|||
|
||||
### Why this model (two tests it has to pass)
|
||||
|
||||
**Cross-platform.** Linux eBPF and macOS ESF can pause an exec and ask a daemon "may this
|
||||
proceed?" Windows WDAC cannot — it is signature-based, with no runtime callback. A
|
||||
decision-daemon model therefore has no Windows mapping. A capability token maps onto all
|
||||
three identically: in every case the enforcement layer only needs to answer "is this
|
||||
execution authorized," and a verified token + a privileged executor that the OS trusts is
|
||||
platform-agnostic. We build for the platform with the tightest constraint.
|
||||
**Cross-platform.** Linux eBPF and macOS ESF can observe or mediate execution at a kernel
|
||||
boundary. Windows App Control (WDAC) instead decides which code may execute from policy; it
|
||||
does not authorize one package transaction or protect every file and registry mutation that
|
||||
an already-trusted process can make. The common primitive is the signed mutation
|
||||
authorization consumed by a privileged executor. Platform enforcement narrows bypasses
|
||||
around that executor, but each platform must state exactly what its kernel mechanism covers.
|
||||
|
||||
**Protects people.** Protection comes down to where the trust root lives. The signing key
|
||||
lives at the server (the human-approval authority), off the host. An attacker who fully owns
|
||||
|
|
@ -76,12 +81,15 @@ The two tests converge on the same answer, which is the signal it's right.
|
|||
| Platform | Enforcement Mechanism | What It Blocks |
|
||||
|----------|----------------------|----------------|
|
||||
| **Linux** | eBPF (syscalls/sys_enter_execve) or AppArmor | apt, dnf, yum, pip, npm, bun, docker (CLI) |
|
||||
| **Windows** | WDAC (Windows Defender Application Control) | winget, npm.cmd, pip.exe, choco, scoop |
|
||||
| **Windows** | App Control (WDAC) for executable trust; RedFlag kernel driver for covered mutation transactions | Untrusted mutation code; covered file/registry writes when the driver exists |
|
||||
| **macOS** | Endpoint Security Framework (ESF) | brew, pip, npm, bun, cargo |
|
||||
|
||||
**Target distinction:** kernel enforcement sits below userspace wrappers. The current eBPF
|
||||
scaffold is not connected to the capability model, so RedFlag does not yet claim that an
|
||||
out-of-band package-manager invocation is blocked.
|
||||
**Target distinction:** App Control and transaction enforcement are different Windows
|
||||
layers. WDAC constrains executable trust; it does not prove that trusted code performed an
|
||||
authorized RedFlag transaction. SEC-030 owns the driver and transaction context needed to
|
||||
deny covered file and registry mutations outside a verified envelope. Neither that driver
|
||||
nor the current Linux eBPF scaffold is connected to the capability model, so RedFlag does
|
||||
not yet claim kernel-level blocking on either platform.
|
||||
|
||||
### Trust Chain
|
||||
|
||||
|
|
@ -252,9 +260,10 @@ The token extends the existing Ed25519 infrastructure rather than introducing ne
|
|||
verifies the local Arch keyring, and refuses downgrades or operation-name lies. Legacy
|
||||
registry entries without local paths are not rehashed. The current unit is not
|
||||
network-isolated.
|
||||
- **Kernel layer (where applicable).** Linux eBPF / Windows WDAC / macOS ESF deny
|
||||
package-manager execution except via the trusted executor. This is defense-in-depth design;
|
||||
the present eBPF scaffold is not wired to the capability model.
|
||||
- **Kernel layer (where applicable).** Linux eBPF and macOS ESF mediate covered execution.
|
||||
Windows WDAC constrains executable trust, while a separate RedFlag driver is required for
|
||||
covered transaction writes. This is defense-in-depth design; none of these transaction
|
||||
enforcement paths is wired to the capability model today.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -283,9 +292,10 @@ runtime evidence support them.
|
|||
rotated, replicated, or held by a federation/guild node without touching the agent↔helper
|
||||
interface. This is the long-term cross-platform answer hidden in a one-line design choice.
|
||||
5. **Kernel stops are defense-in-depth, not a prerequisite.** The capability model protects on
|
||||
a host where eBPF/WDAC/ESF cannot be deployed (locked-down managed box, constrained
|
||||
container). Kernel enforcement raises the cost of bypass; it does not gate whether the model
|
||||
means anything. Partial deployment still moves a host out of the soft-target category.
|
||||
a host where eBPF, a Windows transaction driver, or ESF cannot be deployed (locked-down
|
||||
managed box, constrained container). Kernel enforcement raises the cost of bypass; it does
|
||||
not gate whether the model means anything. Partial deployment still moves a host out of the
|
||||
soft-target category.
|
||||
6. **No doctrinal knobs.** Signing required and forward-only (no downgrade) are ETHOS doctrine,
|
||||
not configurable. The token has no "skip verification" path.
|
||||
|
||||
|
|
@ -476,8 +486,9 @@ and kernel adapters remain.
|
|||
|
||||
## Footer: Assumptions & Connections
|
||||
|
||||
**Assumption:** The capability model is the floor; kernel-level primitives (eBPF, WDAC, ESF)
|
||||
are defense-in-depth on top, not a prerequisite. Userspace wrappers alone are bypassable.
|
||||
**Assumption:** The capability model is the floor; kernel-level transaction primitives
|
||||
(eBPF, a Windows driver, ESF) are defense-in-depth on top, not a prerequisite. WDAC is a
|
||||
separate executable-trust layer. Userspace wrappers alone are bypassable.
|
||||
|
||||
**Current:** The privileged executor has a narrow argv-only API, no shell, and a stripped
|
||||
environment; the Agent that hands it capabilities is unprivileged and holds no signing key.
|
||||
|
|
@ -494,6 +505,4 @@ the helper unit. Neither property applies globally until each migrated backend p
|
|||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-09-01*
|
||||
|
||||
*Last reviewed: 2026-08-26*
|
||||
*Last reviewed: 2026-09-14*
|
||||
|
|
|
|||
Loading…
Reference in a new issue