menu Chancel's blog
rss_feed
Chancel's blog
有善始者实繁,能克终者盖寡。

R2S安装Linux软路由指南

作者:Chancel Yang, 创建:2024-04-21, 字数:26990, 已阅:642, 最后更新:2024-07-28

1. R2S

R2S 是一台优秀的 Arm 服务器,功耗低、双千兆以及价格低的优势,让这个设备非常流行

不少R2S的教程都是以刷入 openwrt 为主,而除了 openwrt 外,R2S 还支持许多第三方系统,例如原生的 Linux 系统 Armbian

Armbian 可以完美地实现透明代理网关,设置起来不像 openwrt 一样,在Web界面上左点右按再保存设置等待生效

使用 R2S + Armbian 来实现完全可以实现一个简洁的透明网关,其网络拓补图如下:

在上图中,省略了光猫的部分,R2S 取代了原路由器,2个千兆接口分别接入路由器的 WAN口 和光猫 LAN口

R2S 将负责宽带拨号以及网络转发、网络分流的功能,这一点与openwrt版的R2S是一模一样的

下面将一步一步地实现拨号、DNS解析以及针对不同IP分别处理直连与科学代理

2. Armbian

Armbian 是一个基于 Linux 的操作系统发行版,专门为ARM架构的单板计算机(如树莓派、Orange Pi等)设计和优化

这当中也包括了 R2S ,可以从 Armbian 的官网上下载得到属于 R2S 的 Armbian 系统镜像

2.1. 系统安装

R2S 的官方镜像地址

下载后的系统镜像是一个 .xz 文件,无需解压 .xz ,直接将镜像写入到内存卡上插入 R2S 即可开机

  • Linux可以使用dd直接写入
  • Windows推荐使用Rufus写入

写入内存卡后,将内存卡插入R2S中,从路由器的 LAN口 连接网线到 R2S 的 LAN口

再接入电源和网线,并观察 LAN 灯是否亮起

如亮起,说明已连接到路由器下,登录到路由器后台,查看R2S被分配到的IP,之后使用SSH远程登录

  • 默认账户:root
  • 默认密码:1234

登录后会提示你进行初始化设置,包括ROOT账户密码以及一个具备管理员权限的账户

2.2. 系统设置

如果访问外网不畅通,可以将 Armbian 的源更换到清华源

安装要用的一些必要软件以及我的一些常用软件

Bash
# 更新软件包
apt update -y
# 更新系统
apt upgrade -y
# 安装必要软件
apt install -y supervisor gcc make iproute2 ipset pppoeconf vim wget git curl unzip dnsutils net-tools tree
# 安装我常用的软件(可选)
apt install -y btop proxychains openssl

以下操作默认采用 ROOT 用户进行操作

2.3. 宽带拨号

编辑:/etc/netplan/armbian-default.yaml

YAML
network:
  version: 2
  renderer: NetworkManager
  ethernets:
    lan0: # 请确保你的LAN口名称是lan0
      dhcp4: no
      addresses: [192.168.1.1/24]
    eth0: # 请确保你的WAN口名称是eth0
      dhcp4: yes

编辑完成后,拔除网线,将路由器的 WAN口 接入 R2S 的 LAN口 ,将R2S的 WAN口 接入光猫的拨号口,将路由器设置为:

  • 静态IP:192.168.2.1
  • 静态网关(R2S的IP):192.168.1.1
  • 静态DNS服务(R2S的IP):192.168.1.1

接着使用ssh会话连接到 192.168.1.1 ,即你的R2S,再进行宽带拨号

Bash
pppoeconf

根据提示填入宽带帐号密码即可

允许IPV4转发,编辑: /etc/sysctl.conf

Bash
...
# 允许IPV4的流量转发
net.ipv4.ip_forward=1

并更新支持IP转发的设置

Bash
sysctl -p

然后添加临时转发规则进行测试

Bash
iptables -t nat -I POSTROUTING -j MASQUERADE

连接路由器Wifi,通过 ping 114.114.114.114 确认网络转发设置是否成功

以上实现了 R2S 拨号并转发网络数据

3. 透明代理网关

网络透明代理(Transparent Proxy)是一种网络代理的方式,在代理服务器和用户之间进行中间层的数据传输

对用户来说,它是透明的,用户无需进行任何额外的配置,这种代理不用在每一个设备上安装代理软件

