diff options
| author | hc <hc@email.ch> | 2025-02-01 10:21:49 +0800 |
|---|---|---|
| committer | hc <hc@email.ch> | 2025-02-01 10:21:49 +0800 |
| commit | 6e2bd1f5053f5244d1294ba5ae2c0ffc047743b6 (patch) | |
| tree | fb1d970b1f952eb372a19d51e360e62c64d36ec6 | |
firstcommit
| -rw-r--r-- | client_ls.py | 21 | ||||
| -rw-r--r-- | client_manager.py | 140 | ||||
| -rw-r--r-- | sys_init.py | 49 |
3 files changed, 210 insertions, 0 deletions
diff --git a/client_ls.py b/client_ls.py new file mode 100644 index 0000000..6aa3a7e --- /dev/null +++ b/client_ls.py | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | |||
| 2 | #lists client information | ||
| 3 | |||
| 4 | import fcntl | ||
| 5 | import json | ||
| 6 | import os | ||
| 7 | |||
| 8 | |||
| 9 | def read_data(): | ||
| 10 | try: | ||
| 11 | with open('/tmp/ssh_sessions.json', 'r') as f: | ||
| 12 | # Get shared lock for reading | ||
| 13 | fcntl.flock(f.fileno(), fcntl.LOCK_SH) | ||
| 14 | try: | ||
| 15 | return json.load(f) | ||
| 16 | finally: | ||
| 17 | fcntl.flock(f.fileno(), fcntl.LOCK_UN) | ||
| 18 | except (FileNotFoundError, ValueError): | ||
| 19 | return {} | ||
| 20 | |||
| 21 | print(read_data()) | ||
diff --git a/client_manager.py b/client_manager.py new file mode 100644 index 0000000..84bf3df --- /dev/null +++ b/client_manager.py | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | |||
| 2 | # main logic | ||
| 3 | |||
| 4 | from sys_init import setup | ||
| 5 | setup() | ||
| 6 | |||
| 7 | import pyinotify | ||
| 8 | import subprocess | ||
| 9 | import re | ||
| 10 | import pwd | ||
| 11 | import os | ||
| 12 | import json | ||
| 13 | import atexit | ||
| 14 | import fcntl | ||
| 15 | |||
| 16 | def get_ssh_port(pid): | ||
| 17 | ''' | ||
| 18 | Jan 31 07:50:28 vultr sshd[43690]: Accepted publickey for root from 210.10.76.5 port 43730 ssh2: ED25519 SHA256:qz9ffMCb3vPlabn3ZHee00qIPBxkDiUiVSorcUkGdII | ||
| 19 | Jan 31 07:50:28 vultr sshd[43690]: pam_unix(sshd:session): session opened for user root(uid=0) by root(uid=0) | ||
| 20 | Jan 31 07:50:29 vultr sshd[43693]: Received disconnect from 210.10.76.5 port 43730:11: disconnected by user | ||
| 21 | Jan 31 07:50:29 vultr sshd[43693]: Disconnected from user root 210.10.76.5 port 43730 | ||
| 22 | Jan 31 07:50:29 vultr sshd[43690]: pam_unix(sshd:session): session closed for user root | ||
| 23 | ''' | ||
| 24 | ''' | ||
| 25 | # less efficient but readable | ||
| 26 | pid = '33216' | ||
| 27 | pids = [] | ||
| 28 | port = -1 | ||
| 29 | for l in lines: | ||
| 30 | l = l.split() | ||
| 31 | if pid == l[1]: | ||
| 32 | commonstring = l[8] | ||
| 33 | for l2 in lines: | ||
| 34 | if commonstring in l2.split(): | ||
| 35 | pids.append(l2.split()[1]) | ||
| 36 | for i in pids: | ||
| 37 | for l in lines: | ||
| 38 | l = l.split() | ||
| 39 | if i in l[1] and "*:" in l[8]: | ||
| 40 | port = l[8][2:] | ||
| 41 | ''' | ||
| 42 | try: | ||
| 43 | lines = subprocess.check_output("lsof -i -n | grep sshd", shell=True, text=True).splitlines() | ||
| 44 | pid = str(pid) | ||
| 45 | pids = {l.split()[1] for l in lines | ||
| 46 | if pid == l.split()[1] or | ||
| 47 | l.split()[8] in [x.split()[8] for x in lines if x.split()[1] == pid]} | ||
| 48 | |||
| 49 | port = next(l.split()[8][2:] for l in lines if l.split()[1] in pids and "*:" in l.split()[8]) | ||
| 50 | return port | ||
| 51 | except: | ||
| 52 | return -1 | ||
| 53 | |||
| 54 | def get_keyname(fingerprint): | ||
| 55 | auth_file = os.path.join(os.path.expanduser(f"~{user}"), ".ssh", "authorized_keys") | ||
| 56 | try: | ||
| 57 | result = subprocess.run(['ssh-keygen', '-lf', f'{auth_file}'], capture_output=True, text=True, shell=False, check=True) | ||
| 58 | # Raises CalledProcessError if command fails, for check = true | ||
| 59 | |||
| 60 | #256 SHA256:d9CBxSLBLzLjYGZDCsO6V+lddlN/elK1hGDBS56cbTo user@host (ED25519) | ||
| 61 | for line in result.stdout.strip().split('\n'): | ||
| 62 | parts = line.split() | ||
| 63 | if fingerprint in parts[1]: | ||
| 64 | return parts[2] | ||
| 65 | return 'not found' | ||
| 66 | |||
| 67 | except subprocess.CalledProcessError as e: | ||
| 68 | print(f"Error executing command: {e}") | ||
| 69 | return None | ||
| 70 | except Exception as e: | ||
| 71 | print(f"Error processing output: {e}") | ||
| 72 | return None | ||
| 73 | |||
| 74 | def write_data(data): | ||
| 75 | with open('/tmp/ssh_sessions.json', 'w') as f: | ||
| 76 | # Get exclusive lock for writing | ||
| 77 | fcntl.flock(f.fileno(), fcntl.LOCK_EX) | ||
| 78 | try: | ||
| 79 | # Clear file and write new data | ||
| 80 | f.seek(0) | ||
| 81 | json.dump(data, f) | ||
| 82 | f.truncate() | ||
| 83 | finally: | ||
| 84 | fcntl.flock(f.fileno(), fcntl.LOCK_UN) | ||
| 85 | |||
| 86 | def handle_log_change(event): | ||
| 87 | global last_position | ||
| 88 | with open(event.pathname, 'r') as f: | ||
| 89 | # Go to end and read new lines | ||
| 90 | f.seek(last_position) | ||
| 91 | new_lines = f.readlines() | ||
| 92 | last_position = f.tell() | ||
| 93 | |||
| 94 | for line in new_lines: | ||
| 95 | #print(f"New log: {line.strip()}") | ||
| 96 | line = line.strip() | ||
| 97 | if f"Accepted publickey for {user}" in line: | ||
| 98 | pid = re.search(r'\[(\d+)\]', line.split()[4]).group(1) | ||
| 99 | port = get_ssh_port(pid) | ||
| 100 | keyname = get_keyname(line.split()[15]) | ||
| 101 | srcip = line.split()[10] | ||
| 102 | #print(pid, port, keyname, srcip) | ||
| 103 | ssh_sessions[pid] = [srcip, keyname, port] | ||
| 104 | ssh_sessions[pid] = { | ||
| 105 | 'srcip': srcip, | ||
| 106 | 'key': keyname, | ||
| 107 | 'pubport': port | ||
| 108 | } | ||
| 109 | write_data(ssh_sessions) | ||
| 110 | if "pam_unix(sshd:session): session closed" in line: | ||
| 111 | pid = re.search(r'\[(\d+)\]', line.split()[4]).group(1) | ||
| 112 | if pid in ssh_sessions: | ||
| 113 | del ssh_sessions[pid] | ||
| 114 | else: | ||
| 115 | print("WARNING! PID NOT FOUND IN SESSION CACHE!") | ||
| 116 | write_data(ssh_sessions) | ||
| 117 | |||
| 118 | print("db: ",ssh_sessions) | ||
| 119 | |||
| 120 | |||
| 121 | |||
| 122 | user = "root" | ||
| 123 | |||
| 124 | # init last_position | ||
| 125 | global last_position | ||
| 126 | with open('/var/log/secure', 'r') as f: | ||
| 127 | f.seek(0, 2) | ||
| 128 | last_position = f.tell() | ||
| 129 | |||
| 130 | global ssh_sessions | ||
| 131 | ssh_sessions = {} | ||
| 132 | |||
| 133 | wm = pyinotify.WatchManager() | ||
| 134 | wm.add_watch('/var/log/secure', pyinotify.IN_MODIFY, handle_log_change) | ||
| 135 | notifier = pyinotify.Notifier(wm) | ||
| 136 | |||
| 137 | print("Watching /var/log/secure...") | ||
| 138 | notifier.loop() | ||
| 139 | |||
| 140 | |||
diff --git a/sys_init.py b/sys_init.py new file mode 100644 index 0000000..5bcd618 --- /dev/null +++ b/sys_init.py | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | |||
| 2 | #installs pip packages and lsof for debian | ||
| 3 | |||
| 4 | import subprocess | ||
| 5 | import sys | ||
| 6 | import os | ||
| 7 | import importlib | ||
| 8 | |||
| 9 | def check_os(): | ||
| 10 | if os.path.exists('/etc/os-release'): | ||
| 11 | with open('/etc/os-release', 'r') as f: | ||
| 12 | content = f.read().lower() | ||
| 13 | if 'debian' in content or 'ubuntu' in content: | ||
| 14 | return 'debian' | ||
| 15 | elif 'fedora' in content: | ||
| 16 | return 'fedora' | ||
| 17 | return None | ||
| 18 | |||
| 19 | def install_package(package): | ||
| 20 | try: | ||
| 21 | importlib.import_module(package) | ||
| 22 | print(f"{package} is already installed") | ||
| 23 | except ImportError: | ||
| 24 | try: | ||
| 25 | subprocess.check_call([sys.executable, "-m", "pip", "install", package]) | ||
| 26 | print(f"Successfully installed {package}") | ||
| 27 | except subprocess.CalledProcessError: | ||
| 28 | print(f"Failed to install {package}") | ||
| 29 | |||
| 30 | def install_lsof(): | ||
| 31 | os_type = check_os() | ||
| 32 | if os_type == 'debian': | ||
| 33 | try: | ||
| 34 | subprocess.check_call(['apt-get', 'update']) | ||
| 35 | subprocess.check_call(['apt-get', 'install', '-y', 'lsof']) | ||
| 36 | print("lsof installed successfully") | ||
| 37 | except subprocess.CalledProcessError: | ||
| 38 | print("Failed to install lsof") | ||
| 39 | else: | ||
| 40 | print("Not a Debian-based system, skipping lsof installation") | ||
| 41 | |||
| 42 | def setup(): | ||
| 43 | # Install pyinotify | ||
| 44 | install_package('pyinotify') | ||
| 45 | # Install lsof if on Debian | ||
| 46 | install_lsof() | ||
| 47 | |||
| 48 | if __name__ == "__main__": | ||
| 49 | setup() | ||
