目录

使用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,可以在初次连接时看到:

https://raw.githubusercontent.com/boatrainlsz/my-image-hosting/main/202207021754563.png

为了便捷,也可以把这个命令写成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不冲突的效果,详细研究了下,是可行的,其原理如下:

  1. 利用ocproxy在本地搭建一个proxy server
  2. 启动openconnect时,通过--script-tun选项指定所有经过proxy server的流量都进入公司vpn
  3. clash中自定义parser,配置访问公司的流量都转入proxy server

图示如下:

https://raw.githubusercontent.com/boatrainlsz/my-image-hosting/main/202207302245656.svg

搭建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,如下所示:

https://raw.githubusercontent.com/boatrainlsz/my-image-hosting/main/202207302216289.png

https://raw.githubusercontent.com/boatrainlsz/my-image-hosting/main/202207302215953.png

但是这样的话,每次更新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

这里我简单定义了如下规则:

  1. 定义一个名为ocproxy的socks5代理,其端口为9052
  2. 域名里含有company.domain以及访问的IP在178.0.0.0/8172.16.0.0/12范围内的请求都转发到ocproxy,当然除了DOMAIN-KEYWORDIP-CIDR,clash支持更多匹配规则,详见rules

配置完成后,启动vpn,更新下Profile,大功告成!

参考资料

https://www.infradead.org/openconnect/download.html

https://www.infradead.org/openconnect/connecting.html

https://askubuntu.com/questions/1043024/how-to-run-openconnect-with-username-and-password-in-a-line-in-the-terminal

https://github.com/Fndroid/clash_for_windows_pkg/issues/988

https://www.v2ex.com/t/775773

https://www.monperrus.net/martin/ssh-over-vpn-with-openconnect

https://github.com/cernekee/ocproxy