需求
作为一名实施人员,通常情况下需要通过各种 VPN 连接到公司和客户的工作环境来完成任务。尤其是疫情的影响,完全在家工作,对 VPN 的利用就更重了。
但是,VPN 装多了,各种虚拟网卡驱动相互之间不一定能好好兼容,即使是 Windows 10 也很容易蓝屏,要是不幸发生在工作过程中,尤其是更新生产系统,那就有得事情做了。
解决方案
我能想到的解决方案,还是虚拟机。把所有的 VPN 都装在虚拟机里边,即使不幸虚拟机挂了,也可以在几分钟内恢复一份快照,而且这个异常完全不会影响到宿主机的系统。
在 Windows 上,常用的虚拟机解决方案有
- 商用的 VMWare
- 免费的 Virtualbox
- Windows 10 自带的 Hyper-V
在 Windows 10 ver20H2 之前,Hyper-V 和其他两个解决方案是互斥的,如果启用了 Hyper-V,那么 VMWare 和 Virtualbox 都不能正常启动。更新到 20H2 之后,它们就可以共存了。
虚拟机设置
为了充分利用宿主机已经安装的程序和各类文件,这套虚拟机只是用来连接到工作网络,宿主机以其为跳板完成工作任务。所以虚拟机选择 Linux 作为操作系统,内存分配 1GB 完全足够。
网络设置方面,虚拟机需要能访问互联网,同时也被宿主机访问,所以网卡类型选择 bridge
或者 nat + host-only
,通过 ip a
命令查看虚拟机的网络地址,测试宿主机能否 ping 通这个地址。
接下来就是安装 VPN 的客户端和相关配置,这里以 CentOS 7 为例。
OpenVPN
安装
1
2
3
4
5
| # 安装 epel
sudo yum install -y epel-release
# 安装 openvpn
sudo yum install -y openvpn
|
配置
创建一个 Shell Script 脚本
1
2
3
4
5
6
7
8
9
| #!/bin/bash
# start OpenVPN client daemon
sudo openvpn --daemon --cd <work-folder> --config <ovpn-file> --log-append /var/log/openvpn.log
# add traffic forward firewall rule
MASQUERADE=$(sudo firewall-cmd --zone=public --query-masquerade)
if [[ "x$MASQUERADE" != "xyes" ]]; then
sudo firewall-cmd --zone=public --add-masquerade
fi
|
运行
直接运行这个脚本
Cisco VPN Client
安装
1
2
3
4
5
| # 安装 epel
sudo yum install -y epel-release
# 安装 vpnc
sudo yum install -y vpnc
|
配置
创建一个 Shell Script 脚本
1
2
3
4
5
6
7
8
9
| #!/bin/bash
# start Cisco VPN Client
sudo vpnc $(dirname $(readlink -f $0))/<cisco-legacy-vpn-config-file>
# add traffic forward firewall rule
MASQUERADE=$(sudo firewall-cmd --zone=public --query-masquerade)
if [[ "x$MASQUERADE" != "xyes" ]]; then
sudo firewall-cmd --zone=public --add-masquerade
fi
|
参考下列例子,准备好 cisco-legacy-vpn-config-file
,文件名随意,放置在脚本的相同目录
Cisco VPN Client Config Sample
1
2
3
4
5
6
| IPSec gateway <VPN server IP>
IPSec ID <Group ID>
IPSec secret <Group Password>
Xauth username <Username>
Xauth password <Password>
Local Port 0
|
运行
直接运行这个脚本
Cisco AnyConnect Secure Mobility Client
安装
1
2
3
4
5
| # 安装 epel
sudo yum install -y epel-release
# 安装 openconnect
sudo yum install -y openconnect
|
配置
创建一个 Shell Script 脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #!/bin/bash
# add traffic forward firewall rule
MASQUERADE=$(sudo firewall-cmd --zone=public --query-masquerade)
if [[ "x$MASQUERADE" != "xyes" ]]; then
sudo firewall-cmd --zone=public --add-masquerade
fi
# start openconnect
# automatically input password
PASSWORD='<password>'
(
echo $PASSWORD
read -p "" X
echo $X
) |
sudo openconnect \
--user <username> \
--os=win \
--csd-wrapper=<bogus-csd-wrapper-script> \
<VPN server IP>
|
Cisco AnyConnect 对客户端的要求比较多,需要检查客户端是否符合服务器安全设置,有些服务端还配置了 2FA,所以不能做到脚本完全自动连接。首先参考下列例子,准备好 bogus-csd-wrapper-script
,文件名随意,放置在脚本的相同目录,以此提交一个符合服务端安全设置的连接请求。
Cisco Anyconnect CSD wrapper Sample
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| #!/bin/bash
if ! xmlstarlet --version >/dev/null 2>&1; then
echo "************************************************************************" >&2
echo "WARNING: xmlstarlet not found in path; CSD token extraction may not work" >&2
echo "************************************************************************" >&2
unset XMLSTARLET
else
XMLSTARLET=true
fi
DATA='endpoint.os.version="Linux";
endpoint.os.servicepack="4.17.9-200.fc28.x86_64";
endpoint.os.architecture="x64";
endpoint.policy.location="Default";
endpoint.device.protection="none";
endpoint.device.protection_version="3.1.03103";
endpoint.device.hostname="<VPN server hostname/IP>";
endpoint.device.port["9217"]="true";
endpoint.device.port["139"]="true";
endpoint.device.port["53"]="true";
endpoint.device.port["22"]="true";
endpoint.device.port["631"]="true";
endpoint.device.port["445"]="true";
endpoint.device.port["9216"]="true";
endpoint.device.tcp4port["9217"]="true";
endpoint.device.tcp4port["139"]="true";
endpoint.device.tcp4port["53"]="true";
endpoint.device.tcp4port["22"]="true";
endpoint.device.tcp4port["631"]="true";
endpoint.device.tcp4port["445"]="true";
endpoint.device.tcp4port["9216"]="true";
endpoint.device.tcp6port["139"]="true";
endpoint.device.tcp6port["53"]="true";
endpoint.device.tcp6port["22"]="true";
endpoint.device.tcp6port["631"]="true";
endpoint.device.tcp6port["445"]="true";
endpoint.device.MAC["FFFF.FFFF.FFFF"]="true";
endpoint.device.protection_extension="3.6.4900.2";
endpoint.fw["IPTablesFW"]={};
endpoint.fw["IPTablesFW"].exists="true";
endpoint.fw["IPTablesFW"].description="IPTables (Linux)";
endpoint.fw["IPTablesFW"].version="1.6.1";
endpoint.fw["IPTablesFW"].enabled="ok";
'
shift
TICKET=
STUB=0
while [ "$1" ]; do
if [ "$1" == "-ticket" ]; then
shift
TICKET=${1//\"/}
fi
if [ "$1" == "-stub" ]; then
shift
STUB=${1//\"/}
fi
shift
done
PINNEDPUBKEY="-s ${CSD_SHA256:+"-k --pinnedpubkey sha256//$CSD_SHA256"}"
URL="https://$CSD_HOSTNAME/+CSCOE+/sdesktop/token.xml?ticket=$TICKET&stub=$STUB"
if [ -n "$XMLSTARLET" ]; then
TOKEN=$(curl $PINNEDPUBKEY -s "$URL" | xmlstarlet sel -t -v /hostscan/token)
else
TOKEN=$(curl $PINNEDPUBKEY -s "$URL" | sed -n '/<token>/s^.*<token>\(.*\)</token>^\1^p')
fi
COOKIE_HEADER="Cookie: sdesktop=$TOKEN"
CONTENT_HEADER="Content-Type: text/xml"
URL="https://$CSD_HOSTNAME/+CSCOE+/sdesktop/scan.xml?reusebrowser=1"
curl $PINNEDPUBKEY -H "$CONTENT_HEADER" -H "$COOKIE_HEADER" --data "$DATA;type=text/xml" "$URL"
|
运行
直接运行这个脚本,如果有 2FA 验证,在提示符后输入验证码
宿主机设置
假设创建的虚拟机采用了 nat + host-only
的网络模式,其中 host-only
的地址为 192.168.147.188
。在虚拟机创建好 VPN 连接后,按照远程工作环境的网络地址 10.100.16.5
,为宿主机添加路由。
打开管理员模式
的命令行提示符,输入以下命令,宿主机在访问 10.100.16.1 - 10.100.16.254 的所有服务器时,会通过虚拟机的网络接口收发数据,以此达到连接远程工作环境的要求。
1
2
| route delete 10.100.16.0
route add -p 10.100.16.0 mask 255.255.255.0 192.168.147.188
|