Initial commit

This commit is contained in:
Florian Rupp 2023-03-12 22:44:14 +01:00
parent 5fdb32606d
commit fd49806b9d
4 changed files with 166 additions and 0 deletions

14
Dockerfile Normal file
View file

@ -0,0 +1,14 @@
FROM python:latest
WORKDIR /usr/app/src
COPY prometheus-ssh-exporter.py ./
COPY requirements.txt ./
RUN pip3 install -r requirements.txt
# Set this to the port you chose in the prometheus-ssh-exporter.py file
EXPOSE 9999
CMD ["python", "./prometheus-ssh-exporter.py"]

View file

@ -1,2 +1,6 @@
# prometheus-ssh-exporter
A Prometheus exporter for monitoring SSH connections
This is a personal project I wrote because I couldn't find any prometheus exporters that I could monitor my SSH connections with.
Note: The docker file is only for development. This program is not meant to be used in a docker container.

147
prometheus-ssh-exporter.py Normal file
View file

@ -0,0 +1,147 @@
import prometheus_client
import subprocess
import time
import argparse
SERVER_HOST = '0.0.0.0'
# Set this port to whatever you want this service to bind to
SERVER_PORT = 9999
FETCH_INTERVAL = 5
class Session:
"""
This class is used to create a Session object containing info on an SSH session
"""
def __init__(self, name, tty, from_, login, idle, jcpu, pcpu, what):
self.name = name
self.tty = tty
self.from_ = from_
self.login = login
self.idle = idle
self.jcpu = jcpu
self.pcpu = pcpu
self.what = what
def __str__(self):
return "%s %s" % (self.name, self.from_)
def __repr__(self):
return "%s %s" % (self.name, self.from_)
def __eq__(self, other):
return self.login == other.login and self.tty == other.tty and self.from_ == other.from_
def to_dict(self):
return {
'name': self.name,
'tty': self.tty,
'from_': self.from_,
'login': self.login,
'idle': self.idle,
'jcpu': self.jcpu,
'pcpu': self.pcpu,
'what': self.what
}
def to_list(self):
return [self.name, self.tty, self.from_, self.login, self.idle, self.jcpu, self.pcpu, self.what]
def contains_user_list(user, other_user_list):
for other_user in other_user_list:
if are_equal(user, other_user):
#print("Found equals: %s and %s" % (user, other_user))
return True
#print("Not found: %s in %s" % (user, other_user_list))
return False
def are_equal(user_list, other_user_list):
assert len(user_list) == len(other_user_list)
for i in range(4):
if user_list[i] != other_user_list[i]:
return False
return True
"""
Returns a list of Session objects
The string generated by the "w -h" command looks like follows:
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
<username> pts/0 192.168.0.X 14:57 1:54m 0.12s 0.12s -bash
"""
def get_w_data():
# Retrieve command output and discard the header
w_data = subprocess.check_output(['w', '-h']).decode('utf-8')
w_data = w_data.split('\n')[:-1]
users = []
# Generate a list of Session objects from the output of the command
for user_string in w_data:
user_string_cleaned = user_string.split()
if len(user_string_cleaned) == 8:
users.append(Session(*user_string_cleaned))
return users
def parse_arguments():
global FETCH_INTERVAL, SERVER_PORT, SERVER_HOST
parser = argparse.ArgumentParser(
prog='python prometheus-ssh-exporter.py',
description='Prometheus exporter for info about SSH sessions')
parser.add_argument('-H', '--host', type=str, default='0.0.0.0', help='Hostname to bind to')
parser.add_argument('-p', '--port', type=int, default=9999, help='Port for the server to listen to')
parser.add_argument('-i', '--interval', type=int, default=15, help='Interval in seconds to fetch SSH sessions data')
args = parser.parse_args()
FETCH_INTERVAL = args.interval
SERVER_PORT = args.port
SERVER_HOST = args.host
"""
This program exports the number of SSH sessions as a metric "ssh_num_sessions" for prometheus.
It applies a label to each increment or decrement of that number, containing the remote IP address.
That way we can filter by the remote IP in Grafana, getting the number of SSH sessions by IP address,
or sum them up to get the total number of sessions.
"""
if __name__ == '__main__':
parse_arguments()
# Start up the server to expose the metrics.
prometheus_client.start_http_server(SERVER_PORT)
print("Started metrics server bound to {}:{}".format(SERVER_HOST, SERVER_PORT))
num_sessions = []
gauge_num_sessions = prometheus_client.Gauge('ssh_num_sessions', 'Number of SSH sessions', ['remote_ip'])
data = get_w_data()
list_data = [user.to_list() for user in data]
# Initial metrics
print("Connections at startup:")
for i in range(len(list_data)):
gauge_num_sessions.labels(remote_ip=list_data[i][2]).inc()
print("Initial connection: {}".format(list_data[i]))
# Generate some requests.
print("Looking for SSH connection changes at interval {}".format(FETCH_INTERVAL))
while True:
list_old_data = list_data
data = get_w_data()
list_data = [user.to_list() for user in data]
num_sessions = len(data)
for i in range(num_sessions):
# Looking for newly found SSH sessions
if not contains_user_list(list_data[i], list_old_data):
print("Session connected: %s" % list_data[i])
gauge_num_sessions.labels(remote_ip=list_data[i][2]).dec()
for i in range(len(list_old_data)):
# Looking for SSH sessions that no longer exist
if not contains_user_list(list_old_data[i], list_data):
print("Session disconnected: %s" % list_old_data[i])
gauge_num_sessions.labels(remote_ip=list_old_data[i][2]).inc()
time.sleep(FETCH_INTERVAL)

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
prometheus_client==0.16.0