Skip to content

Commit

Permalink
Merge pull request #5 from dignajar/JIRA-3733/prometheus-last-connection
Browse files Browse the repository at this point in the history
JIRA-3733 - Prometheus last connection
  • Loading branch information
dignajar authored Feb 16, 2022
2 parents f96c7db + b9e65b8 commit b3c9ff1
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9.9-alpine3.14
FROM python:3.9.10-alpine3.14

ENV PYTHONUNBUFFERED=1
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
Expand Down
10 changes: 7 additions & 3 deletions files/aldap/logs.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import os
import json
from datetime import datetime, timezone
from aldap.http import HTTP
from aldap.parameters import Parameters


class Logs:

def __init__(self, objectName:str):
def __init__(self, objectName:str, includeRequestIP:bool=True):
self.param = Parameters()
self.http = HTTP()

self.objectName = objectName
self.level = self.param.get('LOG_LEVEL', default='INFO')
self.format = self.param.get('LOG_FORMAT', default='JSON')
self.includeRequestIP = includeRequestIP

def __print__(self, level:str, extraFields:dict):
fields = {
'date': datetime.now(tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S"),
'level': level,
'objectName': self.objectName,
'ip': self.http.getRequestIP(),
# 'base-url': self.http.getRequestBaseURL(),
# 'referrer': self.http.getRequestReferrer()
}

# Include request IP
if self.includeRequestIP:
fields['ip'] = self.http.getRequestIP()

# Include extra fields custom by the user
if extraFields is not None:
fields.update(extraFields)
Expand Down
47 changes: 47 additions & 0 deletions files/aldap/prometheus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import redis
from datetime import datetime
from aldap.parameters import Parameters
from aldap.logs import Logs


class Prometheus:

def __init__(self):
self.param = Parameters()
self.logs = Logs(self.__class__.__name__, False)

# Redis server
self.enabled = self.param.get('PROMETHEUS', False, bool)
self.redis_host = self.param.get('REDIS_HOST', 'localhost', str)
self.redis_port = self.param.get('REDIS_PORT', 6379, int)
self.redis_metric_expiration = self.param.get('REDIS_METRIC_EXPIRATION', 600, int) # 600 seconds == 10 minutes

if self.enabled:
self.logs.debug({'message': 'Connecting to Redis.'})
try:
self.redis = redis.Redis(host=self.redis_host, port=self.redis_port, db=0, decode_responses=True)
self.redis.ping()
self.logs.debug({'message': 'Connected to Redis.'})
except redis.exceptions.RedisError:
self.enabled = False
self.logs.error({'message': 'There was an error trying to connect to Redis.'})

def addMetric(self, key, value):
'''
Add item to Redis, key => value
'''
if not self.enabled:
return False
value = 'aldap_'+value # Add prefix "aldap_" to the metric
self.redis.set(key, value)
self.redis.expire(key, self.redis_metric_expiration)
return True

def addLastConnection(self, username:str):
'''
Add user last connection to Redis in Prometheus format
'''
if not self.enabled:
return False
self.addMetric('last_connection_'+username, 'last_connection{username="'+username+'", date="'+str(datetime.now())+'"} 1')
return True
18 changes: 15 additions & 3 deletions files/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import logging
from os import environ
from flask import Flask
from flask import request, session, render_template, redirect, url_for
from flask_session import Session
Expand All @@ -10,6 +8,7 @@
from aldap.bruteforce import BruteForce
from aldap.parameters import Parameters
from aldap.aldap import Aldap
from aldap.prometheus import Prometheus

# --- Parameters --------------------------------------------------------------
param = Parameters()
Expand All @@ -19,7 +18,6 @@

# --- Logging -----------------------------------------------------------------
logs = Logs('main')
#logging.getLogger('werkzeug').setLevel(logging.ERROR) # Set Flask log-level to ERROR

# --- Flask -------------------------------------------------------------------
app = Flask(__name__)
Expand All @@ -40,6 +38,7 @@
app.config.from_object(__name__)
Session(app)


# --- Routes ------------------------------------------------------------------
@app.route('/login', methods=['POST'])
def login():
Expand All @@ -60,6 +59,8 @@ def login():
aldap = Aldap()
if aldap.authentication(username, password):
logs.info({'message':'Login: Authentication successful, adding user and groups to the Session.'})
prometheus = Prometheus()
prometheus.addLastConnection(username)
session['username'] = username
session['groups'] = aldap.getUserGroups(username)
if (protocol in ['http', 'https']) and callback:
Expand All @@ -71,6 +72,7 @@ def login():
bruteForce.addFailure()
return redirect(url_for('index', protocol=protocol, callback=callback, alert=True))


@app.route('/auth', methods=['GET'])
def auth():
logs.debug({'message':'/auth requested.'})
Expand All @@ -91,6 +93,8 @@ def auth():

if authorization:
logs.info({'message':'Basic-Auth: Authorization successful.'})
prometheus = Prometheus()
prometheus.addLastConnection(username)
return 'Authorized', 200, [('x-username', username),('x-groups', ",".join(matchedGroups))]

logs.warning({'message': 'Basic-Auth: Authorization failed.'})
Expand All @@ -108,6 +112,8 @@ def auth():

if authorization:
logs.info({'message':'Session: Authorization successful.'})
prometheus = Prometheus()
prometheus.addLastConnection(session['username'])
return 'Authorized', 200, [('x-username', session['username']),('x-groups', ",".join(matchedGroups))]

logs.warning({'message': 'Session: Authorization failed.'})
Expand All @@ -116,6 +122,7 @@ def auth():
logs.warning({'message': 'Session: Authentication failed.'})
return 'Unauthorized', 401


@app.route('/logout', methods=['GET', 'POST'])
def logout():
logs.debug({'message':'/logout requested.'})
Expand All @@ -125,6 +132,7 @@ def logout():
pass
return redirect(url_for('index'))


@app.route('/', methods=['GET'])
def index():
logs.debug({'message':'/ requested.'})
Expand Down Expand Up @@ -157,12 +165,14 @@ def index():

return render_template('login.html', layout=layout)


@app.before_request
def beforeAll():
logs.debug({'message':'Before-all.'})
if bruteForce.isIpBlocked():
return 'Unauthorized', 401


@app.after_request
def afterAll(response):
logs.debug({'message':'After-all.'})
Expand All @@ -175,10 +185,12 @@ def afterAll(response):
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
return response


@app.errorhandler(HTTPException)
def handle_exception(e):
logs.error({'message': 'Exception.', 'code': e.code, 'name': e.name, 'description': e.description})
return 'Not Found', 404


if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000, ssl_context='adhoc', debug=False, use_reloader=False)
3 changes: 2 additions & 1 deletion files/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ flask==2.0.2
Flask-Session==0.4.0
pyopenssl==20.0.1
python-ldap==3.4.0
cachelib==0.2.0
cachelib==0.2.0
redis==4.1.2

0 comments on commit b3c9ff1

Please sign in to comment.