본문 바로가기
Python

Fortigate 방화벽 전체 로그를 이용한 정책 추출

by Ao1 2024. 7. 25.

필요한 모듈: PySimpleGUI, pandas, openpyxl

Fortigate Firewall Policy.py
0.01MB

 

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from os import path 
import io
import os
import sys

sys.path.append(os.getcwd() + "\lib")

import re
import csv
import shutil
import PySimpleGUI as psg


# Python 2 and 3 compatibility
if (sys.version_info < (3, 0)):
    fd_read_options = 'r'
    fd_write_options = 'wb'
else:
    fd_read_options = 'r'
    fd_write_options = 'w'

# Handful patterns
# -- Entering policy definition block
p_entering_policy_block = re.compile(r'^\s*config firewall policy$', re.IGNORECASE)
p_entering_subpolicy_block = re.compile(r'^\s*config .*$', re.IGNORECASE)

# -- Exiting policy definition block
p_exiting_policy_block = re.compile(r'^end$', re.IGNORECASE)

# -- Commiting the current policy definition and going to the next one
p_policy_next = re.compile(r'^next$', re.IGNORECASE)

# -- Policy number
p_policy_number = re.compile(r'^\s*edit\s+(?P<policy_number>\d+)', re.IGNORECASE)

# -- Policy setting
p_policy_set = re.compile(r'^\s*set\s+(?P<policy_key>\S+)\s+(?P<policy_value>.*)$', re.IGNORECASE)

# Functions
def parse(options):
    """
        Parse the data according to several regexes
        
        @param options:  options
        @rtype: return a list of policies ( [ {'id' : '1', 'srcintf' : 'internal', ...}, {'id' : '2', 'srcintf' : 'external', ...}, ... ] )  
                and the list of unique seen keys ['id', 'srcintf', 'dstintf', ...]
    """
    global p_entering_policy_block, p_exiting_policy_block, p_policy_next, p_policy_number, p_policy_set
    
    in_policy_block = False
    skip_ssl_vpn_policy_block = False
    inspect_next_ssl_vpn_command = False
    
    policy_list = []
    policy_elem = {}
    
    order_keys = []
    
    with io.open('%s[CFG]/%s'%(values['-Files-'],options['input_file']), mode=fd_read_options, encoding=options['input_encoding']) as fd_input:
        
        for line in fd_input:
            line = line.strip()
            
            # We match a policy block
            if p_entering_policy_block.search(line):
                in_policy_block = True
            
            # We are entering a subconfig inside a ssl-vpn action and we want to skip it
            if inspect_next_ssl_vpn_command and not(p_entering_subpolicy_block.search(line)):
                skip_ssl_vpn_policy_block = False
                inspect_next_ssl_vpn_command = False
            
            elif inspect_next_ssl_vpn_command and p_entering_subpolicy_block.search(line):
                inspect_next_ssl_vpn_command = False
                skip_ssl_vpn_policy_block = True
            
            # We are in a policy block
            if in_policy_block:
                if p_policy_number.search(line) and not(skip_ssl_vpn_policy_block):
                    policy_number = p_policy_number.search(line).group('policy_number')
                    policy_elem[u'id'] = policy_number
                    if not('id' in order_keys):
                        order_keys.append(u'id')
                
                # We match a setting
                if p_policy_set.search(line) and not(skip_ssl_vpn_policy_block):
                    policy_key = p_policy_set.search(line).group('policy_key')
                    if not(policy_key in order_keys):
                        order_keys.append(policy_key)
                    
                    policy_value = p_policy_set.search(line).group('policy_value').strip()
                    policy_value = re.sub('["]', '', policy_value)
                    
                    
                    policy_elem[policy_key] = policy_value
                    if policy_key == 'action' and policy_value == 'ssl-vpn':
                        inspect_next_ssl_vpn_command = True
                        skip_ssl_vpn_policy_block = True
                
                # We are done with the current policy id
                if p_policy_next.search(line) and not(skip_ssl_vpn_policy_block):
                    policy_list.append(policy_elem)
                    policy_elem = {}
                    
            
            # We are exiting the policy block
            if p_exiting_policy_block.search(line):
                if skip_ssl_vpn_policy_block == True:
                    skip_ssl_vpn_policy_block = False
                else:
                    in_policy_block = False
    
    return (policy_list, order_keys)