对于一些游戏机、特定系统的设备来说很友好

3.1. 透明代理(Gost)

代理我使用的是 gost ,服务端的部分可以使用商业代理或自建服务,这里假设代理协议是普通的 ss

下载gost:

下载到 /root/gost/ 中,解压后目录如下

Bash
$ tree                                                      
.
├── gost
├── gost_3.0.0-rc8_linux_arm64.tar.gz
├── LICENSE
├── README_en.md
└── README.md

0 directories, 5 files

编辑:/root/gost/config.yaml

YAML
services:
# 1080 端口开启流量中继服务,走的是Chain-0的代理路线
- name: "1080-red" 
  addr: :1080
  handler:
    chain: chain-0
    type: red
  listener:
    type: red
# 1080 端口开启UDP的DNS代理查询,走的是Chain-0的代理路线
- name: "1080-dns"
  addr: :1080
  handler:
    chain: chain-0
    type: dns
  listener:
    type: dns
    metadata:
      mode: udp
  forwarder:
    nodes:
      - name: Google-1
        addr: tls://8.8.8.8:853
      - name: Google-2
        addr: tls://8.8.4.4:853

chains:
- name: chain-0
  hops:
  - name: hop-0
    nodes:
    # SS协议的信息
    - name: "1.2.3.4" 
      addr: "1.2.3.4:48888"
      connector:
        type: ss
        auth:
          username: chacha20-ietf-poly1305
          password: my-passwd

请自行修改 ss 的地址、端口、加密方式、密码,然后运行 gost 服务进行测试

Bash
/root/gost/gost -C /root/gost/config.yml

运行后检查输出无错误,然后使用 supervisor 配置为后台进程

编辑:/etc/supervisor/conf.d/gost.conf

TEXT
[program:gost]
command=/root/gost/gost -C /root/gost/config.yaml
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile_maxbytes=32MB
user=root

更新 supervisor 运行,并检查运行状态

Bash
supervisorctl update
supervisorctl status

3.2. DNS解析(Overture)

Overture 是一个开源的 DNS 加速器和广告过滤器,它旨在提供更快的 DNS 查询响应速度,并屏蔽广告和恶意网站

稍加配置 Overture 也可以解决 DNS 污染

overture仓库地址:

由于 R2S 的 Armbian 自带DNS服务 systemd-resolved.service ,需要先将其禁用

Bash
systemctl stop systemd-resolved.service
systemctl disable systemd-resolved.service

在禁用掉自带的DNS服务后,下载后(V1.8版本)到 /root/overture 中解压,目录输出如下:

Bash
$ tree
.
├── config.yml
├── domain_alternative_sample
├── domain_primary_sample
├── domain_ttl_sample
├── hosts_sample
├── ip_network_alternative_sample
├── ip_network_primary_sample
├── overture-linux-arm64
└── overture-linux-arm64.zip

0 directories, 9 files

在运行 Overture 之前,先查找运营商指定的DNS服务地址

Bash
cat /etc/ppp/resolv.conf

通常来说,宽带服务商的 DNS 服务提供更本地化的 CDN 加速效果,而公共 DNS 由于查询距离较远所以不如宽带运营商的 DNS 服务

编辑:/root/overture/config.yml

YAML
bindAddress: :53
debugHTTPAddress: :55555
dohEnabled: false
primaryDNS:
  # 服务商的DNS服务地址,这里填的是 114.114.114.114 的公共DNS服务
  - name: domestic_dns
    address: 114.114.114.114:53
    protocol: udp
    socks5Address: 
    timeout: 3
    ednsClientSubnet:
      policy: disable
      externalIP:
      noCookie: true
onlyPrimaryDNS: false
alternativeDNS:
  # 国外DNS服务(在Gost中配置为Google)
  - name: foreign_dns
    address: 127.0.0.1:1080
    # 此处UDP对应GOST中的`mode`参数值,可选`tcp`、`udp`
    protocol: udp
    socks5Address:
    timeout: 6
    ednsClientSubnet:
      policy: disable
      externalIP:
      noCookie: true
