ssh-handler 1.0.5__tar.gz → 1.0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {ssh_handler-1.0.5/ssh_handler.egg-info → ssh_handler-1.0.7}/PKG-INFO +20 -1
  2. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/README.md +19 -0
  3. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/pyproject.toml +5 -1
  4. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/__init__.py +1 -1
  5. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/cli.py +54 -0
  6. ssh_handler-1.0.7/ssh_handler/openssh/OpenSSH-ARM64.zip +0 -0
  7. ssh_handler-1.0.7/ssh_handler/openssh/OpenSSH-Win32.zip +0 -0
  8. ssh_handler-1.0.7/ssh_handler/openssh/OpenSSH-Win64.zip +0 -0
  9. ssh_handler-1.0.7/ssh_handler/setup_openssh_server.ps1 +170 -0
  10. {ssh_handler-1.0.5 → ssh_handler-1.0.7/ssh_handler.egg-info}/PKG-INFO +20 -1
  11. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler.egg-info/SOURCES.txt +4 -0
  12. ssh_handler-1.0.7/ssh_handler.egg-info/entry_points.txt +3 -0
  13. ssh_handler-1.0.5/ssh_handler.egg-info/entry_points.txt +0 -2
  14. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/LICENSE +0 -0
  15. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/setup.cfg +0 -0
  16. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/__main__.py +0 -0
  17. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/config.py +0 -0
  18. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/core.py +0 -0
  19. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/credentials.py +0 -0
  20. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/exceptions.py +0 -0
  21. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/ftp.py +0 -0
  22. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/pool.py +0 -0
  23. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/pyqt_worker.py +0 -0
  24. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/results.py +0 -0
  25. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler/winrm_bootstrap.py +0 -0
  26. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler.egg-info/dependency_links.txt +0 -0
  27. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler.egg-info/requires.txt +0 -0
  28. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/ssh_handler.egg-info/top_level.txt +0 -0
  29. {ssh_handler-1.0.5 → ssh_handler-1.0.7}/tests/test_offline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
5
5
  Author: ssh-handler contributors
6
6
  License-Expression: MIT
@@ -224,6 +224,25 @@ Requirements: `pip install "ssh-handler[winrm]"` (pulls in `pywinrm`; uses NTLM
224
224
  domain creds work without Kerberos), and the account must be a **local
225
225
  administrator** on the target. If SSH already works, this code path never runs.
226
226
 
227
+ ### Set up OpenSSH Server on a machine (bundled installer)
228
+
229
+ Installing the package also gives you a one-command, **fully offline** setup for
230
+ the local Windows machine. The OpenSSH ZIPs (ARM64 / Win64 / Win32) ship *inside*
231
+ the wheel, so the installer needs **no internet and no Windows Update** — it picks
232
+ the ZIP matching the CPU architecture, self-elevates to Administrator, installs &
233
+ starts OpenSSH Server, and opens the firewall. (This avoids the
234
+ `Add-WindowsCapability` hang common on locked-down corporate networks.)
235
+
236
+ ```powershell
237
+ pip install ssh-handler
238
+ ssh-handler-setup # offline install + start sshd + firewall (self-elevates)
239
+ ssh-handler-setup --install-pip # also (re)install the package as admin
240
+ ssh-handler-setup --force # reinstall even if sshd already exists
241
+ # equivalent: python -m ssh_handler setup-server
242
+ ```
243
+
244
+ Run it on whichever machine you want to reach over SSH (e.g. the RDP jump box).
245
+
227
246
  When a connection just fails, the error now self-diagnoses — it probes the SSH and
