Built from scratch: a Python tool that parses FortiGate CLI logs, identifies the top 150 most-used ports, and generates a professional multi-page PDF report for the CTO to drive firewall hardening decisions.Construit de zéro : un outil Python qui analyse les logs CLI FortiGate, identifie les 150 ports les plus utilisés, et génère un rapport PDF multi-pages professionnel pour le CTO afin de guider les décisions de hardening firewall.
srcport=XXXX and dstport=XXXXsrcport=XXXX et dstport=XXXXsrcport= values from raw FortiGate log file, Counter aggregation, Top 80 ports table, basic ReportLab PDF generation with 8-page output.srcport= depuis le fichier log FortiGate brut, agrégation Counter, tableau Top 80 ports, génération PDF ReportLab basique en 8 pages.socket.getservbyport() with fallback for unknown ports. Removed redundant "Top 80" block. Final version delivered and used in production for firewall audit.socket.getservbyport() avec fallback pour les ports inconnus. Suppression du bloc "Top 80" redondant. Version finale livrée et utilisée en production pour l'audit firewall.import re import socket from collections import Counter from reportlab.lib.pagesizes import letter from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, PageBreak from reportlab.lib import colors # ── 1. Parse FortiGate CLI log export ────────────────────────────── port_counter = Counter() withopen("fortigate_export.txt", "r", encoding="utf-8", errors="ignore") as f: for line in f: match = re.search(r"srcport=(\d+)", line) if match: port_counter[match.group(1)] += 1# ── 2. Build Top 150 sorted by frequency (descending) ────────────── top_ports = port_counter.most_common(150) table_data = [["Rank", "Port", "Hits (7d)", "Service", "Action"]] for rank, (port, count) inenumerate(top_ports, 1): try: service = socket.getservbyport(int(port)) except: service = "Unknown" action = "Review"if service == "Unknown"else"Keep / Monitor" table_data.append([rank, port, count, service, action]) # ── 3. Generate multi-page PDF report ────────────────────────────── doc = SimpleDocTemplate( "CTO_Firewall_Port_Report_7days.pdf", pagesize=letter, title="Firewall Port Usage Report — 7-Day Analysis" ) # Table styling: alternating rows, header highlight style = TableStyle([ ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#0a0c10')), ('TEXTCOLOR', (0,0), (-1,0), colors.HexColor('#00d4ff')), ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor('#111318'), colors.HexColor('#181c24')]), ('FONTSIZE', (0,0), (-1,-1), 8), ('GRID', (0,0), (-1,-1), 0.3, colors.HexColor('#1e2330')), ]) elements = [Paragraph("Source Port Usage Report (7-Day Analysis)", styles["Title"])] elements.append(Table(table_data, style=style, repeatRows=1)) doc.build(elements) print("✅ PDF report generated: CTO_Firewall_Port_Report_7days.pdf")