Validation de configuration réseau
Utilisez cette compétence pour examiner la configuration réseau avant une fenêtre de changement ou avant qu'une automatisation ne touche les équipements de production.
Quand l'utiliser
- Examen d'extraits de style Cisco IOS ou IOS-XE avant déploiement.
- Audit de configurations générées par des scripts ou templates.
- Recherche de commandes dangereuses, d'adresses IP en double ou de chevauchements de sous-réseaux.
- Vérification que les ACL, route-maps, prefix-lists ou politiques de ligne sont référencés mais pas définis.
- Construction de scripts de pré-vérification légers pour l'automatisation réseau.
Comment ça marche
Considérez la validation de configuration comme une preuve par couches, pas comme un analyseur complet. Les vérifications par expressions régulières sont utiles pour des avertissements de pré-vérification, mais l'approbation finale nécessite toujours l'examen d'un ingénieur réseau sur l'intention, la syntaxe de la plateforme et les étapes de retour arrière.
Validez dans cet ordre :
- Commandes destructrices.
- Exposition des identifiants et du plan de gestion.
- Adresses en double et sous-réseaux qui se chevauchent.
- Références obsolètes aux ACL, route-maps, prefix-lists et interfaces.
- Hygiène opérationnelle : NTP, horodatage, journalisation distante et bannières.
Détection de commandes dangereuses
import re
DANGEROUS_PATTERNS: list[tuple[re.Pattern[str], str]] = [
(re.compile(r"\breload\b", re.I), "reload provoque un arrêt de service"),
(re.compile(r"\berase\s+(startup|nvram|flash)", re.I), "efface le stockage persistant"),
(re.compile(r"\bformat\b", re.I), "formate un système de fichiers de l'équipement"),
(re.compile(r"\bno\s+router\s+(bgp|ospf|eigrp)\b", re.I), "supprime un processus de routage"),
(re.compile(r"\bno\s+interface\s+\S+", re.I), "supprime la configuration d'interface"),
(re.compile(r"\baaa\s+new-model\b", re.I), "modifie le comportement d'authentification"),
(re.compile(r"\bcrypto\s+key\s+(zeroize|generate)\b", re.I), "modifie les clés SSH de l'équipement"),
]
def find_dangerous_commands(lines: list[str]) -> list[dict[str, str | int]]:
findings = []
for line_number, line in enumerate(lines, start=1):
stripped = line.strip()
for pattern, reason in DANGEROUS_PATTERNS:
if pattern.search(stripped):
findings.append({
"line": line_number,
"command": stripped,
"reason": reason,
})
return findings
Adresses IP en double et chevauchements de sous-réseaux
import ipaddress
import re
from collections import Counter
IP_ADDRESS_RE = re.compile(
r"^\s*ip address\s+"
r"(?P<ip>\d{1,3}(?:\.\d{1,3}){3})\s+"
r"(?P<mask>\d{1,3}(?:\.\d{1,3}){3})\b",
re.I | re.M,
)
def extract_interfaces(config: str) -> list[dict[str, str]]:
results = []
current = None
for line in config.splitlines():
if line.startswith("interface "):
current = line.split(maxsplit=1)[1]
continue
match = IP_ADDRESS_RE.match(line)
if current and match:
ip = match.group("ip")
mask = match.group("mask")
network = ipaddress.ip_interface(f"{ip}/{mask}").network
results.append({"interface": current, "ip": ip, "network": str(network)})
return results
def find_duplicate_ips(config: str) -> list[str]:
ips = [entry["ip"] for entry in extract_interfaces(config)]
counts = Counter(ips)
return sorted(ip for ip, count in counts.items() if count > 1)
def find_subnet_overlaps(config: str) -> list[tuple[str, str]]:
networks = [ipaddress.ip_network(entry["network"]) for entry in extract_interfaces(config)]
overlaps = []
for index, left in enumerate(networks):
for right in networks[index + 1:]:
if left.overlaps(right):
overlaps.append((str(left), str(right)))
return overlaps
Vérifications du plan de gestion
Analysez les blocs VTY par section afin que les vérifications d'access-class ne se propagent pas sur des lignes sans rapport.
import re
def iter_blocks(config: str, starts_with: str) -> list[str]:
blocks = []
current: list[str] = []
for line in config.splitlines():
if line.startswith(starts_with):
if current:
blocks.append("\n".join(current))
current = [line]
continue
if current:
if line and not line.startswith(" "):
blocks.append("\n".join(current))
current = []
else:
current.append(line)
if current:
blocks.append("\n".join(current))
return blocks
def check_vty_blocks(config: str) -> list[str]:
issues = []
for block in iter_blocks(config, "line vty"):
if re.search(r"transport\s+input\s+.*telnet", block, re.I):
issues.append("VTY autorise Telnet ; exiger SSH uniquement.")
if not re.search(r"\baccess-class\s+\S+\s+in\b", block, re.I):
issues.append("Le bloc VTY n'a pas de restriction d'access-class entrante.")
if not re.search(r"\bexec-timeout\s+\d+\s+\d+\b", block, re.I):
issues.append("Le bloc VTY n'a pas d'exec-timeout explicite.")
return issues
Vérifications d'hygiène de sécurité
SECURITY_PATTERNS = [
(re.compile(r"\bsnmp-server community\s+(public|private)\b", re.I),
"communauté SNMP par défaut configurée"),
(re.compile(r"\bsnmp-server community\s+\S+", re.I),
"chaîne de communauté SNMPv2 configurée ; préférer SNMPv3 authPriv"),
(re.compile(r"\bip ssh version 1\b", re.I),
"SSH version 1 activé"),
(re.compile(r"\benable password\b", re.I),
"mot de passe enable présent ; utiliser enable secret"),
(re.compile(r"\busername\s+\S+\s+password\b", re.I),
"nom d'utilisateur local utilise password au lieu de secret"),
]
BEST_PRACTICE_PATTERNS = [
(re.compile(r"\bntp server\b", re.I), "serveur NTP"),
(re.compile(r"\bservice timestamps\b", re.I), "horodatage des journaux"),
(re.compile(r"\blogging\s+\S+", re.I), "destination ou buffer de journalisation"),
(re.compile(r"\bsnmp-server group\s+\S+\s+v3\s+priv\b", re.I), "groupe SNMPv3 authPriv"),
(re.compile(r"\bbanner\s+(login|motd)\b", re.I), "bannière de connexion"),
]
def check_security(config: str) -> list[str]:
return [message for pattern, message in SECURITY_PATTERNS if pattern.search(config)]
def check_missing_hygiene(config: str) -> list[str]:
return [
f"Absence de : {description}"
for pattern, description in BEST_PRACTICE_PATTERNS
if not pattern.search(config)
]
Exemples
Pré-vérification avant fenêtre de changement
- Exécutez les vérifications de commandes dangereuses sur l'extrait exact à coller.
- Exécutez les vérifications d'adresses IP en double et de chevauchements de sous-réseaux sur la configuration candidate complète.
- Confirmez que chaque ACL, route-map et prefix-list référencé existe.
- Confirmez les commandes de retour arrière et l'accès hors bande avant toute modification du plan de gestion.
Pré-vérification d'automatisation
Utilisez la validation comme une porte bloquante avant que l'automatisation via Netmiko, NAPALM, Ansible ou l'API du fournisseur ne pousse une configuration générée. Échouez en sécurité sur les commandes dangereuses et les identifiants. Avertissez sur les écarts de bonnes pratiques qui sont hors du périmètre du changement.
Anti-patrons
- Traiter la validation par expressions régulières comme un analyseur d'équipement.
- Appliquer une configuration générée sans simulation préalable (dry-run).
- Recommander des chaînes de communauté SNMPv2 comme exigence de surveillance.
- Vérifier les blocs VTY avec des expressions régulières qui peuvent accidentellement couvrir des sections sans rapport.
- Tester le comportement du pare-feu en désactivant les ACL au lieu de lire les compteurs/journaux.
Voir aussi
- Agent :
network-config-reviewer - Agent :
network-troubleshooter - Compétence :
network-interface-health