ipv6UseAlternativeDNS: false
alternativeDNSConcurrent: false
whenPrimaryDNSAnswerNoneUse: primaryDNS
# 通过文件中的 IP 网络段来决定哪些 IP 地址的请求应该被定向到特定的 DNS 服务器
ipNetworkFile:
  # 符合 `china_ip_list.txt` 中的IP则优先使用 `primary` DNS服务的结果
  primary: /root/china_ip_list.txt
  alternative: /root/overture/ip_network_alternative_sample
# 通过文件中的域名决定哪些 IP 地址的请求应该被定向到特定的 DNS 服务器
domainFile: 
  primary: /root/overture/domain_primary_sample
  # 符合 `gfw_all_domain.txt` 中的域名则优先使用 `alternative` DNS服务的结果
  alternative: /root/gfw_all_domain.txt
  matcher: full-map
hostsFile:
  hostsFile: /root/overture/hosts
  finder: full-map
minimumTTL: 3600
domainTTLFile: /root/overture/domain_ttl_sample
# 缓存的结果数量
cacheSize: 10240
cacheRedisUrl:
cacheRedisConnectionPoolSize:
rejectQType:
  - 255

配置中重要的部分已添加了注释,在配置中使用到的1个国内 IP 文件与国外 GFW 域名名单:

  • /root/china_ip_list.txt
  • /root/gfw_all_domain.txt

2个文件的来源:

由于这两个文件都是需要定期更新,所以写一个脚本来实现

编辑: /root/generate.sh

Bash
#/bin/bash
#author:Chancel.Yang
#date:2023/09/21

wget https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt
curl https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt | base64 -d | sort -u | sed '/^$\|@@/d'| sed 's#!.\+##; s#|##g; s#@##g; s#http:\/\/##; s#https:\/\/##;' | sed '/\*/d; /apple\.com/d; /sina\.cn/d; /sina\.com\.cn/d; /baidu\.com/d; /qq\.com/d' | sed '/^[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$/d' | grep '^[0-9a-zA-Z\.-]\+$' | grep '\.' | sed 's#^\.\+##' | sort -u > temp_gfwlist.txt
curl https://raw.githubusercontent.com/hq450/fancyss/master/rules/gfwlist.conf | sed 's/ipset=\/\.//g; s/\/gfwlist//g; /^server/d' > temp_koolshare.txt
cat temp_gfwlist.txt temp_koolshare.txt | sort -u > gfw_all_domain.txt
rm -f temp_gfwlist.txt temp_koolshare.txt

/root 下执行该脚本会产生 china_ip_list.txtgfw_all_domain.txt 2个文件,如下:

Bash
$ tree -L 1
.
├── china_ip_list.txt
├── gfw_all_domain.txt
├── update_china_and_gfw.sh
├── iptables.sh
└── overture

1 directory, 4 files

运行overture

Bash
overture-linux-arm64 -c config.yml

使用 dig 程序来验证是否能够顺利进行DNS解析,分别对 127.0.0.1:53 和 127.0.0.1:1080 发起 google.com 域名查询,确保结果一致,如下:

Bash
$ dig google.com @127.0.0.1

; <<>> DiG 9.18.24-1-Debian <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64070
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             3600    IN      A       142.250.68.110

;; Query time: 648 msec
;; SERVER: ::1#53(::1) (UDP)
;; WHEN: Sat Jul 27 22:59:49 CST 2024
;; MSG SIZE  rcvd: 65

root@nanopi-r2s ~$ dig google.com @127.0.0.1 -p 1080                                                                   

; <<>> DiG 9.18.24-1-Debian <<>> google.com @127.0.0.1 -p 1080
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51110
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             291     IN      A       142.250.68.110

;; Query time: 0 msec
;; SERVER: 127.0.0.1#1080(127.0.0.1) (UDP)
;; WHEN: Sat Jul 27 22:59:58 CST 2024
;; MSG SIZE  rcvd: 65

使用 supervisor 配置为后台进程

编辑:/etc/supervisor.d/overture.conf

TEXT
[program:overture]
command=/root/overture/overture-linux-arm64 -c /root/overture/config.yml
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile_maxbytes=32MB
user=root

更新 supervisor 运行,并检查状态

Bash
supervisorctl update
supervisorctl status

3.3. 网络分流(iptables)

在完成 gost 与 overture 设置后,需要将流量进行国内外分流

分流规则为:

  • 国内IP直连
  • 国外IP转发

编辑:/root/iptables.sh

Bash
#/bin/bash
#date:2023/09/21

