feat(windows): add recoverable existing-agent credential upgrade

This commit is contained in:
lussierj committed 2026-10-14 13:50:19 +00:00
1 parent 3cfb0d1abe
commit 97a0139e3d
25 files changed
+11122 -108

No files matched your search

+124 -3
View File
@@ -74,9 +74,122 @@ credentials in screenshots, chat, tickets, logs, or source control.
### Existing Agent upgrade
For an already enrolled/configured Agent, do not run the enrollment
bootstrapper. Copy the new canonical Agent files to a temporary directory and
run install.ps1 from an elevated Windows PowerShell session.
An existing legacy Agent must receive a per-Agent credential before the global
shared-secret compatibility path can be retired for its identity. In Agent
Management, use **Upgrade credential** on that Agent. Download the resulting
Windows package, transfer it only to that Agent computer, and run:
.\Upgrade-MinerOpsAgentCredential.ps1
The authorization is bound server-side to the existing Agent ID and its
current Site and Agent Zone. The script validates the local Agent ID, upgrades
the canonical runtime, stops the main Agent task, generates the permanent
secret locally, and recoverably replaces only the credential and durable routing
fields in `agent-config.json` using protected same-directory pending and prior
names. Existing API3 credentials, polling settings,
network ranges, state, logs, and backups are preserved. Before any secret is
written, the script creates and verifies a protected recovery file containing
the original Scheduled Task state, exact transition token, and exact locally
generated credential. The Hub accepts an ambiguous retry only for that same
token and credential.
The Agent directory is a filesystem security boundary. The transition rejects
junctions/reparse points, verifies trusted ancestors, denies standard users
create/delete/rename rights, and revalidates the boundary before each rename.
If an existing Agent directory is discovered insecure, the script first writes
`C:\MinerOpsAgent.minerops-filesystem-trust.blocked-<random>.state` under the trusted
parent, then hardens the directory only to contain the incident. The blocker is
outside the contaminated namespace because changing a DACL does not revoke
file or directory handles opened before repair. The same process never redeems
a token, creates or activates a permanent credential, runs the installer, or
removes that blocker. Ordinary second, third, and later invocations remain
blocked even when every current ACL looks correct.
Recovery, pending, replacement remnants, and late-arriving artifacts are
non-authoritative while the external blocker exists. Known transition state is
quarantined without parsing. A correct filename, syntax, or ACL cannot promote
it on a later run. Invalidate any possibly exposed authorization and retain or
remove evidence only under the approved incident procedure.
### Filesystem trust-epoch recovery
Use this procedure only after confirming the blocker was created by the
containment event and preserving required evidence:
1. Do not delete or edit the external blocker. Do not run an ordinary retry.
2. Use **Restart** to perform a full Windows kernel boot. Shutdown with Fast
Startup is not sufficient: Microsoft documents Fast Startup as hibernating
and restoring kernel session 0. The script identifies the kernel epoch by
the Event Log record anchor of the newest
`Microsoft-Windows-Kernel-Boot` event 27 whose `BootType` is exactly `0`.
Provider, event ID, record ID, and supported event version come from typed
EventRecord properties; namespace-aware XML selection reads only the
`BootType` payload. The new full-boot record ID must be strictly greater
than the stored record ID. Equality, an older/restored record, log clearing
or numbering reset, missing history, duplication, malformed XML, and an
unsupported provider schema all fail closed and require safe operator
intervention. There is no timestamp fallback. The anchor is unchanged by
clock or timezone changes, sleep, hibernation, and Fast Startup. See
Microsoft's [system power-state documentation](https://learn.microsoft.com/windows/win32/power/system-power-states)
and [Fast Startup support guidance](https://learn.microsoft.com/troubleshoot/windows-client/setup-upgrade-and-drivers/fast-startup-causes-system-hibernation-shutdown-fail).
3. For a credential transition, use a fresh transition package if any recovery,
pending, remnant, quarantine, or internal blocker existed. Remove those
artifacts only under the approved incident procedure after invalidating the
old authorization.
4. From the reviewed package, run:
.\Upgrade-MinerOpsAgentCredential.ps1 `
-ApproveFilesystemTrustResetAfterReboot
The script verifies that Windows boot identity changed, rescans the protected
namespace, positively verifies and stops the expected Scheduled Task, and asks the
installer to replace managed runtime without backing up or restoring any
pre-reset runtime. The installer leaves both the Agent task and external
blocker untouched after publication; it has no blocker-deletion path. Before
its first fallible stop/install step, the parent durably records the strictly
validated original task intent as `reset-authorized`; after installer
success it advances the blocker to `runtime-published`. It then verifies the
config against the package/server identity, commits the credential, and
restores the recorded task intent. Only the parent removes the blocker,
after active config and final task state have both been positively verified
and non-recovery transition cleanup has succeeded. Protected recovery stays
until after blocker deletion so an interrupted final cleanup remains safe.
5. For a genuinely new enrollment, preserve evidence and use the approved
protected-recreation incident procedure to remove the contaminated namespace
and its external blocker, then let `Enroll-MinerOpsAgent.ps1` stage a new
protected directory. Enrollment has no in-script blocker-deletion path; its
explicit compatibility switch cannot override a retained blocker.
The explicit switch without a strictly newer canonical full-kernel-boot anchor
fails.
Any suspicious transition artifact, failed runtime publication, failed
credential convergence, invalid final task, or interrupted cleanup retains the
blocker. Runtime publication alone never authorizes deletion. Protected
directory recreation is the alternative when the retained namespace cannot be
verified.
If the script is interrupted after the Hub commits, rerun the same extracted
package without deleting or editing `agent-credential-transition.recovery.json`
or `agent-config.json.pending`. A protected, matching pending config is resumed;
an insecure, malformed, or mismatched artifact fails closed. The original task
intent is durable: an Agent which was running before the first attempt ends
running, while an intentionally stopped Agent remains stopped. Success is not
reported and transition artifacts are not removed until both the active config
and expected final task state are verified. The verifier requires exactly one
enabled root `MinerOpsAgent` task, the SYSTEM service-account/highest principal,
the installed Windows PowerShell action and service-script arguments, and an
exact final state of `Running` for running intent or `Ready` for stopped intent.
Missing, query failure, access denied, malformed, multiple, disabled, queued,
unknown, wrong-action, and wrong-principal results all fail closed and retain
recovery.
After a successful transition, the existing Agent ID, Site, and Zone are
unchanged. Explicit Agent ID plus the new per-Agent secret is required, and the
legacy shared secret is permanently disabled for that identity.
For later code-only upgrades of an Agent that already has a per-Agent
credential, copy the new canonical Agent files to a temporary directory and run
install.ps1 from an elevated Windows PowerShell session.
The installer copies only managed runtime code to C:\MinerOpsAgent and
preserves agent-config.json, agent-state.json, logs\, backups, probe output,
@@ -90,6 +203,14 @@ again. A previously stopped Agent remains stopped. Before replacing runtime
files, the installer copies them to a timestamped backup under
C:\MinerOpsAgent\backups.
Runtime sources, installed destinations, active config, and rollback copies are
rejected when they are reparse points or have more than one hard link. A normal
trusted upgrade creates a fresh random backup directory and never adopts an
existing backup destination. During an explicit post-reboot trust reset, old
runtime is never copied into backup or restored; fresh package content replaces
it while the external blocker remains, and the Agent task stays stopped for
credential verification.
A failed deployment restores managed files, removes tasks created by that
failed attempt, and restores the main Agent's prior running state.