使用openconnect客户端连接公司VPN以解决Clash冲突问题
环境
Deepin 20.6
目的
Cisco Anyconnect和Clash冲突,科学上网和公司vpn无法兼得,一番调查后,按照v站此帖和此issue的说法,决定换用openconnect连接vpn。
下载
从下载页下载tarball,解压缩。
安装
cd openconnect-9.01
./configure --without-gnutls-version-check
sudo make
sudo make install
sudo ldconfig
连接登录vpn
sudo openconnect $ip:$port
输入用户名和密码,并信任证书,一路yes即可
自动登录
为了免去每次都要手动信任证书和输入密码的麻烦,可以用以下命令连接:
echo "password" | sudo openconnect $ip:$port --user=username --passwd-on-stdin --servercert $server_fingerprint
其中password为密码,server_fingerprint为服务器证书的fingerprint,可以在初次连接时看到:
为了便捷,也可以把这个命令写成shell脚本,并将shell脚本目录加入到PATH中,这样只需一个命令即可连接vpn,脚本如下:
command=$1
if [ -z "$command" ]; then
echo "No command supplied, exit"
exit 1
fi
# if command is start
if [ "$command" == "start" ]; then
echo "starting vpn..."
# 这一步根据实际的配置来写
echo "password" | sudo openconnect $ip:$port --user=username --passwd-on-stdin --servercert $server_fingerprint &
echo "vpn started"
fi
if [ "$command" == "stop" ]; then
echo "stopping vpn..."
sudo ps -ef | grep openconnect | grep 9443 | grep -v grep | awk '{print $2}' | sudo xargs kill -9
echo "vpn stopped"
fi
将此脚本命名为vpn,注意不要带后缀.sh
,假设它所在目录为/home/boatrain/tools
,则可以将此路径加入到PATH中:
#在~/.bashrc中添加这一行
export PATH=/home/boatrain/tools:$PATH
这里需要注意配置下执行特权命令免输入密码:
echo "`whoami` ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
#不过更推荐的方式是,在 sudoers.d 目录中,创建一个新的用户规则,而不是调整可能随着系统升级,而非覆盖或者恢复默认值的系统文件:
echo "`whoami` ALL=(ALL) NOPASSWD:ALL" | sudo tee "/etc/sudoers.d/dont-prompt-$USER-for-sudo-password"
这样就可以在终端中直接使用命令:
vpn start
vpn stop
vpn与Clash的冲突问题
按照v站此帖和此issue的说法,需要将openconnect的vpn作为代理节点加入Clash才能达到科学上网和公司vpn不冲突的效果,详细研究了下,是可行的,其原理如下:
- 利用ocproxy在本地搭建一个proxy server
- 启动openconnect时,通过
--script-tun
选项指定所有经过proxy server的流量都进入公司vpn - clash中自定义parser,配置访问公司的流量都转入proxy server
图示如下:
搭建ocproxy
克隆ocproxy到本地,先安装依赖:
sudo apt-get install libevent-dev -y
sudo apt-get install autoconf -y
再按照教程,本地构建二进制文件,注意最后再加上:
#安装到/usr/local/bin
sudo make install
openconnect配置ocproxy
按照此教程,将上面写的vpn脚本进行相应修改:
#省略其他部分
echo "password" | sudo openconnect --no-dtls --script-tun --script "ocproxy -D 9052" $ip:$port --user=username --passwd-on-stdin --servercert $server_fingerprint &
注意,这里加了以下几个参数:
--script
:openconnect连接成功后执行这个脚本
--script-tun
:将流量路由给上面--script
参数指定的程序,这里指定流量路由给本地9052端口的ocproxy服务
--no-dtls
:禁用DTLS和ESP,因为默认openconnect使用基于UDP的DTLS协议,这里不禁用的话,后续的操作无法生效,具体原因暂不清楚。欢迎指出原因
clash配置规则
我们当然可以直接在Profiles->Proxies里直接增加一条Proxy,如下所示:
但是这样的话,每次更新Profile时,会覆盖本地的配置,需要重新配置一遍,这也正是这个issue所要表达的问题。按照issue里的解决办法,我们可以自定义一个parser,这样每次更新Profile完成后,再执行我们自定义的规则。按照教程,配置如下:
parsers:
- url: 你的机场订阅链接
yaml:
prepend-rules:
- DOMAIN-KEYWORD,company.domain,ocproxy
- IP-CIDR,178.0.0.0/8,ocproxy
- IP-CIDR,172.16.0.0/12,ocproxy
append-proxies:
- name: ocproxy
type: socks5
server: 127.0.0.1
port: 9052
这里我简单定义了如下规则:
- 定义一个名为ocproxy的socks5代理,其端口为9052
- 域名里含有
company.domain
以及访问的IP在178.0.0.0/8
和172.16.0.0/12
范围内的请求都转发到ocproxy,当然除了DOMAIN-KEYWORD
和IP-CIDR
,clash支持更多匹配规则,详见rules。
配置完成后,启动vpn,更新下Profile,大功告成!
参考资料
https://www.infradead.org/openconnect/download.html
https://www.infradead.org/openconnect/connecting.html
https://github.com/Fndroid/clash_for_windows_pkg/issues/988
https://www.monperrus.net/martin/ssh-over-vpn-with-openconnect