/usr/sbin/ipset -N china hash:net

# china_ip_list.txt文件在overture中已经下载
for i in $(cat /root/china_ip_list.txt ); 
do
    /usr/sbin/ipset -A china $i; 
done

# 创建一个NAT规则集`SSNAT`
/sbin/iptables -t nat -N SSNAT
# 忽略国内IP
/sbin/iptables -t nat -A SSNAT -p all -m set --match-set china dst -j RETURN
# 忽略局域网IP
/sbin/iptables -t nat -A SSNAT -d 0.0.0.0/8 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 10.0.0.0/8 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 127.0.0.0/8 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 169.254.0.0/16 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 172.16.0.0/12 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 192.168.0.0/16 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 224.0.0.0/4 -j RETURN

# 将剩下IP段(国外)的流量全部转发到gost的透明代理网口1080中
/sbin/iptables -t nat -A SSNAT -p tcp -j REDIRECT --to-port 1080

# 最后将所有进入R2S的数据包转入`SSNAT`规则集合中
/sbin/iptables -t nat -A PREROUTING -p tcp -j SSNAT

# 将出站数据包的源地址进行NAT避免被防火墙拦截
/sbin/iptables -t nat -I POSTROUTING -j MASQUERADE

请再次检查你的 /root 目录,文件应如下:

Bash
$ tree -L 1
.
├── update_china_and_gfw.sh
├── china_ip_list.txt
├── gfw_all_domain.txt
├── iptables.sh
└── overture
├── gost

2 directory, 4 files

然后借助 crontab 设置脚本开机自动执行

Bash
crontab -e

# 在crontab界面中添加开机运行iptables.sh脚本
...
@reboot /bin/sh /root/iptables.sh

设置完毕后重启系统

3.4. 网络测试

在重启后,打开R2S的SSH会话,查看iptables的nat表,参考如下:

Bash
$ iptables -L -n -v -t nat 
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
 5371  341K SSNAT      tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
10260 1026K MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0           

Chain SSNAT (1 references)
 pkts bytes target     prot opt in     out     source               destination         
 2530  171K RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set china dst
    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/8           
    3   120 RETURN     all  --  *      *       0.0.0.0/0            10.0.0.0/8          
    0     0 RETURN     all  --  *      *       0.0.0.0/0            127.0.0.0/8         
    0     0 RETURN     all  --  *      *       0.0.0.0/0            169.254.0.0/16      
    0     0 RETURN     all  --  *      *       0.0.0.0/0            172.16.0.0/12       
  234 14040 RETURN     all  --  *      *       0.0.0.0/0            192.168.0.0/16      
    0     0 RETURN     all  --  *      *       0.0.0.0/0            224.0.0.0/4         
 2604  156K REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            redir ports 1080

再检查 gost 与 overture 的运行日志正常:

  • /var/log/supervisor/gost.log
  • /var/log/supervisor/overture.log

最后,使用任意连入 WI-FI 的任意设备访问如下两个网站,测速并确认IP分别为直连IP和代理IP:

4. DHCP服务

如果路由器支持有线中继,那么也可以让 R2S 充当 DHCP 服务器,从而实现更强的控制效果,例如:

  • 有线中继后, 192.168.2.0/24 段将舍弃,由 R2S 负责分配IP,动态分配 192.168.1.128 到 192.168.1.255 之间的所有IP
  • IP段在 192.168.1.128 到 192.168.1.255 之间的IP设置为不分流,这样对无需走透明代理的智能家居十分友好
  • IP段在 192.168.1.2 到 192.168.1.127 之间的IP则享受分流效果

网络拓补图如下:

以下是实现步骤

4.1. 动态IP分配(isc-dhcp-server)

安装 DHCP 分配服务

Bash
apt update
apt install isc-dhcp-server

编辑:/etc/dhcp/dhcpd.conf

TEXT
...

# 默认的租约时间12小时
default-lease-time 43200;
# 允许的最大租约时间7天
max-lease-time 604800;

...

# 局域网设备将自动分配 192.168.1.128 到 192.168.1.254 的IP段
subnet 192.168.1.0 netmask 255.255.255.0 {
  range 192.168.1.128 192.168.1.254;
  option routers 192.168.1.1;
  option domain-name-servers 192.168.1.1;
}

编辑: /etc/default/isc-dhcp-server

