如何在Mac上强制拆分隧道路由到Cisco VPN


40

有谁知道如何破解路由表(在Mac上),以克服通过Cisco VPN进行的所有事情的VPN路由强制?我几乎想做的就是在VPN上只有10.121。*和10.122。*地址,而其他所有内容都直接进入互联网。

Answers:


27

以下对我有用。连接到Cisco VPN后运行这些。(我使用的是OS X的内置Cisco客户端,而不是Cisco品牌的客户端。)

sudo route -nv add -net 10 -interface utun0
sudo route change default 192.168.0.1

10第一个命令替换为位于隧道另一侧的网络。

替换192.168.0.1为本地网络的网关。

我将其放入bash脚本中,如下所示:

$ cat vpn.sh 
#!/bin/bash

if [[ $EUID -ne 0 ]]; then
    echo "Run this as root"
    exit 1
fi

route -nv add -net 10 -interface utun0
route change default 192.168.0.1

我还找到了有关在连接VPN时如何自动运行此操作的说明,但该操作已在星期五晚了,我不想尝试它:)

编辑:

从那以后,我离开了使用Cisco VPN的工作,所以这是从内存中获得的。

10一个命令中的是您要通过VPN路由的网络。10是的好手10.0.0.0/8。在Tuan Anh Tran的情况下,网络似乎是192.168.5.0/24

至于在第二个命令中指定哪个网关,它应该是您的本地网关。当您登录防止拆分隧道的VPN时,它将通过更改路由表来强制执行该策略,以便所有数据包都在虚拟接口上进行路由。因此,您想将默认路由更改回使用VPN之前的默认路由。

找出网关的最简单方法是netstat -rn在登录VPN之前运行,然后查看“默认”目标右侧的IP地址。例如,这是我现在的盒子上的样子:

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
default            10.0.1.1           UGSc           29        0     en1
10.0.1/24          link#5             UCS             3        0     en1
10.0.1.1           0:1e:52:xx:xx:xx   UHLWIi         55   520896     en1    481
10.0.1.51          7c:c5:37:xx:xx:xx  UHLWIi          0     1083     en1    350
10.0.1.52          127.0.0.1          UHS             0        0     lo0

我的网关是10.0.1.1-它在“默认”目的地的右侧。


1
你能为我解释更多吗?我的ifconfig返回utun0:flags = 8051 <UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1280 inet 192.168.5.102-> 192.168.5.102网络掩码0xffffffff。另外我不明白我应该用上面的数字10代替什么。谢谢。
Tuan Anh Tran 2012年

@TuanAnhTran:我更新了答案。请让我知道是否有帮助。
Mark E. Haase 2012年

对于最新版本,我已经安装了/Applications/VPNClient.app,但没有看到ifconfig列出的utun0。我看到了gif0,stf0和fw0适配器。如何确定哪个是正确的适配器?在没有连接vpn的情况下,ifconfig输出没有任何区别(en1的MTU值除外)。
haridsv

1
对于不熟悉LaunchDaemons的用户,我在这里的答案中写了一个指南,描述了如何在每次连接VPN时调用脚本来运行。(顺便说一句,+ 1很好的答案,但我需要更多一点才能使要旨发挥作用)。
jimbob博士2014年

1
遗憾的是,这在我的Cisco AnyConnect客户端上不起作用。
jeremyjjbrown 2015年

5

利用来自mehaase信息,我编写了一个Python脚本,该脚本实际上简化了Mac上的此过程。运行该脚本时,该脚本将保存您的防火墙信息,启动AnyConnect客户端,等待登录,然后修复路由和防火墙。只需从“终端”运行脚本。

#!/usr/bin/python

# The Cisco AnyConnect VPN Client is often configured on the server to block
# all other Internet traffic. So you can be on the VPN <b>OR</b> you can have
# access to Google, etc.
#
# This script will fix that problem by repairing your routing table and
# firewall after you connect.
#
# The script does require admin (super user) access. If you are prompted for
# a password at the start of the script, just enter your normal Mac login
# password.
#
# The only thing you should need to configure is the vpn_ip_network.
# Mine is 10.x.x.x so I just specify '10' but you could be more specific
# and use something like '172.16'
vpn_ip_network = '10'

import sys
import subprocess


def output_of(cmd):
    lines = subprocess.Popen(cmd if isinstance(cmd, list) else cmd.split(' '), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0]
    try:
        lines = lines.decode('utf-8')
    except Exception:
        pass
    return [line.strip() for line in lines.strip().split('\n')]

