2024-07-03 20:35:16 +00:00
#!/usr/bin/python3
# MIT License
# Copyright (c) 2024 Thomas Williams - https://git.server.wales/thomas
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
2024-07-02 19:42:34 +00:00
import pyodbc
2024-07-04 15:47:37 +00:00
import time
2024-08-19 15:25:22 +00:00
import requests
from retrying import retry
2024-07-06 14:47:41 +00:00
from odbcReferences import driver
2024-08-21 16:08:11 +00:00
from config import logRetentionDaysIPBlock , logRetentionDaysHost , logRetentionDaysURL , maximumSQLAttempts , loggingMode
2024-07-04 13:48:22 +00:00
from datetime import datetime , timedelta
2024-07-02 19:42:34 +00:00
class logsManager :
def __init__ ( self , server , database , username , password ) :
2024-07-06 14:47:41 +00:00
self . conn_str = ' DRIVER= ' + driver + ' ;SERVER= ' + server + ' ;DATABASE= ' + database + ' ;UID= ' + username + ' ;PWD= ' + password
2024-07-02 19:42:34 +00:00
2024-07-03 20:23:36 +00:00
def insertHost ( self , hostname , ipAddress ) :
2024-07-04 15:47:37 +00:00
currentAttempts = 1
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
while currentAttempts < = maximumSQLAttempts :
try :
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
cursor . execute ( " SELECT COUNT(*) FROM monutil_hosts WHERE hostname = ? " , hostname )
if cursor . fetchone ( ) [ 0 ] == 0 :
cursor . execute ( " INSERT INTO monutil_hosts (hostname, ipAddress) VALUES (?, ?) " , hostname , ipAddress )
conn . commit ( )
conn . close ( )
break
else :
cursor . execute ( " UPDATE monutil_hosts SET ipAddress = ? WHERE hostname = ? " , ipAddress , hostname )
conn . close ( )
break
except pyodbc . Error as ex :
currentAttempts + = 1
print ( " SQL Error: {} " . format ( str ( ex ) ) )
if not currentAttempts < = maximumSQLAttempts :
raise
time . sleep ( 1 )
2024-07-03 20:23:36 +00:00
def insertHostLog ( self , hostname , ipAddress , log_time , cpu , memory ) :
2024-07-04 15:47:37 +00:00
currentAttempts = 1
self . insertHost ( hostname , ipAddress )
2024-08-21 12:34:04 +00:00
self . deleteOldLogs ( " hostlogs " , " logTime " , logRetentionDaysHost )
2024-07-04 15:47:37 +00:00
while currentAttempts < = maximumSQLAttempts :
2024-07-02 19:42:34 +00:00
2024-07-04 15:47:37 +00:00
try :
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
cursor . execute ( " INSERT INTO monutil_hostlogs (hostname, logTime, cpu, memory) VALUES (?, ?, ?, ?) " , hostname , log_time , cpu , memory )
conn . commit ( )
conn . close ( )
break
except pyodbc . Error as ex :
2024-07-02 19:42:34 +00:00
2024-07-04 15:47:37 +00:00
currentAttempts + = 1
print ( " Error inserting data: {} " . format ( str ( ex ) ) )
if not currentAttempts < = maximumSQLAttempts :
raise
time . sleep ( 1 )
2024-07-03 20:23:36 +00:00
2024-08-19 15:25:22 +00:00
@retry ( stop_max_attempt_number = 3 , wait_fixed = 1000 )
def geoIPLookup ( self , ip , token ) :
global delayUntil
try :
2024-08-21 11:15:48 +00:00
2024-08-21 10:54:10 +00:00
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
timeThreshold = datetime . now ( ) - timedelta ( days = 1 )
2024-08-19 15:25:22 +00:00
2024-08-21 11:15:48 +00:00
cursor . execute ( " SELECT blockedipaddress, monutil_geoip.hostname, city, region, country, loc, org, postal, timezone FROM monutil_geoip INNER JOIN monutil_ipblock ON monutil_geoip.logID = monutil_ipblock.logID WHERE logtime >= ? AND blockedipaddress = ? " , timeThreshold , ip )
2024-08-19 15:25:22 +00:00
2024-08-21 10:54:10 +00:00
rows = cursor . fetchall ( )
geoinfo = [ ]
for row in rows :
geoinfo = {
" ip " : row . blockedipaddress ,
" hostname " : row . hostname ,
" city " : row . city ,
" region " : row . region ,
" country " : row . country ,
" loc " : row . loc ,
" org " : row . org ,
" postal " : row . postal ,
" timezone " : row . timezone ,
}
2024-08-21 11:15:48 +00:00
2024-08-21 10:54:10 +00:00
if not geoinfo :
2024-08-21 11:15:48 +00:00
2024-08-21 10:54:10 +00:00
if ' delayUntil ' in locals ( ) and datetime . now ( ) < delayUntil :
print ( " Rate limit exceeded. Please wait before trying again. " )
return None
2024-08-21 11:15:48 +00:00
url = f " https://ipinfo.io/ { ip } ?token= { token } "
2024-08-21 10:54:10 +00:00
2024-08-21 11:15:48 +00:00
response = requests . get ( url )
response . raise_for_status ( )
data = response . json ( )
2024-08-19 15:25:22 +00:00
2024-08-21 11:15:48 +00:00
if response . status_code == 429 :
2024-08-21 10:54:10 +00:00
2024-08-21 11:15:48 +00:00
print ( " Rate limit exceeded. Please wait before trying again. " )
today = datetime . now ( )
2024-08-19 15:25:22 +00:00
2024-08-21 10:54:10 +00:00
if today . month == 12 :
2024-08-19 15:25:22 +00:00
2024-08-21 10:54:10 +00:00
delayUntil = datetime ( today . year + 1 , 1 , 1 )
2024-08-19 15:25:22 +00:00
2024-08-21 10:54:10 +00:00
else :
2024-08-19 15:25:22 +00:00
2024-08-21 10:54:10 +00:00
delayUntil = datetime ( today . year , today . month + 1 , 1 )
2024-08-19 15:25:22 +00:00
2024-08-21 10:54:10 +00:00
return None
2024-08-19 15:25:22 +00:00
2024-08-21 11:15:48 +00:00
geoinfo = {
" ip " : data . get ( " ip " ) ,
" hostname " : data . get ( " hostname " ) ,
" city " : data . get ( " city " ) ,
" region " : data . get ( " region " ) ,
" country " : data . get ( " country " ) ,
" loc " : data . get ( " loc " ) ,
" org " : data . get ( " org " ) ,
" postal " : data . get ( " postal " ) ,
" timezone " : data . get ( " timezone " ) ,
}
2024-08-19 15:25:22 +00:00
return geoinfo
except Exception as e :
print ( " An error occurred whilst doing a GeoIP lookup. " , e )
raise
def insertIPBlock ( self , hostname , ipAddress , blockedIPAddress , jail , live , logTime , token ) :
2024-08-19 16:36:43 +00:00
2024-08-19 15:25:22 +00:00
currentAttempts = 1
self . insertHost ( hostname , ipAddress )
2024-08-21 12:34:04 +00:00
self . deleteOldLogs ( " iplogs " , " logTime " , logRetentionDaysIPBlock )
2024-08-19 15:25:22 +00:00
while currentAttempts < = maximumSQLAttempts :
try :
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
2024-08-19 16:36:43 +00:00
2024-08-20 10:42:15 +00:00
if int ( live ) == 0 :
2024-08-19 15:25:22 +00:00
2024-08-19 16:36:43 +00:00
if loggingMode == ' mssql ' or loggingMode == ' mariadb ' :
cursor . execute ( " UPDATE monutil_ipblock SET live = 0 WHERE hostname = ? AND blockedIPAddress = ? AND jail = ? AND live = 1 " , hostname , blockedIPAddress , jail )
elif loggingMode == ' postgresql ' :
cursor . execute ( " UPDATE monutil_ipblock SET live = FALSE WHERE hostname = ? AND blockedIPAddress = ? AND jail = ? AND live = TRUE " , hostname , blockedIPAddress , jail )
else :
raise Exception ( " A serious error has occurred. Unrecognised DBMS. " )
2024-08-19 15:25:22 +00:00
autoIncrementID = 0
else :
2024-08-19 16:36:43 +00:00
2024-08-19 15:25:22 +00:00
if loggingMode == ' mariadb ' :
cursor . execute ( " INSERT INTO monutil_ipblock (hostname, blockedIPAddress, jail, live, logTime) VALUES (?, ?, ?, ?, ?) " , hostname , blockedIPAddress , jail , live , logTime )
cursor . execute ( " SELECT LAST_INSERT_ID() " )
2024-08-19 16:36:43 +00:00
autoIncrementID = cursor . fetchone ( ) [ 0 ]
2024-08-19 15:25:22 +00:00
elif loggingMode == ' mssql ' :
2024-08-19 16:36:43 +00:00
query = cursor . execute ( " INSERT INTO monutil_ipblock (hostname, blockedIPAddress, jail, live, logTime) OUTPUT INSERTED.logID VALUES (?, ?, ?, ?, ?) " , hostname , blockedIPAddress , jail , live , logTime )
autoIncrementID = query . fetchone ( ) [ 0 ]
2024-08-19 15:25:22 +00:00
elif loggingMode == ' postgresql ' :
2024-08-19 16:36:43 +00:00
cursor . execute ( " INSERT INTO monutil_ipblock (hostname, blockedIPAddress, jail, live, logTime) VALUES (?, ?, ?, ?, ?) RETURNING logID; " , hostname , blockedIPAddress , jail , live , logTime )
autoIncrementID = cursor . fetchone ( ) [ 0 ]
2024-08-19 15:25:22 +00:00
else :
raise Exception ( " A serious error has occurred. Unrecognised DBMS. " )
conn . commit ( )
conn . close ( )
if autoIncrementID > 0 :
2024-08-19 16:36:43 +00:00
2024-08-19 15:25:22 +00:00
geoinfo = self . geoIPLookup ( blockedIPAddress , token )
self . insertGeoIPInfo ( autoIncrementID , geoinfo [ " hostname " ] , geoinfo [ " city " ] , geoinfo [ " region " ] , geoinfo [ " country " ] , geoinfo [ " loc " ] , geoinfo [ " org " ] , geoinfo [ " postal " ] , geoinfo [ " timezone " ] )
break
except pyodbc . Error as ex :
currentAttempts + = 1
print ( " Error inserting/updating data in monutil_ipblock: " , ex )
if not currentAttempts < = maximumSQLAttempts :
raise
time . sleep ( 1 )
def insertGeoIPInfo ( self , logID , hostname , city , region , country , loc , org , postal , timezone ) :
currentAttempts = 1
while currentAttempts < = maximumSQLAttempts :
try :
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
cursor . execute ( " INSERT INTO monutil_geoip (logID, hostname, city, region, country, loc, org, postal, timezone) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) " , logID ,
hostname , city , region , country , loc , org , postal , timezone )
conn . commit ( )
conn . close ( )
break
except pyodbc . Error as ex :
currentAttempts + = 1
print ( " Error inserting data in monutil_geoip: " , ex )
if not currentAttempts < = maximumSQLAttempts :
raise
time . sleep ( 1 )
2024-07-03 20:23:36 +00:00
def insertURLLog ( self , hostname , ipAddress , log_time , url , responseTime ) :
2024-07-04 15:47:37 +00:00
currentAttempts = 1
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
self . insertHost ( hostname , ipAddress )
2024-08-21 12:34:04 +00:00
self . deleteOldLogs ( " urllogs " , " logTime " , logRetentionDaysURL )
2024-07-04 15:47:37 +00:00
while currentAttempts < = maximumSQLAttempts :
try :
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
cursor . execute ( " INSERT INTO monutil_urlLogs (hostname, url, logTime, responseTime) VALUES (?, ?, ?, ?) " , hostname , url , log_time , responseTime )
conn . commit ( )
conn . close ( )
break
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
except pyodbc . Error as ex :
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
currentAttempts + = 1
print ( " Error inserting data into monutil_urlLogs: " , ex )
2024-07-03 20:23:36 +00:00
2024-07-04 15:47:37 +00:00
if not currentAttempts < = maximumSQLAttempts :
raise
time . sleep ( 1 )
2024-07-04 13:48:22 +00:00
2024-08-21 12:34:04 +00:00
def deleteOldLogs ( self , logType , logTimeColumn , logRetentionDays ) :
2024-07-04 15:47:37 +00:00
currentAttempts = 1
2024-07-04 13:48:22 +00:00
2024-07-04 15:47:37 +00:00
while currentAttempts < = maximumSQLAttempts :
2024-07-04 13:48:22 +00:00
2024-07-04 15:47:37 +00:00
try :
2024-07-04 13:48:22 +00:00
2024-08-21 12:34:04 +00:00
if logType == ' hostlogs ' :
tableName = ' monutil_hostLogs '
elif logType == ' urllogs ' :
tableName = ' monutil_urlLogs '
elif logType == ' iplogs ' :
2024-11-08 17:03:31 +00:00
tableName = ' monutil_ipblock '
2024-08-21 12:34:04 +00:00
2024-07-04 15:47:37 +00:00
conn = pyodbc . connect ( self . conn_str )
cursor = conn . cursor ( )
2024-07-04 13:48:22 +00:00
2024-07-06 14:47:41 +00:00
if loggingMode == ' mssql ' :
oldestLogQuery = f " SELECT TOP 1 { logTimeColumn } FROM { tableName } ORDER BY { logTimeColumn } ASC "
2024-08-19 16:36:43 +00:00
elif loggingMode == ' mariadb ' or loggingMode == ' postgresql ' :
2024-07-06 14:47:41 +00:00
oldestLogQuery = f " SELECT { logTimeColumn } FROM { tableName } ORDER BY { logTimeColumn } ASC LIMIT 1 "
2024-07-04 15:47:37 +00:00
cursor . execute ( oldestLogQuery )
2024-07-04 16:07:16 +00:00
if cursor . rowcount > 0 :
oldestLogTime = cursor . fetchone ( ) [ 0 ]
if cursor . rowcount > 0 and oldestLogTime < datetime . now ( ) - timedelta ( days = int ( logRetentionDays ) ) :
2024-07-04 15:47:37 +00:00
2024-08-21 12:34:04 +00:00
if logType == ' hostlogs ' :
deleteQuery = f " DELETE FROM { tableName } WHERE { logTimeColumn } < ? "
cursor . execute ( deleteQuery , datetime . now ( ) - timedelta ( days = int ( logRetentionDaysHost ) ) )
conn . commit ( )
elif logType == ' urllogs ' :
deleteQuery = f " DELETE FROM { tableName } WHERE { logTimeColumn } < ? "
cursor . execute ( deleteQuery , datetime . now ( ) - timedelta ( days = int ( logRetentionDaysURL ) ) )
conn . commit ( )
elif logType == ' iplogs ' :
2024-08-21 16:05:29 +00:00
deleteQuery = f " DELETE FROM monutil_geoip WHERE logID IN (SELECT logID FROM monutil_ipblock WHERE { logTimeColumn } < ?) "
2024-08-21 12:34:04 +00:00
cursor . execute ( deleteQuery , datetime . now ( ) - timedelta ( days = int ( logRetentionDaysIPBlock ) ) )
conn . commit ( )
deleteQuery = f " DELETE FROM monutil_ipblock WHERE { logTimeColumn } < ? "
cursor . execute ( deleteQuery , datetime . now ( ) - timedelta ( days = int ( logRetentionDaysIPBlock ) ) )
conn . commit ( )
2024-07-04 15:47:37 +00:00
break
except pyodbc . Error as ex :
currentAttempts + = 1
print ( " Error deleting old logs: {} " . format ( str ( ex ) ) )
if not currentAttempts < = maximumSQLAttempts :
raise
time . sleep ( 1 )