通过openvpn + iptables实现NAT访问远程IDC集群内网

在IDC跨机房,或者docker集群的使用中,经常会有这样的需求:

本地办公网络可以直接访问远程IDC集群的内网。

有两种方案:

  • 双向建立路由表
  • iptables搞定NAT

路由表是最稳妥的做法,但是双向设置比较繁琐,而且容易出错。

本文介绍的是iptables + openvpn方案。

1、方案:

本地LAN <-- iptables+NAT --> 带openvpn的中转软路由 <-- openvpn --> 远程idc内网(本文是两个内网的docker容器模拟)

2、实验环境:

  • vbox-tmp-1 enp0s3连接外网 enp0s8临时内网选internal net,tmpnet,也就是方案中提到的软路由,打开openvpn后,会增加tun0这个网络设备
  • vbox-tmp-2 临时内网选internal net,tmpnet,模拟本地LAN网络
  • docker-openvpn 远程idc集群上docker容器,暴露接口到公网ip上
  • docker-test 远程idc集群上docker容器,和上面的docker在同样内网环境,仅仅用来测试联通性

vbox我用的是virtual box ,可以参考 《Ubuntu通过双网卡+NAT实现共享上网》

3、物理连接

(1) 配置openvpn

#!/bin/bash
VOLUME="/home/coder4/docker_data/openvpn"
dns_ip="8.8.8.8"
vpn_ip="vpn.coder4.com"
# init for first time only
sudo rm -rf $VOLUME
sudo mkdir -p $VOLUME 
sudo chown -R 777 $VOLUME
docker run -v $VOLUME:/etc/openvpn --rm kylemanna/openvpn ovpn_genconfig -u udp://$vpn_ip
docker run -v $VOLUME:/etc/openvpn --rm -it kylemanna/openvpn ovpn_initpki 

(2) 创建openvpn

#!/bin/bash

# submit to tool node
NAME="openvpn"
VOLUME="/home/coder4/docker_data/openvpn"
dns_ip="8.8.8.8"

# stop & run server (should call init_open_vpn_test.sh before) 
docker ps -q -a --filter "name=$NAME" | xargs -I {} docker rm -f {}
docker run \
    --name $NAME \
    --network camp \
    --dns $dns_ip \
    -d \
    -v $VOLUME:/etc/openvpn \
    -p 1194:1194/udp \
    --cap-add=NET_ADMIN \
    --restart always \
    kylemanna/openvpn \
    ovpn_run --cipher AES-128-CBC

上面启动后,对外暴露了1194的udp端口,如果你有防火墙,需要开一下端口。

(3) 生成openvpn证书

#!/bin/bash

if [ x"$#" != x"1" ];then
    echo "Usage: $0 <username>"
    exit -1
fi

USERNAME="$1"
OVPN_FILE="$USERNAME.ovpn"
CIPHER="AES-128-CBC"
DNS_IP="8.8.8.8"
SWARM_ROUTE_CMD="route 10.18.0.0 255.255.0.0"

VOLUME="/home/coder4/docker_data/openvpn"
# generate client cert for username 
docker run -v $VOLUME:/etc/openvpn --rm -it kylemanna/openvpn easyrsa build-client-full $USERNAME nopass
docker run -v $VOLUME:/etc/openvpn --rm kylemanna/openvpn ovpn_getclient $USERNAME > $OVPN_FILE

# post process
sed -i 's/redirect-gateway.*$//' $OVPN_FILE

cat >> $OVPN_FILE <<EOF

# add this line, the swarm network route
$SWARM_ROUTE_CMD

# dns update
dhcp-option DNS $DNS_IP 
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

# security
cipher $CIPHER 

EOF

上面要说明的是:10.18.0.0是我配置的docker network内网,如果你的环境不同,请自行更改

生成命令:

./gen_openvpn_cert.sh coder4

搞好后得到coder4.ovpn

4、本地启动openvpn

在vbox-tmp-1上,启动openvpn

sudo openvpn vpn.coder4.com coder4.opvn

可以尝试ping一下docker-test的内网ip,能ping通就说明openvpn配置对了。

5、配置iptables

还是vbox-tmp-1上

sudo iptables -F
sudo iptables -A FORWARD -o tun0 -i enp0s8 -s 192.168.88.0/24 -d 10.18.0.0/16 -m conntrack --ctstate NEW -j ACCEPT
sudo iptables -A FORWARD -o enp0s3 -i enp0s8 -s 192.168.88.0/24 -m conntrack --ctstate NEW -j ACCEPT
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -t nat -F POSTROUTING
sudo iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE
sudo iptest_doctables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"

主要是根据不同dst ip 做不同nat,默认是走公网ip,如果发现是10.18.0.0,走openvpn的tun0

6、vbox-tmp-2连上vbox-tmp-1作为网关

不写了,具体可以参考《Ubuntu通过双网卡+NAT实现共享上网》

现在vbox-tmp-2上ping一下docker-test的内网ip,应该可以成功了。

这个方案的优点:配置简单,自带了专线加密隧道。

缺点也很明显:没法区分来源ip,只是单向连通

区分ip的事情,可以考虑iptable上做伪装src和dst包,感兴趣的朋友,欢迎分享你们的iptables配置

Leave a Reply

Your email address will not be published. Required fields are marked *