01
Automation · Security Reporting · PythonAutomatisation · Reporting Sécurité · Python

Firewall Log Analyzer
→ CTO PDF Report
Analyseur Logs Firewall
→ Rapport PDF CTO

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.

★★★★★ Tier 1★★★★★ Niveau 1Client X — ConfidentialClient X — Confidentiel8 Development Phases8 Phases de DéveloppementPython · ReportLab · FortiGate CLI
01 — Context & Problem
01 — Contexte & Problème
⚠ The Problem
⚠ Le Problème
The CTO of Client X needed visibility into which network ports were actively used across the organization's infrastructure. Without this data, the firewall had accumulated rules over the years — some legitimate, many obsolete. There was no structured way to identify which ports could safely be closed to reduce the attack surface. Doing this manually through the FortiGate interface would have taken days and been error-prone.
Le CTO du Client X avait besoin de visibilité sur les ports réseau activement utilisés dans l'infrastructure. Sans ces données, le firewall avait accumulé des règles au fil des années — certaines légitimes, beaucoup obsolètes. Il n'existait aucun moyen structuré d'identifier les ports pouvant être fermés en toute sécurité pour réduire la surface d'attaque. Le faire manuellement via l'interface FortiGate aurait pris des jours et aurait été sujet à erreurs.
✓ The Approach
✓ L'Approche
I exported raw traffic logs directly from the FortiGate CLI, then built a Python script from scratch to parse the log file, extract source port usage via regex, count occurrences, rank ports by frequency, resolve service names, and generate a structured multi-page PDF report — ready to be presented to the CTO without any manual data manipulation.
J'ai exporté les logs de trafic bruts directement depuis le CLI FortiGate, puis j'ai construit un script Python de zéro pour analyser le fichier log, extraire l'utilisation des ports source via regex, compter les occurrences, classer les ports par fréquence, résoudre les noms de services, et générer un rapport PDF multi-pages structuré — prêt à être présenté au CTO sans manipulation manuelle de données.
02 — Technical Architecture
02 — Architecture Technique
How the Tool Works
Comment l'Outil Fonctionne
INPUT
FortiGate CLI Log Export
Export Logs CLI FortiGate
Raw text file exported directly from FortiGate CLI containing network traffic entries with fields like srcport=XXXX and dstport=XXXX
Fichier texte brut exporté directement depuis le CLI FortiGate contenant les entrées de trafic réseau avec des champs comme srcport=XXXX et dstport=XXXX
OUTPUT
Multi-page CTO PDF Report
Rapport PDF Multi-pages pour CTO
Professional PDF with executive summary, Top 150 ports ranked by frequency, service name resolution, 7-day analysis, and firewall hardening recommendations
PDF professionnel avec résumé exécutif, Top 150 ports classés par fréquence, résolution des noms de services, analyse sur 7 jours, et recommandations de hardening firewall
PROCESSING PIPELINE
CLI ExportRegex ParseCounter SortService ResolvePDF Generation
03 — Development Phases
03 — Phases de Développement
8 Iterations to Production
8 Itérations vers la Production
PHASE 01
Initial Build — Port Extraction & Basic PDF
Construction Initiale — Extraction Ports & PDF Basique
Built the first working version: regex extraction of srcport= values from raw FortiGate log file, Counter aggregation, Top 80 ports table, basic ReportLab PDF generation with 8-page output.
Première version fonctionnelle : extraction regex des valeurs srcport= depuis le fichier log FortiGate brut, agrégation Counter, tableau Top 80 ports, génération PDF ReportLab basique en 8 pages.
PHASE 02
Bug Fix — NameError on detailed_table_data
Correction Bug — NameError sur detailed_table_data
Resolved a NameError caused by an undefined variable in the detailed table generation block. Script now executes cleanly from start to finish.
Résolution d'un NameError causé par une variable non définie dans le bloc de génération du tableau détaillé. Le script s'exécute maintenant proprement de bout en bout.
PHASE 03
Bug Fix — Unterminated String Literal
Correction Bug — Chaîne de Caractères Non Terminée
Fixed a syntax error introduced during script regeneration: unterminated string literal in the PDF conclusion paragraph. Full script revalidated.
Correction d'une erreur de syntaxe introduite lors d'une régénération du script : chaîne non terminée dans le paragraphe de conclusion PDF. Script entièrement revalidé.
PHASE 04
Visual Enhancement — Table of Contents, Colors, Structure
Amélioration Visuelle — Sommaire, Couleurs, Structure
Added table of contents, color-coded rows (alternating backgrounds), section headers, improved typography hierarchy. Report now professional enough for direct CTO presentation.
Ajout d'un sommaire, coloration des lignes (fonds alternés), en-têtes de section, hiérarchie typographique améliorée. Rapport suffisamment professionnel pour présentation directe au CTO.
PHASE 05
Layout Optimization — Reduced Whitespace, Denser Tables
Optimisation Layout — Réduction Espaces, Tableaux Plus Denses
Reduced excessive whitespace between sections. Compressed table row heights. More data visible per page without sacrificing readability.
Réduction des espaces excessifs entre sections. Hauteurs de lignes compressées. Plus de données visibles par page sans sacrifier la lisibilité.
PHASE 06
Table of Contents Removed
Suppression du Sommaire
CTO feedback: table of contents unnecessary for this type of report. Removed, freeing space for data tables. Report streamlined.
Retour CTO : sommaire inutile pour ce type de rapport. Supprimé, libérant de l'espace pour les tableaux de données. Rapport simplifié.
PHASE 07
Sort by Frequency (Descending)
Tri par Fréquence (Décroissant)
Changed sort order from port number to frequency descending. Most-used ports now appear first — exactly what the CTO needed to prioritize firewall rule review.
Changement de l'ordre de tri du numéro de port vers la fréquence décroissante. Les ports les plus utilisés apparaissent maintenant en premier — exactement ce dont le CTO avait besoin pour prioriser la révision des règles firewall.
PHASE 08 — FINAL
Top 150 Ports + Service Name Database + Production-Ready
Top 150 Ports + Base Noms de Services + Prêt Production
Expanded from Top 80 to Top 150 ports. Added port service name resolution via socket.getservbyport() with fallback for unknown ports. Removed redundant "Top 80" block. Final version delivered and used in production for firewall audit.
Extension de Top 80 à Top 150 ports. Ajout de la résolution des noms de services via 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.
04 — Core Script Logic
04 — Logique Centrale du Script
Key Code — Port Extraction & PDF Generation
Code Clé — Extraction Ports & Génération PDF
Python 3 · ReportLab · FortiGate Log Parser
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")
05 — Technologies Used
05 — Technologies Utilisées
🐍
Python 3
Core scripting language
Langage de script principal
📄
ReportLab
PDF generation library
Bibliothèque génération PDF
🔥
FortiGate CLI
Log source (raw export)
Source des logs (export brut)
🔍
Regex + socket
Parsing & service resolution
Parsing & résolution services
06 — Results & Impact
06 — Résultats & Impact
150
Ports analyzed & ranked
Ports analysés & classés
7
Days of traffic analyzed
Jours de trafic analysés
8
Dev iterations to production
Itérations jusqu'à la production
✓ Business Outcome
✓ Résultat Business
The CTO received a ready-to-use PDF identifying every active port across the network over 7 days, ranked by usage frequency with service name resolution. This enabled informed, data-driven firewall hardening decisions — replacing weeks of manual review with a single automated report. Unused and unknown ports were flagged for immediate review and closure.
Le CTO a reçu un PDF prêt à utiliser identifiant chaque port actif sur le réseau sur 7 jours, classé par fréquence d'utilisation avec résolution des noms de services. Cela a permis des décisions de hardening firewall basées sur des données réelles — remplaçant des semaines de révision manuelle par un rapport automatisé unique. Les ports inutilisés et inconnus ont été signalés pour révision et fermeture immédiates.
All ProjectsTous les ProjetsNext Project →Projet Suivant →