├── Src ├── Client │ ├── __init__.py │ └── ClientScript.py ├── Server │ ├── __init__.py │ ├── __init__.pyc │ ├── Properties.pyc │ ├── ConnectClients.pyc │ ├── DBConnection.pyc │ ├── DHKeyGenerator.pyc │ ├── FetchClients.pyc │ ├── MailNotifier.pyc │ ├── config.xml │ ├── MailNotifier.py │ ├── FetchClients.py │ ├── DBConnection.py │ ├── Properties.py │ ├── DHKeyGenerator.py │ ├── ConnectClients.py │ └── ServerMain.py ├── Test.sh ├── Test.bat └── DBScripts │ └── CreateTables.sql ├── Demo └── Demo.mp4 ├── DesignDoc.docx ├── DesignDoc.pdf └── ReadMe.txt /Src/Client/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Src/Server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Src/Test.sh: -------------------------------------------------------------------------------- 1 | python -m Server.ServerMain -------------------------------------------------------------------------------- /Src/Test.bat: -------------------------------------------------------------------------------- 1 | python -m Server.ServerMain -------------------------------------------------------------------------------- /Demo/Demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Demo/Demo.mp4 -------------------------------------------------------------------------------- /DesignDoc.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/DesignDoc.docx -------------------------------------------------------------------------------- /DesignDoc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/DesignDoc.pdf -------------------------------------------------------------------------------- /Src/Server/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/__init__.pyc -------------------------------------------------------------------------------- /Src/Server/Properties.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/Properties.pyc -------------------------------------------------------------------------------- /Src/Server/ConnectClients.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/ConnectClients.pyc -------------------------------------------------------------------------------- /Src/Server/DBConnection.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/DBConnection.pyc -------------------------------------------------------------------------------- /Src/Server/DHKeyGenerator.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/DHKeyGenerator.pyc -------------------------------------------------------------------------------- /Src/Server/FetchClients.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/FetchClients.pyc -------------------------------------------------------------------------------- /Src/Server/MailNotifier.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vspandan/CollectClientStats/master/Src/Server/MailNotifier.pyc -------------------------------------------------------------------------------- /Src/Server/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Src/DBScripts/CreateTables.sql: -------------------------------------------------------------------------------- 1 | create database IF NOT EXISTS crossoverproject; 2 | 3 | use crossoverproject; 4 | 5 | create table IF NOT EXISTS clientstats ( 6 | ID int NOT NULL AUTO_INCREMENT, 7 | username varchar(255), 8 | timestamp varchar(255), 9 | hostname varchar(255), 10 | IPAddress varchar(255) not NULL, 11 | CPUCount char(2), 12 | CPUUsage varchar(255), 13 | MemoryUsage varchar(255), 14 | Uptime varchar(255), 15 | primary key(ID)); -------------------------------------------------------------------------------- /ReadMe.txt: -------------------------------------------------------------------------------- 1 | 1. Prequisites 2 | . smtplib 3 | . python 2.7 4 | . pywin32 5 | . paramiko 1.8.0 6 | . pycrypto-2.6.1 7 | . psutils 8 | . Diffiehellman 0.13.3 9 | . mysql-connector-python-2.1.4-py2.7-winx64.msi 10 | . mysql 11 | 2. Please configure the SMTP, DB variables in Properties.py file 12 | 3. Connect to MySql instance and run CreateTables.sql 13 | 4. Change directory to root directory of this project and execute the application using following command in CLI. 14 | python -m Server.ServerMain 15 | 5. Requirements not Covered: 16 | Windows security event logs (in case of windows OS). -------------------------------------------------------------------------------- /Src/Server/MailNotifier.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from Server.Properties import SMTP_SERVER,SMTP_PORT, SMTP_EMAIL_ADDR, SMTP_SENDER_PASSWORD, SMTP_SUCC_MSG, SMTP_ERR_MSG 3 | 4 | #Notifies user with a mail 5 | def sendMail (receivers, message): 6 | try: 7 | servr = smtplib.SMTP(SMTP_SERVER,SMTP_PORT) 8 | servr.ehlo() 9 | servr.starttls() 10 | servr.ehlo() 11 | servr.login(SMTP_EMAIL_ADDR, SMTP_SENDER_PASSWORD) 12 | servr.sendmail(SMTP_EMAIL_ADDR, receivers, message) 13 | servr.quit() 14 | print(SMTP_SUCC_MSG) 15 | except smtplib.SMTPException as e: 16 | print(SMTP_ERR_MSG, e) -------------------------------------------------------------------------------- /Src/Server/FetchClients.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as etree 2 | from Server.Properties import CONFIG_FILE 3 | 4 | #assume config.xml has fixed structure and extracts client information 5 | def getClientInfo(): 6 | clients=[] 7 | root=etree.parse(CONFIG_FILE).getroot(); 8 | if root.tag == "clients": 9 | for child in root: 10 | if child.tag == "client": 11 | limits={} 12 | for ch in child: 13 | if ch.tag == "alert": 14 | limits[ch.attrib['type']]=ch.attrib['limit'] 15 | attributes=child.attrib 16 | attributes["limits"]=limits 17 | clients.append(attributes) 18 | return clients -------------------------------------------------------------------------------- /Src/Server/DBConnection.py: -------------------------------------------------------------------------------- 1 | import mysql.connector 2 | from Server.Properties import INSERT_QUERY, DB_PASSWORD, DB_USER, DB_NAME, DB_HOST,\ 3 | DB_ERR_MSG 4 | 5 | #Establishes connection with MySql with MySql DB_HOST 6 | def getDBConnection(): 7 | db = mysql.connector.connect(user=DB_USER, password=DB_PASSWORD, 8 | host=DB_HOST, 9 | database=DB_NAME) 10 | return db; 11 | 12 | 13 | def insertClientStats(data): 14 | try: 15 | db=getDBConnection(); 16 | cur=db.cursor() 17 | cur.execute(INSERT_QUERY,(data['username'],data["timestamp"],data["hostname"],data["IPAddress"],data["CPUCount"],data["CPUUsage"],data["MemoryUsage"],data["Uptime"])); 18 | db.commit() 19 | db.close() 20 | except Exception as e: 21 | print(DB_ERR_MSG,e) -------------------------------------------------------------------------------- /Src/Server/Properties.py: -------------------------------------------------------------------------------- 1 | SMTP_EMAIL_ADDR = "spandan.veggalam@gmail.com" 2 | SMTP_SERVER = "smtp.gmail.com" 3 | SMTP_SENDER_PASSWORD = "123" 4 | SMTP_PORT = 587 5 | 6 | SMTP_SUCC_MSG = "Successfully sent email" 7 | SMTP_ERR_MSG = "Error: unable to send email" 8 | AUTH_FAILED_ERR_MSG = "Client authentication failed." 9 | CLIENT_CONN_ERR_MSG ="Issue in Client Connection" 10 | DB_ERR_MSG = "Error in Handling Database" 11 | ERR_PROCESSING_CLI_RESP = "Error in processing response from client" 12 | CONFIG_FILE = "server/config.xml" 13 | CLIENT_SCRIPT_SRC = "Client\ClientScript.py" 14 | CLIENT_SCRIPT_REMOTE_DEST = 'ClientScript.py' 15 | SSH_REMOTE_EXEC_CMD = "python "+CLIENT_SCRIPT_REMOTE_DEST 16 | 17 | DB_USER = 'root' 18 | DB_PASSWORD = '123' 19 | DB_HOST = '127.0.0.1' 20 | DB_NAME = 'crossoverproject' 21 | INSERT_QUERY = "INSERT INTO `clientstats` (`username`, `timestamp`, `hostname`, `IPAddress`, `CPUCount`, `CPUUsage`,`MemoryUsage`,`Uptime`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)" -------------------------------------------------------------------------------- /Src/Server/DHKeyGenerator.py: -------------------------------------------------------------------------------- 1 | import random, base64 2 | from Crypto.Cipher import AES, ARC2 3 | 4 | # Diffie Hellman Key Generator 5 | class DHKeyGenerator(object): 6 | def __init__(self): 7 | self.prime = 17 8 | self.root = 3 9 | self.secretKey = self.secretnumber() 10 | 11 | # selects a random number as secret key 12 | def secretnumber (self): 13 | secret = int(random.randint(0,100)) 14 | return secret 15 | 16 | # generates public key 17 | def getPublicKey (self): 18 | publicKey = (self.root ** self.secretKey) % self.prime 19 | return publicKey 20 | 21 | #with public key of other party generates shared key 22 | def sharedKeyGen(self,senderPubKey): 23 | return (senderPubKey ** self.secretKey) % self.prime 24 | 25 | # unpad padded bits 26 | def _unpad(s): 27 | return s[:-ord(s[len(s)-1:])] 28 | 29 | #decrypts AES key which is encrypted with RC2 using key exchanged with Diffie Hellman 30 | def ceaserExtractPlain(key,msg): 31 | cipher = ARC2.new(str(key), ARC2.MODE_CFB, "12345678") 32 | return cipher.decrypt(msg) 33 | 34 | # performs AES decryption 35 | def decrypt(key,enc): 36 | enc = base64.b64decode(enc) 37 | iv = enc[:AES.block_size] 38 | cipher = AES.new(key, AES.MODE_CBC, iv) 39 | return _unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8') -------------------------------------------------------------------------------- /Src/Server/ConnectClients.py: -------------------------------------------------------------------------------- 1 | import paramiko, ast 2 | from Server.Properties import CLIENT_SCRIPT_SRC, CLIENT_SCRIPT_REMOTE_DEST, SSH_REMOTE_EXEC_CMD, AUTH_FAILED_ERR_MSG,\ 3 | CLIENT_CONN_ERR_MSG 4 | from Server.DHKeyGenerator import decrypt, DHKeyGenerator, ceaserExtractPlain 5 | 6 | """ 7 | establishes ssh connection to client 8 | transfers client script and executes remotely 9 | receives encrypted response and decrypts it 10 | """ 11 | def connectClient(ipaddr,SMTP_PORT,uname,pword): 12 | try: 13 | s = paramiko.SSHClient() 14 | d=DHKeyGenerator() 15 | s.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 16 | s.connect(ipaddr,SMTP_PORT,username=uname,password=pword,timeout=4) 17 | sftp = s.open_sftp() 18 | sftp.put(CLIENT_SCRIPT_SRC, CLIENT_SCRIPT_REMOTE_DEST) 19 | pk=d.getPublicKey() 20 | stdin, stdout, stderr = s.exec_command(SSH_REMOTE_EXEC_CMD + " -publicKey "+ str(pk)) 21 | stdin.close() 22 | stderr.close() 23 | output=ast.literal_eval(stdout.read()) 24 | sk=d.sharedKeyGen(output["pubkey"]) 25 | key=ceaserExtractPlain(sk,output["enckey"]) 26 | return decrypt(key,output["resp"]) 27 | except paramiko.AuthenticationException as e: 28 | print(ipaddr + ":" + AUTH_FAILED_ERR_MSG, e) 29 | except Exception as e: 30 | print(CLIENT_CONN_ERR_MSG, e) 31 | return None -------------------------------------------------------------------------------- /Src/Server/ServerMain.py: -------------------------------------------------------------------------------- 1 | from Server.FetchClients import getClientInfo 2 | from Server.ConnectClients import connectClient 3 | from threading import Thread 4 | from Server.DBConnection import insertClientStats 5 | from Server.MailNotifier import sendMail 6 | import xml.etree.ElementTree as etree 7 | from Server.Properties import ERR_PROCESSING_CLI_RESP 8 | 9 | #processes the xml response received from client 10 | def processResponse(response): 11 | root=etree.fromstring(response) 12 | data={} 13 | if root.tag == "client": 14 | data['username']=client['username'] 15 | for child in root: 16 | if child.tag == "timestamp": 17 | data["timestamp"]=child.text 18 | continue 19 | if child.tag == "hostname": 20 | data["hostname"]=child.text 21 | continue 22 | if child.tag == "IP_Address": 23 | data["IPAddress"]=child.text 24 | continue 25 | if child.tag == "Statistics": 26 | for ch in child: 27 | if ch.tag == "CPU_Count": 28 | data["CPUCount"]=ch.text 29 | continue 30 | if ch.tag == "CPU": 31 | for chld in ch: 32 | if chld.tag == "idle": 33 | data["CPUUsage"]=str(100-float(chld.text)) 34 | continue 35 | if ch.tag == "Memory": 36 | for chld in ch: 37 | if chld.tag == "PercentageUsed": 38 | data["MemoryUsage"]=chld.text 39 | continue 40 | if ch.tag == "UpTime": 41 | data["Uptime"]=ch.text 42 | continue 43 | return data 44 | 45 | #Thread function, that connects to client and processes the response. 46 | def runThread(client): 47 | try: 48 | response = connectClient(client["ip"], int(client["port"]), client["username"], client["password"]) 49 | if response is None: 50 | return 51 | data=processResponse(response) 52 | insertClientStats(data) 53 | if float(data['MemoryUsage']) > float(client['limits']['memory'][:-1]) or float(data['CPUUsage']) > float(client['limits']['cpu'][:-1]): 54 | print("Sending alert notification") 55 | receivers = [client['mail']] 56 | message = "From: " + client['mail'] + "\nTo: " + client['username'] +" <" + client['mail'] + "> \nSubject: Memory/CPU usage limit exceeded \n\nMemory Usage: " + data['MemoryUsage'] + "%\nCPU Usage: " + data['CPUUsage'] + "%\n" 57 | sendMail(receivers, message) 58 | except Exception as e: 59 | print(ERR_PROCESSING_CLI_RESP,e); 60 | 61 | 62 | #start point of server script 63 | if __name__ == '__main__': 64 | #fetch clients 65 | clients=getClientInfo() 66 | for client in clients: 67 | #create a thread for each client and run 68 | t=Thread(target=runThread,kwargs={"client": client}) 69 | t.setName(client["ip"]) 70 | t.start() 71 | t.join(10) -------------------------------------------------------------------------------- /Src/Client/ClientScript.py: -------------------------------------------------------------------------------- 1 | import psutil, time, datetime, sys, random, socket, base64, hashlib 2 | import xml.etree.ElementTree as etree 3 | from Crypto import Random 4 | from Crypto.Cipher import AES 5 | from Crypto.Cipher import ARC2 6 | 7 | bs=32 8 | 9 | # Diffie Hellman Key Generator 10 | class DHKeyGenerator(object): 11 | def __init__(self): 12 | self.prime = 17 13 | self.root = 3 14 | self.secretKey = self.secretnumber() 15 | 16 | # selects a random number as secret key 17 | def secretnumber (self): 18 | secret = int(random.randint(0,100)) 19 | return secret 20 | 21 | # generates public key 22 | def getPublicKey (self): 23 | publicKey = (self.root ** self.secretKey) % self.prime 24 | return publicKey 25 | 26 | #with public key of other party generates shared key 27 | def sharedKeyGen(self,senderPubKey): 28 | return (senderPubKey ** self.secretKey) % self.prime 29 | 30 | # performs AES encryption on the message 31 | def encrypt(key,msg): 32 | raw = _pad(msg) 33 | iv = Random.new().read(AES.block_size) 34 | cipher = AES.new(key, AES.MODE_CBC, iv) 35 | return base64.b64encode(iv + cipher.encrypt(raw)) 36 | 37 | # generates pad bits 38 | def _pad(s): 39 | return s + (bs - len(s) % bs) * chr(bs - len(s) % bs) 40 | 41 | #encrypts AES key with RC2 using key exchanged by Diffie Hellman 42 | def ceaserGenCipher(key,msg): 43 | cipher = ARC2.new(str(key), ARC2.MODE_CFB, "12345678") 44 | return cipher.encrypt(msg) 45 | 46 | """ 47 | collects system stats and generates xml 48 | xml is encrypted using AES 49 | AES key is encrypted using RC2 50 | """ 51 | def collectSystemStats(d,key,sk,pk): 52 | e=etree.Element("client") 53 | e1=etree.SubElement(e,"timestamp") 54 | e1.text=str(datetime.datetime.now()) 55 | e2=etree.SubElement(e,"hostname") 56 | e2.text=socket.gethostname() 57 | e3=etree.SubElement(e,"IP_Address") 58 | e3.text=str(socket.gethostbyname(socket.gethostname())) 59 | e4=etree.SubElement(e,"Statistics") 60 | 61 | all_cpu_usage=psutil.cpu_times_percent(interval=0.4, percpu=False) 62 | e6=etree.SubElement(e4,"CPU") 63 | c=etree.SubElement(e6,"UserUsage") 64 | c.text=str(all_cpu_usage.user) 65 | c=etree.SubElement(e6,"SystemUsage") 66 | c.text=str(all_cpu_usage.system) 67 | c=etree.SubElement(e6,"idle") 68 | c.text=str(all_cpu_usage.idle) 69 | 70 | e5=etree.SubElement(e4,"Indvidual_CPU") 71 | e6=etree.SubElement(e4,"CPU_Count") 72 | cpu_count=str(psutil.cpu_count()) 73 | e6.text=cpu_count; 74 | 75 | all_cpu_usage=psutil.cpu_times_percent(interval=0.4, percpu=True) 76 | i=1 77 | for cpu_usage in all_cpu_usage: 78 | e6=etree.SubElement(e5,"CPU_Usage") 79 | c=etree.SubElement(e6,"CPU") 80 | c.text=str(i) 81 | i=i+1 82 | c=etree.SubElement(e6,"UserUsage") 83 | c.text=str(cpu_usage.user) 84 | c=etree.SubElement(e6,"SystemUsage") 85 | c.text=str(cpu_usage.system) 86 | c=etree.SubElement(e6,"idle") 87 | c.text=str(cpu_usage.idle) 88 | 89 | memory=psutil.virtual_memory() 90 | e5=etree.SubElement(e4,"Memory") 91 | m=etree.SubElement(e5,"Total") 92 | m.text=str(memory.total) 93 | m=etree.SubElement(e5,"PercentageUsed") 94 | m.text=str(memory.percent) 95 | m=etree.SubElement(e5,"Used") 96 | m.text=str(memory.used) 97 | m=etree.SubElement(e5,"Free") 98 | m.text=str(memory.free) 99 | 100 | memory=psutil.swap_memory() 101 | e5=etree.SubElement(e4,"Swap") 102 | s=etree.SubElement(e5,"Total") 103 | s.text=str(memory.total) 104 | s=etree.SubElement(e5,"PercentageUsed") 105 | s.text=str(memory.percent) 106 | s=etree.SubElement(e5,"Used") 107 | s.text=str(memory.used) 108 | s=etree.SubElement(e5,"Free") 109 | s.text=str(memory.free) 110 | 111 | uptime = time.time() - psutil.boot_time() 112 | e5=etree.SubElement(e4,"UpTime") 113 | e5.text = str(datetime.timedelta(uptime/1000000)) 114 | xml=etree.tostring(e) 115 | 116 | output={} 117 | output["enckey"]=ceaserGenCipher(sk,key) 118 | output["pubkey"]=pk 119 | output["resp"]=encrypt(key, xml) 120 | print(output) 121 | 122 | 123 | #Start point of client script execution 124 | if __name__ == '__main__': 125 | args=sys.argv[1:] 126 | 127 | if len(args) == 2 and args[0] == "-publicKey": 128 | d=DHKeyGenerator() 129 | pk=d.getPublicKey() 130 | sk=d.sharedKeyGen(int(args[1])) 131 | random_key = "this is a random passphrase" 132 | key = hashlib.sha256(random_key.encode()).digest() 133 | collectSystemStats(d,key,sk,pk) --------------------------------------------------------------------------------