ssm-cli 1.1.0.dev3__tar.gz → 1.1.0.dev4__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.
- {ssm_cli-1.1.0.dev3/ssm_cli.egg-info → ssm_cli-1.1.0.dev4}/PKG-INFO +2 -1
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/pyproject.toml +2 -1
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ssh_proxy/forwarding.py +88 -5
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4/ssm_cli.egg-info}/PKG-INFO +2 -1
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli.egg-info/requires.txt +1 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/LICENCE +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/README.md +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/setup.cfg +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/__init__.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/__main__.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/app.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/aws.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/cli.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/click.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/config.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/instances.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/logging.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/selectors/__init__.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/selectors/first.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/selectors/tui.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ssh_proxy/__init__.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ssh_proxy/channels.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ssh_proxy/server.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ssh_proxy/shell.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ssh_proxy/socket.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/ui.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli/xdg.py +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli.egg-info/SOURCES.txt +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli.egg-info/dependency_links.txt +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli.egg-info/entry_points.txt +0 -0
- {ssm_cli-1.1.0.dev3 → ssm_cli-1.1.0.dev4}/ssm_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssm-cli
|
|
3
|
-
Version: 1.1.0.
|
|
3
|
+
Version: 1.1.0.dev4
|
|
4
4
|
Summary: CLI tool to help with SSM functionality, aimed at adminstrators
|
|
5
5
|
Author-email: Simon Fletcher <simon.fletcher@lexisnexisrisk.com>
|
|
6
6
|
License: MIT License
|
|
@@ -39,6 +39,7 @@ Requires-Dist: rich
|
|
|
39
39
|
Requires-Dist: confclasses>=0.3.2
|
|
40
40
|
Requires-Dist: xdg_base_dirs
|
|
41
41
|
Requires-Dist: rich-click
|
|
42
|
+
Requires-Dist: psutil
|
|
42
43
|
Dynamic: license-file
|
|
43
44
|
|
|
44
45
|
# SSM CLI
|
|
@@ -15,6 +15,7 @@ The manager side is the code that runs in a separate process and is responsible
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import io
|
|
18
|
+
import os
|
|
18
19
|
import socket
|
|
19
20
|
from typing import Dict, Any, List
|
|
20
21
|
import time
|
|
@@ -209,6 +210,7 @@ class PortForwardingManagerProcess(multiprocessing.Process):
|
|
|
209
210
|
def __init__(self, pipe: Connection, instance: Instance):
|
|
210
211
|
self.pipe = pipe
|
|
211
212
|
self.instance = instance
|
|
213
|
+
self.parent_pid = os.getppid()
|
|
212
214
|
|
|
213
215
|
with io.StringIO() as f:
|
|
214
216
|
save(CONFIG, f)
|
|
@@ -230,12 +232,54 @@ class PortForwardingManagerProcess(multiprocessing.Process):
|
|
|
230
232
|
for logger_name, level in CONFIG.log.loggers.items():
|
|
231
233
|
set_log_level(level, name=logger_name)
|
|
232
234
|
|
|
235
|
+
|
|
233
236
|
logger.info("manager process started")
|
|
234
|
-
|
|
237
|
+
|
|
238
|
+
# get parent process object
|
|
239
|
+
import psutil
|
|
240
|
+
try:
|
|
241
|
+
self.parent = psutil.Process(self.parent_pid)
|
|
242
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
|
|
243
|
+
logger.warning(f"Could not get parent process object: {e}")
|
|
244
|
+
|
|
245
|
+
# detaching from parent
|
|
246
|
+
try:
|
|
247
|
+
self.detatch_from_proxy()
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error(f"failed to detatch from proxy: {e}")
|
|
250
|
+
|
|
251
|
+
# cleanup handlers
|
|
252
|
+
import atexit
|
|
253
|
+
self.cleanup_called = False
|
|
254
|
+
atexit.register(self.safe_cleanup)
|
|
255
|
+
|
|
235
256
|
self.sessions = []
|
|
236
257
|
self.pipe.send("ready")
|
|
237
258
|
while True:
|
|
238
|
-
|
|
259
|
+
if not self.parent.is_running():
|
|
260
|
+
logger.info("proxy process is no longer running, exiting manager process")
|
|
261
|
+
break
|
|
262
|
+
if not self.process_messages():
|
|
263
|
+
logger.info("proxy pipe closed, exiting manager process")
|
|
264
|
+
break
|
|
265
|
+
|
|
266
|
+
self.safe_cleanup()
|
|
267
|
+
|
|
268
|
+
def safe_cleanup(self):
|
|
269
|
+
""" Used to ensure cleanup only called once """
|
|
270
|
+
if self.cleanup_called:
|
|
271
|
+
return
|
|
272
|
+
self.cleanup_called = True
|
|
273
|
+
self.cleanup()
|
|
274
|
+
|
|
275
|
+
def cleanup(self):
|
|
276
|
+
""" Any cleanup tasks go here """
|
|
277
|
+
logger.info("cleaning up")
|
|
278
|
+
self.close_all_sessions()
|
|
279
|
+
|
|
280
|
+
def process_messages(self):
|
|
281
|
+
try:
|
|
282
|
+
if self.pipe.poll(timeout=1.0):
|
|
239
283
|
msg, data = self.pipe.recv()
|
|
240
284
|
logger.debug(f"received {msg} {data} from proxy")
|
|
241
285
|
reply = None
|
|
@@ -249,9 +293,12 @@ class PortForwardingManagerProcess(multiprocessing.Process):
|
|
|
249
293
|
if reply is not None:
|
|
250
294
|
logger.debug(f"sending {reply} to proxy")
|
|
251
295
|
self.pipe.send(reply)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
296
|
+
|
|
297
|
+
return True
|
|
298
|
+
except (EOFError, OSError) as e:
|
|
299
|
+
logger.warning(f"pipe to proxy closed: {e}")
|
|
300
|
+
return False
|
|
301
|
+
|
|
255
302
|
def open_session(self, host: str, remote_port: int):
|
|
256
303
|
session = None
|
|
257
304
|
for s in self.sessions:
|
|
@@ -274,6 +321,12 @@ class PortForwardingManagerProcess(multiprocessing.Process):
|
|
|
274
321
|
if session.session_id == session_id:
|
|
275
322
|
session.close()
|
|
276
323
|
self.sessions.remove(session)
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
def close_all_sessions(self):
|
|
327
|
+
for session in self.sessions:
|
|
328
|
+
session.close()
|
|
329
|
+
self.sessions = []
|
|
277
330
|
|
|
278
331
|
def is_open(self, session_id: str) -> bool:
|
|
279
332
|
for session in self.sessions:
|
|
@@ -284,6 +337,36 @@ class PortForwardingManagerProcess(multiprocessing.Process):
|
|
|
284
337
|
self.sessions.remove(session)
|
|
285
338
|
break
|
|
286
339
|
return False
|
|
340
|
+
|
|
341
|
+
def detatch_from_proxy(self):
|
|
342
|
+
"""Detach from the parent process to avoid zombies on unix like systems, this will need refining after more testing"""
|
|
343
|
+
if os.name != 'nt':
|
|
344
|
+
os.setsid()
|
|
345
|
+
else:
|
|
346
|
+
import ctypes
|
|
347
|
+
kernel32 = ctypes.windll.kernel32
|
|
348
|
+
|
|
349
|
+
# Set up independent console control handler
|
|
350
|
+
kernel32.SetConsoleCtrlHandler(None, True)
|
|
351
|
+
|
|
352
|
+
# Set independent process priority
|
|
353
|
+
current_process = kernel32.GetCurrentProcess()
|
|
354
|
+
NORMAL_PRIORITY_CLASS = 0x00000020
|
|
355
|
+
kernel32.SetPriorityClass(current_process, NORMAL_PRIORITY_CLASS)
|
|
356
|
+
|
|
357
|
+
# Detach from console if present
|
|
358
|
+
console_window = kernel32.GetConsoleWindow()
|
|
359
|
+
if console_window:
|
|
360
|
+
kernel32.FreeConsole()
|
|
361
|
+
|
|
362
|
+
# Break out of job object (most important)
|
|
363
|
+
ntdll = ctypes.windll.ntdll
|
|
364
|
+
ntdll.NtSetInformationProcess(
|
|
365
|
+
kernel32.GetCurrentProcess(),
|
|
366
|
+
0x11,
|
|
367
|
+
ctypes.byref(ctypes.c_ulong(0)),
|
|
368
|
+
ctypes.sizeof(ctypes.c_ulong)
|
|
369
|
+
)
|
|
287
370
|
|
|
288
371
|
def get_free_port(bind_host="127.0.0.1"):
|
|
289
372
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssm-cli
|
|
3
|
-
Version: 1.1.0.
|
|
3
|
+
Version: 1.1.0.dev4
|
|
4
4
|
Summary: CLI tool to help with SSM functionality, aimed at adminstrators
|
|
5
5
|
Author-email: Simon Fletcher <simon.fletcher@lexisnexisrisk.com>
|
|
6
6
|
License: MIT License
|
|
@@ -39,6 +39,7 @@ Requires-Dist: rich
|
|
|
39
39
|
Requires-Dist: confclasses>=0.3.2
|
|
40
40
|
Requires-Dist: xdg_base_dirs
|
|
41
41
|
Requires-Dist: rich-click
|
|
42
|
+
Requires-Dist: psutil
|
|
42
43
|
Dynamic: license-file
|
|
43
44
|
|
|
44
45
|
# SSM CLI
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|