mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-14 02:10:07 +08:00
211 lines
7.5 KiB
Markdown
211 lines
7.5 KiB
Markdown
---
|
|
name: network-config-validation
|
|
description: Pre-deployment checks for router and switch configuration, including dangerous commands, duplicate addresses, subnet overlaps, stale references, management-plane risk, and IOS-style security hygiene.
|
|
origin: community
|
|
---
|
|
|
|
# Network Config Validation
|
|
|
|
Use this skill to review network configuration before a change window or before
|
|
an automation run touches production devices.
|
|
|
|
## When to Use
|
|
|
|
- Reviewing Cisco IOS or IOS-XE style snippets before deployment.
|
|
- Auditing generated config from scripts or templates.
|
|
- Looking for dangerous commands, duplicate IP addresses, or subnet overlaps.
|
|
- Checking whether ACLs, route-maps, prefix-lists, or line policies are referenced
|
|
but not defined.
|
|
- Building lightweight pre-flight scripts for network automation.
|
|
|
|
## How It Works
|
|
|
|
Treat config validation as layered evidence, not as a complete parser. Regex
|
|
checks are useful for pre-flight warnings, but final approval still needs a
|
|
network engineer to review intent, platform syntax, and rollback steps.
|
|
|
|
Validate in this order:
|
|
|
|
1. Destructive commands.
|
|
2. Credential and management-plane exposure.
|
|
3. Duplicate addresses and overlapping subnets.
|
|
4. Stale references to ACLs, route-maps, prefix-lists, and interfaces.
|
|
5. Operational hygiene such as NTP, timestamps, remote logging, and banners.
|
|
|
|
## Dangerous Command Detection
|
|
|
|
```python
|
|
import re
|
|
|
|
DANGEROUS_PATTERNS: list[tuple[re.Pattern[str], str]] = [
|
|
(re.compile(r"\breload\b", re.I), "reload causes downtime"),
|
|
(re.compile(r"\berase\s+(startup|nvram|flash)", re.I), "erases persistent storage"),
|
|
(re.compile(r"\bformat\b", re.I), "formats a device filesystem"),
|
|
(re.compile(r"\bno\s+router\s+(bgp|ospf|eigrp)\b", re.I), "removes a routing process"),
|
|
(re.compile(r"\bno\s+interface\s+\S+", re.I), "removes interface configuration"),
|
|
(re.compile(r"\baaa\s+new-model\b", re.I), "changes authentication behavior"),
|
|
(re.compile(r"\bcrypto\s+key\s+(zeroize|generate)\b", re.I), "changes device SSH keys"),
|
|
]
|
|
|
|
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
|
|
```
|
|
|
|
## Duplicate IPs And Subnet Overlaps
|
|
|
|
```python
|
|
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
|
|
```
|
|
|
|
## Management-Plane Checks
|
|
|
|
Parse VTY blocks by section so access-class checks do not spill across unrelated
|
|
lines.
|
|
|
|
```python
|
|
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 allows Telnet; require SSH only.")
|
|
if not re.search(r"\baccess-class\s+\S+\s+in\b", block, re.I):
|
|
issues.append("VTY block has no inbound access-class source restriction.")
|
|
if not re.search(r"\bexec-timeout\s+\d+\s+\d+\b", block, re.I):
|
|
issues.append("VTY block has no explicit exec-timeout.")
|
|
return issues
|
|
```
|
|
|
|
## Security Hygiene Checks
|
|
|
|
```python
|
|
SECURITY_PATTERNS = [
|
|
(re.compile(r"\bsnmp-server community\s+(public|private)\b", re.I),
|
|
"default SNMP community configured"),
|
|
(re.compile(r"\bsnmp-server community\s+\S+", re.I),
|
|
"SNMPv2 community string configured; prefer SNMPv3 authPriv"),
|
|
(re.compile(r"\bip ssh version 1\b", re.I),
|
|
"SSH version 1 enabled"),
|
|
(re.compile(r"\benable password\b", re.I),
|
|
"enable password is present; use enable secret"),
|
|
(re.compile(r"\busername\s+\S+\s+password\b", re.I),
|
|
"local username uses password instead of secret"),
|
|
]
|
|
|
|
BEST_PRACTICE_PATTERNS = [
|
|
(re.compile(r"\bntp server\b", re.I), "NTP server"),
|
|
(re.compile(r"\bservice timestamps\b", re.I), "log timestamps"),
|
|
(re.compile(r"\blogging\s+\S+", re.I), "logging destination or buffer"),
|
|
(re.compile(r"\bsnmp-server group\s+\S+\s+v3\s+priv\b", re.I), "SNMPv3 authPriv group"),
|
|
(re.compile(r"\bbanner\s+(login|motd)\b", re.I), "login banner"),
|
|
]
|
|
|
|
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"Missing {description}"
|
|
for pattern, description in BEST_PRACTICE_PATTERNS
|
|
if not pattern.search(config)
|
|
]
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Change-Window Preflight
|
|
|
|
1. Run dangerous-command checks on the exact snippet to be pasted.
|
|
2. Run duplicate IP and subnet overlap checks against the full candidate config.
|
|
3. Confirm every referenced ACL, route-map, and prefix-list exists.
|
|
4. Confirm rollback commands and out-of-band access before any management-plane
|
|
change.
|
|
|
|
### Automation Preflight
|
|
|
|
Use validation as a blocking gate before Netmiko, NAPALM, Ansible, or vendor API
|
|
automation pushes a generated config. Fail closed on dangerous commands and
|
|
credentials. Warn on best-practice gaps that are outside the change scope.
|
|
|
|
## Anti-Patterns
|
|
|
|
- Treating regex validation as a device parser.
|
|
- Applying generated config without a dry-run diff.
|
|
- Recommending SNMPv2 community strings as a monitoring requirement.
|
|
- Checking VTY blocks with regex that can accidentally span unrelated sections.
|
|
- Testing firewall behavior by disabling ACLs instead of reading counters/logs.
|
|
|
|
## See Also
|
|
|
|
- Agent: `network-config-reviewer`
|
|
- Agent: `network-troubleshooter`
|
|
- Skill: `network-interface-health`
|