Linux 上的 AnyConnect:安全地脚本化登录流程
Cisco AnyConnect 的 vpn 二进制从 stdin 读取应答,这足以实现可重复的登录。构建序列,保护文件,验证,然后停止。
随 Cisco AnyConnect 一同发布的 vpn 客户端的行为像一场对话:它打印一个提示,等待一行输入,再打印下一个提示。把这场对话从文件或管道喂进去,同一个二进制就能无人值守地运行。技巧在于一次重定向加上一份有序的应答清单,而决定脚本是否可靠的,是这份清单,不是重定向。
实践中会有两件事把它搞坏。当网关多问一个问题时,提示的顺序就会错位;而把密码存下来,会把一次打字错误变成一次账户锁定。请把序列当作可版本化的数据,这样在任何网关变更之后都能对比。宁可大声失败,也不要重试,因为循环恰恰就是打错的密码变成被锁账户的过程。
一份序列,按客户端提问的顺序
先交互式运行一次客户端,按出现顺序记下每一个提示。这份记录就成了应答文件。把文件重定向进二进制时,第一行必须是连接命令本身,这就是序列以主机名而不是用户名开头的原因。
connect vpn.example.com
0
<username>
<password>
y
exit
光秃秃的 0 在网关给出列表时选择第一个连接配置文件;请使用你的网关实际打印的索引。y 接受登录横幅,所以如果没有横幅就删掉这一行,并在网关提出额外问题的地方插入一行。末尾的 exit 让客户端在最后一个应答被消耗后干净地退出。
/opt/cisco/anyconnect/bin/vpn -s < anyconnect.txt
这里的非交互模式不是可选项。没有 -s 时,客户端可能接管终端、忽略被重定向的输入;有了它,应答从流中读取,进程正常返回。在二进制需要提权的地方,把调用包进 sudo -n,让脚本立刻失败,而不是卡在密码提示上。
应答文件在哪里泄漏
含密码的文件就是凭据,所以给它凭据该有的权限:模式 600,属主是执行任务的账户,绝不放在共享或同步目录里。创建时就带上模式,而不是事后修补,因为在写入和 chmod 之间文件会短暂对所有用户可读。
install -m 600 /dev/null ~/.config/anyconnect.txt
chmod 700 ~/.config
另一半是 argv。作为参数传入的密码,任何能列出进程的本地用户都看得见,它还会落进 shell 历史。改为从文件描述符或环境变量读取,并给载荷加上引号,让 shell 无法在其内部展开任何东西。
printf '%s\n' 'connect vpn.example.com' '0' "$VPN_USER" "$VPN_PASS" 'y' 'exit' \
| sudo -n /opt/cisco/anyconnect/bin/vpn -s
注意引号。在双引号内部,含有 ! 的密码可能在客户端看到之前就被交互式 shell 展开,失败会以一条看起来像协议问题的通用 event-not-found 错误出现。
先证明隧道,再把它关掉
已连接状态只能证明客户端相信它有一个会话。挑一台只从企业网络内部应答的主机,在做任何别的事之前先探测一次。那里的失败是路由或策略故障,重跑登录不会改变它。
/opt/cisco/anyconnect/bin/vpn state
curl -sS -o /dev/null -w '%{http_code}\n' --max-time 10 https://<internal-host>/
/opt/cisco/anyconnect/bin/vpn disconnect
在断开前后分别检查状态输出。留着陈旧会话的客户端可能拒绝下一次连接,而脚本仍然报告成功;如果只看退出码,这种故障模式是看不见的。把两次读数做对比,而不是单独相信第二次。shell 配置文件里的别名足够日常使用,而任何排程运行的东西,都值得像其他无人值守任务一样做凭据与身份审查。
当固定清单不再安全
应答文件编码的是今天的提示顺序。横幅变更、强制改密或二次因子挑战,都会把它变成可能锁定账户的循环,所以脚本必须在第一个意外应答处退出,而不是重启。
捕获客户端打印的内容,返回非零状态,把决定权留给调用方。在网关要求二次因子的场合,诚实的答案是该配置文件无法无人值守登录,而不是序列还需要再多一行。
轮换属于同一次审查。如果凭据被保存,就要决定谁能读它、它何时更换,以及主机重建时由什么把它清除。一个因为没人碰过密码而用了一年的脚本不是设计,而是一场在等轮换策略的事故。
不是 AnyConnect 的配置文件
有两种更老的 Cisco 凭据风格常与之混淆。vpnc 配置文件会要求 IPSec ID,那其实是组名,然后是组密钥,再然后是你自己的凭据,三者都来自运营网关的人。传统 Cisco IPsec 与 AnyConnect 是不同协议,所以 vpnc 到不了只支持 AnyConnect 的服务;openconnect --protocol=anyconnect 才是做这件事的开源客户端。
.pcf 文件描述的是更老的客户端配置。里面的主机、组名和组密码可以在支持 Cisco IPsec 的客户端里手工重新输入,不过这个文件并不是通用导入,任何非默认端口或隧道模式在迁移时通常都会丢失。
从网关实际提供的东西出发,再挑会说那种协议的客户端。从别的站点复制配置,正是人们最后去调试那些从来不是问题的凭据的方式。