在IDC跨机房,或者docker集群的使用中,经常会有这样的需求:
本地办公网络可以直接访问远程IDC集群的内网。
有两种方案:
路由表是最稳妥的做法,但是双向设置比较繁琐,而且容易出错。
本文介绍的是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
VOLUME="/home/coder4/docker_data/openvpn"
# init for first time only
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
#!/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
#!/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
VOLUME="/home/coder4/docker_data/openvpn"
# stop & run server (should call init_open_vpn_test.sh before)
docker ps -q -a --filter "name=$NAME" | xargs -I {} docker rm -f {}
-v $VOLUME:/etc/openvpn \
ovpn_run --cipher AES-128-CBC
#!/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
#!/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证书
if [ x"$#" != x"1" ];then
echo "Usage: $0 <username>"
OVPN_FILE="$USERNAME.ovpn"
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
sed -i 's/redirect-gateway.*$//' $OVPN_FILE
# add this line, the swarm network route
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
#!/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
#!/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
./gen_openvpn_cert.sh coder4
./gen_openvpn_cert.sh coder4
搞好后得到coder4.ovpn
4、本地启动openvpn
在vbox-tmp-1上,启动openvpn
sudo openvpn vpn.coder4.com coder4.opvn
sudo openvpn vpn.coder4.com coder4.opvn
sudo openvpn vpn.coder4.com coder4.opvn
可以尝试ping一下docker-test的内网ip,能ping通就说明openvpn配置对了。
5、配置iptables
还是vbox-tmp-1上
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"
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"
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配置