VPN Playbook
https://github.com/ma-wenqian/vpn-playbook
A complete guide and toolkit for automating login to Cisco AnyConnect / Cisco Secure Client VPNs that use TOTP-based two-factor authentication, plus a Dockerized OpenConnect setup for split-tunnel / proxy-chain use.
Tags: HKU, Cisco AnyConnect, Cisco Secure Client, OpenConnect, 2FA, TOTP, VPN Guide
English
Overview
Most institutional VPNs built on Cisco AnyConnect / Cisco Secure Client (this repo was written against HKU’s vpn2fa.hku.hk, but the approach generalizes to any similarly-configured gateway) require two factors to log in:
- A username + password.
- A 6-digit TOTP code, usually the same code you’d read off Google Authenticator, Authy, iCloud Keychain, or Microsoft Authenticator.
That TOTP code is just HMAC-SHA1(secret, current_time_step) truncated to 6 digits — there is nothing magic about the authenticator app. Once you have extracted the Base32 secret used to seed your authenticator (usually shown as text or a QR code during 2FA enrollment), you can compute the same code yourself and drive the whole login non-interactively:
- On Unix (macOS/Linux):
oathtool --totp -b "$SECRET" - On Windows: the
totp-goCLI,totp-go "$SECRET"
⚠️ Protect this secret like a password. Anyone who has it can generate valid 2FA codes for your account indefinitely — it does not rotate or expire the way a single OTP code does. Never commit a config file that contains your real secret, password, or username to a public (or even private, shared) repository. The
config.bat/config.shfiles in this repo are templates with placeholder values only — fill in your real details locally and keep that change out of git.
Repository layout
1 | . |
Mobile
- iOS: no full automation is possible; at most you can auto-fill the TOTP code via the Passwords/Authenticator autofill integration.
- Android: not investigated in this repo.
Cisco AnyConnect / Secure Client (desktop, CLI-only)
Don’t launch the GUI client — everything below drives the command-line interface so the login can be scripted.
macOS / Linux
On a standard desktop install, the CLI lives at:
1 | /opt/cisco/secureclient/bin/vpn |
(macOS users can alternatively install the iOS-style AnyConnect client, which also supports split tunneling — see the Chinese section below for the caveat.)
Prerequisites
1 | # Debian/Ubuntu |
You also need the Cisco AnyConnect/Secure Client already installed (so /opt/cisco/secureclient/bin/vpn exists).
Quick install
1 | curl -fsSL https://sh.mawenqian.com/cisco-unix-install.sh | bash |
This drops cisco-vpn.sh and a config.sh template into /opt/cisco-vpn (override with INSTALL_DIR=...; needs sudo to create the directory, matching the OpenConnect installers below), makes cisco-vpn.sh executable, and symlinks it to ~/.local/bin/ciscovpn. If ~/.local/bin isn’t already on your PATH, it appends an export PATH=... line to your .bashrc/.zshrc/.profile (whichever matches $SHELL) so ciscovpn actually works after you restart your shell, instead of silently symlinking somewhere unreachable — it checks for that exact line first, so re-running the installer never duplicates it. Re-running it later refreshes cisco-vpn.sh without touching your config.sh, so it doubles as an update mechanism.
Or install manually:
- Copy
Cisco/unix/cisco-vpn.shandCisco/unix/config.shwherever you like. - Edit
config.shwith your realVPN_HOST,VPN_USER,VPN_PASSWORDandVPN_SECRET. - Make the script executable:
chmod +x cisco-vpn.sh
Usage
1 | ciscovpn connect # disconnect any existing session, then log in |
Under the hood, cisco-vpn.sh generates a fresh TOTP code with oathtool and pipes a scripted command sequence into the CLI’s -s (stdin-scripted) mode — the same mechanism the Windows side uses via vpncli.exe -s, since the CLI doesn’t accept connect <host> as a plain command-line argument on either platform; it only reacts to that sequence typed at its own interactive VPN> prompt:
1 | printf 'connect %s\n%s\n%s\n%s\n' "$VPN_HOST" "$VPN_USER" "$VPN_PASSWORD" "$OTP" \ |
Nothing touches disk — the credentials only exist in the pipe between this script and the CLI process for the moment it takes to read them.
Windows
The current install path for Cisco Secure Client is:
1 | C:\Program Files (x86)\Cisco\Cisco Secure Client\vpncli.exe |
There’s no direct Windows equivalent of oathtool, so this repo generates TOTP codes with totp-go instead — a small standalone CLI, no Python required.
Prerequisites: totp-go on PATH (the installer below fetches it for you if it’s missing; manually it’s irm https://sh.mawenqian.com/totp-go-install.ps1 | iex).
Quick install
1 | irm https://sh.mawenqian.com/cisco-win-install.ps1 | iex |
This drops cisco-vpn.bat and a config.bat template into %USERPROFILE%\.local\opt\cisco-vpn (override with -InstallDir ...), installs totp-go if it isn’t already on PATH, and asks whether to create “Connect VPN” / “Disconnect VPN” shortcuts on your Desktop — say yes and you never have to touch a terminal again. Re-running the installer later refreshes cisco-vpn.bat without touching your config.bat.
Shortcut icons: Cisco/win/icon/connect.ico and Cisco/win/icon/disconnect.ico are used by default (fetched from the repo, one per shortcut). To use your own instead, pass -ConnectIconUrl <url> / -DisconnectIconUrl <url> pointing at a .ico file — a shortcut’s icon has to be a local .ico/.exe/.dll file (Windows has no notion of a remote icon URL), so a supplied URL is downloaded once into the install folder and pointed at locally.
Or install manually:
- Copy
Cisco/win/cisco-vpn.batandCisco/win/config.batinto the same folder. - Install
totp-go:irm https://sh.mawenqian.com/totp-go-install.ps1 | iex - Edit
config.batwith your realVPN_HOST,VPN_USER,VPN_PASSWORDandVPN_SECRET— the same variable names used by the unix side’sconfig.sh. - Optional: put a shortcut to
cisco-vpn.baton your desktop so you can just double-click to connect.
Usage
Double-click cisco-vpn.bat (or run it from a terminal). It will:
- Print the current VPN state.
- Force-disconnect any existing session.
- Load
config.bat, generate a fresh TOTP code withtotp-go "%VPN_SECRET%", and write a scripted input file straight to%TEMP%\cisco_input.txtfrom within the batch script (no helper process involved). - Pipe that file into
vpncli.exe -s(scripted mode) to complete the login, then delete the temp file.
One-click disconnect: the Quick Install shortcut above already creates this, or make one yourself with this target:
1 | C:\Windows\System32\cmd.exe /c "C:\Program Files (x86)\Cisco\Cisco Secure Client\vpncli.exe" disconnect & timeout /nobreak /t 3 |
To change either shortcut’s icon later: right-click it → Properties → Change Icon.
OpenConnect (Docker, SOCKS5 proxy)
The stock Cisco client doesn’t support split tunneling and doesn’t play well alongside other VPNs/proxies. OpenConnect is protocol-compatible with AnyConnect and works better for this. See Mark4551124015/HKU-VPN for a bare-metal reference setup.
This repo instead provides a Dockerized OpenConnect container that exposes a local SOCKS5 proxy on port 1080 — point any app (or chain another VPN/proxy through it) at socks5://127.0.0.1:1080, e.g. to reach services like ChatGPT/Claude from a network where OpenConnect is the only way out.
Two variants are provided — pick one:
| Script | Proxy backend | Notes |
|---|---|---|
openconnect-setup.sh |
microsocks | Lighter weight, SOCKS5 only |
openconnect-gost-setup.sh |
gost | Heavier, supports more protocols/chaining features |
Container images referenced by these scripts are built from ma-wenqian/dockerfiles/openconnect.
Quick install (Linux/macOS):
1 | # lighter option: OpenConnect + microsocks |
Each script:
- Creates an install directory (
/opt/openconnect-heador/opt/openconnect-gost, overridable viaINSTALL_DIR). - Drops a
.envtemplate (host/user/password/TOTP secret + optional local network routing) withchmod 600. - Writes a
Dockerfileanddocker-compose.yamlthat build the proxy + OpenConnect image.
Then:
1 | cd /opt/openconnect-head # or /opt/openconnect-gost |
The container reconnects automatically if the OpenConnect session drops (--force-dpd=30 + a restart loop), and exposes a health check that pings 1.1.1.1 through the tunnel.
If you want other devices on your LAN to reach the proxy (not just the Docker host), set LOCAL_NETWORK / LOCAL_GATEWAY in .env. If it’s only used from the host machine, you can leave those at their defaults.
中文
https://github.com/ma-wenqian/vpn-playbook
概述
一般情况下,能用 Cisco AnyConnect(现称 Cisco Secure Client)的场景,基本都可以用 OpenConnect 替代。从便捷到进阶,本仓库提供了对应的脚本,辅助自动登录学校(或类似配置的)VPN。
目前 Cisco 基本都启用了双重认证:先输入 VPN 地址,然后输入账号、密码,接着输入二次验证码——也就是常见存储在 Google Authenticator、iCloud 钥匙串、Microsoft Authenticator 中的那个 6 位验证码。这个验证码本质上是基于一个固定的 Base32 密钥和当前时间戳计算得到的(TOTP),并没有什么”黑盒”逻辑。因此,只要在绑定 2FA 时提取到这个固定密钥,就可以自己算出验证码,从而实现全程自动登录:
- Unix 平台:使用
oathtool工具,OTP=$(oathtool --totp -b "$SECRET") - Windows 平台:使用
totp-go命令行工具,totp-go "$SECRET"
⚠️ 请像保管密码一样保管这个密钥! 任何拿到这个密钥的人都可以无限期地为你的账号生成有效的二次验证码——它不像单次验证码那样会失效。切勿把填入了真实用户名/密码/密钥的配置文件提交到(哪怕是私有的)代码仓库。本仓库中的
config.bat/config.sh都只是占位模板,请在本地填入真实信息后不要把这部分改动提交到 git。
仓库结构
1 | . |
移动端
- iOS:无法做到完全自动化,顶多是借助自动填充功能填入验证码。
- Android:本仓库暂未研究。
Cisco(桌面端,仅命令行)
不要启动 GUI 界面,全部使用命令行实现自动化操作。
Unix(macOS / Linux)
常规桌面版安装后,CLI 路径是:
1 | /opt/cisco/secureclient/bin/vpn |
(macOS 也可以选择安装 iOS 端的 AnyConnect App,这样还能做到 split tunnel,但功能和稳定性因版本而异。)
前置依赖
1 | # Debian/Ubuntu |
同时需要已经安装好 Cisco AnyConnect/Secure Client(确保 /opt/cisco/secureclient/bin/vpn 存在)。
一键安装
1 | curl -fsSL https://sh.mawenqian.com/cisco-unix-install.sh | bash |
会把 cisco-vpn.sh 和一份 config.sh 模板放到 /opt/cisco-vpn(可用 INSTALL_DIR=... 覆盖;创建该目录需要 sudo,和下面 OpenConnect 的安装脚本一致),赋予 cisco-vpn.sh 执行权限,并软链接到 ~/.local/bin/ciscovpn。如果 ~/.local/bin 还不在你的 PATH 里,脚本会自动往 .bashrc/.zshrc/.profile(根据 $SHELL 判断用哪个)追加一行 export PATH=...,这样重启终端后 ciscovpn 才真的能直接调用,而不是软链接到一个够不着的地方——追加前会先检查这行是否已存在,所以重复运行安装脚本不会重复添加。以后重新运行这条命令即可更新 cisco-vpn.sh,不会覆盖你的 config.sh。
也可以手动安装:
- 把
Cisco/unix/cisco-vpn.sh和Cisco/unix/config.sh拷贝到任意目录。 - 编辑
config.sh,填入真实的VPN_HOST、VPN_USER、VPN_PASSWORD、VPN_SECRET。 - 赋予执行权限:
chmod +x cisco-vpn.sh
使用
1 | ciscovpn connect # 先断开已有连接,再登录 |
脚本内部会用 oathtool 现算一个 TOTP 验证码,然后把一段脚本化的命令序列,通过管道传给 CLI 的 -s(stdin 脚本化)模式——这和 Windows 那边用 vpncli.exe -s 是同一套机制,因为不管在哪个平台,这个 CLI 都不接受把 connect <host> 直接当命令行参数传入,它只认在自己那个交互式的 VPN> 提示符下输入的命令序列:
1 | printf 'connect %s\n%s\n%s\n%s\n' "$VPN_HOST" "$VPN_USER" "$VPN_PASSWORD" "$OTP" \ |
全程不落盘——账号密码只存在于这个脚本和 CLI 进程之间的管道里,读取完就没了。
Windows
最新的安装目录是:
1 | C:\Program Files (x86)\Cisco\Cisco Secure Client\vpncli.exe |
因为 Windows 上没有现成的 oathtool 等价物,所以这里改用 totp-go 生成验证码——一个独立的小型命令行工具,不需要 Python。
前置依赖:PATH 中要有 totp-go(下面的一键安装脚本会在检测不到时自动帮你安装;手动安装的话执行 irm https://sh.mawenqian.com/totp-go-install.ps1 | iex)。
一键安装
1 | irm https://sh.mawenqian.com/cisco-win-install.ps1 | iex |
会把 cisco-vpn.bat 和一份 config.bat 模板放到 %USERPROFILE%\.local\opt\cisco-vpn(可用 -InstallDir ... 覆盖),在 totp-go 不在 PATH 中时自动安装它,然后询问是否要在桌面创建 “Connect VPN” / “Disconnect VPN” 快捷方式——选择创建之后就再也不用碰终端了。以后重新运行安装脚本只会刷新 cisco-vpn.bat,不会动你的 config.bat。
快捷方式图标:默认使用仓库自带的 Cisco/win/icon/connect.ico 和 Cisco/win/icon/disconnect.ico(两个快捷方式各用各的)。想用自己的图标,传入 -ConnectIconUrl <url> / -DisconnectIconUrl <url> 指向一个 .ico 文件即可——快捷方式的图标必须是本地的 .ico/.exe/.dll 文件(Windows 本身不支持直接用远程 URL 做图标),所以提供的 URL 会先下载到安装目录,再指向本地文件。
也可以手动安装:
- 把
Cisco/win/cisco-vpn.bat和Cisco/win/config.bat放到同一个目录。 - 安装
totp-go:irm https://sh.mawenqian.com/totp-go-install.ps1 | iex - 编辑
config.bat,填入真实的VPN_HOST、VPN_USER、VPN_PASSWORD、VPN_SECRET——变量名和 unix 端的config.sh完全一致。 - 可选:把
cisco-vpn.bat的快捷方式放到桌面,以后双击即可自动连接。
使用
双击运行 cisco-vpn.bat(或在终端里直接执行),它会依次:
- 打印当前 VPN 状态;
- 强制断开已有连接;
- 加载
config.bat,用totp-go "%VPN_SECRET%"现算一个 TOTP 验证码,直接在批处理脚本内把脚本化的登录输入写入%TEMP%\cisco_input.txt(不再经过任何辅助进程); - 把这个文件通过管道传给
vpncli.exe -s(脚本模式)完成登录,然后删除临时文件。
一键断开:上面的一键安装已经自动创建了这个快捷方式;也可以自己创建一个,目标设置为:
1 | C:\Windows\System32\cmd.exe /c "C:\Program Files (x86)\Cisco\Cisco Secure Client\vpncli.exe" disconnect & timeout /nobreak /t 3 |
以后想换图标:右键快捷方式 → 属性 → 更改图标。
OpenConnect(Docker 化,SOCKS5 代理)
因为默认的 Cisco 客户端不支持 split tunnel 模式,且与其他 VPN/代理协作不友好,所以可以用兼容 AnyConnect 协议的 OpenConnect 替代。直接裸机运行的方案可以参考 Mark4551124015/HKU-VPN。
这里提供的是一个 Docker 化的部署方式,并映射出一个 1080 端口的 SOCKS5 代理,可以直接使用,或作为其他 VPN 做代理链的一环,比如访问 ChatGPT / Claude 等服务。
提供了两种方案,二选一即可:
| 脚本 | 代理后端 | 说明 |
|---|---|---|
openconnect-setup.sh |
microsocks | 更轻量,只做 SOCKS5 |
openconnect-gost-setup.sh |
gost | 更重一些,支持更多协议/代理链能力 |
脚本引用的镜像构建自 ma-wenqian/dockerfiles/openconnect。
一键安装(Linux/macOS):
1 | # 更轻量:OpenConnect + microsocks |
每个脚本都会:
- 创建安装目录(默认
/opt/openconnect-head或/opt/openconnect-gost,可通过INSTALL_DIR环境变量覆盖); - 生成一个
.env模板(VPN 地址/账号/密码/TOTP 密钥 + 可选的本地网段路由),并设置权限为chmod 600; - 写入对应的
Dockerfile与docker-compose.yaml,用于构建代理 + OpenConnect 镜像。
然后:
1 | cd /opt/openconnect-head # 或 /opt/openconnect-gost |
容器内置了断线自动重连逻辑(--force-dpd=30 + 重启循环),并配置了健康检查(通过隧道 ping 1.1.1.1)。
如果希望局域网内的其他设备也能访问这个代理(而不仅仅是运行 Docker 的这台机器),需要在 .env 中设置 LOCAL_NETWORK / LOCAL_GATEWAY;如果只在本机使用,保持默认即可忽略这两项。