TEXT
...
# 限定 DHCP 服务作用的接口为 `lan0`
INTERFACESv4="lan0"

启动和开机自启 isc-dhcp-server 服务

Bash
systemctl enable --now isc-dhcp-server

最后开启路由器的有线中继模式,开启成功后,可以在 R2S 中查看当前已分配的IP列表:

Bash
$ dhcp-lease-list
Reading leases from /var/lib/dhcp/dhcpd.leases
MAC                IP              hostname       valid until         manufacturer        
===============================================================================================
40:31:3c:d9:ef:65  192.168.1.171  chunmi-cooker- 2024-07-09 08:43:22 XIAOMI Electronics,CO.,LTD
64:82:14:b4:f6:6c  192.168.1.165  NarwalRobotics 2024-07-09 08:43:49 FN-LINK TECHNOLOGY Ltd.
78:8b:2a:9d:e9:a1  192.168.1.166  chuangmi.camer 2024-07-09 08:44:09 Zhen Shi Information Technology (Shanghai) Co., Ltd.
7c:c2:94:23:e0:5b  192.168.1.168  zhimi-airpurif 2024-07-09 08:40:16 Beijing Xiaomi Mobile Software Co., Ltd
94:f8:27:b0:63:b1  192.168.1.157  chuangmi_camer 2024-07-09 08:44:05 Shanghai Imilab Technology Co.Ltd
cc:b5:d1:7c:26:cb  192.168.1.164  midjd6-fridge- 2024-07-09 08:42:18 Beijing Xiaomi Mobile Software Co., Ltd
d4:f0:ea:69:a2:0c  192.168.1.167  philips-light- 2024-07-09 08:42:36 Beijing Xiaomi Mobile Software Co., Ltd
d4:f0:ea:6a:ae:7d  192.168.1.160  yeelink-light- 2024-07-09 08:42:09 Beijing Xiaomi Mobile Software Co., Ltd
d4:f0:ea:85:10:5a  192.168.1.162  yeelink-light- 2024-07-09 08:42:08 Beijing Xiaomi Mobile Software Co., Ltd
d4:f0:ea:85:19:30  192.168.1.163  yeelink-light- 2024-07-09 08:42:13 Beijing Xiaomi Mobile Software Co., Ltd
dc:ed:83:61:9b:fd  192.168.1.172  lumi-acpartner 2024-07-09 08:43:34 Beijing Xiaomi Mobile Software Co., Ltd
dc:ed:83:ec:17:3c  192.168.1.170  lumi-acpartner 2024-07-09 08:43:52 Beijing Xiaomi Mobile Software Co., Ltd

如果分配IP出现问题,可以通过 journalctl 来查看 DHCP 服务日志排查问题:

Bash
journalctl -xeu isc-dhcp-server.service -f

4.2. 动态IP忽略透明代理(iptables)

在晚餐配置 DHCP 的 IP 分配后,局域网内的设备将默认被分配到 192.168.1.128 到 192.168.1.254 之间

结合之前 iptables 脚本就可以实现对动态IP不进行透明代理的效果

重新编辑:/root/iptables.sh

Bash
#/bin/bash
#date:2023/09/21

/usr/sbin/ipset -N china hash:net

# china_ip_list.txt文件在overture中已经下载
for i in $(cat /root/china_ip_list.txt ); 
do
    /usr/sbin/ipset -A china $i; 
done

# 创建一个NAT规则集`SSNAT`
/sbin/iptables -t nat -N SSNAT
# 国内IP
/sbin/iptables -t nat -A SSNAT -p all -m set --match-set china dst -j RETURN
# 忽略局域网IP
/sbin/iptables -t nat -A SSNAT -d 0.0.0.0/8 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 10.0.0.0/8 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 127.0.0.0/8 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 169.254.0.0/16 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 172.16.0.0/12 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 192.168.0.0/16 -j RETURN
/sbin/iptables -t nat -A SSNAT -d 224.0.0.0/4 -j RETURN

# 忽略192.168.1.128到192.168.1.255之间的IP
/sbin/iptables -t nat -A SSNAT -s 192.168.1.128/25 -j RETURN

# 将剩下IP段(国外)的流量全部转发到gost的透明代理网口1080中
/sbin/iptables -t nat -A SSNAT -p tcp -j REDIRECT --to-port 1080

