Firewall drop stats / pfsense firewall log analyzer



  • Hello,

    Is there anyway to view stats for blocked connections by the firewall? I found this old post http://forum.pfsense.org/index.php/topic,22941.msg118123.html#msg118123 and I am interested in the stats portion, but not the based on a rule portion. I don't care what firewall rule is causing the block. I guess what I am looking for is a firewall log analyzer that would tell me the number of times a connection to a specific port has been blocked (e.g. 22, 80, 3306 etc) and by what IP. I don't care if it is built into pfsense or an analyzer on another machine with the logs being sent by syslog. I found webfwlog http://webfwlog.sourceforge.net/ and fwlogwatch http://fwlogwatch.inside-security.de/, but it does not support the pf log format. Splunk was also a dead end http://answers.splunk.com/questions/10508/parsing-pfsense-logs . I did see the glTail http://doc.pfsense.org/index.php/Realtime_Remote_pfSense_Log_Visualization_with_glTail but that was much more than what I wanted.  Any help or suggestion would be appreciated.

    Thanks



  • Ow i am subscribing to this one!

    This is exactly what i was looking at while back. My main goals is to monitor the IP's that produce high traffic on logged ports and the ones that run against the blocked ones. This would help find out who is trying to mess with services they should not.

    The glTail does look good, unfortunately i could not get it to work, i would not parse the log of pfSense 2.0.

    -m4rcu5



  • m4rcu5,

    I ended up writing a VERY simple and rough solution in python that used regular expression to find the parts I wanted and submit them into a MySQL database. If you are interested I will send you the code.

    UPDATE: Here is the code

    #!/usr/bin/python
    
    import re,urllib2,MySQLdb,datetime,os
    from urllib import urlopen
    from xml.dom.minidom import parse, parseString
    from xml.etree import ElementTree as ET
    
    #API key for ipinfodb.com
    apikey = "GET YOUR OWN KEY"
    #import the file
    input = open('/home/user/pfsenseparser/grepped.log', 'r')
    #error log
    error_output = open('/home/user/pfsenseparser/error.log', 'a')
    #output files
    output = open('final.txt', 'a')
    
    # this allows for the IP to Lat/Long conversion
    url = "http://api.ipinfodb.com/v2/ip_query.php?key="+apikey+"&ip="
    
    #MySQL Connect
    db = MySQLdb.connect("localhost","pfsenseparser","user","password")
    cursor = db.cursor()
    
    #what time is it?
    now = datetime.datetime.now()
    error_output.write('<------Started at: ' + now.strftime("%Y-%m-%d %H:%M") + '--->\n') 
    
    #number of new entries
    num_new_data = 0
    num_exist_data = 0
    
    #testing variable no SQL or file write if set to 0
    testing = 1
    
    for line in input:
    
    #www.txt2re.com
    #################################################
    
    	re1='((?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Sept|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?))'	# Month 1
    	re2='.*?'	# Non-greedy match on filler
    	re3='((?:(?:[0-2]?\\d{1})|(?:[3][0,1]{1})))(?![\\d])'	# Day 1
    	re4='.*?'	# Non-greedy match on filler
    	re5='((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)'	# HourMinuteSec 1
    	re6='.*?'	# Non-greedy match on filler
    	re7='(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?![\\d])'	# Uninteresting: ipaddress
    	re8='.*?'	# Non-greedy match on filler
    	re9='((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?![\\d])'	# IPv4 IP Address 1
    	re10='.*?'	# Non-greedy match on filler
    	re11='(\\d+)'	# Integer Number 1
    	re12='.*?'	# Non-greedy match on filler
    	re13='((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(?![\\d])'	# IPv4 IP Address 2
    	re14='.*?'	# Non-greedy match on filler
    	re15='(\\d+)'	# Integer Number 2
    
    	rg = re.compile(re1+re2+re3+re4+re5+re6+re7+re8+re9+re10+re11+re12+re13+re14+re15,re.IGNORECASE|re.DOTALL)
    	m = rg.search(line)
    	if m:
    	        month1=m.group(1)
    	        day1=m.group(2)
    	        time1=m.group(3)
    	        ipaddress1=m.group(4)
    	        int1=m.group(5)
    	        ipaddress2=m.group(6)
    	        int2=m.group(7)
    	        #print "("+month1+")"+"("+day1+")"+"("+time1+")"+"("+ipaddress1+")"+"("+int1+")"+"("+ipaddress2+")"+"("+int2+")"+"\n"
    
    ##############################
    
    		#ipaddress1 is the one we want
    		#lets see if it is in the database before we ask ipinfodb
    		sqlipcheck = "SELECT * FROM pfsenseparser WHERE Ip_Address = ('%s')" % (ipaddress1)
    		cursor.execute(sqlipcheck)
    		data = cursor.fetchall()
    		ipcheck = ""
    		for row in data:
    			daycheck = row[2]
    			timecheck = row[3]
    			ipcheck = row[4]
    		if ipcheck != ipaddress1:
    			#This is a new IP Address
    			error_output.write(ipaddress1+' = New Data\n')
    			url2 = ipaddress1+"&timezone=false"
    		        url3 = url+url2
    		        urlobj = urllib2.urlopen(url3)
    		        data = urlobj.read()
    	   	        urlobj.close()
    		        dom = ET.XML(data)
    			city = dom.findtext("City")
    			country = dom.findtext("CountryName")
    			region = dom.findtext("RegionName")
    			region = region.replace("'", "")
    	 	        lat = dom.findtext("Latitude")
    		        long = dom.findtext("Longitude")
    			if testing == 1:
    				cursor.execute("INSERT INTO pfsenseparser (Month,Day,Time,Ip_Address, Port_Num,Lat,Longitude,City,Country_Name,Region,Num_Connect,Type) VALUES (%s, %s, %s,%s, %s, %s, %s, %s, %s, %s, 1, \"firewall\")", (month1,day1,time1,ipaddress1,int2,lat,long,city,country,region))
    				db.commit()
    			        output.write(month1+","+day1+","+time1+","+ipaddress1+","+int1+","+lat+","+long+","+ipaddress2+","+int2+" "+"\n")
    	        	        #output2.write(month1+","+day1+","+int1+c1+int2+","+ipaddress1+","+int1+","+lat+","+long+","+ipaddress2+","+int2+" "+"\n")
    			num_new_data = num_new_data+1
    		elif timecheck != time1 and daycheck != day1 and reservedip == -1:
    			#This is an existing IP Address but not a duplicate
    			sql2 = "SELECT Num_Connect FROM pfsenseparser WHERE Ip_Address = ('%s')" % (ipaddress1)
    			cursor.execute(sql2)
    			data = cursor.fetchall()
    			for row in data:
    				num = row[0]
    				num_new = int(num)+1
    				sql5 = "UPDATE pfsenseparser SET Num_Connect = ('%d') WHERE Ip_Address = ('%s')" % (num_new,ipaddress1)
    				if testing == 1:
    					cursor.execute(sql5)
    					db.commit()
    			num_exist_data = num_exist_data+1
    
    now2 = datetime.datetime.now()
    if testing == 1:
    #send me a text
    	os.system('echo \'parser.sh just ran with ' + str(num_new_data) + ' new entries and ' + str(num_exist_data) + ' existing entries\' | mailx youremailhere ')
    	#write to error.log
    	#insert timestamp and new entries into database
    	if num_new_data != 0:
    		cursor.execute("INSERT INTO pf_meta (Last_Run,New_Data) VALUES (NOW(),'Y')")
    	else:
    		cursor.execute("INSERT INTO pf_meta (Last_Run,New_Data) VALUES (NOW(),'N')")
    	error_output.write('Ended at: ' + now2.strftime("%Y-%m-%d %H:%M") + ' with ' + str(num_new_data) + ' new entries and ' + str(num_exist_data) + ' existing entries\n')
    else:
    	 os.system('echo \'Just Testing\' | mailx youremailhere ')
    db.close()
    
    

    Like I said this is very rough. I have a mysql database setup with the correct tables. My end goal was to view the data in a web browser and in google maps. This is why I used the ipinfodb.com lat long lookup. If I find time I will post a more comprehensive how to on my blog. Link to follow…

    Link to more info: http://blog.poultonfam.com/brad/2011/04/18/custom-pfsense-firewall-log-analyzer/


Log in to reply