OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

一、引言

(一)搭建 OpenVPN

 我们将搭建 OpenVPN 服务端,并在客户端本地添加路由表,实现国内外 IP 的访问分流。

(二)配合 DNSmasq 实现域名分流

 使用 DNSmasq 配置国内外域名的解析,将访问流量引导到最优的解析节点,从而提升整体访问效率。

二、环境

(一)系统

  • Windows 10 家庭中文版
  • Ubuntu 18.04

(二)软件

  • OpenVPN GUI – v11.13.0.0
  • OpenVPN – v2.4.4
  • Dnsmasq – v2.79

三、OpenVPN 服务端的安装和配置

(一)安装 OpenVPN

 首先,使用

apt

包管理器安装 OpenVPN。执行以下命令:

apt install openvpn

 安装完成后,运行以下命令检查 OpenVPN 是否安装成功:

openvpn --version

 如果安装成功,会看到OpenVPN 的版本信息输出:

OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

(二)配置 OpenVPN

 OpenVPN 配置文件通常位于

/etc/openvpn

目录下,编辑

/etc/openvpn/server.conf

文件,并根据需求进行调整。以下是一个基本的配置示例:

local 0.0.0.0 #服务端绑定地址
port 8080     #服务端绑定端口
proto udp
dev tun
ca ca.crt
dh dh.pem
server 192.168.90.0 255.255.255.0 #客户端分配地址
ifconfig-pool-persist ipp.txt
push "redirect-gateway"
push "dhcp-option DNS 192.168.90.1"
keepalive 10 120
cipher AES-256-GCM
status openvpn-status.log
log /var/log/openvpn.log
verb 3
explicit-exit-notify 1

script-security 3
auth-user-pass-verify /etc/openvpn/checkpsw.sh via-env #客户端用户名/密码登陆校验脚本
username-as-common-name
verify-client-cert none

 在 OpenVPN 配置文件中,我们使用

push "dhcp-option DNS 192.168.90.1"

是为了避免国内部分域名遭遇 DNS 污染。如果你希望使用自己的 DNS 服务器(例如局域网中的 DNS 服务器),可以将其替换为:

push "dhcp-option DNS 192.168.90.1"

 这样,客户端在连接到 OpenVPN 时将会使用

192.168.90.1

作为 DNS 服务器。

 配置修改完成后,执行以下命令重启 OpenVPN 服务,使更改生效:

systemctl restart openvpn@server

(三)配置 NAT

 为了让 OpenVPN 客户端能够通过 VPN 访问外网,我们需要配置 NAT(网络地址转换)。假设我们给客户端分配的地址段是 192.168.90.0/24,并且 OpenVPN 服务器的物理网卡是 ens3(可以连接外网),可以使用以下 iptables 命令进行配置:

# 设置 NAT 规则,使得客户端的流量能够通过服务器访问外网
iptables -t nat -I POSTROUTING -s 192.168.90.0/24 -o ens3 -j MASQUERADE
# 允许从客户端到 VPN 服务器的流量转发
iptables -I FORWARD -s 192.168.90.0/24 -j ACCEPT
# 允许从 VPN 服务器转发流量到客户端
iptables -I FORWARD -d 192.168.90.0/24 -j ACCEPT

(四)启动 OpenVPN 服务

1. 设置 OpenVPN 开机自启

systemctl enable openvpn@server

2. 启动 OpenVPN 服务

systemctl start openvpn@server

3. 检查 OpenVPN 是否启动成功

netstat -anp | grep 8080

 如果 OpenVPN 正在监听该端口,命令输出中将显示相关信息,表明服务已成功启动。

OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

四、DNSmasq 服务器安装

(一)安装 DNSmasq

  1. 安装 DNSmasq

 使用

apt

包管理器安装 DNSmasq:

apt install dnsmasq

2. 检查 DNSmasq 安装是否成功

dnsmasq --version

 如果安装成功,你将看到类似如下的版本信息:

OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

(二)配置 DNSmasq

1. 编辑/etc/dnsmasq.conf文件

no-hosts
no-resolv
cache-size=1000
listen-address=192.168.90.1 #此处设置为OpenVPN虚拟网卡IP
port=53
all-servers
server=8.8.8.8
server=8.8.4.4

2. 编辑/etc/resolv.conf文件

nameserver 192.168.90.1

 设置域名解析服务地址为 DNSmasq 监听地址”192.168.90.1″。

 由于

systemd-resolved

服务会自动修改

/etc/resolv.conf

