feat(security): run the Windows plugin runner as LocalService, not SYSTEM
The PunktfunkScripting scheduled task ran operator-installed plugin code as SYSTEM with -RunLevel Highest — any plugin defect was a full compromise of the box. The principal is now NT AUTHORITY\LocalService (minimal privileges, no password to manage, exists at boot, loopback networking works), registered without RunLevel: - installer: New-ScheduledTaskPrincipal -UserId 'LocalService' - plugins enable: converges the principal idempotently (migrating tasks an older installer or a dev box registered as SYSTEM) BEFORE starting, then grants LocalService read — via icacls, full-System32-path — on exactly the two SYSTEM/Admins-DACL'd files the runner's connect() needs: the scoped plugin-token and the TLS-pin cert.pem. Never mgmt-token. plugins disable revokes the grants; plugins status now prints the task principal so the migration is verifiable. - build-scripting.ps1 mirrors the convergence + grants on dev deploys. The usage text also mentions the new --allow-public-registry gate that lands with the supply-chain commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+10
-1
@@ -82,7 +82,16 @@ const runBun = (action: "add" | "remove", pkgs: string[], opts: PkgOpts): void =
|
||||
log(`${action === "add" ? "installing" : "removing"} ${pkgs.join(", ")} in ${dir}`);
|
||||
// `process.execPath` is the bun running this file (the vendored one under the package), so a
|
||||
// system-wide bun on PATH is not required. Inherit stdio so `bun`'s progress reaches the user.
|
||||
const res = Bun.spawnSync([process.execPath, action, ...pkgs], {
|
||||
const args = [process.execPath, action, ...pkgs];
|
||||
// Windows: install file COPIES, never bun's default hardlinks. A hardlinked file's canonical
|
||||
// path resolves into the installing admin's per-user bun cache
|
||||
// (C:\Users\<admin>\.bun\install\cache\…), which the de-privileged LocalService runner cannot
|
||||
// traverse — imports die with EPERM even though the plugins-dir DACL grants read (seen live
|
||||
// on-glass). copyfile keeps the plugins tree self-contained under %ProgramData%.
|
||||
if (action === "add" && process.platform === "win32") {
|
||||
args.push("--backend=copyfile");
|
||||
}
|
||||
const res = Bun.spawnSync(args, {
|
||||
cwd: dir,
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user