Skip to content

Commit 0dbe27f

Browse files
add windows wolfsshd/sftp github action test
1 parent b813327 commit 0dbe27f

File tree

1 file changed

+398
-0
lines changed

1 file changed

+398
-0
lines changed

.github/workflows/windows-sftp.yml

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

0 commit comments

Comments
 (0)