qssh 0.2.0__tar.gz → 0.2.2__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.
- {qssh-0.2.0/src/qssh.egg-info → qssh-0.2.2}/PKG-INFO +16 -9
- {qssh-0.2.0 → qssh-0.2.2}/README.md +15 -8
- {qssh-0.2.0 → qssh-0.2.2}/pyproject.toml +1 -1
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh/__init__.py +2 -2
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh/cli.py +15 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh/connector.py +52 -15
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh/session.py +10 -0
- {qssh-0.2.0 → qssh-0.2.2/src/qssh.egg-info}/PKG-INFO +16 -9
- {qssh-0.2.0 → qssh-0.2.2}/LICENSE +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/setup.cfg +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh/py.typed +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh.egg-info/SOURCES.txt +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh.egg-info/dependency_links.txt +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh.egg-info/entry_points.txt +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh.egg-info/requires.txt +0 -0
- {qssh-0.2.0 → qssh-0.2.2}/src/qssh.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qssh
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Quick SSH session manager - save your VM credentials and connect with a single command
|
|
5
5
|
Author: joan-code6
|
|
6
6
|
License-Expression: MIT
|
|
@@ -47,7 +47,7 @@ pip install qssh
|
|
|
47
47
|
### 1. Add a new session
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
qssh add
|
|
50
|
+
qssh add myserver
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
You'll be prompted for:
|
|
@@ -59,7 +59,7 @@ You'll be prompted for:
|
|
|
59
59
|
### 2. Connect to your VM
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
qssh
|
|
62
|
+
qssh myserver
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
That's it! You're connected.
|
|
@@ -79,8 +79,8 @@ That's it! You're connected.
|
|
|
79
79
|
## Examples
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
# Add a session for your
|
|
83
|
-
qssh add
|
|
82
|
+
# Add a session for your myserver VM
|
|
83
|
+
qssh add myserver
|
|
84
84
|
# Host: 192.168.1.100
|
|
85
85
|
# Username: admin
|
|
86
86
|
# Port [22]: 22
|
|
@@ -88,7 +88,7 @@ qssh add outcraft
|
|
|
88
88
|
# Password: ********
|
|
89
89
|
|
|
90
90
|
# Now just connect with:
|
|
91
|
-
qssh
|
|
91
|
+
qssh myserver
|
|
92
92
|
|
|
93
93
|
# List all your sessions
|
|
94
94
|
qssh list
|
|
@@ -97,7 +97,7 @@ qssh list
|
|
|
97
97
|
qssh remove old-server
|
|
98
98
|
|
|
99
99
|
# Show details of a session
|
|
100
|
-
qssh show
|
|
100
|
+
qssh show myserver
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
## Using SSH Keys
|
|
@@ -110,12 +110,19 @@ qssh add myserver
|
|
|
110
110
|
# Username: deploy
|
|
111
111
|
# Port [22]: 22
|
|
112
112
|
# Auth type (password/key) [password]: key
|
|
113
|
-
# Key file path: ~/.ssh/
|
|
113
|
+
# Key file path [~/.ssh/id_rsa]: ~/.ssh/my_key
|
|
114
|
+
# Key passphrase (leave empty if none): ********
|
|
114
115
|
```
|
|
115
116
|
|
|
117
|
+
Supported key types:
|
|
118
|
+
- RSA
|
|
119
|
+
- Ed25519
|
|
120
|
+
- ECDSA
|
|
121
|
+
- DSS
|
|
122
|
+
|
|
116
123
|
## Configuration
|
|
117
124
|
|
|
118
|
-
Sessions are stored in `~/.qssh/sessions.yaml`. Passwords are stored encoded (not plaintext) but for maximum security, consider using SSH keys
|
|
125
|
+
Sessions are stored in `~/.qssh/sessions.yaml`. Passwords and key passphrases are stored encoded (not plaintext) but for maximum security, consider using SSH keys without passphrases or with an SSH agent.
|
|
119
126
|
|
|
120
127
|
## License
|
|
121
128
|
|
|
@@ -15,7 +15,7 @@ pip install qssh
|
|
|
15
15
|
### 1. Add a new session
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
qssh add
|
|
18
|
+
qssh add myserver
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
You'll be prompted for:
|
|
@@ -27,7 +27,7 @@ You'll be prompted for:
|
|
|
27
27
|
### 2. Connect to your VM
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
qssh
|
|
30
|
+
qssh myserver
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
That's it! You're connected.
|
|
@@ -47,8 +47,8 @@ That's it! You're connected.
|
|
|
47
47
|
## Examples
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
# Add a session for your
|
|
51
|
-
qssh add
|
|
50
|
+
# Add a session for your myserver VM
|
|
51
|
+
qssh add myserver
|
|
52
52
|
# Host: 192.168.1.100
|
|
53
53
|
# Username: admin
|
|
54
54
|
# Port [22]: 22
|
|
@@ -56,7 +56,7 @@ qssh add outcraft
|
|
|
56
56
|
# Password: ********
|
|
57
57
|
|
|
58
58
|
# Now just connect with:
|
|
59
|
-
qssh
|
|
59
|
+
qssh myserver
|
|
60
60
|
|
|
61
61
|
# List all your sessions
|
|
62
62
|
qssh list
|
|
@@ -65,7 +65,7 @@ qssh list
|
|
|
65
65
|
qssh remove old-server
|
|
66
66
|
|
|
67
67
|
# Show details of a session
|
|
68
|
-
qssh show
|
|
68
|
+
qssh show myserver
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
## Using SSH Keys
|
|
@@ -78,12 +78,19 @@ qssh add myserver
|
|
|
78
78
|
# Username: deploy
|
|
79
79
|
# Port [22]: 22
|
|
80
80
|
# Auth type (password/key) [password]: key
|
|
81
|
-
# Key file path: ~/.ssh/
|
|
81
|
+
# Key file path [~/.ssh/id_rsa]: ~/.ssh/my_key
|
|
82
|
+
# Key passphrase (leave empty if none): ********
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
Supported key types:
|
|
86
|
+
- RSA
|
|
87
|
+
- Ed25519
|
|
88
|
+
- ECDSA
|
|
89
|
+
- DSS
|
|
90
|
+
|
|
84
91
|
## Configuration
|
|
85
92
|
|
|
86
|
-
Sessions are stored in `~/.qssh/sessions.yaml`. Passwords are stored encoded (not plaintext) but for maximum security, consider using SSH keys
|
|
93
|
+
Sessions are stored in `~/.qssh/sessions.yaml`. Passwords and key passphrases are stored encoded (not plaintext) but for maximum security, consider using SSH keys without passphrases or with an SSH agent.
|
|
87
94
|
|
|
88
95
|
## License
|
|
89
96
|
|
|
@@ -126,6 +126,7 @@ def add_session(name: str):
|
|
|
126
126
|
|
|
127
127
|
password = None
|
|
128
128
|
key_file = None
|
|
129
|
+
key_passphrase = None
|
|
129
130
|
|
|
130
131
|
if auth_type == "password":
|
|
131
132
|
password_raw = Prompt.ask("[bold]Password[/]", password=True)
|
|
@@ -136,6 +137,10 @@ def add_session(name: str):
|
|
|
136
137
|
"[bold]Key file path[/]",
|
|
137
138
|
default="~/.ssh/id_rsa"
|
|
138
139
|
)
|
|
140
|
+
# Ask for passphrase (optional)
|
|
141
|
+
passphrase_raw = Prompt.ask("[bold]Key passphrase[/] (leave empty if none)", password=True, default="")
|
|
142
|
+
if passphrase_raw:
|
|
143
|
+
key_passphrase = Session.encode_password(passphrase_raw)
|
|
139
144
|
|
|
140
145
|
# Create and save session
|
|
141
146
|
session = Session(
|
|
@@ -146,6 +151,7 @@ def add_session(name: str):
|
|
|
146
151
|
auth_type=auth_type,
|
|
147
152
|
password=password,
|
|
148
153
|
key_file=key_file,
|
|
154
|
+
key_passphrase=key_passphrase,
|
|
149
155
|
)
|
|
150
156
|
|
|
151
157
|
manager.add(session)
|
|
@@ -229,6 +235,7 @@ def edit_session(name: str):
|
|
|
229
235
|
|
|
230
236
|
password = session.password
|
|
231
237
|
key_file = session.key_file
|
|
238
|
+
key_passphrase = getattr(session, 'key_passphrase', None)
|
|
232
239
|
|
|
233
240
|
if auth_type == "password":
|
|
234
241
|
if Confirm.ask("Update password?", default=False):
|
|
@@ -236,11 +243,18 @@ def edit_session(name: str):
|
|
|
236
243
|
if password_raw:
|
|
237
244
|
password = Session.encode_password(password_raw)
|
|
238
245
|
key_file = None
|
|
246
|
+
key_passphrase = None
|
|
239
247
|
else:
|
|
240
248
|
key_file = Prompt.ask(
|
|
241
249
|
"[bold]Key file path[/]",
|
|
242
250
|
default=session.key_file or "~/.ssh/id_rsa"
|
|
243
251
|
)
|
|
252
|
+
if Confirm.ask("Update key passphrase?", default=False):
|
|
253
|
+
passphrase_raw = Prompt.ask("[bold]Key passphrase[/] (leave empty if none)", password=True, default="")
|
|
254
|
+
if passphrase_raw:
|
|
255
|
+
key_passphrase = Session.encode_password(passphrase_raw)
|
|
256
|
+
else:
|
|
257
|
+
key_passphrase = None
|
|
244
258
|
password = None
|
|
245
259
|
|
|
246
260
|
# Update session
|
|
@@ -252,6 +266,7 @@ def edit_session(name: str):
|
|
|
252
266
|
auth_type=auth_type,
|
|
253
267
|
password=password,
|
|
254
268
|
key_file=key_file,
|
|
269
|
+
key_passphrase=key_passphrase,
|
|
255
270
|
)
|
|
256
271
|
|
|
257
272
|
manager.add(updated)
|
|
@@ -29,12 +29,12 @@ class SSHConnector:
|
|
|
29
29
|
Exit code from SSH process
|
|
30
30
|
"""
|
|
31
31
|
if session.auth_type == "key":
|
|
32
|
-
return self.
|
|
32
|
+
return self._connect_with_key_paramiko(session)
|
|
33
33
|
else:
|
|
34
34
|
return self._connect_with_paramiko(session)
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
"""Connect using SSH key authentication.
|
|
36
|
+
def _connect_with_key_paramiko(self, session: Session) -> int:
|
|
37
|
+
"""Connect using SSH key authentication via paramiko.
|
|
38
38
|
|
|
39
39
|
Args:
|
|
40
40
|
session: Session configuration
|
|
@@ -43,19 +43,56 @@ class SSHConnector:
|
|
|
43
43
|
Exit code
|
|
44
44
|
"""
|
|
45
45
|
key_path = os.path.expanduser(session.key_file) if session.key_file else None
|
|
46
|
+
passphrase = session.get_key_passphrase() if hasattr(session, 'get_key_passphrase') else None
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
try:
|
|
49
|
+
# Create SSH client
|
|
50
|
+
client = paramiko.SSHClient()
|
|
51
|
+
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
52
|
+
|
|
53
|
+
# Load the private key
|
|
54
|
+
pkey = None
|
|
55
|
+
if key_path and os.path.exists(key_path):
|
|
56
|
+
try:
|
|
57
|
+
# Try different key types
|
|
58
|
+
for key_class in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey, paramiko.DSSKey]:
|
|
59
|
+
try:
|
|
60
|
+
pkey = key_class.from_private_key_file(key_path, password=passphrase)
|
|
61
|
+
break
|
|
62
|
+
except paramiko.SSHException:
|
|
63
|
+
continue
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f"[qssh] Error loading key: {e}")
|
|
66
|
+
return 1
|
|
67
|
+
|
|
68
|
+
# Connect with key
|
|
69
|
+
client.connect(
|
|
70
|
+
hostname=session.host,
|
|
71
|
+
port=session.port,
|
|
72
|
+
username=session.username,
|
|
73
|
+
pkey=pkey,
|
|
74
|
+
look_for_keys=False,
|
|
75
|
+
allow_agent=False,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Start interactive shell
|
|
79
|
+
self._interactive_shell(client)
|
|
80
|
+
|
|
81
|
+
client.close()
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
except paramiko.AuthenticationException:
|
|
85
|
+
print("[qssh] Authentication failed. Check your key or passphrase.")
|
|
86
|
+
return 1
|
|
87
|
+
except paramiko.SSHException as e:
|
|
88
|
+
print(f"[qssh] SSH error: {e}")
|
|
89
|
+
return 1
|
|
90
|
+
except FileNotFoundError:
|
|
91
|
+
print(f"[qssh] Key file not found: {key_path}")
|
|
92
|
+
return 1
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"[qssh] Connection error: {e}")
|
|
95
|
+
return 1
|
|
59
96
|
|
|
60
97
|
def _connect_with_paramiko(self, session: Session) -> int:
|
|
61
98
|
"""Connect using paramiko for automatic password authentication.
|
|
@@ -20,6 +20,7 @@ class Session:
|
|
|
20
20
|
auth_type: str = "password" # "password" or "key"
|
|
21
21
|
password: Optional[str] = None # base64 encoded
|
|
22
22
|
key_file: Optional[str] = None
|
|
23
|
+
key_passphrase: Optional[str] = None # base64 encoded
|
|
23
24
|
|
|
24
25
|
def to_dict(self) -> dict:
|
|
25
26
|
"""Convert session to dictionary for storage."""
|
|
@@ -41,6 +42,15 @@ class Session:
|
|
|
41
42
|
return self.password
|
|
42
43
|
return None
|
|
43
44
|
|
|
45
|
+
def get_key_passphrase(self) -> Optional[str]:
|
|
46
|
+
"""Decode and return the key passphrase."""
|
|
47
|
+
if self.key_passphrase:
|
|
48
|
+
try:
|
|
49
|
+
return base64.b64decode(self.key_passphrase.encode()).decode()
|
|
50
|
+
except Exception:
|
|
51
|
+
return self.key_passphrase
|
|
52
|
+
return None
|
|
53
|
+
|
|
44
54
|
@staticmethod
|
|
45
55
|
def encode_password(password: str) -> str:
|
|
46
56
|
"""Encode password for storage."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qssh
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Quick SSH session manager - save your VM credentials and connect with a single command
|
|
5
5
|
Author: joan-code6
|
|
6
6
|
License-Expression: MIT
|
|
@@ -47,7 +47,7 @@ pip install qssh
|
|
|
47
47
|
### 1. Add a new session
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
qssh add
|
|
50
|
+
qssh add myserver
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
You'll be prompted for:
|
|
@@ -59,7 +59,7 @@ You'll be prompted for:
|
|
|
59
59
|
### 2. Connect to your VM
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
qssh
|
|
62
|
+
qssh myserver
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
That's it! You're connected.
|
|
@@ -79,8 +79,8 @@ That's it! You're connected.
|
|
|
79
79
|
## Examples
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
# Add a session for your
|
|
83
|
-
qssh add
|
|
82
|
+
# Add a session for your myserver VM
|
|
83
|
+
qssh add myserver
|
|
84
84
|
# Host: 192.168.1.100
|
|
85
85
|
# Username: admin
|
|
86
86
|
# Port [22]: 22
|
|
@@ -88,7 +88,7 @@ qssh add outcraft
|
|
|
88
88
|
# Password: ********
|
|
89
89
|
|
|
90
90
|
# Now just connect with:
|
|
91
|
-
qssh
|
|
91
|
+
qssh myserver
|
|
92
92
|
|
|
93
93
|
# List all your sessions
|
|
94
94
|
qssh list
|
|
@@ -97,7 +97,7 @@ qssh list
|
|
|
97
97
|
qssh remove old-server
|
|
98
98
|
|
|
99
99
|
# Show details of a session
|
|
100
|
-
qssh show
|
|
100
|
+
qssh show myserver
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
## Using SSH Keys
|
|
@@ -110,12 +110,19 @@ qssh add myserver
|
|
|
110
110
|
# Username: deploy
|
|
111
111
|
# Port [22]: 22
|
|
112
112
|
# Auth type (password/key) [password]: key
|
|
113
|
-
# Key file path: ~/.ssh/
|
|
113
|
+
# Key file path [~/.ssh/id_rsa]: ~/.ssh/my_key
|
|
114
|
+
# Key passphrase (leave empty if none): ********
|
|
114
115
|
```
|
|
115
116
|
|
|
117
|
+
Supported key types:
|
|
118
|
+
- RSA
|
|
119
|
+
- Ed25519
|
|
120
|
+
- ECDSA
|
|
121
|
+
- DSS
|
|
122
|
+
|
|
116
123
|
## Configuration
|
|
117
124
|
|
|
118
|
-
Sessions are stored in `~/.qssh/sessions.yaml`. Passwords are stored encoded (not plaintext) but for maximum security, consider using SSH keys
|
|
125
|
+
Sessions are stored in `~/.qssh/sessions.yaml`. Passwords and key passphrases are stored encoded (not plaintext) but for maximum security, consider using SSH keys without passphrases or with an SSH agent.
|
|
119
126
|
|
|
120
127
|
## License
|
|
121
128
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|