# 最后将所有进入R2S的数据包转入`SSNAT`规则集合中
/sbin/iptables -t nat -A PREROUTING -p tcp -j SSNAT

# 将出站数据包的源地址进行NAT避免被防火墙拦截
/sbin/iptables -t nat -I POSTROUTING -j MASQUERADE

对于需要使用透明代理的设备,则可以通过 DHCP 服务来指定分配到 192.168.1.2 至 192.168.1.127 之间的IP,这些IP将被透明代理处理

编辑:/etc/dhcp/dhcpd.conf

TEXT
...

# 默认的租约时间12小时
default-lease-time 43200;
# 允许的最大租约时间7天
max-lease-time 604800;

...

# 局域网设备将自动分配 192.168.1.128 到 192.168.1.254 的IP段
subnet 192.168.1.0 netmask 255.255.255.0 {
  range 192.168.1.128 192.168.1.254;
  option routers 192.168.1.1;
  option domain-name-servers 192.168.1.1;

  # 固定某个设备的IP为192.168.1.50
  host My-Windows-PC {
        hardware ethernet 00:a0:ae:aa:a0:ac;
        fixed-address 192.168.1.50;
    }
}

停止 dhcp 服务并清空分配信息然后重新启动

Bash
systemctl stop isc-dhcp-server
rm /var/lib/dhcp/dhcpd.leases
systemctl start isc-dhcp-server

使用被固定IP的设备进行网络测试,确认分流效果,再使用无分流设备,确认只有直连的效果

如果发现网络有异常或者没有走透明代理,可以使用 watch 结合 iptables 来监控数据包走向来判断问题:

Bash
watch -n 1 iptables -L -n -v -t nat

5. 进阶

5.1. nftables

nftables 是一个用于 Linux 操作系统的网络包过滤和分类框架,旨在替代 iptables 及其相关的工具(如 ip6tables、arptables 和 ebtables)

由 Netfilter 项目开发,并在 Linux 内核 3.13 版本中引入,相较于 iptables 而言,对高并发、高流量的场景性能提升很大

在设备性能不佳的情况下,采用 nftables 来取代 iptables 提升性能可一定程度上缓解性能问题

于是,你可以改写上面 iptables 的脚本如下:

Bash
#!/bin/bash

# 清除旧的 /usr/sbin/nftables 规则(如果有)
/usr/sbin/nft flush ruleset

# 创建一个新的 `nat` 表
/usr/sbin/nft add table ip nat

# 创建 NAT 链
/usr/sbin/nft add chain ip nat SSNAT { type nat hook prerouting priority 0\; }
/usr/sbin/nft add chain ip nat POSTROUTING { type nat hook postrouting priority 100\; policy accept\; }

# 创建一个名为 `china` 的集合,添加 'flags interval' 以支持前缀
/usr/sbin/nft add set ip nat china { type ipv4_addr\; flags interval\; }

# 从 curl 获取 IP 地址并直接添加到集合中
{
    echo "add element ip nat china {"
    curl -s https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt | while read -r ip; do
        echo "$ip,"
    done
    echo "}"
} | /usr/sbin/nft -f -


# 符合国内IP列表的则直接转发出去
/usr/sbin/nft add rule ip nat SSNAT ip daddr @china return

# 局域网以及一些常见网段也选择直接转发
/usr/sbin/nft add rule ip nat SSNAT ip daddr 0.0.0.0/8 return
/usr/sbin/nft add rule ip nat SSNAT ip daddr 10.0.0.0/8 return
/usr/sbin/nft add rule ip nat SSNAT ip daddr 127.0.0.0/8 return
/usr/sbin/nft add rule ip nat SSNAT ip daddr 169.254.0.0/16 return
/usr/sbin/nft add rule ip nat SSNAT ip daddr 172.16.0.0/12 return
/usr/sbin/nft add rule ip nat SSNAT ip daddr 192.168.0.0/16 return
/usr/sbin/nft add rule ip nat SSNAT ip daddr 224.0.0.0/4 return

# 来源IP为192.168.11.100到192.168.11.254的IP也RETURN
/usr/sbin/nft add rule ip nat SSNAT ip saddr 192.168.11.128/25 return

# 不代理的特定IP
/usr/sbin/nft add rule ip nat SSNAT ip daddr 103.99.178.98 return

