mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
fix(ssh): enforce fixture ACL trustee closure
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -1457,13 +1457,34 @@ jobs:
|
||||
$account = New-LocalUser -Name $fixtureUser -Password $password -PasswordNeverExpires `
|
||||
-UserMayNotChangePassword -AccountNeverExpires
|
||||
$userSid = $account.SID.Value
|
||||
$creatorSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
||||
$systemSid = 'S-1-5-18'
|
||||
$administratorsSid = 'S-1-5-32-544'
|
||||
|
||||
function Set-FixtureAcl([string]$Path, [string[]]$Grants) {
|
||||
function Set-FixtureAcl([string]$Path, [string[]]$Grants, [string[]]$ExpectedSids) {
|
||||
& icacls.exe $Path /inheritance:r | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) { throw "Failed to remove inherited ACLs from $Path" }
|
||||
# Why: key generation adds a creator ACE that /grant:r does not replace for other trustees.
|
||||
& icacls.exe $Path /remove "*$creatorSid" | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) { throw "Failed to remove creator ACL from $Path" }
|
||||
$arguments = @($Path, '/grant:r') + $Grants
|
||||
& icacls.exe @arguments | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) { throw "Failed to grant fixture ACLs on $Path" }
|
||||
$acl = Get-Acl -LiteralPath $Path
|
||||
$rules = @($acl.Access)
|
||||
if (-not $acl.AreAccessRulesProtected -or @($rules | Where-Object { $_.IsInherited }).Count -ne 0) {
|
||||
throw "Fixture ACL inheritance remains enabled on $Path"
|
||||
}
|
||||
if (@($rules | Where-Object { $_.AccessControlType -ne 'Allow' }).Count -ne 0) {
|
||||
throw "Fixture ACL contains a deny rule on $Path"
|
||||
}
|
||||
$actualSids = @($rules | ForEach-Object {
|
||||
$_.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
|
||||
} | Sort-Object -Unique)
|
||||
$expected = @($ExpectedSids | Sort-Object -Unique)
|
||||
if (($actualSids -join "`n") -ne ($expected -join "`n")) {
|
||||
throw "Fixture ACL trustee closure changed on $Path`: $($actualSids -join ',')"
|
||||
}
|
||||
}
|
||||
function Set-FixtureOwner([string]$Path, [string]$OwnerSid) {
|
||||
& icacls.exe $Path /setowner "*$OwnerSid" | Out-Host
|
||||
@@ -1471,8 +1492,13 @@ jobs:
|
||||
}
|
||||
$systemFull = '*S-1-5-18:(OI)(CI)F'
|
||||
$administratorsFull = '*S-1-5-32-544:(OI)(CI)F'
|
||||
Set-FixtureAcl $fixture @($systemFull, $administratorsFull, "*$userSid`:RX")
|
||||
Set-FixtureAcl $remoteRoot @($systemFull, $administratorsFull, "*$userSid`:(OI)(CI)M")
|
||||
$fixtureTrustees = @($systemSid, $administratorsSid, $userSid)
|
||||
Set-FixtureAcl $fixture @($systemFull, $administratorsFull, "*$userSid`:RX") $fixtureTrustees
|
||||
Set-FixtureAcl $remoteRoot @(
|
||||
$systemFull,
|
||||
$administratorsFull,
|
||||
"*$userSid`:(OI)(CI)M"
|
||||
) $fixtureTrustees
|
||||
|
||||
$clientKey = Join-Path $fixture 'client-key'
|
||||
$hostKey = Join-Path $fixture 'host-key'
|
||||
@@ -1486,9 +1512,12 @@ jobs:
|
||||
'*S-1-5-18:F',
|
||||
'*S-1-5-32-544:F',
|
||||
"*$userSid`:R"
|
||||
)
|
||||
) $fixtureTrustees
|
||||
Set-FixtureOwner $authorizedKeys $userSid
|
||||
Set-FixtureAcl $hostKey @('*S-1-5-18:F', '*S-1-5-32-544:F')
|
||||
Set-FixtureAcl $hostKey @(
|
||||
'*S-1-5-18:F',
|
||||
'*S-1-5-32-544:F'
|
||||
) @($systemSid, $administratorsSid)
|
||||
Set-FixtureOwner $hostKey 'S-1-5-18'
|
||||
|
||||
$port = 22224
|
||||
|
||||
@@ -179,6 +179,11 @@ describe('SSH relay runtime artifact workflow', () => {
|
||||
expect(start.run).toContain("if ($nativeFile.Name -eq 'libcrypto.dll') { continue }")
|
||||
expect(start.run).toContain("$signature.Status -ne 'Valid'")
|
||||
expect(start.run).toContain('CN=Microsoft Corporation')
|
||||
expect(start.run).toContain('[Security.Principal.WindowsIdentity]::GetCurrent().User.Value')
|
||||
expect(start.run).toContain('& icacls.exe $Path /remove "*$creatorSid"')
|
||||
expect(start.run).toContain('$acl.AreAccessRulesProtected')
|
||||
expect(start.run).toContain('Fixture ACL trustee closure changed')
|
||||
expect(start.run).toContain('@($systemSid, $administratorsSid)')
|
||||
expect(start.run).not.toContain('Add-WindowsCapability')
|
||||
expect(start.run).toContain('New-LocalUser')
|
||||
expect(start.run.indexOf("Join-Path $fixture 'account-owned'")).toBeLessThan(
|
||||
|
||||
+13
-1
@@ -38,7 +38,19 @@ common name plus audited thumbprint `F5877012FBD62FABCBDC8D8CEE9C9585BA30DF79` o
|
||||
policy and is locally green: 10/10 focused workflow cases with one declared live skip, all three
|
||||
PowerShell blocks parsed, 283/283 artifact contracts, typecheck, full lint/reliability/max-lines,
|
||||
formatting, diff, and protected-resolver isolation. Commit/push the isolated correction and require
|
||||
fresh exact-head native x64/arm64 proof.
|
||||
fresh exact-head native x64/arm64 proof. Exact head
|
||||
`130d57d951b5e5cfb0e1e21183ce116684924e2b`, artifact run `29540409361`, and x64 job
|
||||
`87761260157` prove the complete exact-signer policy before exposing a narrower ACL RED: portable
|
||||
OpenSSH rejects the host key because `icacls /grant:r` did not remove the key creator's explicit
|
||||
runner-account ACE. Service start fails, and ownership-safe teardown passes. ARM64 job `87761260166`
|
||||
independently reaches the same RED with creator SID
|
||||
`S-1-5-21-1882319117-3219095328-2125279949-500`; its teardown also passes. Remove the known creator
|
||||
SID, assert the exact allowed SID closure, and rerun.
|
||||
`E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-PORTABLE-ACL-CORRECTION-LOCAL-001` records that isolated
|
||||
correction locally green: protected allow-only ACLs remove the creator and require exact trustee SID
|
||||
closure; 10/10 focused workflow cases, all three PowerShell blocks, 283/283 artifact contracts,
|
||||
typecheck, full lint/reliability/max-lines, format, diff, and protected-resolver isolation pass. Push
|
||||
for fresh exact-head x64/arm64 proof.
|
||||
`E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-AUDIT-001` fixes the loopback-only official Microsoft
|
||||
server, fixture-owned non-admin account/ACL, exact host-key trust, Windows PowerShell 5.1,
|
||||
serial/default and four-channel metrics, cancellation/collision/cleanup, and deterministic teardown
|
||||
|
||||
+74
-1
@@ -9,7 +9,7 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
|
||||
Date created: 2026-07-14<br>
|
||||
Last updated: 2026-07-15<br>
|
||||
Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — 2026-07-15, Codex implementation owner: bounded official Windows OpenSSH fixture correction**. Keep every capability disconnected from every production/default SSH path. Real Apple/SignPath rehearsals remain an explicit late gate and are not on this package's critical path.<br>
|
||||
Session checkpoint: **EXACT-SIGNER FIXTURE TRUST LOCALLY GREEN / EXACT-HEAD NATIVE PROOF NEXT — 2026-07-15, Codex implementation owner** — exact head `85731b3feff90047f67b716363b1549b0d79ee2c`, artifact run `29539266437`, and x64/arm64 jobs `87757772975`/`87757772957` pass their pinned archive hashes, exact 15-file native closures, pinned `libcrypto.dll` hashes, prior runtime contracts/build/smoke, and full-size cache gates before both disproving only the guessed `NotSigned` policy: target-native Authenticode reports `libcrypto.dll` as `Valid`. Both ownership-safe teardowns pass before any SSH account/service creation. `E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-SIGNER-AUDIT-001` audits all 30 pinned PEs; `E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-EXACT-SIGNER-CORRECTION-LOCAL-001` implements the exact common-name/thumbprint fixture policy and passes focused, PowerShell syntax, 283 artifact-contract, typecheck, lint/reliability/max-lines, format, diff, and isolation gates. Commit/push the isolated correction and obtain fresh exact-head native proof. Production runtime bytes still cross authenticated SSH and no product remote downloads anything. Do not connect a product caller, change legacy upload/fallback/default behavior, enable a tuple, publish an artifact, or perform SignPath work. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Session checkpoint: **PORTABLE HOST-KEY ACL CORRECTION LOCALLY GREEN / EXACT-HEAD PROOF NEXT — 2026-07-15, Codex implementation owner** — exact pushed head `130d57d951b5e5cfb0e1e21183ce116684924e2b`, artifact run `29540409361`, and x64/arm64 jobs `87761260157`/`87761260166` pass checkout/toolchain, 283 contracts, clean-build equality/smoke, full-size cache, both pinned archive/library hashes, exact 15-file closure, and every audited common-name/thumbprint check before independently exposing the same narrower RED. Portable OpenSSH rejects the generated host key because `icacls /grant:r` leaves each key creator's explicit runner-account ACE; both service starts fail and both ownership-safe teardowns pass. `E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-PORTABLE-ACL-CORRECTION-LOCAL-001` removes the known creator SID from every protected fixture ACL, asserts protected/allow-only exact trustee SID closure before service start, and passes focused, PowerShell syntax, 283 artifact-contract, typecheck, lint/reliability/max-lines, format, diff, and isolation gates. Commit/push and obtain fresh x64/arm64 proof. Production runtime bytes still cross authenticated SSH and no product remote downloads anything. Do not connect a product caller, change legacy upload/fallback/default behavior, enable a tuple, publish an artifact, or perform SignPath work. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)<br>
|
||||
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693)
|
||||
|
||||
@@ -18943,6 +18943,79 @@ config/vitest.config.ts --maxWorkers=1 --reporter=dot config/scripts/ssh-relay-r
|
||||
Legacy remains the sole production/default path; product/Beta/settings/fallback/tuple/publication/
|
||||
signing remain absent.
|
||||
|
||||
### E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-PORTABLE-ACL-CI-RED-001 — creator ACE survives grant replacement
|
||||
|
||||
- Date/owner/head/run: 2026-07-15, Codex implementation owner; exact pushed head
|
||||
`130d57d951b5e5cfb0e1e21183ce116684924e2b`, artifact run
|
||||
[29540409361](https://github.com/stablyai/orca/actions/runs/29540409361), x64 job `87761260157`
|
||||
on `windows-2022`, runner `GitHub Actions 1000058197`; ARM64 job `87761260166` on
|
||||
`windows-11-arm`, runner `GitHub Actions 1000058198`.
|
||||
- Prior-boundary proof: checkout/toolchain, all 283 artifact contracts, exact Node inputs, two clean
|
||||
runtime builds/equality/native smoke, full-size desktop extraction/cache, pinned archive and
|
||||
`libcrypto.dll` hashes, exact 15-name native closure, `Valid` Authenticode, the exact third-party
|
||||
library identity, and both exact accepted executable identities all pass. The signer correction is
|
||||
therefore target-natively proven on x64.
|
||||
- Exact RED: fixture account/key/config/service creation proceeds, then `Start-Service sshd` fails.
|
||||
The captured portable OpenSSH diagnostic names explicit creator access for runner SID
|
||||
`S-1-5-21-4137868374-391603265-4009536220-500` on `host-key`, reports `UNPROTECTED PRIVATE KEY
|
||||
FILE`, ignores the key, and exits with no host keys. `icacls /inheritance:r` removed inherited
|
||||
access, but `/grant:r` replaced only the named SYSTEM/Administrators grants and did not remove the
|
||||
creator-specific explicit ACE added by key generation.
|
||||
- Independent ARM64 RED: all the same prior runtime/cache/archive/closure/exact-signer gates pass.
|
||||
`Start-Service sshd` fails in five seconds, and the portable diagnostic names its native runner's
|
||||
distinct creator SID `S-1-5-21-1882319117-3219095328-2125279949-500` on `host-key`, followed by
|
||||
the same `UNPROTECTED PRIVATE KEY FILE` and no-host-keys exit. This confirms an
|
||||
architecture-independent ACL-construction defect rather than a binary/signer difference.
|
||||
- Cleanup: the two-second `always()` teardown passes, deletes the owned fixed-name service, account,
|
||||
client state, and ownership-marked fixture root, and reports no cleanup failure. ARM64's teardown
|
||||
independently passes in four seconds. No SSH connection or transfer begins on either architecture.
|
||||
- Evidence-driven correction: retain all signer/archive/closure gates. Remove the known current
|
||||
creator SID from each protected fixture ACL before applying exact grants, then enumerate ACL
|
||||
trustees by SID and fail unless the resulting set exactly matches the path's declared SYSTEM,
|
||||
Administrators, and optional fixture-user closure. Perform this before config validation/service
|
||||
creation. The HTML plan records this fixture-only requirement; product runtime/transfer trust is
|
||||
unchanged.
|
||||
- Residual boundary: implement only exact fixture ACL closure plus its workflow oracle, rerun
|
||||
local/static/PowerShell gates, and require fresh exact-head x64/arm64 live
|
||||
service/auth/full-size/cancellation/collision/connection-reuse/cleanup proof. Legacy remains the
|
||||
sole production/default path; product/Beta/settings/fallback/tuple/publication/signing remain absent.
|
||||
|
||||
### E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-PORTABLE-ACL-CORRECTION-LOCAL-001 — creator-free exact fixture ACL closure is locally green
|
||||
|
||||
- Date/owner/base/runner: 2026-07-15, Codex implementation owner; uncommitted workflow, oracle,
|
||||
HTML-plan, and checklist correction atop exact RED head
|
||||
`130d57d951b5e5cfb0e1e21183ce116684924e2b` on Apple arm64, macOS 26.2 build 25C56,
|
||||
Node 26.5.0, pnpm 10.24.0. Native Windows execution remains required.
|
||||
- Correction: capture the current Windows creator SID, protect each fixture DACL from inheritance,
|
||||
remove every ACE for that known creator, and apply only the declared SYSTEM, Administrators, and
|
||||
optional fixture-user grants. Before config validation/service creation, `Get-Acl` must report a
|
||||
protected DACL, zero inherited rules, allow-only rules, and an exact sorted SID trustee set. The
|
||||
host key permits only SYSTEM/Administrators; the fixture root, remote root, and `authorized_keys`
|
||||
also permit only the fixture user. Existing exact owner assignments remain unchanged.
|
||||
- Focused purpose/oracle command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1
|
||||
src/main/ssh/ssh-relay-runtime-windows-system-ssh-openssh-full-size.test.ts
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs` passes 10/10 workflow cases with one declared
|
||||
live-suite skip in 2.61 seconds real; maximum RSS is 141,869,056 bytes and peak footprint is
|
||||
97,261,400 bytes. Independent YAML extraction passes all three start/measure/stop blocks through
|
||||
PowerShell `[scriptblock]::Create()`.
|
||||
- Release-contract command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1 --reporter=dot config/scripts/ssh-relay-runtime-*.test.mjs`
|
||||
passes 50 files and 283/283 cases in 16.78 seconds real; maximum RSS is 195,395,584 bytes and peak
|
||||
footprint is 98,047,736 bytes.
|
||||
- Static evidence: `/usr/bin/time -l pnpm typecheck` passes in 4.02 seconds real; maximum RSS is
|
||||
1,280,770,048 bytes and peak footprint is 97,425,144 bytes. `/usr/bin/time -l pnpm lint` passes in
|
||||
20.52 seconds real with only the 26 pre-existing warnings, all 41 reliability gates, the 355-entry
|
||||
max-lines ratchet, localization, and bundled-guide checks; maximum RSS is 2,103,885,824 bytes and
|
||||
peak footprint is 97,015,616 bytes. Targeted formatting, `git diff --check`, and exact
|
||||
protected-file equality against `130d57d951b5e5cfb0e1e21183ce116684924e2b` pass; only the fixture
|
||||
workflow, its oracle, and three planning artifacts are modified.
|
||||
- Residual boundary: local macOS syntax/contracts cannot prove Windows ACL mutation/translation,
|
||||
service/auth, PowerShell 5.1, full-size transfer, cancellation, collision, connection reuse, or
|
||||
teardown. Commit/push the isolated correction and require fresh exact-head x64/arm64 proof. Legacy
|
||||
remains the sole production/default path; product/Beta/settings/fallback/tuple/publication/signing
|
||||
remain absent.
|
||||
|
||||
## Accepted Gaps
|
||||
|
||||
No product gap is accepted merely because it appears in this list. Each entry requires explicit
|
||||
|
||||
@@ -1665,8 +1665,10 @@
|
||||
</td>
|
||||
<td>
|
||||
This trust exception provisions only the loopback CI server. Production clients
|
||||
and SSH hosts perform no fixture download, and it does not weaken runtime
|
||||
artifact, native-signing, transfer, install, or launch trust.
|
||||
and SSH hosts perform no fixture download. Fixture host-key and authorized-key
|
||||
ACL creation removes creator-specific access and asserts the exact allowed SID
|
||||
closure before service start; none of this weakens runtime artifact,
|
||||
native-signing, transfer, install, or launch trust.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
Reference in New Issue
Block a user