连接到 Cisco VPN 时如何维持本地 LAN 访问?
当使用 Cisco VPN 连接时,服务器能够指示客户端阻止本地 LAN 访问。
假设无法关闭此服务器端选项,那么如何在连接 Cisco VPN 客户端时允许本地 LAN 访问?
我曾经认为这只是添加路由以更高的指标捕获 LAN 流量的问题,例如:
Network
Destination Netmask Gateway Interface Metric
10.0.0.0 255.255.0.0 10.0.0.3 10.0.0.3 20 <--Local LAN
10.0.0.0 255.255.0.0 192.168.199.1 192.168.199.12 1 <--VPN Link
尝试删除该10.0.x.x -> 192.168.199.12
路线没有任何效果:
>route delete 10.0.0.0
>route delete 10.0.0.0 mask 255.255.0.0
>route delete 10.0.0.0 mask 255.255.0.0 192.168.199.1
>route delete 10.0.0.0 mask 255.255.0.0 192.168.199.1 if 192.168.199.12
>route delete 10.0.0.0 mask 255.255.0.0 192.168.199.1 if 0x3
尽管它可能仍然只是一个路由问题,但添加或删除路由的尝试会失败。
思科 VPN 客户端驱动程序在网络堆栈中的哪个级别上执行什么操作以覆盖本地管理员管理其机器的能力?
Cisco VPN 客户端不可能使用魔法。它仍然是我电脑上运行的软件。它使用什么机制来干扰我机器的网络?当 IP/ICMP 数据包到达网络时会发生什么?网络堆栈中的哪个位置会吞噬数据包?
也可以看看
- Cisco VPN 没有互联网连接
- Cisco VPN 客户端中断与我的 LDAP 服务器的连接
- Cisco VPN 阻止 Windows 7 浏览
- 如何在连接到 Cisco VPN 时禁止在 Windows XP 中创建路由?
- 使用 VPN 时重新路由本地 LAN 和 Internet 流量
- VPN 客户端“允许本地 LAN 访问”
- 允许 VPN 3000 集中器上的 VPN 客户端进行本地 LAN 访问配置示例
- 当我连接到 VPN 时 LAN 访问消失
- Windows XP 文档:路线
编辑:我还没尝试过的事情:
>route delete 10.0.*
更新:由于思科已经放弃了他们的旧客户端,转而采用 AnyConnect(基于 HTTP SSL 的 VPN),这个尚未解决的问题只能作为历史遗留问题了。
接下来,我们可以尝试解决他们的新客户也遇到了同样的问题。
答案1
Anyconnect 的问题在于,它首先修改路由表,然后照看它并在您手动修改时修复它。我找到了一种解决方法。适用于版本 3.1.00495、3.1.05152、3.1.05170,以及 3.1 系列中的其他任何版本。可能适用于其他版本,至少类似的想法应该可行,前提是代码不会被重写。幸运的是,思科已将保姆“婴儿醒了”调用放入共享库中。因此,我们的想法是通过 LD_PRELOAD 阻止 vpnagentd 的操作。
首先我们创建一个文件
hack.c
:#include <sys/socket.h> #include <linux/netlink.h> int _ZN27CInterfaceRouteMonitorLinux20routeCallbackHandlerEv() { int fd=50; // max fd to try char buf[8192]; struct sockaddr_nl sa; socklen_t len = sizeof(sa); while (fd) { if (!getsockname(fd, (struct sockaddr *)&sa, &len)) { if (sa.nl_family == AF_NETLINK) { ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); } } fd--; } return 0; }
注意:此代码仅适用于 Linux。要将此解决方案应用于 macOS 计算机,请参阅macOS 适配版本。
然后像这样编译它:
gcc -o libhack.so -shared -fPIC hack.c
安装
libhack.so
到Cisco库路径:sudo cp libhack.so /opt/cisco/anyconnect/lib/
关闭代理:
/etc/init.d/vpnagentd stop
确保它确实处于关闭状态
ps auxw | grep vpnagentd
如果没有,
kill -9
只是为了确定一下。如果有
/etc/init.d/vpnagentd
,则通过添加调用底层可执行文件的位置来修复它,LD_PRELOAD=/opt/cisco/anyconnect/lib/libhack.so
使其看起来像这样:LD_PRELOAD=/opt/cisco/anyconnect/lib/libhack.so /opt/cisco/anyconnect/bin/vpnagentd
更现代的 AnyConnect 安装不再使用
/etc/init.d/vpnagentd
,/lib/systemd/system/vpnagentd.service
在这种情况下,您会需要类似以下内容:sudo mv /opt/cisco/anyconnect/bin/vpnagentd /opt/cisco/anyconnect/bin/vpnagentd.orig && { echo '#!/bin/bash' && echo "LD_PRELOAD=$HOME/vpn/libhack.so exec /opt/cisco/anyconnect/bin/vpnagentd.orig" } | sudo tee /opt/cisco/anyconnect/bin/vpnagentd && sudo chmod +x /opt/cisco/anyconnect/bin/vpnagentd
现在启动代理:
/etc/init.d/vpnagentd start
修复 iptables,因为 AnyConnect 会干扰它们:
iptables-save | grep -v DROP | iptables-restore
您可能希望在这里做一些更高级的事情,以仅允许访问某些 LAN 主机。
现在按照您的需要修复路线,例如:
route add -net 192.168.1.0 netmask 255.255.255.0 dev wlan0
检查它们是否真的存在:
路线-n
此 hack 的先前简化版本提供了一个仅执行“返回 0;”的函数 - 该发帖者指出“到目前为止,我观察到的唯一副作用是 vpnagentd 使用了 100% 的 CPU(如 top 报告所示),但总体 CPU 只有 3% 的用户和 20% 的系统,并且系统响应完美。我对其进行了跟踪,它似乎在空闲时循环执行两个选择,并快速从两个选择中返回,但它从未读取或写入 - 我想我使用 LD_PRELOAD 剪切出的调用应该是读取。可能有一种更简洁的方法可以做到这一点,但到目前为止对我来说已经足够好了。如果有人有更好的解决方案,请分享。”
这个简单黑客的问题是它导致单个 CPU 核心一直处于 100% 状态,从而有效地将硬件 CPU 线程数减少一个 - 无论您的 vpn 连接是否处于活动状态。我注意到代码正在执行的选择是在 netlink 套接字上,当路由表更改时,它会发送 vpnagentd 数据。vpnagentd 不断注意到 netlink 套接字上有一条新消息并调用 routeCallBackHandler 来处理它,但由于这个简单的黑客不会清除新消息,它只会不断被调用。上面提供的新代码会刷新 netlink 数据,因此导致 100% CPU 的无限循环不会发生。
如果某些操作不起作用,请gdb -p $(pidof vpnagentd)
在连接后执行以下操作:
b socket
c
bt
并查看您处于哪个调用中。然后只需猜测您要剪掉哪一个,将其添加到hack.c并重新编译。
答案2
这非常复杂,但如果您使用 VMWare Player 或类似程序创建一个最小 VM,并在其中运行 Cisco AnyConnect VPN 客户端,则可能可以使用 VMWare 虚拟网络适配器根据需要设置路由,或者只是使用 VM 通过 Cisco SSL VPN 访问所需的任何资源并将文件“拖放”到/从您的实际机器。
答案3
对于那些希望在使用 Cisco AnyConnect SSL VPN 时保持对路由表控制的用户,请查看OpenConnect。它既支持 Cisco AnyConnect SSL VPN,又不会试图破坏或“保护”路由表条目。@Vadzim提到了这一点以上评论。
在尝试了所有方法(除了修补 AnyConnect 安全移动客户端)后,我成功地在 Windows 上用以下方法替换了它:OpenConnect 图形用户界面。这使我能够保持与本地资源的连接(并更新路由表)。
我在 Windows 上使用 OpenConnect,但它也支持 Linux、BSD 和 macOS(以及其他平台)根据项目页面。
答案4
谢谢萨沙·帕切夫对于上面的精彩破解。
vpnagentd
还会通过覆盖对 所做的更改来干扰解析器/etc/resolv.conf
。我最终通过赢得与它的竞争解决了这个问题:
#!/bin/bash
dnsfix() {
[ -f /etc/resolv.conf.vpnbackup ] || echo "Not connected?" >&2 || return 0 # do nothing in case of failure
while ! diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup #>/dev/null
do
cat /etc/resolv.conf.vpnbackup >/etc/resolv.conf
done
chattr +i /etc/resolv.conf
diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup >/dev/null
}
while ! dnsfix
do
echo "Retrying..."
chattr -i /etc/resolv.conf
done
chattr -i /etc/resolv.conf
断开连接时不要忘记。
我正在尝试通过拦截回调来解决它,就像上面的路由方法一样,但还找不到相应的回调或方法。
更新 1/2:Astrace
透露vpnagentd
正在使用inotify
API 来监控解析器文件更改。从那时起,事情就变得糟糕了。以下是额外的 hack:
int _ZN18CFileSystemWatcher11AddNewWatchESsj(void *string, unsigned int integer)
{
return 0;
}
当然,这有点过头了,因为它会禁用全部代理的文件监视。但似乎工作正常。
下面的 vpn 客户端包装器脚本集成了所有功能(已更新以包含此附加黑客功能)。chattr
不再使用/需要。
更新 3:修复了脚本中的用户名/密码设置。它现在使用vpn.conf
具有下述格式的文件(以及仅限 root 的权限)。
#!/bin/bash
# Change this as needed
CONF="/etc/vpnc/vpn.conf"
# vpn.conf format
#gateway <IP>
#username <username>
#password <password>
#delete_routes <"route spec"...> eg. "default gw 0.0.0.0 dev cscotun0"
#add_routes <"route spec"...> eg. "-net 192.168.10.0 netmask 255.255.255.0 dev cscotun0" "-host 10.10.10.1 dev cscotun0"
ANYCONNECT="/opt/cisco/anyconnect"
usage() {
echo "Usage: $0 {connect|disconnect|state|stats|hack}"
exit 1
}
CMD="$1"
[ -z "$CMD" ] && usage
ID=`id -u`
VPNC="$ANYCONNECT/bin/vpn"
dnsfix() {
[ -f /etc/resolv.conf.vpnbackup ] || echo "Not connected?" >&2 || return 0 # do nothing in case of failure
while ! diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup >/dev/null
do
cat /etc/resolv.conf.vpnbackup >/etc/resolv.conf
done
# chattr +i /etc/resolv.conf
diff -q /etc/resolv.conf /etc/resolv.conf.vpnbackup >/dev/null
}
case "$CMD" in
"connect")
[ $ID -ne 0 ] && echo "Needs root." && exit 1
HOST=`grep ^gateway $CONF | awk '{print $2}'`
USER=`grep ^user $CONF | awk '{print $2}'`
PASS=`grep ^password $CONF | awk '{print $2}'`
OLDIFS=$IFS
IFS='"'
DEL_ROUTES=(`sed -n '/^delete_routes/{s/delete_routes[ \t\"]*//;s/\"[ \t]*\"/\"/g;p}' $CONF`)
ADD_ROUTES=(`sed -n '/^add_routes/{s/add_routes[ \t\"]*//;s/\"[ \t]*\"/\"/g;p}' $CONF`)
IFS=$OLDIFS
/usr/bin/expect <<EOF
set vpn_client "$VPNC";
set ip "$HOST";
set user "$USER";
set pass "$PASS";
set timeout 5
spawn \$vpn_client connect \$ip
match_max 100000
expect {
timeout {
puts "timeout error\n"
spawn killall \$vpn_client
exit 1
}
">> The VPN client is not connected." { exit 0};
">> state: Disconnecting" { exit 0};
"Connect Anyway?"
}
sleep .1
send -- "y\r"
expect {
timeout {
puts "timeout error\n"
spawn killall \$vpn_client
exit 1
}
"Username:"
}
sleep .1
send -- "\$user\r"
expect {
timeout {
puts "timeout error\n"
spawn killall \$vpn_client
exit 1
}
"Password: "
}
send -- "\$pass\r";
expect eof
EOF
sleep 2
# iptables
iptables-save | grep -v DROP | iptables-restore
# routes
for ROUTE in "${DEL_ROUTES[@]}"
do
# echo route del $ROUTE
route del $ROUTE
done
for ROUTE in "${ADD_ROUTES[@]}"
do
# echo route add $ROUTE
route add $ROUTE
done
# dns
while ! dnsfix
do
echo "Try again..."
# chattr -i /etc/resolv.conf
done
echo "done."
;;
"disconnect")
# [ $ID -ne 0 ] && echo "Needs root." && exit 1
# dns
# chattr -i /etc/resolv.conf
$VPNC disconnect
;;
"state"|"stats")
$VPNC $CMD
;;
"hack")
[ $ID -ne 0 ] && echo "Needs root." && exit 1
/etc/init.d/vpnagentd stop
sleep 1
killall -9 vpnagentd 2>/dev/null
cat - >/tmp/hack.c <<EOF
#include <sys/socket.h>
#include <linux/netlink.h>
int _ZN27CInterfaceRouteMonitorLinux20routeCallbackHandlerEv()
{
int fd=50; // max fd to try
char buf[8192];
struct sockaddr_nl sa;
socklen_t len = sizeof(sa);
while (fd) {
if (!getsockname(fd, (struct sockaddr *)&sa, &len)) {
if (sa.nl_family == AF_NETLINK) {
ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
}
}
fd--;
}
return 0;
}
int _ZN18CFileSystemWatcher11AddNewWatchESsj(void *string, unsigned int integer)
{
return 0;
}
EOF
gcc -o /tmp/libhack.so -shared -fPIC /tmp/hack.c
mv /tmp/libhack.so $ANYCONNECT
sed -i "s+^\([ \t]*\)$ANYCONNECT/bin/vpnagentd+\1LD_PRELOAD=$ANYCONNECT/lib/libhack.so $ANYCONNECT/bin/vpnagentd+" /etc/init.d/vpnagentd
rm -f /tmp/hack.c
/etc/init.d/vpnagentd start
echo "done."
;;
*)
usage
;;
esac