# 将剩下IP段(国外)的流量全部转发到gost的透明代理网口1080中
/usr/sbin/nft add rule ip nat SSNAT tcp dport "{ 1-65535 }" redirect to :1080

# 将出站数据包的源地址进行NAT避免被防火墙拦截
/usr/sbin/nft add rule ip nat POSTROUTING masquerade

将以上脚本取代 iptables.sh 文件,并设置开机执行

Bash
...
# For more information see the manual pages of crontab(5) and cron(8)
# 
# m h  dom mon dow   command
@reboot /bin/bash /root/nftables.sh

重启后检查规则是否正确:

Bash
nft list ruleset

5.2. CoreDNS

CoreDNS 是 Kubernetes 中默认的 DNS 解决方案,也是一个灵活且可扩展的 DNS 服务器,与 Overture 相比:

  1. 插件化设计:CoreDNS 的最大特点是其高度可扩展的插件架构,通过插件,CoreDNS 可以实现各种功能
  2. 灵活配置:CoreDNS 的配置文件(Corefile)非常灵活,用户可以根据需求动态调整和组合插件,以实现特定的 DNS 功能
  3. 性能:CoreDNS 具有高性能,能够处理大量的 DNS 请求,并且通过插件机制优化特定的操作路径

CoreDNS 适合更复杂的 DNS 配置需求,同时对性能的需求也更高,一般的 Arm 架构设备会较为吃力

所以,不建议内存小于4G的设备使用

仓库地址:

在仓库的 Releases 中可下载最新版本的二进制程序,假设目录为 /root/coredns,编写一个简单的配置文件

编辑: /root/coredns/Corefile

Text only
.:53 {
    loadbalance
    reload 30s
    log . "{remote} {name} {type} {proto} {duration}"
    forward . 114.114.114.114
}

运行程序

Bash
./coredns -conf Corefile

尝试解析国内地址

Bash
$ dig 163.com @127.0.0.1

; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> 163.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 725
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 7, ADDITIONAL: 9

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;163.com.                       IN      A

;; ANSWER SECTION:
163.com.                502     IN      A       59.111.160.244

;; AUTHORITY SECTION:
163.com.                502     IN      NS      ns2.166.com.
163.com.                502     IN      NS      ns1.nease.net.
163.com.                502     IN      NS      ns6.nease.net.
163.com.                502     IN      NS      ns8.166.com.
163.com.                502     IN      NS      ns4.nease.net.
163.com.                502     IN      NS      ns5.nease.net.
163.com.                502     IN      NS      ns3.nease.net.

;; ADDITIONAL SECTION:
ns8.166.com.            502     IN      A       18.182.82.158
ns2.166.com.            502     IN      A       103.71.201.3
ns3.nease.net.          502     IN      A       103.72.12.150
ns4.nease.net.          502     IN      A       103.72.12.151
ns5.nease.net.          502     IN      A       103.71.201.3
ns6.nease.net.          502     IN      A       54.228.156.72
ns1.nease.net.          502     IN      A       42.186.35.222
ns8.166.com.            502     IN      A       44.228.163.69

;; Query time: 40 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Wed Jul 24 16:11:06 CST 2024
;; MSG SIZE  rcvd: 319

Coredns 也可以实现国外域名走代理解析,国内域名走运营商解析,步骤如下:

  1. 生成适合 coredns 的大陆域名处理规则
  2. 设置大陆与国外的处理分流策略

编辑: /root/coredns/generate-zones.sh

Bash
#!/bin/bash

# 检查参数
if [ "$#" -ne 1 ]; then
    echo "Usage: $0 <forward_address>"
    exit 1
fi

FORWARD_ADDRESS=$1
URL="https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/accelerated-domains.china.conf"
OUTPUT_FILE="/root/coredns/chinazones"

# 创建输出文件(如果文件存在则清空内容)
> "$OUTPUT_FILE"

# 使用 curl 获取文件内容并处理
curl -s "$URL" | while read -r line; do
    # 从每一行中提取域名部分
    if [[ $line =~ ^server=/(.*)/.*$ ]]; then
        DOMAIN="${BASH_REMATCH[1]}"
        echo "forward $DOMAIN $FORWARD_ADDRESS" >> "$OUTPUT_FILE"
    fi
done

echo "zones file has been generated."

Coredns 生成 Zones 参考官方文档