文件,我们希望手动管理 DNS 配置,可以通过以下步骤停止并禁用该服务:

  1. 执行
systemctl stop systemd-resolved
  1. 停止服务。
  2. 执行
systemctl disable systemd-resolved
  1. 禁用开机自启。

(三)下载国内域名列表

 为了实现国内外域名的分流解析,我们需要下载并配置相应的域名列表文件。可以通过

wget

命令下载以下两个配置文件:

  1. 下载加速域名列表(
accelerated-domains.china.conf
  1. ):
wget /etc/dnsmasq.d/accelerated-domains.china.conf

2. 下载 Google 域名列表(

google.china.conf

):

wget /etc/dnsmasq.d/google.china.conf

 当 DNS 查询的域名在这两个列表中时,DNSmasq 会将请求转发到指定的 DNS 服务器进行解析。国内域名将被转发给国内 DNS 服务器解析,而国外域名则交给 Google DNS(8.8.8.8)处理。

(四)修改 DNS 配置

 由于部分公共 DNS 服务器(如

114.114.114.114

)可能会遭遇 CDN 加速,导致国内域名解析到国外 IP,这会影响国内域名的正常访问。因此,我们需要将这两个配置文件中的

114.114.114.114

替换为本地的 DNS 服务器地址,以确保国内域名正确解析到国内 IP。

  1. 使用以下命令将
114.114.114.114
  1. 替换为北京本地的 DNS 服务器(如:
202.106.0.20
  1. ):
vim /etc/dnsmasq.d/accelerated-domains.china.conf
# 在 Vim 中执行以下替换命令
:%s/114.114.114.114/202.106.0.20/g
# 保存并退出
:wq

2. 同样的操作需要在

google.china.conf

文件中执行:

vim /etc/dnsmasq.d/dnsmasq.d/google.china.conf
# 在 Vim 中执行以下替换命令
:%s/114.114.114.114/202.106.0.20/g
# 保存并退出
:wq

 通过以上步骤,国内域名将会使用你指定的本地 DNS 服务器(如

202.106.0.20

)进行解析,确保访问国内资源时不会受到 CDN 的影响。

(五)启动 DNSmasq

  1. 设置开机启动
systemctl enable dnsmasq

2. 启动 DNSmasq 服务

service dnsmasq start

3. 检查 DNSmasq 是否启动成功

netstat -anp | grep 53

 如果 DNSmasq 启动成功,能看到类似以下的输出,表明它正在监听 53 端口:

OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

(六)测试 DNSmasq

 执行

dig baidu.com

dig github.com
OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

五、OpenVPN 客户端配置路由表

(一)获取国内 IP 地址

 首先,需要获取国内 IP 地址的列表。可以从以下网站下载国内 IP 地址段的文件:

 该文件包含了中国所有 IP 地址段。

(二)执行

build.py

生成路由批处理脚本

 接下来,我们需要使用

build.py

脚本来解析国内 IP 地址并生成相应的路由配置文件(

add_route.bat

del_route.bat

)。执行以下命令:

python .\build.py .\all_cn_cidr.txt add_route.bat del_route.bat 192.168.2.254

其中:

all_cn_cidr.txt
  • 是从上面下载的国内 IP 地址列表文件。
add_route.bat
  • 是生成的用于添加路由的批处理脚本。
del_route.bat
  • 是生成的用于删除路由的批处理脚本。
192.168.2.254
  • 是你的默认网关地址。也就是说,这是客户端未连接 VPN 时,有线或无线网卡的默认网关地址。你需要根据实际情况填写本机的默认网关地址。

 通过执行上述命令,

build.py

将根据国内 IP 地址段生成相应的路由脚本。

(三)build.py 脚本代码

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import sys
import re
import os
import math
import copy

def usage():
    print('./build [route_file] [add_file] [del_file] [gateway]')
    print('./build route.text add_route.bat del_route.bat 192.186.90.5')

#合并子网
def marge_subnet(ip_tables):
    count = 2
    start_i, end_i, prev_i = 0, -1, 0
    new_ip_tables = []
    serials = [ip_tables[0]]
    for i in range(1, len(ip_tables)):
        if ip_tables[i-1].is_serial(ip_tables[i]):
            serials.append(ip_tables[i])
            continue
    
        flag = False
        while len(serials) > 0:
            count = 2
            while len(serials) >= count and serials[0].get_last_net_no() % count == 0:
                serials[0] = serials[0] << 1
                count = count * 2
            count = 1 if count == 2 else int(count / 2)
            new_ip_tables.append(serials[0])
            serials = serials[count:]
        if len(serials) > count:
            new_ip_tables = new_ip_tables + serials[count:]            
        serials = [ip_tables[i]]

    new_ip_tables = new_ip_tables + serials   
    return new_ip_tables

