#!/usr/bin/python3 # ---------------------------------------- # (c) 2022-present https://ipthreat.net # ---------------------------------------- # setup instructions for Linux auth log # - sudo apt install python-pip python3-pip python-dateutil python3-dateutil && pip install pytz && pip3 install pytz # - you can get an api key created from https://ipthreat.net/account?tab=apikeys # - set crontab environment variable: IP_THREAT_API_KEY="YOUR_API_KEY" # - more info about env var: https://stackoverflow.com/questions/2229825/where-can-i-set-environment-variables-that-crontab-will-use # - example with an hourly crontab, change path to your Linux distro if needed # - change the 59 to your favorite minute or ideally a random number of 0-59 to help spread the load out # - (sudo crontab -l ; echo '59 * * * * python3 /opt/scripts/ipthreat.py /var/log/auth.log /opt/scripts/auth.log.csv')| crontab - # ---------------------------------------- # # Usage # ---------------------------------------- # python3 ipthreat.py [input file] [output csv file] # ---------------------------------------- # # imports import csv import os.path import pytz import re import requests import sys import dateutil.parser as dateParser from dateutil.tz import tzlocal # ips to ignore, these will never be submitted - fill in with your own ip address IPS_IGNORE = { }; # must set this env var on your host machine IP_THREAT_API_KEY = os.environ.get('IP_THREAT_API_KEY') IP_THREAT_POST_URL = 'https://api.ipthreat.net/api/bulkreportcsv' IP_THREAT_FLAGS = 'BruteForce' IP_THREAT_SYSTEM_ATTACKED = 'SSH' # command line arguments: [input log file name] [output csv file name] logFileName = sys.argv[1] csvFileName = sys.argv[2] # parse log file using this regex, you can replace with your own regex, be sure to keep a timestamp, notes and ipaddress group, these are required reStr = r'^(?P[A-Z0-9\s]+[0-9]+:[0-9]+:[0-9]+)[^:]+:\s(?PInvalid\suser\s|failed\spassword\s|did\snot\sreceive\sidentification\sstring\s).*?\s(?P[^\s]+)\s+port\s+[0-9]+' regExp = re.compile(reStr, re.IGNORECASE) # store the last offset read from the file so we can process a log file every hour or day and not repeat the same data logFileNameLastPiece = os.path.basename(logFileName) offsetFileName = csvFileName + '.offset' # assume local time from tzlocal call, can replace with a hard-coded timezone if your log doesn't use the system local timezone localtimezone = tzlocal() # open up a new csv file, need the newline='' otherwise, strangely, a blank line is inserted in the csv file after every row foundRow = False lastLogFileOffset = 0 with open(csvFileName, 'w', newline='', encoding='utf-8') as csvFile: csvWriter = csv.writer(csvFile, lineterminator='\r\n') csvWriter.writerow(['IP', 'Counter', 'Flags', 'Notes', 'SystemAttacked', 'Timestamp']) # open up the log file to parse with open(logFileName, 'r') as logFile: # if we have an offset file, see if we need to seek into the log file if os.path.isfile(offsetFileName): with open(offsetFileName, 'r') as offsetFileRead: offsetStr = offsetFileRead.readline() startPos = int(offsetStr) logFile.seek(0, os.SEEK_END) # if offset is now beyond end of file, log file was truncated, start over if logFile.tell() >= startPos: logFile.seek(startPos) else: logFile.seek(0, os.SEEK_SET) # read the log file line by line line = '' while True: line = logFile.readline().rstrip() if not line: break # perform regex match against current line mat = regExp.match(line) # did we get a match? if mat is not None: #print(line) foundRow = True # parse out the timestamp into a date object dateObj = dateParser.parse(mat['timestamp']) # convert to utc time dateObj = dateObj.astimezone(pytz.UTC) # format a proper iso8601 UTC date/time using Z as the timezone dateStr = dateObj.isoformat().replace('+00:00', 'Z') # grab the ip address ipAddress = mat['ipaddress'] if ipAddress not in IPS_IGNORE: # write a new line to the csv file csvWriter.writerow([ipAddress, 1, IP_THREAT_FLAGS, mat['notes'].strip(), IP_THREAT_SYSTEM_ATTACKED, dateStr]) # update current offset in the offset file so we can pick up where we left off later lastLogFileOffset = logFile.tell(); # upload csv to ipthreat api if foundRow: # open csv file to post to api with open(csvFileName, 'rb') as postCSV: # get file size csvPos = postCSV.tell() postCSV.seek(0, os.SEEK_END) csvSize = postCSV.tell() postCSV.seek(csvPos) print(f'Posting {csvSize} bytes to ipthreat api') # set api key and post the file headers = {'X-API-KEY': IP_THREAT_API_KEY, 'Content-Type' : 'application/octet-stream'} response = requests.post(IP_THREAT_POST_URL, data=postCSV, headers=headers, timeout=60) if response.status_code >= 400: raise Exception(f'Error posting csv file, status: {response.status_code}, body: {response.content}') # write the new offset to file with open(offsetFileName, "w") as offsetFileWrite: offsetFileWrite.write(str(lastLogFileOffset) + '\n') offsetFileWrite.write(logFileName + '\n') sys.exit(os.X_OK)