Skip to content

Commit 352db62

Browse files
add windows wolfsshd/sftp github action test
1 parent b813327 commit 352db62

1 file changed

Lines changed: 399 additions & 0 deletions

File tree

.github/workflows/windows-sftp.yml

Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
name: Windows wolfsshd SFTP Test
2+
3+
# This workflow tests wolfsshd and SFTP on Windows with:
4+
# 1. Basic test: wolfsshd + SFTP client (pwd, ls, put/get small file)
5+
# 2. Large file test: WOLFSSH_NO_SFTP_TIMEOUT, WOLFSSH_MAX_SFTP_RW=10485760,
6+
# WOLFSSH_MAX_CHN_NAMESZ=4200 - get and put a 3GB file
7+
8+
on:
9+
push:
10+
branches: [ '*' ]
11+
pull_request:
12+
branches: [ '*' ]
13+
14+
env:
15+
WOLFSSL_SOLUTION_FILE_PATH: wolfssl64.sln
16+
SOLUTION_FILE_PATH: wolfssh.sln
17+
USER_SETTINGS_H_NEW: wolfssh/ide/winvs/user_settings.h
18+
USER_SETTINGS_H: wolfssl/IDE/WIN/user_settings.h
19+
INCLUDE_DIR: wolfssh
20+
WOLFSSL_BUILD_CONFIGURATION: Release
21+
WOLFSSH_BUILD_CONFIGURATION: Release
22+
BUILD_PLATFORM: x64
23+
TARGET_PLATFORM: 10
24+
TEST_PORT: 22222
25+
26+
jobs:
27+
build:
28+
runs-on: windows-latest
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
include:
33+
- test_type: basic
34+
artifact_name: wolfssh-windows-build
35+
- test_type: large_rw
36+
artifact_name: wolfssh-windows-build-large-rw
37+
38+
steps:
39+
- uses: actions/checkout@v4
40+
with:
41+
repository: wolfssl/wolfssl
42+
path: wolfssl
43+
44+
- uses: actions/checkout@v4
45+
with:
46+
path: wolfssh
47+
48+
- name: Add MSBuild to PATH
49+
uses: microsoft/setup-msbuild@v1
50+
51+
- name: Update user_settings.h for wolfSSL build
52+
working-directory: ${{env.GITHUB_WORKSPACE}}
53+
shell: bash
54+
run: |
55+
sed -i 's/#if 0/#if 1/g' ${{env.USER_SETTINGS_H_NEW}}
56+
cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}}
57+
58+
- name: Restore wolfSSL NuGet packages
59+
working-directory: ${{ github.workspace }}\wolfssl
60+
run: nuget restore ${{env.WOLFSSL_SOLUTION_FILE_PATH}}
61+
62+
- name: Build wolfssl library
63+
working-directory: ${{ github.workspace }}\wolfssl
64+
run: msbuild /m /p:PlatformToolset=v142 /p:Platform=${{env.BUILD_PLATFORM}} /p:Configuration=${{env.WOLFSSL_BUILD_CONFIGURATION}} /t:wolfssl ${{env.WOLFSSL_SOLUTION_FILE_PATH}}
65+
66+
- name: Upload wolfSSL build artifacts
67+
if: matrix.test_type == 'basic'
68+
uses: actions/upload-artifact@v4
69+
with:
70+
name: wolfssl-windows-build
71+
path: |
72+
wolfssl/IDE/WIN/${{env.WOLFSSL_BUILD_CONFIGURATION}}/${{env.BUILD_PLATFORM}}/**
73+
wolfssl/IDE/WIN/${{env.WOLFSSL_BUILD_CONFIGURATION}}/**
74+
wolfssl/${{env.WOLFSSL_BUILD_CONFIGURATION}}/${{env.BUILD_PLATFORM}}/**
75+
wolfssl/${{env.WOLFSSL_BUILD_CONFIGURATION}}/**
76+
77+
- name: Update user_settings.h for sshd and SFTP
78+
working-directory: ${{env.GITHUB_WORKSPACE}}
79+
shell: bash
80+
run: |
81+
# Enable SSHD, SFTP support (second #if 0 block)
82+
sed -i 's/#if 0/#if 1/g' ${{env.USER_SETTINGS_H_NEW}}
83+
cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}}
84+
# For large_rw test: add SFTP large file defines
85+
if [ "${{ matrix.test_type }}" = "large_rw" ]; then
86+
echo "" >> ${{env.USER_SETTINGS_H}}
87+
echo "/* SFTP large file test defines */" >> ${{env.USER_SETTINGS_H}}
88+
echo "#define WOLFSSH_NO_SFTP_TIMEOUT" >> ${{env.USER_SETTINGS_H}}
89+
echo "#define WOLFSSH_MAX_SFTP_RW 10485760" >> ${{env.USER_SETTINGS_H}}
90+
echo "#define WOLFSSH_MAX_CHN_NAMESZ 4200" >> ${{env.USER_SETTINGS_H}}
91+
echo "Added WOLFSSH_NO_SFTP_TIMEOUT, WOLFSSH_MAX_SFTP_RW=10485760, WOLFSSH_MAX_CHN_NAMESZ=4200"
92+
fi
93+
94+
- name: Restore NuGet packages
95+
working-directory: ${{ github.workspace }}\wolfssh\ide\winvs
96+
run: nuget restore ${{env.SOLUTION_FILE_PATH}}
97+
98+
- name: Build wolfssh
99+
working-directory: ${{ github.workspace }}\wolfssh\ide\winvs
100+
run: msbuild /m /p:PlatformToolset=v142 /p:Platform=${{env.BUILD_PLATFORM}} /p:WindowsTargetPlatformVersion=${{env.TARGET_PLATFORM}} /p:Configuration=${{env.WOLFSSH_BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}}
101+
102+
- name: Upload wolfSSH build artifacts
103+
uses: actions/upload-artifact@v4
104+
with:
105+
name: ${{ matrix.artifact_name }}
106+
if-no-files-found: error
107+
path: |
108+
wolfssh/ide/winvs/**/Release/**
109+
110+
test:
111+
needs: build
112+
runs-on: windows-latest
113+
strategy:
114+
fail-fast: false
115+
matrix:
116+
include:
117+
- test_type: basic
118+
artifact_name: wolfssh-windows-build
119+
- test_type: large_rw
120+
artifact_name: wolfssh-windows-build-large-rw
121+
122+
steps:
123+
- uses: actions/checkout@v4
124+
with:
125+
path: wolfssh
126+
127+
- name: Download wolfSSH build artifacts
128+
uses: actions/download-artifact@v4
129+
with:
130+
name: ${{ matrix.artifact_name }}
131+
path: .
132+
133+
- name: Download wolfSSL build artifacts
134+
uses: actions/download-artifact@v4
135+
with:
136+
name: wolfssl-windows-build
137+
path: .
138+
139+
- name: Create Windows user testuser and authorized_keys
140+
working-directory: ${{ github.workspace }}\wolfssh
141+
shell: pwsh
142+
run: |
143+
$homeDir = "C:\Users\testuser"
144+
$sshDir = "$homeDir\.ssh"
145+
$authKeysFile = "$sshDir\authorized_keys"
146+
$pw = 'T3stP@ss!xY9'
147+
148+
New-Item -ItemType Directory -Path $homeDir -Force | Out-Null
149+
New-Item -ItemType Directory -Path $sshDir -Force | Out-Null
150+
151+
$o = net user testuser $pw /add /homedir:$homeDir 2>&1
152+
if ($LASTEXITCODE -ne 0) {
153+
if ($o -match "already exists") {
154+
net user testuser /homedir:$homeDir 2>$null
155+
} else {
156+
Write-Host "net user failed: $o"
157+
exit 1
158+
}
159+
}
160+
Add-Content -Path $env:GITHUB_ENV -Value "TESTUSER_PASSWORD=$pw"
161+
162+
"" | Out-File -FilePath $authKeysFile -Encoding ASCII -NoNewline
163+
icacls $authKeysFile /grant "testuser:R" /q
164+
165+
$sid = (New-Object System.Security.Principal.NTAccount("testuser")).Translate([System.Security.Principal.SecurityIdentifier]).Value
166+
$profKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid"
167+
if (-not (Test-Path $profKey)) { New-Item -Path $profKey -Force | Out-Null }
168+
Set-ItemProperty -Path $profKey -Name "ProfileImagePath" -Value $homeDir -Force
169+
170+
- name: Create wolfSSHd config file
171+
working-directory: ${{ github.workspace }}\wolfssh
172+
shell: pwsh
173+
run: |
174+
$keyPath = Join-Path "${{ github.workspace }}" "wolfssh\keys\server-key.pem"
175+
$keyPathFull = (Resolve-Path $keyPath -ErrorAction Stop)
176+
$configContent = @"
177+
Port ${{env.TEST_PORT}}
178+
PasswordAuthentication yes
179+
PermitRootLogin yes
180+
HostKey $($keyPathFull.Path)
181+
AuthorizedKeysFile C:\Users\testuser\.ssh\authorized_keys
182+
"@
183+
$configContent | Out-File -FilePath sshd_config_test -Encoding ASCII
184+
Get-Content sshd_config_test
185+
186+
- name: Find wolfSSH executables
187+
working-directory: ${{ github.workspace }}\wolfssh
188+
shell: pwsh
189+
run: |
190+
$searchRoot = "${{ github.workspace }}"
191+
$sshdExe = Get-ChildItem -Path $searchRoot -Recurse -Filter "wolfsshd.exe" -ErrorAction SilentlyContinue |
192+
Where-Object { $_.FullName -like "*Release*" } | Select-Object -First 1
193+
if ($sshdExe) {
194+
Add-Content -Path $env:GITHUB_ENV -Value "SSHD_PATH=$($sshdExe.FullName)"
195+
} else {
196+
Write-Host "ERROR: wolfsshd.exe not found"
197+
exit 1
198+
}
199+
200+
$sftpExe = Get-ChildItem -Path $searchRoot -Recurse -Filter "wolfsftp.exe" -ErrorAction SilentlyContinue |
201+
Where-Object { $_.FullName -like "*Release*" } | Select-Object -First 1
202+
if (-not $sftpExe) {
203+
$sftpExe = Get-ChildItem -Path $searchRoot -Recurse -Filter "wolfsftp-client.exe" -ErrorAction SilentlyContinue |
204+
Where-Object { $_.FullName -like "*Release*" } | Select-Object -First 1
205+
}
206+
if ($sftpExe) {
207+
Add-Content -Path $env:GITHUB_ENV -Value "SFTP_PATH=$($sftpExe.FullName)"
208+
} else {
209+
Write-Host "ERROR: SFTP client exe not found"
210+
exit 1
211+
}
212+
213+
- name: Copy wolfSSL DLL to executable directory
214+
working-directory: ${{ github.workspace }}
215+
shell: pwsh
216+
run: |
217+
$sshdPath = $env:SSHD_PATH
218+
$sshdDir = Split-Path -Parent $sshdPath
219+
if (Test-Path (Join-Path $sshdDir "wolfssl.lib")) { exit 0 }
220+
$wolfsslDll = Get-ChildItem -Path "${{ github.workspace }}\wolfssl" -Recurse -Filter "wolfssl.dll" -ErrorAction SilentlyContinue | Select-Object -First 1
221+
if ($wolfsslDll) {
222+
Copy-Item -Path $wolfsslDll.FullName -Destination (Join-Path $sshdDir "wolfssl.dll") -Force
223+
}
224+
225+
- name: Grant service access to config and keys
226+
working-directory: ${{ github.workspace }}\wolfssh
227+
shell: pwsh
228+
run: |
229+
icacls (Get-Location).Path /grant "NT AUTHORITY\SYSTEM:(OI)(CI)RX" /T /q
230+
231+
- name: Start wolfSSHd as Windows service
232+
working-directory: ${{ github.workspace }}\wolfssh
233+
shell: pwsh
234+
run: |
235+
$sshdPath = $env:SSHD_PATH
236+
$configPathFull = (Resolve-Path "sshd_config_test").Path
237+
$serviceName = "wolfsshd"
238+
239+
$existingService = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
240+
if ($existingService) {
241+
if ($existingService.Status -eq 'Running') { Stop-Service -Name $serviceName -Force }
242+
sc.exe delete $serviceName | Out-Null
243+
Start-Sleep -Seconds 2
244+
}
245+
246+
$binPath = "`"$sshdPath`" -f `"$configPathFull`" -p ${{env.TEST_PORT}}"
247+
sc.exe create $serviceName binPath= $binPath
248+
sc.exe start $serviceName
249+
Start-Sleep -Seconds 5
250+
251+
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
252+
if ($service.Status -ne 'Running') {
253+
Write-Host "ERROR: Service failed to start"
254+
sc.exe query $serviceName
255+
exit 1
256+
}
257+
Add-Content -Path $env:GITHUB_ENV -Value "SSHD_SERVICE_NAME=$serviceName"
258+
259+
- name: Test SFTP get non-existent file (no hang, correct error)
260+
working-directory: ${{ github.workspace }}\wolfssh
261+
shell: pwsh
262+
timeout-minutes: 1
263+
run: |
264+
$sftpPath = $env:SFTP_PATH
265+
$getCommands = "get /this_file_does_not_exist_xyz /tmp/copy.dat`nquit"
266+
$getCommands | Out-File -FilePath sftp_get_nonexistent_commands.txt -Encoding ASCII
267+
268+
$proc = Start-Process -FilePath $sftpPath `
269+
-ArgumentList "-u", "testuser", "-P", $env:TESTUSER_PASSWORD, "-h", "localhost", "-p", "${{env.TEST_PORT}}" `
270+
-RedirectStandardInput "sftp_get_nonexistent_commands.txt" `
271+
-RedirectStandardOutput "sftp_get_nonexistent_out.txt" `
272+
-RedirectStandardError "sftp_get_nonexistent_err.txt" `
273+
-Wait -NoNewWindow -PassThru
274+
275+
Write-Host "=== SFTP Output ==="
276+
if (Test-Path sftp_get_nonexistent_out.txt) { Get-Content sftp_get_nonexistent_out.txt }
277+
Write-Host "=== SFTP Error ==="
278+
if (Test-Path sftp_get_nonexistent_err.txt) { Get-Content sftp_get_nonexistent_err.txt }
279+
280+
if ($proc.ExitCode -eq 0) {
281+
Write-Host "ERROR: Expected non-zero exit for get of non-existent file (got 0)"
282+
exit 1
283+
}
284+
Write-Host "PASS: SFTP get non-existent file failed correctly (exit $($proc.ExitCode)), did not hang"
285+
286+
- name: Test SFTP connection (basic)
287+
if: matrix.test_type == 'basic'
288+
working-directory: ${{ github.workspace }}\wolfssh
289+
shell: pwsh
290+
run: |
291+
$sftpPath = $env:SFTP_PATH
292+
$testCommands = "pwd`nls`nquit"
293+
$testCommands | Out-File -FilePath sftp_commands.txt -Encoding ASCII
294+
295+
$process = Start-Process -FilePath $sftpPath `
296+
-ArgumentList "-u", "testuser", "-P", $env:TESTUSER_PASSWORD, "-h", "localhost", "-p", "${{env.TEST_PORT}}" `
297+
-RedirectStandardInput "sftp_commands.txt" `
298+
-RedirectStandardOutput "sftp_output.txt" `
299+
-RedirectStandardError "sftp_error.txt" `
300+
-Wait -NoNewWindow -PassThru
301+
302+
Get-Content sftp_output.txt
303+
Get-Content sftp_error.txt
304+
if ($process.ExitCode -ne 0) {
305+
Write-Host "ERROR: SFTP basic test failed with exit $($process.ExitCode)"
306+
exit 1
307+
}
308+
Write-Host "Basic SFTP test passed"
309+
310+
- name: Create 3GB test file and run SFTP get/put (large_rw)
311+
if: matrix.test_type == 'large_rw'
312+
working-directory: ${{ github.workspace }}\wolfssh
313+
shell: pwsh
314+
timeout-minutes: 25
315+
run: |
316+
$sftpPath = $env:SFTP_PATH
317+
$workDir = (Get-Location).Path
318+
$largeFile = Join-Path $workDir "large_test.dat"
319+
$getDest = Join-Path $workDir "large_test_copy.dat"
320+
321+
# Create 3GB file (3072 MB)
322+
Write-Host "Creating 3GB test file..."
323+
$fs = [System.IO.File]::Create($largeFile)
324+
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
325+
$buf = New-Object byte[] 10485760 # 10MB chunks
326+
$remaining = 3221225472 # 3GB
327+
while ($remaining -gt 0) {
328+
$rng.GetBytes($buf)
329+
$toWrite = [Math]::Min($buf.Length, $remaining)
330+
$fs.Write($buf, 0, $toWrite)
331+
$remaining -= $toWrite
332+
}
333+
$fs.Close()
334+
335+
$hash = Get-FileHash -Path $largeFile -Algorithm SHA256
336+
$hash.Hash | Out-File -FilePath large_test.dat.sha256
337+
Write-Host "Created 3GB file, SHA256: $($hash.Hash)"
338+
339+
# SFTP PUT (upload)
340+
Write-Host "SFTP PUT 3GB file..."
341+
$putCommands = "put $largeFile /large_test.dat`nquit"
342+
$putCommands | Out-File -FilePath sftp_put_commands.txt -Encoding ASCII
343+
$proc = Start-Process -FilePath $sftpPath `
344+
-ArgumentList "-u", "testuser", "-P", $env:TESTUSER_PASSWORD, "-h", "localhost", "-p", "${{env.TEST_PORT}}" `
345+
-RedirectStandardInput "sftp_put_commands.txt" `
346+
-RedirectStandardOutput "sftp_put_out.txt" `
347+
-RedirectStandardError "sftp_put_err.txt" `
348+
-Wait -NoNewWindow -PassThru
349+
350+
if ($proc.ExitCode -ne 0) {
351+
Get-Content sftp_put_out.txt
352+
Get-Content sftp_put_err.txt
353+
Write-Host "ERROR: SFTP PUT failed"
354+
exit 1
355+
}
356+
Write-Host "PUT succeeded"
357+
358+
# SFTP GET (download)
359+
Write-Host "SFTP GET 3GB file..."
360+
$getCommands = "get /large_test.dat $getDest`nquit"
361+
$getCommands | Out-File -FilePath sftp_get_commands.txt -Encoding ASCII
362+
$proc2 = Start-Process -FilePath $sftpPath `
363+
-ArgumentList "-u", "testuser", "-P", $env:TESTUSER_PASSWORD, "-h", "localhost", "-p", "${{env.TEST_PORT}}" `
364+
-RedirectStandardInput "sftp_get_commands.txt" `
365+
-RedirectStandardOutput "sftp_get_out.txt" `
366+
-RedirectStandardError "sftp_get_err.txt" `
367+
-Wait -NoNewWindow -PassThru
368+
369+
if ($proc2.ExitCode -ne 0) {
370+
Get-Content sftp_get_out.txt
371+
Get-Content sftp_get_err.txt
372+
Write-Host "ERROR: SFTP GET failed"
373+
exit 1
374+
}
375+
Write-Host "GET succeeded"
376+
377+
# Verify integrity
378+
$expectedHash = (Get-Content large_test.dat.sha256).Trim()
379+
$actualHash = (Get-FileHash -Path $getDest -Algorithm SHA256).Hash
380+
if ($expectedHash -ne $actualHash) {
381+
Write-Host "ERROR: SHA256 mismatch - PUT/GET corruption"
382+
Write-Host "Expected: $expectedHash"
383+
Write-Host "Actual: $actualHash"
384+
exit 1
385+
}
386+
Write-Host "PASS: 3GB SFTP get/put with WOLFSSH_MAX_SFTP_RW=10485760 succeeded"
387+
388+
- name: Cleanup
389+
if: always()
390+
working-directory: ${{ github.workspace }}\wolfssh
391+
shell: pwsh
392+
run: |
393+
$serviceName = $env:SSHD_SERVICE_NAME
394+
if (-not $serviceName) { $serviceName = "wolfsshd" }
395+
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
396+
if ($service) {
397+
if ($service.Status -eq 'Running') { Stop-Service -Name $serviceName -Force }
398+
sc.exe delete $serviceName | Out-Null
399+
}

0 commit comments

Comments
 (0)