diff --git a/CREATE_FIREWALL_TOOL.md b/CREATE_FIREWALL_TOOL.md new file mode 100644 index 0000000..ec3fece --- /dev/null +++ b/CREATE_FIREWALL_TOOL.md @@ -0,0 +1,119 @@ +# Guide de Développement : Créer son propre outil Firewall + +Ce guide explique comment étendre l'outil d'analyse réseau en ajoutant un nouveau outil pour les firewalls. + +> **Note :** Tout au long de ce guide, remplacez **`OUTIL`** (en majuscule ou minuscule) par le nom de l'outil que vous souhaitez développer'. + +--- + +## Étape 1 : Création du script de l'outil + +Dans le dossier `Parseurs_config_Firewall/src/scripts/` (ou `script/` selon votre arborescence), créez un nouveau fichier Python nommé **`export_OUTIL.py`**. + +*Il est fortement recommandé de s'inspirer des parseurs existants comme `export_matrice_routage.py`, `export_matrice_flux.py` ou `export_interfaces.py`.* + +Voici le squelette de code à utiliser : + +```python +def export_to_excel(json_file_path, output_file_excel): + """ + Export firewall data from JSON to Excel + Args: + json_file_path: Path to the JSON file to process + output_file_excel: Path to the output Excel file + """ + + # Votre code +``` + +## Étape 2 : Intégration dans le moteur principal (`main.py`) + +Pour que le script global prenne en charge le nouveau modèle, ouvrez le fichier `Parseurs_config_Firewall/src/main.py` : + +1. Importez votre nouvelle fonction en haut du fichier : +```python +from scripts.export_OUTIL import export_to_excel as export_OUTIL_to_excel +``` + +2. Ajoutez la condition correspondante dans le bloc de sélection de l'outil : +```python +if "-X" in sys.argv: + print(f"\nGénération de l'export les OUTIL...") + if "-o" in sys.argv: + o_index = sys.argv.index("-o") + if o_index + 1 < len(sys.argv): + output_file_excel = os.path.join(f"{output_path}OUTIL_{firewall_type}_{sys.argv[o_index + 1]}.xlsx") + else: + print("Erreur: nom de fichier de sortie manquant après '-o'.") + sys.exit(1) + else: + timestamp = time.strftime("%Y%m%d") + output_file_excel = f"{output_path}OUTIL_{firewall_type}_{timestamp}.xlsx" + output_file_excel = verify_if_file_exists(output_file_excel) + excel_file = export_OUTIL_to_excel(output_file_json, output_file_excel) + print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}") +``` +> **Note :** Remplacez **`X`** de la première ligne par la lettre souhaitez (en dehors de celles déjà existante [o,f,r,i])'. + +3. Mettez à jour les instructions textuelles d'usage affichées dans la console si le paramètre saisi est incorrect afin d'inclure `"OUTIL"` dans la liste des choix valides. +```python +print(" -X Generate OUTIL report (optional)") +``` +> **Note :** Remplacez **`X`** par la lettre précédemmment choisi'. + + +## Étape 3 : Adaptation de l'Interface Graphique (GUI) + +Afin que le nouveau modèle apparaisse dans l'interface graphique unifiée : + +1. Ouvrez le fichier de gestion de la GUI (`gui_firewall.py` racine ou script GUI dédié). +2. Dans la fonction `open_firewall_gui_multi` : + +- Ajouter la variable : +```python +OUTIL = tk.BooleanVar() +``` + +- Dans la fonction `process()`, ajouter : +```python +if OUTIL.get(): + cmd.append("-X") +``` + +- Ajouter égalemment dans la fonction `open_firewall_gui_multi`, le bouton de sélection de l'outil avec les autres existant : +```python +ttk.Checkbutton( + content, + text="Description courte de l'outil", + variable=OUTIL + ).pack(anchor="w", padx=10, pady=(0, 0)) + OUTIL.set(True) +``` + +3. Dans la fonction `open_firewall_gui` : + +- Répéter les mêmes opérations que pour `open_firewall_gui_multi` +- Dans la fonction `update_output_label(*args)` ajouter et modifier les éléments suivants : +```python +def update_output_label(*args): + ... + if output_var.get(): #ligne déjà existante + f_OUTIL = os.path.join(OUTPUT_DIR, f"OUTIL_{fw}_{output_var.get()}.xlsx") + ... + else : + f_OUTIL = os.path.join(OUTPUT_DIR, f"OUTIL_{fw}_{dt}.xlsx") + ... + + output_label_var.set("Fichier de sortie :\n" + f_json + ("\n" + f_flux if matrice_flux.get() else "") + ("\n" + f_routage if matrice_routage.get() else "") + ("\n" + f_interfaces if interfaces.get() else "") + ("\n" + f_OUTIL if OUTIL.get() else "")) #ligne déjà existante a modifier +``` +- Ajouter vers la fin de la fonction `open_firewall_gui` avec les autres existant : +```python +OUTIL.trace_add("write", update_output_label) +``` + +4. Ouvrez le fichier de gestion principale de la GUI (`main.py` racine ou script GUI dédié). + +- Dans la fonction `open_main_gui()` ajouter dans le Label du Firewall : +```python +"\n + possibilité de générer un rapport des OUTIL", +``` \ No newline at end of file diff --git a/CREATE_SWITCH_PARSING.md b/CREATE_SWITCH_PARSING.md index 910a23d..decd06e 100644 --- a/CREATE_SWITCH_PARSING.md +++ b/CREATE_SWITCH_PARSING.md @@ -404,7 +404,7 @@ Pour que le script global prenne en charge le nouveau modèle, ouvrez le fichier import scripts.json_modele as json_modele ``` -2. Ajoutez la condition correspondante dans le bloc de sélection du type de firewall : +2. Ajoutez la condition correspondante dans le bloc de sélection du type de switch : ```python elif switch_type == "modele": json_modele.generate_json_modele(input_data, output_file1_json, output_file2_json) diff --git a/CREATE_SWITCH_TOOL.md b/CREATE_SWITCH_TOOL.md new file mode 100644 index 0000000..357ebee --- /dev/null +++ b/CREATE_SWITCH_TOOL.md @@ -0,0 +1,119 @@ +# Guide de Développement : Créer son propre outil Switch + +Ce guide explique comment étendre l'outil d'analyse réseau en ajoutant un nouveau outil pour les switches. + +> **Note :** Tout au long de ce guide, remplacez **`OUTIL`** (en majuscule ou minuscule) par le nom de l'outil que vous souhaitez développer'. + +--- + +## Étape 1 : Création du script de l'outil + +Dans le dossier `Parseurs_logs_Switch/src/scripts/` (ou `script/` selon votre arborescence), créez un nouveau fichier Python nommé **`export_OUTIL.py`**. + +*Il est fortement recommandé de s'inspirer des parseurs existants comme `export_rapport_stack.py`, `export_rapport_interfaces.py`, `export_rapport_uplink.py` ou `export_schema_infra.py`.* + +Voici le squelette de code à utiliser : + +```python +def main(output_dir, output_file) + """ + Export switch data from JSON to Excel + Args: + json_file_path: Path to the JSON file to process + output_file: Path to the output Excel file + """ + + # Votre code +``` + +## Étape 2 : Intégration dans le moteur principal (`main.py`) + +Pour que le script global prenne en charge le nouveau modèle, ouvrez le fichier `Parseurs_logs_Switch/src/main.py` : + +1. Importez votre nouvelle fonction en haut du fichier : +```python +from scripts.export_OUTIL import main as rapport_OUTIL +``` + +2. Ajoutez la condition correspondante dans le bloc de sélection de l'outil : +```python +if "-X" in sys.argv: + print(f"\nGénération de OUTIL...") + if "-o" in sys.argv: + o_index = sys.argv.index("-o") + if o_index + 1 < len(sys.argv): + output_file_html = os.path.join(f"{output_path}OUTIL_{switch_type}_{sys.argv[o_index + 1]}.html") + else: + print("Erreur: nom de fichier de sortie manquant après '-o'.") + sys.exit(1) + else: + timestamp = time.strftime("%Y%m%d") + output_file_html = f"{output_path}OUTIL_{switch_type}_{timestamp}.html" + output_file_html = verify_if_file_exists(output_file_html) + html_file = OUTIL(output_path, output_file_html) + print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file1_json} & {output_file2_json}\n - HTML: {html_file}") +``` +> **Note :** Remplacez **`X`** de la première ligne par la lettre souhaitez (en dehors de celles déjà existante [o,d,u,s,i])'. + +3. Mettez à jour les instructions textuelles d'usage affichées dans la console si le paramètre saisi est incorrect afin d'inclure `"OUTIL"` dans la liste des choix valides. +```python +print(" -X Generate OUTIL report (optional)") +``` +> **Note :** Remplacez **`X`** par la lettre précédemmment choisi'. + + +## Étape 3 : Adaptation de l'Interface Graphique (GUI) + +Afin que le nouveau modèle apparaisse dans l'interface graphique unifiée : + +1. Ouvrez le fichier de gestion de la GUI (`gui_switch.py` racine ou script GUI dédié). +2. Dans la fonction `open_switch_gui_multi` : + +- Ajouter la variable : +```python +OUTIL = tk.BooleanVar() +``` + +- Dans la fonction `process()`, ajouter : +```python +if OUTIL.get(): + cmd.append("-X") +``` + +- Ajouter égalemment dans la fonction `open_switch_gui_multi`, le bouton de sélection de l'outil avec les autres existant : +```python +ttk.Checkbutton( + content, + text="Description courte de l'outil", + variable=OUTIL + ).pack(anchor="w", padx=10, pady=(0, 0)) + OUTIL.set(True) +``` + +3. Dans la fonction `open_switch_gui` : + +- Répéter les mêmes opérations que pour `open_switch_gui_multi` +- Dans la fonction `update_output_label(*args)` ajouter et modifier les éléments suivants : +```python +def update_output_label(*args): + ... + if output_var.get(): #ligne déjà existante + f_OUTIL = os.path.join(OUTPUT_DIR, f"OUTIL_{fw}_{output_var.get()}.xlsx") + ... + else : + f_OUTIL = os.path.join(OUTPUT_DIR, f"OUTIL_{fw}_{dt}.xlsx") + ... + + output_label_var.set("Fichier de sortie :\n" + s_json1 + "\n" + s_json2 + ("\n" + s_uplink if rapport_uplink.get() else "") + ("\n" + s_stack if rapport_stack.get() else "") + ("\n" + s_interfaces if rapport_interfaces.get() else "") + ("\n" + s_schema if schema_infra.get() else "") + ("\n" + f_OUTIL if OUTIL.get() else "")) #ligne déjà existante a modifier +``` +- Ajouter vers la fin de la fonction `open_switch_gui` avec les autres existant : +```python +OUTIL.trace_add("write", update_output_label) +``` + +4. Ouvrez le fichier de gestion principale de la GUI (`main.py` racine ou script GUI dédié). + +- Dans la fonction `open_main_gui()` ajouter dans le Label du Switch : +```python +"\n + possibilité de générer un rapport des OUTIL", +``` \ No newline at end of file diff --git a/Computacenter_logo.png b/Computacenter_logo.png new file mode 100644 index 0000000..5e5091c Binary files /dev/null and b/Computacenter_logo.png differ diff --git a/Parseurs_config_Firewall/README.md b/Parseurs_config_Firewall/README.md index 6f230c1..f7709c5 100644 --- a/Parseurs_config_Firewall/README.md +++ b/Parseurs_config_Firewall/README.md @@ -6,6 +6,7 @@ Cet outil permet de **parser les configurations de différents types de firewall - Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure. - Il founit également la possibilité de générer une **matrice de routage au format Excel** pour visualiser les routes statiques dans l’infrastructure. +- Il founit également la possibilité de générer le **détail des interfaces au format Excel** pour visualiser la configuration des interfaces. ## Fonctionnalités principales @@ -46,6 +47,10 @@ Cet outil permet de **parser les configurations de différents types de firewall - Script Python qui utilise le JSON normalisé pour générer automatiquement une matrice Excel détaillant : - les routes statiques +7. **Génération des interfaces** + - Script Python qui utilise le JSON normalisé pour générer automatiquement une matrice Excel détaillant : + - la configuration des interfaces + ## Utilisation ### Pré-requis @@ -59,9 +64,9 @@ pip install -r .\src\requirements.txt #### Commandes principales ```bash -python3 .\src\main.py stormshield .\src\input\backup\ -f -r -python3 .\src\main.py paloalto .\src\input\nomfichier -f -r -python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r +python3 .\src\main.py stormshield .\src\input\backup\ -f -r -i +python3 .\src\main.py paloalto .\src\input\nomfichier -f -r -i +python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r -i ``` #### Options @@ -70,6 +75,8 @@ python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r | -o [nom_fichier] | Spécifie le nom du fichier JSON de sortie (optionnel) | -f | Génère un rapport Excel de type matrice de flux (optionnel) | -r | Génère un rapport Excel de type matrice de routage (optionnel) +| -i | Generate interface report (optional) + --- ## Arborescence du projet diff --git a/Parseurs_config_Firewall/src/main.py b/Parseurs_config_Firewall/src/main.py index cc80ab4..f8a0323 100644 --- a/Parseurs_config_Firewall/src/main.py +++ b/Parseurs_config_Firewall/src/main.py @@ -6,6 +6,7 @@ from scripts.json_Stormshield import generate_json_stormshield from scripts.json_Forcepoint import generate_json_forcepoint from scripts.export_matrice_flux import export_to_excel as export_flux_to_excel from scripts.export_matrice_routage import export_to_excel as export_routing_to_excel +from scripts.export_interfaces import export_to_excel as export_interfaces_to_excel def verify_if_file_exists(name): base, ext = os.path.splitext(name) @@ -24,6 +25,7 @@ def main(): print(" -o Specify output JSON file name (optional)") print(" -f Generate matrix flux report (optional)") print(" -r Generate routing matrix report (optional)") + print(" -i Generate interface report (optional)") sys.exit(1) firewall_type = sys.argv[1].lower() @@ -85,5 +87,21 @@ def main(): excel_file = export_routing_to_excel(output_file_json, output_file_excel) print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}") + if "-i" in sys.argv: + print(f"\nGénération de l'export les interfaces...") + if "-o" in sys.argv: + o_index = sys.argv.index("-o") + if o_index + 1 < len(sys.argv): + output_file_excel = os.path.join(f"{output_path}interfaces_{firewall_type}_{sys.argv[o_index + 1]}.xlsx") + else: + print("Erreur: nom de fichier de sortie manquant après '-o'.") + sys.exit(1) + else: + timestamp = time.strftime("%Y%m%d") + output_file_excel = f"{output_path}interfaces_{firewall_type}_{timestamp}.xlsx" + output_file_excel = verify_if_file_exists(output_file_excel) + excel_file = export_interfaces_to_excel(output_file_json, output_file_excel) + print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/Parseurs_config_Firewall/src/scripts/export_interfaces.py b/Parseurs_config_Firewall/src/scripts/export_interfaces.py new file mode 100644 index 0000000..f67a226 --- /dev/null +++ b/Parseurs_config_Firewall/src/scripts/export_interfaces.py @@ -0,0 +1,201 @@ +import json +import pandas as pd +from openpyxl import load_workbook +from openpyxl.styles import PatternFill, Border, Side, Alignment, Font +from openpyxl.utils import get_column_letter + + +center = Alignment(horizontal="center", vertical="center", wrap_text=True) +left = Alignment(horizontal="left", vertical="center", wrap_text=True) + +header_fill = PatternFill(start_color="2F5496", fill_type="solid") +iface_fill = PatternFill(start_color="BDD7EE", fill_type="solid") +sub_fill = PatternFill(start_color="DDEBF7", fill_type="solid") +disabled_fill= PatternFill(start_color="F2F2F2", fill_type="solid") + +thick_border = Border(*(Side(style="thick"),)*4) +thin_border = Border(*(Side(style="thin"),)*4) +medium_side = Side(style="medium") + + +def _thin(top=False, bottom=False, left_s=False, right_s=False): + return Border( + top=Side(style="thin") if top else Side(style=None), + bottom=Side(style="thin") if bottom else Side(style=None), + left=Side(style="thin") if left_s else Side(style=None), + right=Side(style="thin") if right_s else Side(style=None), + ) + + +def _build_vrf_map(data): + """Retourne {interface_name: vrf_name} depuis network-instances.""" + vrf_map = {} + for ni in data.get("openconfig-network-instance:network-instances", {}).get("network-instance", []): + vrf_name = ni.get("name", "") + for iface in ni.get("interfaces", {}).get("interface", []): + iface_id = iface.get("id", "") + if iface_id: + vrf_map[iface_id] = vrf_name + return vrf_map + + +def _parse_interfaces(data): + vrf_map = _build_vrf_map(data) + rows = [] + + for iface in data.get("openconfig-interfaces:interfaces", {}).get("interface", []): + cfg = iface.get("config", {}) + iface_name = cfg.get("name", iface.get("name", "")) + iface_type = cfg.get("type", "") + iface_desc = cfg.get("description", "") + enabled = cfg.get("enabled", True) + + subinterfaces = iface.get("subinterfaces", {}).get("subinterface", []) + + if not subinterfaces: + rows.append({ + "Interface": iface_name, + "Type": iface_type, + "Description": iface_desc, + "Enabled": "Oui" if enabled else "Non", + "VLAN": "", + "IP": "", + "Préfixe": "", + "VRF": vrf_map.get(iface_name, ""), + "_is_sub": False, + }) + else: + for sub in subinterfaces: + sub_cfg = sub.get("config", {}) + vlan = sub.get("index", sub_cfg.get("index", "")) + sub_name = f"{iface_name}.{vlan}" if vlan != 0 else iface_name + sub_enabled = sub_cfg.get("enabled", enabled) + + addresses = ( + sub.get("oc-ip:ipv4", {}) + .get("oc-ip:addresses", {}) + .get("oc-ip:address", []) + ) + + if not addresses: + rows.append({ + "Interface": iface_name, + "Type": iface_type, + "Description": iface_desc, + "Enabled": "Oui" if sub_enabled else "Non", + "VLAN": "" if vlan == 0 else vlan, + "IP": "", + "Préfixe": "", + "VRF": vrf_map.get(sub_name, vrf_map.get(iface_name, "")), + "_is_sub": vlan != 0, + }) + else: + for addr in addresses: + addr_cfg = addr.get("oc-ip:config", {}) + rows.append({ + "Interface": iface_name, + "Type": iface_type, + "Description": iface_desc, + "Enabled": "Oui" if sub_enabled else "Non", + "VLAN": "" if vlan == 0 else vlan, + "IP": addr_cfg.get("oc-ip:ip", ""), + "Préfixe": addr_cfg.get("oc-ip:prefix-length", ""), + "VRF": vrf_map.get(sub_name, vrf_map.get(iface_name, "")), + "_is_sub": vlan != 0, + }) + + return rows + + + +def _style_interfaces(ws): + COLS = { + "Interface": 1, + "Type": 2, + "Description": 3, + "Enabled": 4, + "VLAN": 5, + "IP": 6, + "Préfixe": 7, + "VRF": 8, + } + N = len(COLS) + + for col in range(1, N + 1): + cell = ws.cell(row=1, column=col) + cell.fill = header_fill + cell.font = Font(bold=True, color="FFFFFF", size=10) + cell.alignment = center + cell.border = thin_border + + ws.freeze_panes = "A2" + + max_width = {c: len(str(ws.cell(row=1, column=c).value or "")) for c in range(1, N + 1)} + + prev_iface = None + group_start = 2 + + for row in range(2, ws.max_row + 1): + iface_val = ws.cell(row=row, column=COLS["Interface"]).value + enabled = ws.cell(row=row, column=COLS["Enabled"]).value + is_last = (row == ws.max_row) + next_iface = ws.cell(row=row + 1, column=COLS["Interface"]).value if not is_last else None + + if enabled == "Non": + row_fill = disabled_fill + elif ws.cell(row=row, column=COLS["VLAN"]).value: + row_fill = sub_fill + else: + row_fill = iface_fill + + for col in range(1, N + 1): + cell = ws.cell(row=row, column=col) + if col != COLS["Interface"]: + cell.fill = row_fill + cell.alignment = left if col not in (COLS["Enabled"], COLS["VLAN"], COLS["Préfixe"]) else center + cell.border = thin_border + + val = cell.value + max_width[col] = max(max_width[col], len(str(val or ""))) + + if iface_val != next_iface or is_last: + if row > group_start: + ws.merge_cells( + start_row=group_start, start_column=COLS["Interface"], + end_row=row, end_column=COLS["Interface"] + ) + for r in range(group_start, row + 1): + c = ws.cell(r, COLS["Interface"]) + c.fill = iface_fill + c.font = Font(bold=True, size=10) + c.alignment = center + c.border = thick_border + group_start = row + 1 + + ws.row_dimensions[row].height = 18 + + for col, width in max_width.items(): + ws.column_dimensions[get_column_letter(col)].width = min(width + 4, 45) + + +def export_to_excel(json_file_path: str, output_file_excel: str) -> str: + with open(json_file_path, "r", encoding="utf-8") as f: + data = json.load(f) + + rows = _parse_interfaces(data) + + export_rows = [{k: v for k, v in r.items() if k != "_is_sub"} for r in rows] + + df = pd.DataFrame(export_rows, columns=[ + "Interface", "Type", "Description", "Enabled", "VLAN", "IP", "Préfixe", "VRF" + ]) + + with pd.ExcelWriter(output_file_excel, engine="openpyxl") as writer: + df.to_excel(writer, sheet_name="Interfaces", index=False) + + wb = load_workbook(output_file_excel) + _style_interfaces(wb["Interfaces"]) + wb.save(output_file_excel) + + print(f"✓ Export interfaces OK : {output_file_excel}") + return output_file_excel \ No newline at end of file diff --git a/Parseurs_config_Firewall/src/scripts/export_matrice_flux.py b/Parseurs_config_Firewall/src/scripts/export_matrice_flux.py index 5b1ed23..8097aa5 100644 --- a/Parseurs_config_Firewall/src/scripts/export_matrice_flux.py +++ b/Parseurs_config_Firewall/src/scripts/export_matrice_flux.py @@ -8,6 +8,8 @@ from openpyxl import load_workbook from scripts.style_excel.style_address_groups import style_address_groups from scripts.style_excel.style_service_groups import style_service_groups from scripts.style_excel.style_matrice_flux import style_matrice_flux +from scripts.style_excel.style_addresses import style_addresses +from scripts.style_excel.style_services import style_services def export_to_excel(json_file_path, output_file_excel): """ @@ -192,20 +194,46 @@ def export_to_excel(json_file_path, output_file_excel): for m in cfg.get("members", []) ]) + df_addresses = pd.DataFrame([ + { + "Name": name, + "IP / Masque": cfg.get("ip_netmask", "") if "/" in cfg.get("ip_netmask", "") + else (cfg.get("ip_netmask", "") + "/32" if cfg.get("ip_netmask") else ""), + "Description": cfg.get("description", ""), + "Tags": cfg.get("misc", ""), + } + for name, cfg in sorted(addresses.items()) + ]) + + df_services = pd.DataFrame([ + { + "Name": name, + "Protocol": cfg.get("protocol", ""), + "Port": cfg.get("port", ""), + "Description": cfg.get("description", ""), + } + for name, cfg in sorted(services.items()) + ]) + # === Export Excel === firewall_type = data.get("firewall-device", {}).get("type", "firewall").replace(" ","_").lower() date = time.strftime("%Y%m%d") with pd.ExcelWriter(output_file_excel, engine="openpyxl") as writer: - df_addr.to_excel(writer,sheet_name="Address-Groups",index=False) - df_srv.to_excel(writer,sheet_name="Service-Groups",index=False) - df.to_excel(writer,sheet_name="Matrice Flux",index=False,startrow=1) + df_addresses.to_excel(writer, sheet_name="Addresses", index=False) + df_addr.to_excel(writer, sheet_name="Address-Groups", index=False) + df_services.to_excel(writer, sheet_name="Services", index=False) + df_srv.to_excel(writer, sheet_name="Service-Groups", index=False) + df.to_excel(writer, sheet_name="Matrice Flux", index=False, startrow=1) + wb = load_workbook(output_file_excel) if "Sheet1" in wb.sheetnames: del wb["Sheet1"] + style_addresses(wb["Addresses"]) style_address_groups(wb["Address-Groups"], address_groups) + style_services(wb["Services"]) style_service_groups(wb["Service-Groups"], service_groups) style_matrice_flux(wb["Matrice Flux"]) diff --git a/Parseurs_config_Firewall/src/scripts/export_modele.py b/Parseurs_config_Firewall/src/scripts/export_modele.py index d4d01d7..c4dcf77 100644 --- a/Parseurs_config_Firewall/src/scripts/export_modele.py +++ b/Parseurs_config_Firewall/src/scripts/export_modele.py @@ -38,37 +38,46 @@ class ParserMixin: "name": interface.name, "config": { "name": interface.name, - "type": "iana-if-type:ethernetCsmacd" if interface.interface_type == "ethernet" else "iana-if-type:ieee8023adLag", + "type": interface.interface_type if interface.interface_type else None, "enabled": interface.enabled, "description": interface.comment or "" } } if interface.ip: - intf_config["subinterfaces"] = { - "subinterface": [ - { + subinterfaces = [ + { + "index": interface.misc or 0, + "config": { "index": interface.misc or 0, - "config": { - "index": interface.misc or 0, - "enabled": interface.enabled - }, - "oc-ip:ipv4": { - "oc-ip:addresses": { - "oc-ip:address": [ - { + "enabled": interface.enabled + }, + "oc-ip:ipv4": { + "oc-ip:addresses": { + "oc-ip:address": [ + { + "oc-ip:ip": interface.ip, + "oc-ip:config": { "oc-ip:ip": interface.ip, - "oc-ip:config": { - "oc-ip:ip": interface.ip, - "oc-ip:prefix-length": interface.netmask if interface.netmask else 24 - } + "oc-ip:prefix-length": interface.netmask if interface.netmask else 24 } - ] - } + } + ] } } - ] - } + } + ] + + if interface.interface_type == "vlan": + subinterfaces.append({ + "index": interface.vlan or None, + "config": { + "index": interface.vlan or None, + } + }) + + intf_config["subinterfaces"] = {"subinterface": subinterfaces} + openconfig["openconfig-interfaces:interfaces"]["interface"].append(intf_config) diff --git a/Parseurs_config_Firewall/src/scripts/json_Stormshield.py b/Parseurs_config_Firewall/src/scripts/json_Stormshield.py index ba43f08..b15bc8b 100644 --- a/Parseurs_config_Firewall/src/scripts/json_Stormshield.py +++ b/Parseurs_config_Firewall/src/scripts/json_Stormshield.py @@ -46,13 +46,17 @@ class StormshieldParser(ParserMixin): current_section = line.strip("[]") continue + inline_comment = "" + if "#" in line: + inline_comment = line.split("#", 1)[1].strip() + if current_section in ("Host", "Network", "Fqdn"): match = re.match(r"([^=]+)=([^,#]+)", line) if match: name = match.group(1).strip() ip = match.group(2).strip() self.config["address_objects"].append( - AddressObject(name=name, ip_netmask=ip) + AddressObject(name=name, ip_netmask=ip, description=inline_comment) ) elif current_section == "Service": @@ -62,7 +66,7 @@ class StormshieldParser(ParserMixin): port = match.group(2) proto = match.group(3).lower() self.config["service_objects"].append( - ServiceObject(name=name, protocol=proto, port=port) + ServiceObject(name=name, protocol=proto, port=port, description=inline_comment) ) def _parse_address_groups(self): @@ -156,18 +160,7 @@ class StormshieldParser(ParserMixin): def _add_interface(self, section_name, data): """Crée un objet Interface à partir d’une section complète""" - if section_name.lower().startswith("vlan"): - iface_type = "vlan" - elif section_name.lower().startswith("bridge"): - iface_type = "bridge" - elif section_name.lower().startswith("wifi"): - iface_type = "wifi" - elif section_name.lower().startswith("loopback"): - iface_type = "loopback" - elif section_name.lower().startswith("agg"): - iface_type = "aggregate" - else: - iface_type = "ethernet" + iface_type = section_name.rstrip("0123456789") enabled = data.get("State", "0") == "1" name = data.get("Name", section_name) @@ -187,6 +180,7 @@ class StormshieldParser(ParserMixin): comment=comment, interface_type=iface_type, enabled=enabled, + vlan=data.get("Tag") if data.get("Tag") and iface_type == "vlan" else None ) self.config["interfaces"].append(interface) diff --git a/Parseurs_config_Firewall/src/scripts/objets/data.py b/Parseurs_config_Firewall/src/scripts/objets/data.py index 7035710..6b31ec5 100644 --- a/Parseurs_config_Firewall/src/scripts/objets/data.py +++ b/Parseurs_config_Firewall/src/scripts/objets/data.py @@ -61,6 +61,7 @@ class Interface: comment: str = None interface_type: str = None enabled: bool = True + vlan: str = None @dataclass class SecurityRule: diff --git a/Parseurs_config_Firewall/src/scripts/style_excel/style_addresses.py b/Parseurs_config_Firewall/src/scripts/style_excel/style_addresses.py new file mode 100644 index 0000000..73f0be7 --- /dev/null +++ b/Parseurs_config_Firewall/src/scripts/style_excel/style_addresses.py @@ -0,0 +1,44 @@ +from openpyxl.styles import PatternFill, Border, Side, Alignment, Font +from openpyxl.utils import get_column_letter + +center = Alignment(horizontal="center", vertical="center") +left = Alignment(horizontal="left", vertical="center") + +red_fill = PatternFill(start_color="FF9999", fill_type="solid") +yellow_fill = PatternFill(start_color="FFFF99", fill_type="solid") + +thick_border = Border(*(Side(style="thick"),)*4) +thin_border = Border(*(Side(style="thin"),)*4) + +def style_addresses(ws): + + col_name, col_ip, col_desc, col_tags = 1, 2, 3, 4 + + max_width = {1: 0, 2: 0, 3: 0, 4: 0} + + for row in range(2, ws.max_row + 1): + name = ws.cell(row=row, column=col_name).value + ip = ws.cell(row=row, column=col_ip).value + desc = ws.cell(row=row, column=col_desc).value + tags = ws.cell(row=row, column=col_tags).value + + ws.cell(row=row, column=col_name).fill = red_fill + ws.cell(row=row, column=col_name).font = Font(bold=True) + ws.cell(row=row, column=col_name).alignment = left + ws.cell(row=row, column=col_name).border = thin_border + + for col in (col_ip, col_desc, col_tags): + ws.cell(row=row, column=col).alignment = left + ws.cell(row=row, column=col).border = thick_border + ws.cell(row=row, column=col).fill = yellow_fill + + max_width[1] = max(max_width[1], len(str(name or ""))) + max_width[2] = max(max_width[2], len(str(ip or ""))) + max_width[3] = max(max_width[3], len(str(desc or ""))) + max_width[4] = max(max_width[4], len(str(tags or ""))) + + for col in max_width: + ws.column_dimensions[get_column_letter(col)].width = max_width[col] + 2 + + for row in range(2, ws.max_row + 1): + ws.row_dimensions[row].height = 18 \ No newline at end of file diff --git a/Parseurs_config_Firewall/src/scripts/style_excel/style_services.py b/Parseurs_config_Firewall/src/scripts/style_excel/style_services.py new file mode 100644 index 0000000..182cc0d --- /dev/null +++ b/Parseurs_config_Firewall/src/scripts/style_excel/style_services.py @@ -0,0 +1,51 @@ +from openpyxl.styles import PatternFill, Border, Side, Alignment, Font +from openpyxl.utils import get_column_letter + +center = Alignment(horizontal="center", vertical="center") +left = Alignment(horizontal="left", vertical="center") + +red_fill = PatternFill(start_color="FF9999", fill_type="solid") +yellow_fill = PatternFill(start_color="FFFF99", fill_type="solid") + +thick_border = Border(*(Side(style="thick"),)*4) +thin_border = Border(*(Side(style="thin"),)*4) + +def style_services(ws): + + col_name, col_proto, col_port, col_desc = 1, 2, 3, 4 + + max_width = {1: 0, 2: 0, 3: 0, 4: 0} + + for row in range(2, ws.max_row + 1): + name = ws.cell(row=row, column=col_name).value + proto = ws.cell(row=row, column=col_proto).value + port = ws.cell(row=row, column=col_port).value + desc = ws.cell(row=row, column=col_desc).value + + ws.cell(row=row, column=col_name).fill = red_fill + ws.cell(row=row, column=col_name).font = Font(bold=True) + ws.cell(row=row, column=col_name).alignment = left + ws.cell(row=row, column=col_name).border = thin_border + + ws.cell(row=row, column=col_proto).alignment = center + ws.cell(row=row, column=col_proto).border = thick_border + ws.cell(row=row, column=col_proto).fill = yellow_fill + + ws.cell(row=row, column=col_port).alignment = center + ws.cell(row=row, column=col_port).border = thick_border + ws.cell(row=row, column=col_port).fill = yellow_fill + + ws.cell(row=row, column=col_desc).alignment = left + ws.cell(row=row, column=col_desc).border = thick_border + ws.cell(row=row, column=col_desc).fill = yellow_fill + + max_width[1] = max(max_width[1], len(str(name or ""))) + max_width[2] = max(max_width[2], len(str(proto or ""))) + max_width[3] = max(max_width[3], len(str(port or ""))) + max_width[4] = max(max_width[4], len(str(desc or ""))) + + for col in max_width: + ws.column_dimensions[get_column_letter(col)].width = max_width[col] + 2 + + for row in range(2, ws.max_row + 1): + ws.row_dimensions[row].height = 18 \ No newline at end of file diff --git a/README.md b/README.md index b2e8cf1..1f6ad73 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Données extraites : Fichiers et dossiers de configurations d'origine. Rapports générés : - Matrice de flux (Excel) pour visualiser les communications et les règles de trafic. - Matrice de routage (Excel) pour cartographier les routes statiques. +- Détail des interfaces (Excel) pour visualiser la configuration des interfaces. ``` - Module Switch diff --git a/gui_firewall.py b/gui_firewall.py index 3cb00fc..d55d076 100644 --- a/gui_firewall.py +++ b/gui_firewall.py @@ -75,11 +75,12 @@ class ToolTip: def open_firewall_gui_multi(app, OUTPUT_DIR, HELP_FILE_FW, FIREWALL_DIR, FIREWALL_MAIN): app2 = tk.Toplevel(app) app2.title("Analyse Configuration Firewall - Plusieurs firewall") - app2.geometry("800x450") + app2.geometry("800x500") app2.resizable(True, True) matrice_flux = tk.BooleanVar() matrice_routage = tk.BooleanVar() + interfaces = tk.BooleanVar() content = make_scrollable(app2) @@ -158,6 +159,8 @@ def open_firewall_gui_multi(app, OUTPUT_DIR, HELP_FILE_FW, FIREWALL_DIR, FIREWAL cmd.append("-f") if matrice_routage.get(): cmd.append("-r") + if interfaces.get(): + cmd.append("-i") cmd.append("-o") if item["type"] == "file": @@ -376,9 +379,16 @@ def open_firewall_gui_multi(app, OUTPUT_DIR, HELP_FILE_FW, FIREWALL_DIR, FIREWAL content, text="Générer la matrice de routage en Excel (route statique uniquement)", variable=matrice_routage - ).pack(anchor="w", padx=10, pady=(0, 10)) + ).pack(anchor="w", padx=10, pady=(0, 0)) matrice_routage.set(True) + ttk.Checkbutton( + content, + text="Générer la configuration des interfaces en Excel", + variable=interfaces + ).pack(anchor="w", padx=10, pady=(0, 10)) + interfaces.set(True) + ttk.Label( content, text="Dossier de sortie :", @@ -418,7 +428,7 @@ def open_firewall_gui(root, BASE_DIR): app = tk.Toplevel(root) app.title("Analyse Configuration Firewall") - app.geometry("800x450") + app.geometry("800x500") app.resizable(True, True) firewall_var = tk.StringVar() @@ -427,6 +437,7 @@ def open_firewall_gui(root, BASE_DIR): matrice_flux = tk.BooleanVar() matrice_routage = tk.BooleanVar() + interfaces = tk.BooleanVar() def browse_input(): fw_type = firewall_var.get() @@ -453,22 +464,15 @@ def open_firewall_gui(root, BASE_DIR): f_json = os.path.join(OUTPUT_DIR, f"{fw}_{output_var.get()}.json") f_flux = os.path.join(OUTPUT_DIR, f"matrice_flux_{fw}_{output_var.get()}.xlsx") f_routage = os.path.join(OUTPUT_DIR, f"matrice_routage_{fw}_{output_var.get()}.xlsx") + f_interfaces = os.path.join(OUTPUT_DIR, f"interfaces_{fw}_{output_var.get()}.xlsx") else: dt = datetime.now().strftime("%Y%m%d") f_json = os.path.join(OUTPUT_DIR, f"{fw}_{dt}.json") f_flux = os.path.join(OUTPUT_DIR, f"matrice_flux_{fw}_{dt}.xlsx") f_routage = os.path.join(OUTPUT_DIR, f"matrice_routage_{fw}_{dt}.xlsx") + f_interfaces = os.path.join(OUTPUT_DIR, f"interfaces_{fw}_{dt}.xlsx") - if not matrice_flux.get(): - if not matrice_routage.get(): - output_label_var.set("Fichier de sortie :\n" + f_json) - else: - output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_routage) - else: - if not matrice_routage.get(): - output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_flux) - else: - output_label_var.set("Fichiers de sortie :\n" + f_json + "\n" + f_flux + "\n" + f_routage) + output_label_var.set("Fichier de sortie :\n" + f_json + ("\n" + f_flux if matrice_flux.get() else "") + ("\n" + f_routage if matrice_routage.get() else "") + ("\n" + f_interfaces if interfaces.get() else "")) app.update_idletasks() def open_output_folder(): @@ -518,9 +522,10 @@ def open_firewall_gui(root, BASE_DIR): if matrice_flux.get(): cmd.append("-f") - if matrice_routage.get(): cmd.append("-r") + if interfaces.get(): + cmd.append("-i") print("Commande exécutée :", " ".join(cmd)) print("Dossier courant (cwd) :", FIREWALL_DIR) @@ -636,15 +641,23 @@ def open_firewall_gui(root, BASE_DIR): app, text="Générer la matrice de routage en Excel (route statique uniquement)", variable=matrice_routage - ).pack(anchor="w", padx=10, pady=(0, 10)) + ).pack(anchor="w", padx=10, pady=(0, 0)) matrice_routage.set(True) + ttk.Checkbutton( + app, + text="Générer la configuration des interfaces en Excel", + variable=interfaces + ).pack(anchor="w", padx=10, pady=(0, 10)) + interfaces.set(True) + output_label_var = tk.StringVar() ttk.Label(app, textvariable=output_label_var).pack(anchor="w", padx=10) firewall_var.trace_add("write", update_output_label) output_var.trace_add("write", update_output_label) matrice_flux.trace_add("write", update_output_label) matrice_routage.trace_add("write", update_output_label) + interfaces.trace_add("write", update_output_label) update_output_label() ttk.Button( diff --git a/help_Firewall.md b/help_Firewall.md index d72a67d..2f9a451 100644 --- a/help_Firewall.md +++ b/help_Firewall.md @@ -6,6 +6,7 @@ Cet outil permet de **récupérer les données des configurations de différents - Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure. - Il founit également la possibilité de générer une **matrice de routage au format Excel** pour visualiser les routes statiques dans l’infrastructure. +- Il founit également la possibilité de générer le **détail des interfaces au format Excel** pour visualiser la configuration des interfaces. ## Utilisation diff --git a/main.py b/main.py index e5e9a83..640a5d6 100644 --- a/main.py +++ b/main.py @@ -58,9 +58,10 @@ def bootstrap_venv(): def relaunch_in_venv(): print("[INFO] Relance du script dans le venv…") + script_path = os.path.abspath(__file__) subprocess.check_call([ venv_python(), - __file__ + script_path ]) sys.exit(0) @@ -68,14 +69,30 @@ def relaunch_in_venv(): def open_main_gui(): root = tk.Tk() root.title("Analyse Réseau") - root.geometry("650x350") + root.geometry("650x500") root.resizable(True, True) + header_frame = tk.Frame(root) + header_frame.pack(fill="x", pady=10, padx=10) + + logo_path = os.path.join(BASE_DIR, "Computacenter_logo.png") + if os.path.isfile(logo_path): + try: + logo_img = tk.PhotoImage(file=logo_path) + + logo_img = logo_img.subsample(15, 15) + + logo_label = tk.Label(header_frame, image=logo_img) + logo_label.image = logo_img + logo_label.pack(side="right") + except Exception as e: + print(f"[WARN] Impossible de charger le logo : {e}") + ttk.Label( root, text="Analyse Réseau", font=("Arial", 16, "bold") - ).pack(pady=20) + ).pack(pady=(0, 20)) ttk.Label( root, @@ -94,7 +111,8 @@ def open_main_gui(): root, text="(Convertir les données au format normalisé Yang dans un fichier JSON)" \ "\n + possibilité de générer une matrice de flux en Excel" \ - "\n + possibilité de générer une matrice de routage en Excel (route statique uniquement)", + "\n + possibilité de générer une matrice de routage en Excel (route statique uniquement)" \ + "\n + possibilité de générer un rapport des interfaces en Excel", font=("Arial", 9, "italic"), anchor="center", justify="center"