旧版防火墙因不支持标准API和现代SSH免密登录,需用Expect模拟交互完成自动化配置同步;脚本需处理动态提示符、分页、确认提示及超时重试,并通过Shell封装保障密码安全。

旧版防火墙设备(如某些定制化嵌入式防火墙、早期型号的网御、天融信、或基于Linux 2.4内核的私有系统)通常不支持标准API、Ansible模块或现代SSH密钥免密登录,且交互式CLI存在密码二次验证、分页提示(如 --More--)、命令确认(Are you sure? [y/n])等阻断因素。此时用纯Shell无法可靠完成自动化配置同步,必须借助 Expect 模拟人工交互流程。
一、确认环境与基础依赖
Expect 不是默认安装组件,需手动部署:
- CentOS/RHEL:运行 yum install -y expect(若仓库不可用,可编译安装 tcl/expect 源码)
- Ubuntu/Debian:运行 apt-get install -y expect
- 验证:执行 expect -v 应输出版本号(建议 ≥5.45)
- 确保目标防火墙开放 SSH(通常为 22 或私有端口),且允许密码登录(Expect 本质依赖密码交互)
二、编写健壮的 Expect 同步脚本
关键在于匹配动态提示符、处理超时、规避分页与确认陷阱。以下为典型结构(保存为 sync_fw_rules.exp):
<font size="2">#!/usr/bin/expect -f
set timeout 30
set host [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set config_file [lindex $argv 3]
<h1>启动SSH连接</h1>
<p>spawn ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 $user@$host</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill8828" title="Obsidian (Linux)"><img
src="https://img.php.cn/upload/skill/000/000/081/179228531040284.jpg" alt="Obsidian (Linux)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill8828" title="Obsidian (Linux)" class="overflowclass">Obsidian (Linux)</a>
<p class="overflowclass">操作 Obsidian 库(纯 Markdown 笔记),并通过 notesmd-cli 实现自动化。</p>
</div>
<a rel="nofollow" href="/xiazai/skill8828" title="Obsidian (Linux)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<h1>匹配登录提示(兼容不同设备:login: / Password: / # / $ / >)</h1>
<p>expect {
-re ".<em>[Pp]assword.</em>" { send "$pass\r" }
-re ".<em>[Ll]ogin.</em>" { send "$user\r"; exp_continue }
timeout { exit 1 }
}</p>
<h1>等待进入特权模式(可能需输入enable密码)</h1>
<p>expect {
-re ".<em>[#\$>]]" { }
-re ".</em>[Ee]nable.*" { send "$pass\r"; exp_continue }
timeout { exit 2 }
}</p>
<h1>关闭分页(常见命令:term len 0 / screen-length disable / more off)</h1>
<p>send "terminal length 0\r"
expect {
-re ".*[#\$>]]" { }
timeout { send "\r"; exp_continue }
}</p>
<h1>上传并应用规则(假设设备支持 paste 模式或 load 命令)</h1>
<p>send "configure terminal\r"
expect {
-re ".*\(config\)#" { }
timeout { exit 3 }
}</p>
<h1>逐行读取本地规则文件并发送(避免单次过长导致丢包)</h1>
<p>set fp [open $config_file r]
while {[gets $fp line] != -1} {
if {[string trim $line] ne "" && ![regexp "^#" $line]} {
send "$line\r"
expect {
-re ".<em>\(config\)#" { }
-re ".</em>[Yy]/[Nn]" { send "y\r"; exp_continue }
timeout { send "\r"; exp_continue }
}
}
}
close $fp</p>
<h1>保存配置(命令因设备而异:write / save / commit)</h1>
<p>send "write memory\r"
expect {
-re ".<em>[Ss]uccess.</em>|.<em>[Oo]K.</em>|.<em>saved.</em>" { }
-re ".*[Yy]/[Nn]" { send "y\r"; exp_continue }
timeout { exit 4 }
}</p>
<p>send "exit\r"
expect eof</p></font>
三、Shell 封装与安全调用
避免在命令行暴露密码,使用临时变量或配置文件传递敏感信息:
- 创建封装脚本 run_sync.sh:
<font size="2">#!/bin/bash FW_HOST="192.168.10.5" FW_USER="admin" FW_PASS="Sec@2023" RULES_FILE="./rules/fw-prod.conf" <h1>密码通过环境变量传入Expect(更安全)</h1> <p>export FW_PASS expect ./sync_fw_rules.exp "$FW_HOST" "$FW_USER" "$FW_PASS" "$RULES_FILE"</p></font>
- 赋予执行权限:chmod +x sync_fw_rules.exp run_sync.sh
- 运行:./run_sync.sh
- 日志增强(可选):在 expect 脚本开头添加 log_file ./sync_$(date +%F).log
四、调试与容错要点
旧设备响应不稳定,需针对性处理:
- 提示符正则要宽泛:用 -re ".*[#\$>\]]" 而非硬编码 "#",适配不同设备符号
- 超时后重试逻辑:对关键步骤(如登录、enable)添加简单循环,避免单次失败即退出
- 规避中文乱码:在 spawn 前加 set env(LANG) C,防止匹配中文提示失败
- 规则文件格式检查:确保无 DOS 换行(dos2unix rules.conf),否则 Expect 可能卡在 ^M 字符
- 首次运行前,先手动 SSH 登录一次,确认交互流程(是否有多级菜单、是否需输入 serial number 等隐藏步骤)










