mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-11 23:40:28 -10:00
* feat(sandbox): online/offline Windows network identity (run approved network commands) Approved network commands bricked on Windows with "windows sandbox setup is out of date: network policy changed": the setup marker was locked to network=deny, so any command granted network (curl, git push, npm install) ran with network=allow and failed marker validation. Fix with a two-identity model that fits Zero's restricted-token architecture — no extra logon users, no DPAPI, no admin at runtime: mint one synthetic "offline-marker" SID per sandbox home and scope the persistent WFP block filter to THAT SID only. Network is then gated purely by the restricted token's SID set: - deny (offline): token = write-capability SIDs + offline-marker SID -> block filter matches -> no network; writes jailed by the capability SIDs. - allow (online, approved): token = write-capability SIDs only -> filter never matches -> network reaches out; writes jailed identically. The setup marker becomes mode-AGNOSTIC: it fingerprints the provisioned offline infrastructure (NetworkInfraHash + OfflineFilterSID), not the per-command mode, so one setup validly serves both an allow and a deny command. Schema bumped 3->4 (forces a clean re-setup; old markers scoped the filter to write SIDs). - windows_runner.go: Offline SID in WindowsCapabilitySIDs (schema 2, back-compat upgrade) + WindowsOfflineMarkerSID + windowsRuntimeTokenSIDs. - windows_network.go: BuildWindowsNetworkInfraPlan + WindowsNetworkInfraHash; drop the now-dead mode-coupled BuildWindowsNetworkPlan/*Hash helpers. - windows_setup.go: marker schema 4; validation drops per-command-mode equality. - windows_setup_windows.go: setup always installs the offline-scoped infra filter. - windows_command_runner_windows.go: compose the token SID set per network mode. Cross-platform core fully tested (mode selector, mode-independent infra+hash, back-compat SID upgrade, one-marker-validates-both-modes regression, schema-3 rejected); sandbox/doctor suites + windows cross-compile green. The WFP ALE_USER_ID/restricting-SID behavior needs the gated Windows integration smoke test. * docs(sandbox): note the Schannel HTTPS limitation under the Windows restricted token Document the known SEC_E_NO_CREDENTIALS limitation at the token-composition site: an approved online command reaches the network, but HTTPS via Windows Schannel fails inside the restricted token because Schannel can't acquire its per-user TLS credential under a WRITE_RESTRICTED/LUA token. Fundamental restricted-token ↔ Schannel incompatibility, unsolved even in the reference sandboxes (codex#17459). Workarounds: degraded mode or the in-process web_fetch tool. Comment-only. * docs(sandbox): keep the Schannel-limitation note vendor-neutral
69 lines
2.4 KiB
Go
69 lines
2.4 KiB
Go
//go:build windows
|
|
|
|
package sandbox
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
func runWindowsSandboxSetup(config WindowsSandboxSetupConfig, stderr io.Writer) int {
|
|
// Applying the WFP network filters and workspace ACLs requires Administrator
|
|
// rights; without them WFP fails deep inside with a raw ACCESS_DENIED (0x5).
|
|
// Check up front and return an actionable message instead.
|
|
if !windowsProcessIsElevated() {
|
|
fmt.Fprintln(stderr, WindowsSandboxSetupName+": Administrator rights are required. Re-run `zero sandbox setup` from an elevated (Run as administrator) terminal.")
|
|
return 1
|
|
}
|
|
plan, err := BuildWindowsACLPlan(config.commandConfig())
|
|
if err != nil {
|
|
fmt.Fprintln(stderr, WindowsSandboxSetupName+": "+err.Error())
|
|
return 1
|
|
}
|
|
// Always provision the mode-INDEPENDENT infrastructure: the outbound block
|
|
// filters scoped to the offline-marker SID. Runtime gates network per command
|
|
// by whether the token carries that SID, so one setup serves both modes.
|
|
networkPlan, err := BuildWindowsNetworkInfraPlan(config.commandConfig())
|
|
if err != nil {
|
|
fmt.Fprintln(stderr, WindowsSandboxSetupName+": "+err.Error())
|
|
return 1
|
|
}
|
|
rollback, err := applyWindowsACLPlan(plan)
|
|
if err != nil {
|
|
fmt.Fprintln(stderr, WindowsSandboxSetupName+": "+err.Error())
|
|
return 1
|
|
}
|
|
if err := applyWindowsNetworkPlan(networkPlan); err != nil {
|
|
if rollbackErr := rollback(); rollbackErr != nil {
|
|
fmt.Fprintf(stderr, "%s: %v; rollback failed: %v\n", WindowsSandboxSetupName, err, rollbackErr)
|
|
return 1
|
|
}
|
|
fmt.Fprintln(stderr, WindowsSandboxSetupName+": "+err.Error())
|
|
return 1
|
|
}
|
|
if _, err := WriteWindowsSandboxSetupMarker(config); err != nil {
|
|
if rollbackErr := rollback(); rollbackErr != nil {
|
|
fmt.Fprintf(stderr, "%s: %v; rollback failed: %v\n", WindowsSandboxSetupName, err, rollbackErr)
|
|
return 1
|
|
}
|
|
fmt.Fprintln(stderr, WindowsSandboxSetupName+": "+err.Error())
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// windowsProcessIsElevated reports whether the current process runs with an
|
|
// elevated (Administrator) token. On any error obtaining the token it returns
|
|
// true so the setup proceeds and surfaces the real WFP/ACL error rather than a
|
|
// false "needs admin" claim.
|
|
func windowsProcessIsElevated() bool {
|
|
var token windows.Token
|
|
if err := windows.OpenProcessToken(windows.CurrentProcess(), windows.TOKEN_QUERY, &token); err != nil {
|
|
return true
|
|
}
|
|
defer token.Close()
|
|
return token.IsElevated()
|
|
}
|