summaryrefslogtreecommitdiff
path: root/client_manager.py
blob: 84bf3df482b8c7e88e95daccd583a52de1d2442e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

# main logic 

from sys_init import setup
setup()

import pyinotify
import subprocess
import re
import pwd
import os
import json
import atexit
import fcntl

def get_ssh_port(pid):
    '''
    Jan 31 07:50:28 vultr sshd[43690]: Accepted publickey for root from 210.10.76.5 port 43730 ssh2: ED25519 SHA256:qz9ffMCb3vPlabn3ZHee00qIPBxkDiUiVSorcUkGdII
    Jan 31 07:50:28 vultr sshd[43690]: pam_unix(sshd:session): session opened for user root(uid=0) by root(uid=0)
    Jan 31 07:50:29 vultr sshd[43693]: Received disconnect from 210.10.76.5 port 43730:11: disconnected by user
    Jan 31 07:50:29 vultr sshd[43693]: Disconnected from user root 210.10.76.5 port 43730
    Jan 31 07:50:29 vultr sshd[43690]: pam_unix(sshd:session): session closed for user root
    '''
    '''
    # less efficient but readable
    pid = '33216'
    pids = []
    port = -1
    for l in lines:
        l = l.split()
        if pid == l[1]:
            commonstring = l[8]
            for l2 in lines:
                if commonstring in l2.split():
                    pids.append(l2.split()[1])
    for i in pids:
        for l in lines:
            l = l.split()
            if i in l[1] and "*:" in l[8]:
                port = l[8][2:]
    '''
    try:
        lines = subprocess.check_output("lsof -i -n | grep sshd", shell=True, text=True).splitlines()
        pid = str(pid)
        pids = {l.split()[1] for l in lines
                if pid == l.split()[1] or
                l.split()[8] in [x.split()[8] for x in lines if x.split()[1] == pid]}

        port = next(l.split()[8][2:] for l in lines if l.split()[1] in pids and "*:" in l.split()[8])
        return port
    except:
        return -1

def get_keyname(fingerprint):
    auth_file = os.path.join(os.path.expanduser(f"~{user}"), ".ssh", "authorized_keys")
    try:
        result = subprocess.run(['ssh-keygen', '-lf', f'{auth_file}'], capture_output=True, text=True, shell=False, check=True)   
        # Raises CalledProcessError if command fails, for check = true

        #256 SHA256:d9CBxSLBLzLjYGZDCsO6V+lddlN/elK1hGDBS56cbTo user@host (ED25519)        
        for line in result.stdout.strip().split('\n'):
            parts = line.split()
            if fingerprint in parts[1]:
                return parts[2]
        return 'not found'
        
    except subprocess.CalledProcessError as e:
        print(f"Error executing command: {e}")
        return None
    except Exception as e:
        print(f"Error processing output: {e}")
        return None

def write_data(data):
    with open('/tmp/ssh_sessions.json', 'w') as f:
        # Get exclusive lock for writing
        fcntl.flock(f.fileno(), fcntl.LOCK_EX)
        try:
            # Clear file and write new data
            f.seek(0)
            json.dump(data, f)
            f.truncate()
        finally:
            fcntl.flock(f.fileno(), fcntl.LOCK_UN)

def handle_log_change(event):
    global last_position
    with open(event.pathname, 'r') as f:
        # Go to end and read new lines
        f.seek(last_position)
        new_lines = f.readlines()
        last_position = f.tell()
        
        for line in new_lines:
            #print(f"New log: {line.strip()}")
            line = line.strip() 
            if f"Accepted publickey for {user}" in line:
                pid = re.search(r'\[(\d+)\]', line.split()[4]).group(1)
                port = get_ssh_port(pid)
                keyname = get_keyname(line.split()[15]) 
                srcip = line.split()[10]
                #print(pid, port, keyname, srcip)
                ssh_sessions[pid] = [srcip, keyname, port]
                ssh_sessions[pid] = {
                    'srcip': srcip,
                    'key': keyname,
                    'pubport': port
                }
                write_data(ssh_sessions)
            if "pam_unix(sshd:session): session closed" in line:
                pid = re.search(r'\[(\d+)\]', line.split()[4]).group(1)
                if pid in ssh_sessions:
                    del ssh_sessions[pid]
                else:
                    print("WARNING! PID NOT FOUND IN SESSION CACHE!")
                write_data(ssh_sessions) 

            print("db: ",ssh_sessions)



user = "root"

# init last_position
global last_position
with open('/var/log/secure', 'r') as f:
    f.seek(0, 2)
    last_position = f.tell()

global ssh_sessions 
ssh_sessions = {}

wm = pyinotify.WatchManager()
wm.add_watch('/var/log/secure', pyinotify.IN_MODIFY, handle_log_change)
notifier = pyinotify.Notifier(wm)

print("Watching /var/log/secure...")
notifier.loop()