-
Notifications
You must be signed in to change notification settings - Fork 96
[3/3] Add Windows SSH server support #480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
EthanHeilman
merged 21 commits into
openpubkey:main
from
fdcastel:add-windows-ssh-server-support
Mar 31, 2026
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
134c1b2
Add support for OpenSSH Servers on Windows
fdcastel 26ce26e
Address PR #480 review feedback
fdcastel b54a26d
Add Windows ARM64 support to GitHub Actions workflows
fdcastel 9ce9304
Fix Windows ARM64 integration test: increase timeout and add setup-go
fdcastel 7d914e6
docs: Add Windows Server and Windows 11 ARM64 support to compatibilit…
fdcastel 7250ab3
fix: Update copyright year to 2026 in logpath_windows.go
fdcastel c0fb2cd
fix: Address Copilot review comments on PR #480
fdcastel b6f8c0e
docs: Improve README with missing commands, Windows server install, a…
fdcastel 4717edf
fix: Address PR #480 review feedback
fdcastel 970adf4
fix: Improve OpenSSH version detection for LocalSystem account compat…
fdcastel 07c9a3e
Remove LocalSystem account usage, standardize on opksshuser
fdcastel 34c8ba2
refactor: Update GitHub Actions workflow for Windows to use matrix st…
fdcastel 4eefaa3
gha-windows: add PATH verification for install/uninstall
fdcastel fa4c45a
gha-windows: fix timeout, update cache action, remove exit code annot…
fdcastel 7dba4de
gha-windows: fix expected SSH failure step
fdcastel c3a8a36
gha-windows: improve PATH handling during OpenSSH Server installation
fdcastel d9a0d35
fix: Handle UTF-8 BOM in config file parsing
fdcastel 8bc5801
Address Copilot review comments on PR #480
fdcastel ecfbee4
fix: Remove -AuthCmdUser from GHA workflow invocation
fdcastel df51ade
ci: Run only Pester tests for Windows in integration test matrix
fdcastel 29f3b2a
ci: Move Pester tests to test-windows job, clean up integration test …
fdcastel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| name: Test GitHub Provider Windows | ||
|
|
||
| on: | ||
| push: | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Test on ${{ matrix.name }} | ||
| runs-on: ${{ matrix.runner }} | ||
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| timeout-minutes: ${{ matrix.timeout }} | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - name: Windows Server 2022 | ||
| runner: windows-2022 | ||
| timeout: 10 | ||
| cache: true | ||
| - name: Windows Server 2025 | ||
| runner: windows-2025 | ||
| timeout: 10 | ||
| cache: true | ||
| - name: Windows 11 ARM64 | ||
| runner: windows-11-arm | ||
| timeout: 15 | ||
| cache: false | ||
|
|
||
| steps: | ||
| - name: Install OpenSSH Server | ||
| run: | | ||
| Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | ||
|
|
||
| - name: Create default OpenSSH config files | ||
| run: | | ||
| Start-Service sshd | ||
| Start-Sleep -Seconds 2 | ||
| Stop-Service sshd | ||
|
|
||
| - name: Enable OpenSSH Server logs | ||
| run: | | ||
| $sshdConfig = "$env:ProgramData\ssh\sshd_config" | ||
| (Get-Content $sshdConfig -Raw).Replace('#SyslogFacility AUTH', 'SyslogFacility LOCAL0').Replace('#LogLevel INFO', 'LogLevel Debug3') | | ||
| Set-Content $sshdConfig | ||
|
|
||
| - name: Checkout | ||
| uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Cache Go modules | ||
| if: matrix.cache | ||
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | ||
| with: | ||
| path: | | ||
| ~/go/pkg/mod | ||
| ~/.cache/go-build | ||
| key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-go- | ||
|
|
||
| - name: Install Go | ||
| uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 | ||
| with: | ||
| go-version-file: 'go.mod' | ||
|
|
||
| - name: Install dependencies | ||
| run: go mod download | ||
|
|
||
| - name: Build opkssh | ||
| run: go build -v -o opkssh.exe | ||
|
|
||
| - name: Install opkssh with local binary | ||
| run: | | ||
| powershell -ExecutionPolicy Bypass -File "scripts/windows/Install-OpksshServer.ps1" ` | ||
| -InstallFrom "$PWD\opkssh.exe" ` | ||
| -NoSshdRestart ` | ||
| -Verbose | ||
|
|
||
| - name: Verify system PATH after install | ||
| run: | | ||
| $installDir = 'C:\Program Files\opkssh' | ||
| $regPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' | ||
| $systemPath = (Get-ItemProperty -Path $regPath -Name Path).Path | ||
| $folders = $systemPath -split [IO.Path]::PathSeparator | ||
| $found = $folders | Where-Object { $_.TrimEnd([IO.Path]::DirectorySeparatorChar) -eq $installDir } | ||
| if (-not $found) { | ||
| throw "FAIL: '$installDir' was NOT found in the system PATH after install" | ||
| } | ||
| Write-Host "PASS: '$installDir' is in the system PATH after install" | ||
|
|
||
| - name: Add GitHub provider to opkssh configuration | ||
| run: | | ||
| $providersPath = 'C:\ProgramData\opk\providers' | ||
| if ((Get-Content -Path $providersPath -Raw) -notmatch "`r?`n$") { | ||
| Add-Content -Path $providersPath -Value '' | ||
| } | ||
| Add-Content -Path $providersPath -Value 'https://token.actions.githubusercontent.com github oidc' | ||
|
|
||
| - name: Add current repository to policy | ||
| run: | | ||
| & 'C:\Program Files\opkssh\opkssh.exe' add runneradmin "repo:${env:GITHUB_REPOSITORY}:ref:${env:GITHUB_REF}" https://token.actions.githubusercontent.com | ||
|
|
||
| - name: Start SSH service | ||
| run: Start-Service sshd | ||
|
|
||
| - name: Test SSH connection without opkssh (should fail) | ||
| run: | | ||
| ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no runneradmin@localhost dir 2>&1 | ||
| if ($LASTEXITCODE -eq 0) { throw "SSH should have failed but succeeded" } | ||
| Write-Host "SSH correctly rejected without opkssh (exit code: $LASTEXITCODE)" | ||
| exit 0 | ||
|
|
||
| - name: Login with GitHub OIDC | ||
| run: | | ||
| & 'C:\Program Files\opkssh\opkssh.exe' login github --print-id-token | ||
|
|
||
| - name: Test SSH connection with opkssh (should pass) | ||
| run: | | ||
| ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no runneradmin@localhost dir | ||
|
|
||
| - name: Debug - Dump opkssh config | ||
| run: | | ||
| Get-ChildItem 'C:\ProgramData\opk' -Recurse -ErrorAction Continue | ||
| Get-Content 'C:\ProgramData\opk\providers' -ErrorAction Continue | ||
| Get-Content 'C:\ProgramData\opk\auth_id' -ErrorAction Continue | ||
| if: always() | ||
|
|
||
| - name: Debug - Dump opkssh logs | ||
| run: | | ||
| Get-Content 'C:\ProgramData\opk\logs\opkssh.log' -ErrorAction Continue | ||
| if: always() | ||
|
|
||
| - name: Debug - Dump sshd logs | ||
| run: | | ||
| Get-Content 'C:\ProgramData\ssh\logs\sshd.log' -ErrorAction Continue | ||
| if: always() | ||
|
|
||
| - name: Uninstall opkssh | ||
| run: | | ||
| powershell -ExecutionPolicy Bypass -File "scripts/windows/Uninstall-OpksshServer.ps1" ` | ||
| -Force ` | ||
| -NoSshdRestart ` | ||
| -Verbose | ||
|
|
||
| - name: Verify system PATH after uninstall | ||
| run: | | ||
| $installDir = 'C:\Program Files\opkssh' | ||
| $regPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' | ||
| $systemPath = (Get-ItemProperty -Path $regPath -Name Path).Path | ||
| $folders = $systemPath -split [IO.Path]::PathSeparator | ||
| $found = $folders | Where-Object { $_.TrimEnd([IO.Path]::DirectorySeparatorChar) -eq $installDir } | ||
| if ($found) { | ||
| throw "FAIL: '$installDir' is still in the system PATH after uninstall" | ||
| } | ||
| Write-Host "PASS: '$installDir' was correctly removed from the system PATH after uninstall" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This workflow runs
go mod download/go buildwithout first pinning the Go toolchain viaactions/setup-go(unlike the repo’s other workflows). Windows runner images can change their preinstalled Go version over time, which can make CI flaky or inconsistent withgo.mod’s toolchain. Add a setup-go step usinggo-version-file: go.modfor reproducibility.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Added
actions/setup-go@v6.3.0withgo-version-file: 'go.mod'to bothgha-windows.ymlandgha-windows-2025.yml, matching the pattern used by the other workflows in this repo.