Validation de Configuration Réseau
Utilisez cette compétence pour réviser une configuration réseau avant une fenêtre de changement ou avant qu'une exécution d'automatisation ne touche les équipements de production.
Quand l'utiliser
- Réviser des extraits de style Cisco IOS ou IOS-XE avant déploiement.
- Auditer une configuration générée par des scripts ou des modèles.
- Rechercher des commandes dangereuses, des adresses IP en double ou des chevauchements de sous-réseaux.
- Vérifier si les ACL, route-maps, prefix-lists ou politiques de ligne sont référencés mais non définis.
- Construire des scripts de pré-vol légers pour l'automatisation réseau.
Comment ça fonctionne
Traitez la validation de configuration comme une preuve en couches, pas comme un parseur complet. Les vérifications par expressions régulières sont utiles pour les avertissements de pré-vol, mais l'approbation finale nécessite toujours qu'un ingénieur réseau examine 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 comme 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 temps d'arrêt"),
(re.compile(r"\berase\s+(startup|nvram|flash)", re.I), "efface le stockage persistant"),
(re.compile(r"\bformat\b", re.I), "formate le 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 pour que les vérifications d'access-class ne se propagent pas sur des lignes non liées.
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("Le VTY autorise Telnet ; privilégier 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 de source par 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 de délai d'expiration explicite (exec-timeout).")
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 tampon 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é-vol pour Fenêtre de Changement
- Exécutez les vérifications de commandes dangereuses sur l'extrait exact à coller.
- Exécutez les vérifications d'IP en double et de chevauchement 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é-vol pour Automatisation
Utilisez la validation comme porte de blocage avant que Netmiko, NAPALM, Ansible ou l'API du fournisseur d'automatisation ne pousse une configuration générée. Échouez en mode fermé sur les commandes dangereuses et les identifiants. Avertissez sur les lacunes de bonnes pratiques qui sont en dehors du périmètre du changement.
Anti-Patterns
- Traiter la validation par expressions régulières comme un parseur d'équipement.
- Appliquer une configuration générée sans comparaison préalable (dry-run).
- Recommander des chaînes de communauté SNMPv2 comme exigence de supervision.
- Vérifier les blocs VTY avec des expressions régulières qui peuvent accidentellement couvrir des sections non liées.
- Tester le comportement du pare-feu en désactivant les ACL au lieu de lire les compteurs et journaux.
Voir Aussi
- Agent :
network-config-reviewer - Agent :
network-troubleshooter - Compétence :
network-interface-health