網路配置驗證 是做什麼的?
使用此技能在變更窗口前或自動化操作接觸正式設備前審查網路配置。
使用時機
- 在部署前審查 Cisco IOS 或 IOS-XE 風格的配置片段。
- 審核由腳本或範本產生的配置。
- 檢查危險指令、重複 IP 位址或子網重疊。
- 確認 ACL、路由映射 (route-map)、前綴列表 (prefix-list) 或線路策略是否被參照但未定義。
- 為網路自動化建立輕量級預檢腳本。
運作方式
將配置驗證視為分層的證據收集,而非完整的解析器。正規表示式檢查對預檢警告很有幫助,但最終核准仍需網路工程師審查意圖、平台語法及回滾步驟。
請依以下順序進行驗證:
- 破壞性指令。
- 憑證與管理平面暴露。
- 重複位址與子網重疊。
- ACL、路由映射、前綴列表及介面的過時參照。
- 操作衛生,例如 NTP、時間戳記、遠端日誌與橫幅。
危險指令偵測
import re
DANGEROUS_PATTERNS: list[tuple[re.Pattern[str], str]] = [
(re.compile(r"\breload\b", re.I), "重載會導致服務中斷"),
(re.compile(r"\berase\s+(startup|nvram|flash)", re.I), "清除持續性儲存空間"),
(re.compile(r"\bformat\b", re.I), "格式化設備檔案系統"),
(re.compile(r"\bno\s+router\s+(bgp|ospf|eigrp)\b", re.I), "移除路由協定進程"),
(re.compile(r"\bno\s+interface\s+\S+", re.I), "移除介面配置"),
(re.compile(r"\baaa\s+new-model\b", re.I), "改變驗證行為"),
(re.compile(r"\bcrypto\s+key\s+(zeroize|generate)\b", re.I), "改變設備 SSH 金鑰"),
]
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
重複 IP 與子網重疊
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
管理平面檢查
透過區塊解析 VTY 段落,避免 access-class 檢查跨越不相關的行。
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 允許 Telnet;請僅使用 SSH。")
if not re.search(r"\baccess-class\s+\S+\s+in\b", block, re.I):
issues.append("VTY 區塊缺少入站 access-class 來源限制。")
if not re.search(r"\bexec-timeout\s+\d+\s+\d+\b", block, re.I):
issues.append("VTY 區塊未設定明確的 exec-timeout。")
return issues
安全衛生檢查
SECURITY_PATTERNS = [
(re.compile(r"\bsnmp-server community\s+(public|private)\b", re.I),
"使用了預設 SNMP 社群字串"),
(re.compile(r"\bsnmp-server community\s+\S+", re.I),
"配置了 SNMPv2 社群字串;建議使用 SNMPv3 authPriv"),
(re.compile(r"\bip ssh version 1\b", re.I),
"啟用了 SSH 版本 1"),
(re.compile(r"\benable password\b", re.I),
"存在 enable password;請改用 enable secret"),
(re.compile(r"\busername\s+\S+\s+password\b", re.I),
"本地使用者使用 password 而非 secret"),
]
BEST_PRACTICE_PATTERNS = [
(re.compile(r"\bntp server\b", re.I), "NTP 伺服器"),
(re.compile(r"\bservice timestamps\b", re.I), "日誌時間戳記"),
(re.compile(r"\blogging\s+\S+", re.I), "日誌目的地或緩衝區"),
(re.compile(r"\bsnmp-server group\s+\S+\s+v3\s+priv\b", re.I), "SNMPv3 authPriv 群組"),
(re.compile(r"\bbanner\s+(login|motd)\b", re.I), "登入橫幅"),
]
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"缺少 {description}"
for pattern, description in BEST_PRACTICE_PATTERNS
if not pattern.search(config)
]
範例
變更窗口預檢
- 對即將貼上的確切片段執行危險指令檢查。
- 對完整的候選配置執行重複 IP 與子網重疊檢查。
- 確認每個被參照的 ACL、路由映射 (route-map) 與前綴列表 (prefix-list) 皆已定義。
- 在進行任何管理平面變更前,確認回滾指令與帶外存取功能正常。
自動化預檢
在 Netmiko、NAPALM、Ansible 或供應商 API 自動化推送產生的配置前,將驗證視為封鎖閘道。在發現危險指令與憑證時應失敗關閉(fail closed);對超出變更範圍的最佳實務缺口則僅發出警告。
反模式
- 將正規表示式驗證視為設備解析器。
- 未執行乾執行比對 (dry-run diff) 就直接套用產生的配置。
- 以監控需求為由推薦使用 SNMPv2 社群字串。
- 使用可能意外跨越不相關區段的正規表示式來檢查 VTY 區塊。
- 透過停用 ACL 而非讀取計數器或日誌來測試防火牆行為。
相關資源
- 代理:
network-config-reviewer - 代理:
network-troubleshooter - 技能:
network-interface-health