#IP和MASK
class ip_mask:
    def __init__(self, ip):
        self.start = {}
        self.ip = copy.deepcopy(ip)
        self.mask = []
        self.last_mask_index = 0

    def __lt__(self, other):
        return self.ip < other.ip

    def __eq__(self, other):
        return self.ip == other.ip

    def __lshift__(self, value):
        while value >= 8 and self.last_mask_index >= 0:
            self.mask[self.last_mask_index] = (self.mask[self.last_mask_index] << 8 & 255)
            value = value - 8
            self.last_mask_index = self.last_mask_index - 1
        if self.last_mask_index >= 0 and value > 0:
            self.mask[self.last_mask_index] = (self.mask[self.last_mask_index] << value & 255)
        return self
        
    def set_mask(self, mask):
        self.mask = copy.deepcopy(mask)
        for i in range(len(self.mask) - 1, -1, -1):
            if mask[i] != 0:
                self.last_mask_index = i
                break

    def is_serial(self, other):
        flag = False
        for i in range(len(self.ip)):
            if self.ip[i] + 1 == other.ip[i] or self.ip[i] - 1 == other.ip[i]:
                flag = True
            elif self.ip[i] != other.ip[i]:
                return False
            elif self.ip[i] != 0:
                if flag:
                    return False
        return flag

    def get_last_net_no(self):
        return self.ip[self.last_mask_index]

#解析IP地址
def parse_ip_table(content):
    lines = content.split('\n')
    ip_tables = []
    for line in lines:
        line = line.split('/')
        if len(line) != 2:
            continue
        ip = list(map(int, line[0].split('.')))
        im = ip_mask(ip)
        im.set_mask([255,255,255,255])
        im = im << (32 - int(line[1]))
        ip_tables.append(im)
        
    return ip_tables

#添加路由
def add_route(file, ip_tables, gateway):
    f = open(file, 'w')
    for ip_mask in ip_tables:
        f.write('route add %d.%d.%d.%d mask %d.%d.%d.%d %s\n' % 
            (ip_mask.ip[0], ip_mask.ip[1], ip_mask.ip[2], ip_mask.ip[3], ip_mask.mask[0], ip_mask.mask[1], ip_mask.mask[2], ip_mask.mask[3], gateway))
    f.write('ipconfig /flushdns\n')
    f.write('pause\n')

#删除路由
def del_route(file, ip_tables):
    f = open(file, 'w')
    for ip_mask in ip_tables:
        f.write('route delete %d.%d.%d.%d\n' % (ip_mask.ip[0], ip_mask.ip[1], ip_mask.ip[2], ip_mask.ip[3]))
    f.write('ipconfig /flushdns\n')
    f.write('pause\n')

def main():

    if len(sys.argv) != 5:
        usage()
        return
    #####
    route_file = sys.argv[1]
    add_file = sys.argv[2]
    del_file = sys.argv[3]
    gateway = sys.argv[4]
    #####
    route_content = open(route_file, 'r+').read()
    ip_tables = parse_ip_table(route_content);
    ip_tables.sort()
    sub_net = len(ip_tables)
    ip_tables = marge_subnet(ip_tables)
    #####
    add_route(add_file, ip_tables, gateway)
    del_route(del_file, ip_tables)
    #####
    
main()

(四)添加路由

  1. 右键以管理员身份运行
add_route.bat

 由于需要修改系统路由表,必须以管理员权限运行生成的

add_route.bat

脚本。右键点击

add_route.bat

文件,选择“以管理员身份运行”。

2. 等待脚本执行

 运行后,

add_route.bat

脚本将添加大约 5000 条路由。这个过程可能需要几分钟时间,等待脚本完成执行。当脚本执行完毕时,命令行会显示“请按任意键继续…”,此时按下回车键退出。

3. 检查路由是否添加成功

 执行完脚本后,可以使用以下命令检查路由表是否已经成功添加:

route print

 如果路由添加成功,能看到类似以下的输出:

OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

六、OpenVPN 客户端测试

(一)启动 OpenVPN 客户端

 启动OpenVPN客户端,并连接成功。

(二)测试百度的延迟

ping www.baidu.com
OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

(三)测试 GitHub 的延迟

ping www.github.com
OpenVPN 实现国内外流量分流+DNSmasq 实现国内外域名分流解析

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注