前言
项目这版要上一个图书馆查询功能,而学校的图书馆查询系统只能内网访问,在内网写完后端之后,问题来了,如何让部署在外网的服务器访问学校内网?
几番周折拿到了学校VPN,用的是f5vpn(从未听过...),在服务器中安装官网提供的Client,问题多多。最后在Github上看到大牛写的 f5vpn-login。这个f5vpn客户端基于Python(太好了,起码能看懂源码...),能在类Unix上运行,OSX和Ubuntu测试可用。
然而,这只是开始...
Overview
f5vpn本地测试通过,但在服务器测试发现:当连接上vpn时,SSH立即中断。
猜想是VPN配置路由时出现问题了,由于我们的主服务器和这台用来代理内网的服务器是同一个地域,可以互通。用主服务器SSH登录上这台服务器,服务器确实是连接上学校VPN了,但ping不通学校内网。
断开VPN,查看阿里云默认的路由表
~ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 120.27.119.247 0.0.0.0 UG 0 0 0 eth1
10.0.0.0 10.174.247.247 255.0.0.0 UG 0 0 0 eth0
10.174.240.0 * 255.255.248.0 U 0 0 0 eth0
100.64.0.0 10.174.247.247 255.192.0.0 UG 0 0 0 eth0
121.41.76.0 * 255.255.252.0 U 0 0 0 eth1
172.16.0.0 10.174.247.247 255.240.0.0 UG 0 0 0 eth0
默认路由表中可见,10.0.0.0 和 172.16.0.0 会通过阿里云内网。
再次连接VPN,查看连接VPN后路由表,此时网络架构如下图。
~ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.16.16.233 128.0.0.0 UG 0 0 0 ppp0
default 172.16.16.233 0.0.0.0 UG 0 0 0 ppp0
default 120.27.119.247 0.0.0.0 UG 0 0 0 eth1
10.0.0.0 10.174.247.247 255.0.0.0 UG 0 0 0 eth0
10.174.240.0 * 255.255.248.0 U 0 0 0 eth0
100.64.0.0 10.174.247.247 255.192.0.0 UG 0 0 0 eth0
121.41.76.0 * 255.255.252.0 U 0 0 0 eth1
172.16.0.0 10.174.247.247 255.240.0.0 UG 0 0 0 eth0
128.0.0.0 172.16.16.233 128.0.0.0 UG 0 0 0 ppp0
172.16.16.1 * 255.255.255.255 UG 0 0 0 ppp0
有三个默认路由,但最终走的是 172.16.16.233(学校VPN),这可以解析为何我们不能通过外网连接服务器。而当我们ping 10.1.18.2(学校内网) 时,因为匹配到路由表中的 10.0.0.0,强制使用阿里云的路由 10.174.247.247,所以会通过阿里云内网访问 10.1.18.2(学校内网) ,也就ping不通啦。
解决方案
阿里云默认路由设置造成的IP网段冲突,可以通过网卡配置文件 /etc/network/interfaces
修改
# up route add -net 172.16.0.0 netmask 255.240.0.0 gw 10.174.247.247 dev eth0
up route add -net 100.64.0.0 netmask 255.192.0.0 gw 10.174.247.247 dev eth0 # 并不知道这是啥
up route add -net 10.174.0.0 netmask 255.255.0.0 gw 10.174.247.247 dev eth0 # 本服务器网段
up route add -net 10.251.0.0 netmask 255.255.0.0 gw 10.174.247.247 dev eth0 # 主服务器网段
up route add -net 10.202.0.0 netmask 255.255.0.0 gw 10.174.247.247 dev eth0 # DNS服务器网段
先将与vpn网段冲突的 172.16.0.0 路由注释掉(谷歌后发现这个路由好像没啥用),然后将 10.0.0.0 细分成主服务器网段(便于内网互通)、DNS服务器网段和服务器自身网段。
重启后再次查看路由表,成功ping到内网
~ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.16.16.233 128.0.0.0 UG 0 0 0 ppp0
default 172.16.16.233 0.0.0.0 UG 0 0 0 ppp0
default 120.27.119.247 0.0.0.0 UG 0 0 0 eth1
10.174.0.0 10.174.247.247 255.255.0.0 UG 0 0 0 eth0
10.174.240.0 * 255.255.248.0 U 0 0 0 eth0
10.202.0.0 10.174.247.247 255.255.0.0 UG 0 0 0 eth0
10.251.0.0 10.174.247.247 255.255.0.0 UG 0 0 0 eth0
100.64.0.0 10.174.247.247 255.192.0.0 UG 0 0 0 eth0
121.41.76.0 * 255.255.252.0 U 0 0 0 eth1
172.16.0.0 10.174.247.247 255.240.0.0 UG 0 0 0 eth0
128.0.0.0 172.16.16.233 128.0.0.0 UG 0 0 0 ppp0
172.16.16.1 * 255.255.255.255 UG 0 0 0 ppp0
ping内网问题解决了,但怎样可以指定特定IP走VPN呢?同样的,只需配置路由。
路由Destination的default表示 0.0.0.0,即使所有流量都通过该路由。我们先将上面路由表中第一第二条via ppp0 的路由删掉。
route del default netmask 128.0.0.0
route del default gw 172.16.16.233
然后将 10.1.18.0(学校查询系统内网段)添加进路由
route add -net 10.1.18.0/24 gw 172.16.16.233
~ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 120.27.119.247 0.0.0.0 UG 0 0 0 eth1
10.1.18.0 172.16.16.55 255.255.255.0 UG 0 0 0 ppp0
10.174.0.0 10.174.247.247 255.255.0.0 UG 0 0 0 eth0
10.174.240.0 * 255.255.248.0 U 0 0 0 eth0
10.202.0.0 10.174.247.247 255.255.0.0 UG 0 0 0 eth0
10.251.0.0 10.174.247.247 255.255.0.0 UG 0 0 0 eth0
100.64.0.0 10.174.247.247 255.192.0.0 UG 0 0 0 eth0
120.27.116.0 * 255.255.252.0 U 0 0 0 eth1
172.16.16.1 * 255.255.255.255 UH 0 0 0 ppp0
上图为路由表和网络架构
完成后,能通过外网访问测试服务器,后端也能连接学校内网返回数据了!
PS:如何能让 f5vpn-login 添加指定ip的路由?
class Linux2Platform(Platform):
def setup_route(self, ifname, gateway_ip, net, bits, action):
if bits == 32:
host_or_net = ["-host", net]
else:
host_or_net = ["-net", net, 'netmask', bits2mask[bits]]
# run_as_root(['/sbin/route', action] + host_or_net +
# ['gw', gateway_ip])
打开f5vpn-login.py
,注释上图两行,并添加如下两行,其中"-net"对应的'10.1.18.0'为指定ip。
def ppp_ip_up(iface_name, tty, local_ip, remote_ip):
revdns_domains = []
for net, bits in routes_to_add:
platform.setup_route(iface_name, local_ip, '.'.join(map(str, net)), bits, 'add')
revdns_domains.extend(routespec_to_revdns(net, bits))
run_as_root(['/sbin/route', 'add'] + ["-net", '10.1.18.0', "netmask", '255.255.255.0'] +['gw', local_ip]) # 添加的语句,添加的语句,添加的语句
Reference:
[1] http://blog.fens.me/vpn-aliyun/
[2] http://linux.vbird.org/linux_server/0230router.php