执行脚本:

Bash
# 参数是代理解析国外域名的端口
$ bash /root/coredns/generate-zones.sh 127.0.0.1:5301

127.0.0.1:5301 是待会在Coredns配置文件中设置的国内域名解析服务器,检查并确保 /root/coredns/chinazons 的文件内容如下:

Text only
forward zvereff.com 127.0.0.1:5301
forward zynamics.com 127.0.0.1:5301
forward zyns.com 127.0.0.1:5301
forward zyxel.com 127.0.0.1:5301
forward zzcartoon.com 127.0.0.1:5301
...

接下来只需引入 zones 到 Coredns 即可

编辑: /root/coredns/Corefile

TEXT
.:53 {
    # 拒绝IPV6解析请求
    rewrite stop type AAAA A
    template ANY AAAA {
        # 设置响应码为 NOERROR
        rcode NOERROR
        # 设置权威信息,包含 SOA 记录
        authority "{{ .Zone }} 3600 {{ .Class }} SOA ns1.home.net. dnsadmin.home.net (2022122100 14400 3600 604800 30)"
    }

    # 启用负载均衡
    loadbalance

    # 每 30 秒重新加载配置
    reload 30s

    # 导入所有大陆域名的处理规则
    import /root/coredns/chinazones

    # 将剩下的所有DNS查询转发到本地的 5302 端口
    forward . 127.0.0.1:5302
}

# 大陆域名解析
.:5301 {
    # 将查询转发到大陆的 DNS 服务器,建议使用运营商的 DNS 解析器,享受 CDN 加速
    forward . 114.114.114.114 223.5.5.5 {
        # 使用顺序策略进行转发
        policy sequential
        # 优先使用 UDP 协议
        prefer_udp
        # 设置查询过期时间为 7200 秒
        expire 7200s
    }

    log . "{remote} -> {114.114.114.114/223.5.5.5} {name} {type} {proto} {duration}"

    # 启用缓存
    cache 
}

# 国外域名解析
.:5302 {
    forward . 127.0.0.1:1080 {
        # 优先使用 UDP 协议
        prefer_udp
        # 设置查询过期时间为 7200 秒
        expire 21600s
    }

    log . "{remote} -> {127.0.0.1:1080} {name} {type} {proto} {duration}"

    # 启用缓存
    cache 
}

分别访问 bilibili.com 和 doc.docker.com ,观察 coredns 日志,可以看到分流处理

TEXT
[INFO] 192.168.4.20 -> {114.114.114.114/223.5.5.5} www.bilibili.com. HTTPS udp 0.001724367s
[INFO] 192.168.4.20 -> {114.114.114.114/223.5.5.5} www.bilibili.com. A udp 0.014779333s
[INFO] 192.168.4.20 -> {114.114.114.114/223.5.5.5} a.w.bilicdn1.com. HTTPS udp 0.015117338s
...
[INFO] 192.168.4.20 -> {127.0.0.1:1080} docs.docker.com. HTTPS udp 0.00227847s
[INFO] 192.168.4.20 -> {127.0.0.1:1080} docs.docker.com. HTTPS udp 2.00356337s
[INFO] 192.168.4.20 -> {127.0.0.1:1080} docs.docker.com. HTTPS udp 4.00517469s
[INFO] 192.168.4.20 -> {127.0.0.1:1080} . NS udp 0.000151141s
[INFO] 192.168.4.20 -> {127.0.0.1:1080} . NS udp 0.000135951s

6. easydns

无论是 Overture 还是 Coredns 都不太合适我,我的需求比较少:

  • 根据域名列表将 DNS 请求分别转发到国内或国外的上游 DNS 服务器
  • 使用缓存机制缓存 DNS 响应
  • 支持 UDP 和 TCP 协议
  • 提供详细的 DNS 解析日志记录

基于以上需求,我使用了 Golang 编写了一个简易的 DNS 服务器:

只需下载 Releases 中的二进制文件,直接执行即可,如:

Bash
./easydns -d 114.114.114.114:53 -o 8.8.8.8:53 -f domestic-domain.txt -p 53 -l 4096

仓库中也包含了 domestic-domain.txt 文件的生成,功能简单代码量也很少,欢迎审阅

7. 尾语

以上内容较为复杂,还需耐心检查每一步的日志输出


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]