sys.stdout.write("Mac Account Login ")
good_firewall_ids = set([line.partition(' ')[0] for line in output_of('sudo ipfw -a list')])
sys.stdout.write('Firewall Saved.\n')

gateway = None
for line in output_of('route get default'):
    name, delim, value = line.partition(':')
    if name == 'gateway':
        gateway = value.strip()
        p = subprocess.Popen(['/Applications/Cisco/Cisco AnyConnect VPN Client.app/Contents/MacOS/Cisco AnyConnect VPN Client'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        was_disconnected = False
        while True:
            line = p.stdout.readline()
            if line == '' or p.poll():
                sys.stdout.write("Never connected!\n")
                break
            try:
                line = line.decode('utf-8')
            except Exception:
                pass
            if 'Disconnected' in line:
                sys.stdout.write('Waiting for you to enter your VPN password in the VPN client...\n')
                was_disconnected = True
            if 'Connected' in line:
                if was_disconnected:
                    subprocess.Popen(['sudo','route','-nv','add','-net',vpn_ip_network,'-interface','utun0'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).wait()
                    subprocess.Popen(['sudo','route','change','default',gateway], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).wait()
                    unfriendly_firewall_ids = list(set([line.partition(' ')[0] for line in output_of('sudo ipfw -a list')])-good_firewall_ids)
                    extra = ''
                    if unfriendly_firewall_ids:
                        subprocess.Popen('sudo ipfw delete'.split(' ') + unfriendly_firewall_ids, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).wait()
                        sys.stdout.write("VPN connection established, routing table repaired and %d unfriendly firewall rules removed!\n" % len(unfriendly_firewall_ids))
                    else:
                        sys.stdout.write("VPN connection established and routing table repaired!\n")
                else:
                    try:
                        subprocess.Popen.kill(p)
                        sys.stdout.write('VPN was already connected. Extra VPN client closed automatically.\n')
                    except Exception:
                        sys.stdout.write('VPN was already connected. Please close the extra VPN client.\n')
                break
        break
else:
    sys.stdout.write("Couldn't get gateway. :-(\n")

4

上一个答案中的Python脚本很有帮助,但是它并没有考虑AnyConnect用来接管设备上其他接口(例如VMware接口)的路由。它还无法处理多个VPN网络。

这是我使用的脚本:

#!/bin/bash

HOME_NETWORK=192.168
HOME_GATEWAY=192.168.210.1
WORK_NETWORKS="X.X.X.X/12 10.0.0.0/8 X.X.X.X/16"

# What should the DNS servers be set to?
DNS_SERVERS="10.192.2.45 10.216.2.51 8.8.8.8"

##
## Do not edit below this line if you do not know what you are doing.
##
function valid_ip()
{
    local  ip=$1
    local  stat=1

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

# Nuke any DENY firewall rules
for RULE in `sudo ipfw list | grep deny | awk '{print $1}' | xargs`; do sudo ipfw delete $RULE; done

# Delete any routes for the home network that Anyconnect might have made
sudo route delete -net ${HOME_NETWORK}
sudo route add -net ${HOME_NETWORK} ${HOME_GATEWAY}

# Get the AnyConnect interface
ANYCONNECT_INTERFACE=`route get 0.0.0.0 | grep interface | awk '{print $2}'`

# Add the work routes
for NETWORK in ${WORK_NETWORKS}; do
    sudo route -nv add -net ${NETWORK} -interface ${ANYCONNECT_INTERFACE}
done

# Set the default gateway
sudo route change default ${HOME_GATEWAY}

# Mass route changes
for NET in `netstat -nr | grep -e ^${HOME_NETWORK} | grep utun1 | awk '{print $1}' | xargs`; do 
    if valid_ip ${NET}; then
        echo "Changing route for network"
        sudo route change ${NET} ${HOME_GATEWAY}
    else
        echo "Changing route for host"
        sudo route change -net ${NET} ${HOME_GATEWAY}
    fi      
done

# Set the nameservers
sudo scutil << EOF
open
d.init
d.add ServerAddresses * ${DNS_SERVERS}
set State:/Network/Service/com.cisco.anyconnect/DNS
quit
EOF

这与cisco anyconnect 3.1.0兼容吗?我不认为它...您是否有3.1.0的更新脚本?谢谢!
2015年

2

您的管理员更有可能希望设置VPN连接以对10.121。*和10.122。*子网使用本地路由,并让远程(您的家用计算机)路由其余所有请求。(这节省了他们的带宽和责任)

您是否正在使用思科的“ VPN客户端”?OS X?

如果您使用OS X的VPN(通过网络“首选项”窗格设置),则应该可以单击“高级”,然后选择“按需VPN”选项卡。然后提供必要的子网以供VPN使用。


4
OS X vpn客户端具有单独的“ Cisco”选项(与PPTP和L2TP分开),没有“按需VPN”选项。
Mark E. Haase

2

我想要一个本机“应用程序”,可以在登录时运行(并保持运行/隐藏)以启用拆分隧道路由,类似于Locamatic的功能。也许我会在某个时候分叉Locamatic并使用它。我也可以将此AppleScript上传到Github。正如这个答案所暗示的,我不想惹一个守护进程。

该脚本假定VPN具有默认VPN (Cisco IPSec)名称,并且VPN路由为10.10.10.1/22> 10.10.20.10。这些将需要更改/添加其他路由。netstat -rn连接VPN时(在启用此脚本之前)运行terminal> 以查看添加了VPN的路由。

该脚本还会在Notification Center中生成咆哮式通知:)

在此处输入图片说明

我遇到了Mark E. Haase问题,因为我的Cisco VPN将现有网关从a 路由修改UCScUGScI(特定于en0接口的)路由,并将VPN网关添加为UCS路由,因此必须删除两个默认网关并重新添加原始UGSc默认网关

感谢StackExchange / google,这是我的第一个AppleScript,如果不经过几个小时的搜索,就无法将其组合在一起。

欢迎提出建议/更正/优化!

AppleScript(GitHubGist):

global done
set done to 0

on idle
    set status to do shell script "scutil --nc status "VPN (Cisco IPSec)" | sed -n 1p"
    # do shell script "scutil --nc start "VPN (Cisco IPSec)"
    if status is "Connected" then
        if done is not 1 then
            display notification "VPN Connected, splitting tunnel"
            set gateway to do shell script "( netstat -rn | awk '/default/ {if ( index($6, \"en\") > 0 ){print $2} }' ) # gets non-VPN default gateway"
            do shell script "sudo route delete default" # deletes VPN-assigned global (UCS) default gateway
            do shell script "sudo route delete default -ifscope en0" # deletes en0 interface-specific (UGScI) LOCAL non-vpn gateway that prevents it being re-added as global default gateway
            do shell script "sudo route add default " & gateway # re-adds LOCAL non-vpn gateway (from get command above) as global default gateway
            do shell script "sudo route add 10.10.10.1/22 10.10.20.10" # adds VPN route
            display notification "VPN tunnel has been split"
            set done to 1
        end if
    else
        if done is not 2 then
            display notification "VPN Disconnected"
            set done to 2

        end if
    end if
    return 5
end idle

另存为应用程序:

分割隧道应用程序保存设置

右键单击>显示软件包内容,将以下内容添加到info.plist(这会从扩展坞中隐藏应用程序图标,因此必须使用活动监视器或终端> pkill -f 'Split Tunnel'退出该应用程序,如果您希望扩展坞图标,则将其忽略:

<key>LSBackgroundOnly</key>
<string>1</string>

routeNOPASSWD使用以下代码准确地创建一个新的单行文件(无扩展名)(如果操作不正确,这可以防止sudo访问,请使用google visudo以获取更多信息-这允许AppleScript中的sudo命令在没有密码提示的情况下运行,如果您不想,则省略需要更改路由表时显示密码提示):

%admin ALL = (ALL) NOPASSWD: /sbin/route

将此文件复制到 /etc/sudoers.d

在终端中运行以下命令(第二个命令将提示输入密码-这允许sudo routeAppleScript中的命令在不提示输入密码的情况下运行,如果在更改路由表时需要输入密码,请省略)

chmod 440 /private/etc/sudoers.d/routeNOPASSWD
sudo chown root /private/etc/sudoers.d/routeNOPASSWD

最终将应用添加到系统偏好>用户和组>登录项

拆分隧道登录项


1

您应该能够要求要连接的路由器的管理员来设置一个单独的“组”,该组进行隧道分割,并为您提供一个PCF文件,其中包含该组的组名和组密码。


2
那不会发生的:)他们很偏执

1

我遇到了同样的问题,并且由于@mehaase而得以解决

创建~/vpn.sh@mehaase回答后,您可以使用以下步骤将其放入可运行的应用程序自动化脚本中:

  1. 使用Automator创建一个新的应用程序
  2. 在“库”>“实用程序”下添加“运行AppleScript”
  3. 输入: do shell script "sudo ~/vpn.sh" with administrator privileges

您可能还需要chmod 700 ~/vpn.sh从Terminal 运行以赋予脚本执行权限。

之后连接到VPN,你可以简单地运行此应用程序的脚本。输入您的管理员密码,然后单击确定-完成。:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.