def generate_csv(results, keys, options):
    """
        Generate a plain csv file
    """
    if results and keys:
        with io.open('%s[CSV]\%s'%(values['-Files-'],options['output_file']), mode=fd_write_options, encoding=options['output_encoding']) as fd_output:
            spamwriter = csv.writer(fd_output, delimiter=options['delimiter'], quoting=csv.QUOTE_ALL, lineterminator='\n')
            
            if not(options['skip_header']):
                spamwriter.writerow(keys)
            
            for policy in results:
                output_line = []
                
                for key in keys:
                    if key in policy.keys():
                        output_line.append(policy[key])
                    else:
                        output_line.append('')
            
                spamwriter.writerow(output_line)
                if options['newline']:
                    spamwriter.writerow('')
        
        fd_output.close()
    
    return None

def Make_CSV(File):
    name, ext = os.path.splitext(File)
    
    options = dict(input_file = File, output_file = '%s.csv'%name, skip_header = False, newline = False, delimiter = ','
               , input_encoding = 'utf-8', output_encoding = 'utf-8-sig')
    
    #print(options)
    
    if (sys.version_info < (3, 0)):
        options.output_encoding = None
    
    results, keys = parse(options)
    
    generate_csv(results, keys, options)
    
    return None

def Make_CFG(File):
    name, ext = os.path.splitext(File)
    text = []
    stop_code = 0
    
    with open('%s\%s'%(values['-Files-'],File), 'r', encoding='utf-8') as log_file:

        for i, line in enumerate(log_file):
            line = line.strip()

            if 'config firewall policy' == line:
                text.append(line)
                stop_code = 1

            elif 'end' == line and stop_code == 1:
                text.append(line)
                break
                
            else:
                text.append(line)


    log_file.close()
        
    with open(os.path.abspath('%s[CFG]/%s.cfg'%(values['-Files-'],name)), 'a+',encoding='utf-8') as cfg_file:
        
        for j in range(len(text)):
            cfg_file.write('%s\n'%text[j])

    cfg_file.close()
    
        
if __name__ == "__main__" :
    psg.theme('DarkBlack1')

    # GUI 구성 레이아웃
    layout = [[psg.Frame(layout=[[psg.Input(size=(60,1),change_submits=True,key='-Files-',disabled=True),
                                  psg.FolderBrowse(size=(10,1))]],
                         title='Log 폴더 선택',element_justification='c',vertical_alignment='c')],
              [psg.Frame(layout=[[psg.Ok(size=(10,3)),psg.Exit(size=(10,3))]],title='',border_width=0,vertical_alignment='c')]]
    
    window = psg.Window('Fortigate Firewall Policy', layout, grab_anywhere=True, finalize=True)      # make gui window
    
    while True:
        event, values = window.read()   # Read the event that happened and the values dictionary
        
        if event in (None, 'Exit', 'Quit'):     # If user closeddow with X or if user clicked "Exit" button then exit
            break
        if event == 'Ok':
            LOG_Files = os.listdir(values['-Files-'])
            os.mkdir('%s[CFG]'%values['-Files-'])
            os.mkdir('%s[CSV]'%values['-Files-'])
  
            try:
                for log in range(len(LOG_Files)):
                    Make_CFG(LOG_Files[log])
                    
                CFG_Files = os.listdir('%s[CFG]'%values['-Files-'])    
                
                for cfg in range(len(CFG_Files)):
                    Make_CSV(CFG_Files[cfg])
                    
                    
            except Exception as e:
                shutil.rmtree('%s[CFG]'%values['-Files-'])
                shutil.rmtree('%s[CSV]'%values['-Files-'])
                

    window.close()