param( [string]$RemoteHost = "", [string]$RemoteUser = "root", [string]$SshKeyPath = "$env:USERPROFILE\.ssh\dsk.pem", [string]$LocalJenkinsHost = "127.0.0.1", [int]$LocalJenkinsPort = 8080, [int]$LocalAgentPort = 50000, [int]$RemoteJenkinsPort = 18080, [int]$RemoteAgentPort = 50000, [int]$RestartDelaySeconds = 10 ) $ErrorActionPreference = "Stop" function Write-Log { param([string]$Message) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Write-Output "[$timestamp] $Message" } $ssh = (Get-Command ssh.exe -ErrorAction Stop).Source $remote = "$RemoteUser@$RemoteHost" if (-not $RemoteHost) { throw "RemoteHost is required." } if (-not (Test-Path -LiteralPath $SshKeyPath)) { throw "SSH key not found: $SshKeyPath" } while ($true) { $args = @( "-i", $SshKeyPath, "-o", "StrictHostKeyChecking=accept-new", "-o", "ExitOnForwardFailure=yes", "-o", "ServerAliveInterval=30", "-o", "ServerAliveCountMax=3", "-N", "-R", "127.0.0.1:${RemoteJenkinsPort}:${LocalJenkinsHost}:${LocalJenkinsPort}", "-R", "127.0.0.1:${RemoteAgentPort}:${LocalJenkinsHost}:${LocalAgentPort}", $remote ) Write-Log "Starting Jenkins agent reverse tunnel: $remote" & $ssh @args $exitCode = $LASTEXITCODE Write-Log "Reverse tunnel exited, exitCode=$exitCode; retrying in ${RestartDelaySeconds}s." Start-Sleep -Seconds $RestartDelaySeconds }