228
247
  RDP ports and tells you *why* (e.g. "Port 22 is closed but RDP (3389) is open … no
229
248
  SSH server listening"). Call `ssh.diagnose()` for a pre-flight reachability check.
@@ -193,6 +193,25 @@ Requirements: `pip install "ssh-handler[winrm]"` (pulls in `pywinrm`; uses NTLM
193
193
  domain creds work without Kerberos), and the account must be a **local
194
194
  administrator** on the target. If SSH already works, this code path never runs.
195
195
 
196
+ ### Set up OpenSSH Server on a machine (bundled installer)
197
+
198
+ Installing the package also gives you a one-command, **fully offline** setup for
199
+ the local Windows machine. The OpenSSH ZIPs (ARM64 / Win64 / Win32) ship *inside*
200
+ the wheel, so the installer needs **no internet and no Windows Update** — it picks
201
+ the ZIP matching the CPU architecture, self-elevates to Administrator, installs &
202
+ starts OpenSSH Server, and opens the firewall. (This avoids the
203
+ `Add-WindowsCapability` hang common on locked-down corporate networks.)
204
+
205
+ ```powershell
206
+ pip install ssh-handler
207
+ ssh-handler-setup # offline install + start sshd + firewall (self-elevates)
208
+ ssh-handler-setup --install-pip # also (re)install the package as admin
209
+ ssh-handler-setup --force # reinstall even if sshd already exists
210
+ # equivalent: python -m ssh_handler setup-server
211
+ ```
212
+
213
+ Run it on whichever machine you want to reach over SSH (e.g. the RDP jump box).
214
+
196
215
  When a connection just fails, the error now self-diagnoses — it probes the SSH and
197
216
  RDP ports and tells you *why* (e.g. "Port 22 is closed but RDP (3389) is open … no
198
217
  SSH server listening"). Call `ssh.diagnose()` for a pre-flight reachability check.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ssh-handler"
7
- version = "1.0.5"
7
+ version = "1.0.7"
8
8
  description = "Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -32,6 +32,10 @@ all = ["keyring>=23.0", "scp>=0.14", "PyQt5>=5.15", "pywinrm>=0.4.3"]
32
32
 
33
33
  [project.scripts]
34
34
  ssh-handler = "ssh_handler.cli:main"
35
+ ssh-handler-setup = "ssh_handler.cli:setup_server_main"
35
36
 
36
37
  [tool.setuptools]
37
38
  packages = ["ssh_handler"]
39
+
40
+ [tool.setuptools.package-data]
41
+ ssh_handler = ["*.ps1", "openssh/*.zip"]
@@ -22,7 +22,7 @@ dependencies (PyQt5 / scp) don't break the core package.
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
- __version__ = "1.0.5"
25
+ __version__ = "1.0.7"
26
26
 
27
27
  from .config import SSHConfig, FTPConfig
28
28
  from .core import SSHHandler, ShellSession
@@ -15,9 +15,11 @@ Credentials (password never echoed, never stored in plaintext):
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
+ import os
18
19
  import sys
19
20
  import json
20
21
  import argparse
22
+ import subprocess
21
23
 
22
24
  from .config import SSHConfig
23
25
  from .core import SSHHandler
@@ -26,6 +28,35 @@ from .exceptions import SSHError
26
28
  from .results import CommandResult, TransferResult
27
29
 
28
30
 
31
+ def _setup_script_path() -> str:
32
+ """Absolute path to the bundled OpenSSH setup PowerShell script."""
33
+ return os.path.join(os.path.dirname(__file__), "setup_openssh_server.ps1")
34
+
35
+
36
+ def setup_server_main(argv=None) -> int:
37
+ """
38
+ Console entry point (`ssh-handler-setup`): run the bundled PowerShell script
39
+ that installs & starts OpenSSH Server, opens the firewall, and optionally
40
+ pip-installs the package. Self-elevates to Administrator. Windows only.
41
+
42
+ Any extra args are forwarded to the script, e.g.:
43
+ ssh-handler-setup --InstallPip --Port 22
44
+ """
45
+ if os.name != "nt":
46
+ print("ssh-handler-setup only runs on Windows (it sets up OpenSSH Server).",
47
+ file=sys.stderr)
48
+ return 2
49
+ script = _setup_script_path()
50
+ if not os.path.exists(script):
51
+ print(f"Bundled setup script not found: {script}", file=sys.stderr)
52
+ return 1
53
+ extra = list(argv) if argv is not None else sys.argv[1:]
54
+ cmd = ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass",
55
+ "-File", script] + extra
56
+ print(f"Launching OpenSSH Server setup (will prompt for Administrator)...")
57
+ return subprocess.call(cmd)
58
+
59
+
29
60
  def _add_conn_args(p: argparse.ArgumentParser) -> None:
30
61
  p.add_argument("--host", required=True)
31
62
  p.add_argument("--port", type=int, default=22)
@@ -107,6 +138,29 @@ def main(argv=None) -> int:
107
138
  p_info = sub.add_parser("info", help="connect and report remote OS")
108
139
  _add_conn_args(p_info)
109
140
 
141
+ p_setup = sub.add_parser("setup-server",
142
+ help="install & start OpenSSH Server on THIS Windows "
143
+ "machine (self-elevates to Administrator)")
144
+ p_setup.add_argument("--install-pip", action="store_true",
145
+ help="also pip-install ssh-handler[winrm]")
146
+ p_setup.add_argument("--port", type=int, default=22)
147
+
148
+ # setup-server takes no connection args and runs the bundled PowerShell script
149
+ if argv is None:
150
+ _argv = sys.argv[1:]
151
+ else:
152
+ _argv = list(argv)
153
+ if _argv and _argv[0] == "setup-server":
154
+ forward = []
155
+ rest = _argv[1:]
156
+ if "--install-pip" in rest:
157
+ forward.append("-InstallPip")
158
+ if "--port" in rest:
159
+ i = rest.index("--port")
160
+ if i + 1 < len(rest):
161
+ forward += ["-Port", rest[i + 1]]
162
+ return setup_server_main(forward)
163
+
110
164
  args = parser.parse_args(argv)
111
165
 
112
166
  # store-credential is handled without a connection
@@ -0,0 +1,170 @@
1
+ <#
2
+ .SYNOPSIS
3
+ One-shot OpenSSH Server setup for a Windows machine, fully OFFLINE.
4
+ Installs from the OpenSSH ZIP bundled with the ssh-handler package
5
+ (chosen by CPU architecture), starts sshd, opens the firewall, and
6
+ optionally pip-installs the package. Self-elevates to Administrator.
7
+
8
+ .DESCRIPTION
9
+ No Windows Update / Features-on-Demand and no internet required - this
10
+ avoids the Add-WindowsCapability hang on locked-down corporate networks.
11
+ The matching ZIP (ARM64 / Win64 / Win32) ships inside the package under
12
+ .\openssh\ next to this script.
13
+
14
+ .PARAMETER InstallPip
15
+ Also run `pip install -U "ssh-handler[winrm]"` (needs Python+pip on PATH).
16
+
17
+ .PARAMETER Port
18
+ SSH port to open in the firewall (default 22).
19
+
20
+ .PARAMETER ZipPath
21
+ Use a specific OpenSSH ZIP instead of the bundled one (override).
22
+
23
+ .PARAMETER Force
24
+ Reinstall even if sshd already exists.
25
+
26
+ .EXAMPLE
27
+ powershell -ExecutionPolicy Bypass -File .\setup_openssh_server.ps1
28
+ .EXAMPLE
29
+ ssh-handler-setup --install-pip
30
+ #>
31
+
32
+ param(
33
+ [switch]$InstallPip,
34
+ [int]$Port = 22,
35
+ [string]$ZipPath,
36
+ [switch]$Force
37
+ )
38
+
39
+ $ErrorActionPreference = 'Stop'
40
+ $ProgressPreference = 'SilentlyContinue'
41
+
42
+ # --- 1. Self-elevate to Administrator -------------------------------------- #
43
+ $isAdmin = ([Security.Principal.WindowsPrincipal]`
44
+ [Security.Principal.WindowsIdentity]::GetCurrent()`
45
+ ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
46
+
47
+ if (-not $isAdmin) {
48
+ Write-Host "Elevating to Administrator..." -ForegroundColor Yellow
49
+ $argList = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" -Port $Port"
50
+ if ($InstallPip) { $argList += " -InstallPip" }
51
+ if ($Force) { $argList += " -Force" }
52
+ if ($ZipPath) { $argList += " -ZipPath `"$ZipPath`"" }
53
+ Start-Process powershell -Verb RunAs -ArgumentList $argList
54
+ exit
55
+ }
56
+
57
+ Write-Host "=== OpenSSH Server setup (Administrator, offline) ===" -ForegroundColor Cyan
58
+
59
+ function Test-SshdPresent {
60
+ return [bool](Get-Service -Name sshd -ErrorAction SilentlyContinue)
61
+ }
62
+
63
+ # --- 2. Pick the right bundled ZIP by CPU architecture --------------------- #
64
+ function Get-OSArch {
65
+ try {
66
+ $a = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
67
+ if ($a) { return $a }
68
+ } catch {}
69
+ $p = $env:PROCESSOR_ARCHITEW6432
70
+ if (-not $p) { $p = $env:PROCESSOR_ARCHITECTURE }
71
+ switch ($p) {
72
+ 'AMD64' { 'X64' } 'ARM64' { 'Arm64' } 'x86' { 'X86' } default { $p }
73
+ }
74
+ }
75
+
76
+ function Resolve-BundledZip {
77
+ $arch = Get-OSArch
78
+ switch -Regex ($arch) {
79
+ 'Arm64' { $name = 'OpenSSH-ARM64.zip' }
80
+ 'X64' { $name = 'OpenSSH-Win64.zip' }
81
+ 'X86' { $name = 'OpenSSH-Win32.zip' }
82
+ default { throw "Unsupported architecture: $arch" }
83
+ }
84
+ Write-Host (" Detected architecture: {0} -> {1}" -f $arch, $name)
85
+ return (Join-Path $PSScriptRoot ("openssh\" + $name))
86
+ }
87
+
88
+ # --- 3. Install OpenSSH Server from the ZIP -------------------------------- #
89
+ if ((Test-SshdPresent) -and -not $Force) {
90
+ Write-Host "OpenSSH Server already present (use -Force to reinstall)." -ForegroundColor Green
91
+ }
92
+ else {
93
+ $zip = if ($ZipPath) { $ZipPath } else { Resolve-BundledZip }
94
+ if (-not (Test-Path $zip)) {
95
+ throw "OpenSSH ZIP not found: $zip"
96
+ }
97
+ Write-Host "Installing OpenSSH Server from: $zip"
98
+
99
+ $dest = Join-Path $env:ProgramFiles 'OpenSSH'
100
+ $tmp = Join-Path $env:TEMP ("openssh_" + [guid]::NewGuid().ToString('N'))
101
+ try {
102
+ Expand-Archive -Path $zip -DestinationPath $tmp -Force
103
+ # the archive nests a single OpenSSH-XXX folder
104
+ $srcDir = Get-ChildItem $tmp -Directory | Select-Object -First 1
105
+ if (-not $srcDir) { $srcDir = Get-Item $tmp }
106
+
107
+ if (-not (Test-Path $dest)) {
108
+ New-Item -ItemType Directory -Path $dest | Out-Null
109
+ }
110
+ Copy-Item (Join-Path $srcDir.FullName '*') $dest -Recurse -Force
111
+ Write-Host " Files copied to $dest"
112
+
113
+ $installer = Join-Path $dest 'install-sshd.ps1'
114
+ if (-not (Test-Path $installer)) { throw "install-sshd.ps1 missing in $dest" }
115
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $installer | Out-Null
116
+ Write-Host " OpenSSH Server installed." -ForegroundColor Green
117
+ }
118
+ finally {
119
+ if (Test-Path $tmp) { Remove-Item $tmp -Recurse -Force -ErrorAction SilentlyContinue }
120
+ }
121
+
122
+ # add the install dir to the machine PATH so ssh/sshd are callable
123
+ $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
124
+ if ($machinePath -notlike "*$dest*") {
125
+ [Environment]::SetEnvironmentVariable('Path', "$machinePath;$dest", 'Machine')
126
+ }
127
+ }
128
+
129
+ # --- 4. Service + firewall -------------------------------------------------- #
130
+ Write-Host "Configuring sshd service and firewall..."
131
+ Set-Service -Name sshd -StartupType Automatic
132
+ Start-Service sshd
133
+
134
+ $ruleName = "OpenSSH-Server-In-TCP"
135
+ if (-not (Get-NetFirewallRule -Name $ruleName -ErrorAction SilentlyContinue)) {
136
+ New-NetFirewallRule -Name $ruleName -DisplayName 'OpenSSH Server (sshd)' `
137
+ -Enabled True -Direction Inbound -Protocol TCP -Action Allow `
138
+ -LocalPort $Port | Out-Null
139
+ Write-Host " Firewall rule added for TCP $Port." -ForegroundColor Green
140
+ }
141
+ else {
142
+ Write-Host " Firewall rule already present." -ForegroundColor Green
143
+ }
144
+
145
+ # --- 5. Optional: pip install the package ---------------------------------- #
146
+ if ($InstallPip) {
147
+ Write-Host "Installing ssh-handler (pip)..."
148
+ try {
149
+ python -m pip install --upgrade pip | Out-Null
150
+ python -m pip install -U "ssh-handler[winrm]"
151
+ Write-Host " ssh-handler installed." -ForegroundColor Green
152
+ }
153
+ catch {
154
+ Write-Warning "pip install failed: $($_.Exception.Message)"
155
+ }
156
+ }
157
+
158
+ # --- 6. Verify -------------------------------------------------------------- #
159
+ Write-Host "`n=== Verification ===" -ForegroundColor Cyan
160
+ $svc = Get-Service sshd
161
+ Write-Host ("sshd status : {0}" -f $svc.Status)
162
+ $test = Test-NetConnection localhost -Port $Port -WarningAction SilentlyContinue
163
+ Write-Host ("port {0} open : {1}" -f $Port, $test.TcpTestSucceeded)
164
+
165
+ if ($svc.Status -eq 'Running' -and $test.TcpTestSucceeded) {
166
+ Write-Host "`nDone. This machine now accepts SSH on port $Port." -ForegroundColor Green
167
+ }
168
+ else {
169
+ Write-Warning "Setup finished but sshd is not listening yet - check the messages above."
170
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
5
5
  Author: ssh-handler contributors
6
6
  License-Expression: MIT
@@ -224,6 +224,25 @@ Requirements: `pip install "ssh-handler[winrm]"` (pulls in `pywinrm`; uses NTLM
224
224
  domain creds work without Kerberos), and the account must be a **local
225
225
  administrator** on the target. If SSH already works, this code path never runs.
226
226
 
227
+ ### Set up OpenSSH Server on a machine (bundled installer)
228
+
229
+ Installing the package also gives you a one-command, **fully offline** setup for
230
+ the local Windows machine. The OpenSSH ZIPs (ARM64 / Win64 / Win32) ship *inside*
231
+ the wheel, so the installer needs **no internet and no Windows Update** — it picks
232
+ the ZIP matching the CPU architecture, self-elevates to Administrator, installs &
233
+ starts OpenSSH Server, and opens the firewall. (This avoids the
234
+ `Add-WindowsCapability` hang common on locked-down corporate networks.)
235
+
236
+ ```powershell
237
+ pip install ssh-handler
238
+ ssh-handler-setup # offline install + start sshd + firewall (self-elevates)
239
+ ssh-handler-setup --install-pip # also (re)install the package as admin
240
+ ssh-handler-setup --force # reinstall even if sshd already exists
241
+ # equivalent: python -m ssh_handler setup-server
242
+ ```
243
+
244
+ Run it on whichever machine you want to reach over SSH (e.g. the RDP jump box).
245
+
227
246
  When a connection just fails, the error now self-diagnoses — it probes the SSH and
228
247
  RDP ports and tells you *why* (e.g. "Port 22 is closed but RDP (3389) is open … no
229
248
  SSH server listening"). Call `ssh.diagnose()` for a pre-flight reachability check.
@@ -12,6 +12,7 @@ ssh_handler/ftp.py
12
12
  ssh_handler/pool.py
13
13
  ssh_handler/pyqt_worker.py
14
14
  ssh_handler/results.py
15
+ ssh_handler/setup_openssh_server.ps1
15
16
  ssh_handler/winrm_bootstrap.py
16
17
  ssh_handler.egg-info/PKG-INFO
17
18
  ssh_handler.egg-info/SOURCES.txt
@@ -19,4 +20,7 @@ ssh_handler.egg-info/dependency_links.txt
19
20
  ssh_handler.egg-info/entry_points.txt
20
21
  ssh_handler.egg-info/requires.txt
21
22
  ssh_handler.egg-info/top_level.txt
23
+ ssh_handler/openssh/OpenSSH-ARM64.zip
24
+ ssh_handler/openssh/OpenSSH-Win32.zip
25
+ ssh_handler/openssh/OpenSSH-Win64.zip
22
26
  tests/test_offline.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ssh-handler = ssh_handler.cli:main
3
+ ssh-handler-setup = ssh_handler.cli:setup_server_main
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- ssh-handler = ssh_handler.cli:main
File without changes
File without changes