Who rated this post

Who rated this post

RIMBAULT
L0 Member

[API/Minemeld] Create dynamic DoS protection

We got a lot of external ressources, and we subissing a huge amount of flooding attacks.
These attacks blocked by security policies, but as firewalls compute each packets using CPU our CPU's cluster reach 100% all day long.
So we created a DoS protection policy.

First we create Custom Report - which select top 25 source addresses from external which are allowed and session end reason due to aged-out.

We collect these informations and export it using API to our InfluxDB. We put it on list and update ou external dynamic list using minemeld. (Scripts are avalaible)
And we create a Dos Protection policy.

We execute these scripts every 5minutes and we synchronize our EDL every 5minutes too.

This script is currently used for a PanOS 8.1 (XML response content)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# *************************************************************************
# ****       Script Dos Protection PALOALTO                            ****
# ****       Get IP Source which try to flood our network              ****
# ****       and put it on DoS protection rule through Minemeld        ****
# *************************************************************************
# ****                  AMU - DOSI-Pôle réseau                         ****
# ****           Contact: cyril.rimbault@univ-amu.fr                   ****
# *************************************************************************

import xml.etree.ElementTree as ET
import requests
import time
import json
from influxdb import InfluxDBClient
import dns.reversename
import dns.resolver
from dns.exception import DNSException
import subprocess

#############################
# Get API key from each FW
#############################
def GetApiKey(url,user,password, *args):
        querystring = {"type":"keygen" , "user": user , "password":password}
        header = {'Accept':"application/json"}
        response = requests.request("GET", url , headers=header, params=querystring)
        tree = ET.fromstring(response.content)
        for child in tree.iter('result'):
                for key in child:
                        key = key.text
#       print key
        return key
#############################
# Get report ID
#############################
def GetReportID(url,key,reportname, *args):
        querystring = {"type":"report" , "async":"yes" , "reporttype":"custom" , "vsys":"vsys2" , "reportname": reportname , "key":key}
        header = {'Accept':"application/json"}
        response = requests.request("GET", url , headers=header, params=querystring)
        tree = ET.fromstring(response.content)
        for child in tree.iter('result'):
                for job in child:
                        job = job.text
#       print job
        return job
#############################
# Get XML result
#############################
def GetResponse(url,key,job,report,aged, *args):
	n=5
	if aged==True:
		n=20
        file = "./tmp/data"
        if url == "https://pa-5260-stj/api":
            file = file + "-STJ"
        else:
            file = file + "-STC"
        job = str(job)
        cmd = "<show><report><id>"+job+"</id></report></show>"
        querystring = {"type":"op","cmd":cmd , "key":key}
        header = {'Accept':"application/json"}
        time.sleep(n)
	response = requests.request("GET", url , headers=header, params=querystring)
        time.sleep(n)
        data = open(file,"w")
        data.write(response.content)
        data.close()

        return file
#############################
# Parsing and sending informations to InfluxDB
#############################
def Sendfile(url,report,file,aged, *args):
        if url == "https://pa-5260-stj/api":
            report = report + "-STJ"
        else:
            report = report + "-STC"
        user = #InfluxDB User
        pwd = #DB Password

        client = InfluxDBClient(host = "YourDB", port=8086, username = user, password = pwd)
        client.switch_database('PALOALTO')
        arbre=ET.parse(file)

        root=arbre.getroot()
        tag = root.tag

        dict = {}

        for child in root.iter():
                if child.tag == 'src':
                        src=child.text.strip()
                        #print src
		if child.tag == 'resolved-src':
			ressrc=child.text.strip()
                if child.tag == 'sessions':
                        session = child.text.strip()
                        dict[src]=int(session)
                if child.tag == 'repeatcnt':
                        count = child.text.strip()
                        dict[src]=int(count)

        for src, value  in sorted(dict.items(), key=lambda item:item[1], reverse= True ):

                json_body = [ {
                                "measurement": report,
                                "tags": {
                                        "src":src,
                                        },
                                "fields": {
                                        "count":dict[src]
                                        #"resolve-name":ressrc
					}
                                }
                                ]
                try:
                        client.write_points(json_body)
                except Exception as e:
                        print e

#	print ("SEND OK"+":"+report)
        return report,aged
#############################
# Requesting source IP which is over thresold
#############################
def Querydb (dbname,aged, *args):

    user = #InfluxDB User
    pwd = #DB Password

    client = InfluxDBClient(host = 'YourDB', port=8086, username = user, password = pwd)
    client.switch_database('PALOALTO')

    if aged == True:
        query = "SELECT \"src\",\"count\" FROM \"" + dbname + "\"  WHERE (\"count\" > 500 AND TIME >= now() - 15m)"
    else:
        query = "SELECT \"src\",\"count\" FROM  \"" + dbname + "\" WHERE (\"count\" > 200 AND TIME >= now() - 15m)"

    rs = client.query(query)
    result = list(rs.get_points(measurement=dbname))

    data = open("./tmp/IP2ban","a")
#   print json.dumps(result, indent = 4)
    for item in result:
    	data.write(item["src"])
	data.write("\n")
    data.close()
#############################
# Aggregate host IP to subnet
#############################
def AggregIP():

    command = ["perl","./AgregRzo","./tmp/IP2ban","./tmp/listeAGGR"]
    try:
	process = subprocess.Popen(command,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        process.communicate()

    except Exception as e:
	print ("Erreur script aggreg",e)
	pass

def main():
    url = #list your firewalls
    user = #Api Username
    password = #PassWordSECURE
    reportname = #Create your custom report Monitor>Manage Custom Reports> Add
    data = open("./tmp/IP2ban","w") # Temporary file
    data.close()
    aged = False
    for site in url:
        key = GetApiKey(site,user,password)
        for report in reportname:
	    if report=="TOP_AGED-OUT":
		aged = True
            job = GetReportID(site,key,report)
            file = GetResponse(site,key,job,report,aged)
            name = Sendfile(site,report,file,aged)
            Querydb(name[0],name[1])
    AggregIP()


if __name__ == '__main__':
        main()

 

Who rated this post