作者:E4b9a6, 创建:2023-03-02, 字数:17230, 已阅:215, 最后更新:2023-03-02
多年来,Openwrt
作为旁路由方案非常完美的契合了我的需求,使用起来非常方便,加上OpenWrt可以自行编译,在保障了一定程度上的网络安全同时还有着数量相当广的插件来实现许多有意思的功能
随着使用时间变多,对插件的依赖逐渐变小,Openwrt
逐渐变得重起来了,而且在界面上去实现一些路由规则、Bash脚本有些隔靴搔痒,于是打算实现一个旁路由透明代理和分流的方案,为此查阅了一些资料,计划实现如下功能:
思路如下:
iptables
搭配ipset
实现国内外IP分流overture
解除国内的DNS污染下面将实践上述的方案,我的信息如下
最终,我希望局域网中的其他设备将网关地址和DNS设置为192.168.10.2
,将可以实现自动分流(国内直连,国外代理)
透明网关:即网关的存在对网络用户来说是透明的,它们无需对其进行特殊的配置或设置(例如手动设置代理),用户可以像访问任何其他网络设备一样,与透明网关进行通信,无需关心网关做了什么代理策略
原理如下图
此文建议有一定网络基础知识的同学实践,如果卡在一些简单的步骤说明可能不那么合适自定义透明网关
Alpine是一个非常迷你的操作系统,docker容器大小仅为5Mb,ISO镜像也仅有100+M,非常适合用来定制Linux透明网关
下载Alpine
下载使用任意虚拟机软件虚拟该系统,我的配置是 $2C4T/1024 RAM/1Gb Disk$
开启虚拟机后,输入root
用户登录,无需密码,然后输入setup-Alpine
开始安装系统
之后的安装过程与其他发行版安装无异,安装较为简单,此处不再赘述
在系统安装完成后,编辑文件:/etc/network/interfaces
内容参考如下如下
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.10.2
netmask 255.255.255.0
gateway 192.168.10.1
nameservers 114.114.114.114 # 暂时设置DNS为114.114.114.114
修改完成后重启网络,测试网络是否畅通
service networking restart
在确保网络正常后,设置允许机器转发局域网请求
编辑文件:/etc/sysctl.conf
添加如下字段到文件尾
net.ipv4.ip_forward=1
并更新sysctl配置
sysctl -p
在重启网络之后,为了加快软件下载,将软件源重定义到清华源
编辑文件:/etc/apk/repositories
#/media/cdrom/apks
https://mirrors.tuna.tsinghua.edu.cn/Alpine/v3.16/main
https://mirrors.tuna.tsinghua.edu.cn/Alpine/v3.16/community
https://mirrors.tuna.tsinghua.edu.cn/Alpine/edge/main
https://mirrors.tuna.tsinghua.edu.cn/Alpine/edge/community
https://mirrors.tuna.tsinghua.edu.cn/Alpine/edge/testing
然后更新apk仓库
apk update
接下来准备相关工具,安装以下软件,并初始化运行
apk add supervisor curl iptables vim wget ipset
service iptables save
service iptables start
service supervisord start
由于Alpine没有使用systemd来管理系统,需要借助crontab
手动添加以上部分软件的自启动
crontab -e
设置supervisord
跟iptables
服务在开机之后自启动
# do daily/weekly/monthly maintenance
# min hour day month weekday command
...
@reboot /sbin/service supervisord start
@reboot /sbin/service iptables start
到此,系统的初始化设置完毕
gost是一款使用golang编写的网络安全隧道工具,支持大部分主流协议,目前更新到V3.0版本,本文使用v2.11版本
仓库地址:https://github.com/ginuerzh/gost
在服务端下载gost
并使用gzip
解压
gzip -d gost-linux-amd64-2.11.2.gz
运行最简单的代理服务
./gost-linux-amd64-2.11.2 -L=ss://chacha20:password@:8338
回到我们本地的Alpine系统,一样先下载gost
并使用gzip
解压
gzip -d gost-linux-amd64-2.11.2.gz
下载完成后先尝试运行
./gost -L=:18080 -F="ss://chacha20:password@server_ip:8338"
测试代理是否正常
~$ export http_proxy=http://127.0.0.1:18080
~$ curl cip.cc
IP : 1.2.3.4
地址 : 中国 香港 keaiduo.com
数据二 : 香港 | 可爱多优先公司
数据三 : 中国香港 | 可爱多优先公司
URL : http://www.cip.cc/1.2.3.4
可以看到成功使用了服务端的IP
在确认直连测试ss连接是正常后,开启gost的透明网关模式(中继代理),如下
# 仅修改了监听协议为red
./gost -L=red://:28080 -F="ss://chacha20:password@server_ip:8338"
检查以上命令运行无报错输出后结束,写入到supervisor
中作为daemon程序运行
编辑文件:/etc/supervisor.d/gost.ini
[program:gost-18080]
command=/root/gost/gost -L=:18080 -F="ss://chacha20:password@server_ip:8338"
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
user=root
[program:gost-28080]
command=/root/gost/gost -L=red://:28080 -F="ss://chacha20:password@server_ip:8338"
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
user=root
上面的配置将运行2个监听,分别是代理监听18080
和中继转发监听28080
更新supervisor运行
supervisorctl update
检查运行状态
supervisorctl status
iptables是Linux中用来管理网络包的用户态工具,大部分操作都需要超级用户权限,其路径通常位于/sbin/iptables
iptables是由table、chain、rules组成的,用不同的表来处理不同类型的数据包,用链来处理不同时期的数据包,用规则来管理数据包的行为
要使用iptables,要掌握一定的基础网络知识,这里受限于篇幅,仅查阅了一些关于透明网关相关的iptables操作
iptables的基本构成是4表5链
此外还包含以下动作(即-j
参数值)
以iptables -A INPUT -p all -s 192.168.1.0/24 -j ACCEPT
为例
其中-A
表示append,-p
表示协议类型,-s
表示来源,-j
表示动作
由上可以看出iptables的基础命令形式
iptables -t 表名 <-A/I/D/R> 规则链名 [规则号] <-i/o 网络设备> -p 协议名 <-s 源IP/源子网> --sport 源端口 <-d 目标IP/目标子网> --dport 目标端口 -j 动作
相关参数解释
接下来,我们可以看看下面的例子,帮助理解iptables的命令行规则
创建与删除规则示例
# 允许192.168.1.0/24网段访问本机22端口
iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT
# 查看刚才创建的规则
iptables -L -n -v
# 删除“允许192.168.1.0/24网段访问本机22端口”规则
iptables -D INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT
iptables常见操作例子
# 允许访问本机22,80,443端口
iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPT
# 允许本机访问192.168.1.0/24网段的所有22端口
iptables -A OUTPUT -p tcp -d 192.168.1.0/24 --dport 22 -j ACCEPT
# 禁止访问192.168.1.1(注意协议类型为all)
iptables -A OUTPUT -p all -d 192.168.1.1 -j DROP
# 将本机端口12222的请求转发到22
iptables -t nat -A PREROUTING -p tcp --dport 12222 -j REDIRECT --to-port 22
# 限制本机端口80的并发数量最高为200,每分钟最高100个(应对DDOS攻击)
iptables -A INPUT -p tcp --dport 80 -m limit --limit 100/minute --limit-burst 200 -j ACCEPT
# 限制本机的ICMP请求
iptables -A INPUT -p icmp -j DROP
# 限制mac地址访问本机
iptables -A INPUT -m mac --mac-source 32:e6:37:5c:40 -j DROP
# 清除所有规则
iptables -F
下面是设置网关转发需要用到的NAT表操作
# 创建一个nat表,名为SSNAT
iptables -t nat -N SSNAT
# 为SSNAT表设置遇到192.168.1.1/20网段的目标IP网段则终止当前链返回上一个调用链
iptables -t nat -A SSNAT -d 192.168.1.1/20 -j RETURN
# 为SSNAT表设置将所有tcp请求转发到本机18080端口
iptables -t nat -A SSNAT -p tcp -j REDIRECT --to-port 18080
# 将SSNAT链中所有的规则追加到OUTPUT链中
iptables -t nat -A SSNAT -p tcp -j SHADOWSOCKS
# 查看所有NAT表规则
iptables -t nat -L -v -n
掌握了以上的iptables基础操作,就可以操作iptables转发来实现透明网关
iptables在进行地址集合查找时效率不高,可引入iptables的扩展插件ipset
来处理地址合集
要区分国内外ip,首先需要一份国内的IP网段
有了国内ip地址合集就可以实现针对国内外ip走不同代理策略(国外走代理,国内直连)
编辑文件:/root/iptables/iptables.sh
#/bin/bash
#author:Chancel.Yang
#date:2023/09/21
remote_ip=[your_server_ip]
local_port=[your_local_port]
/usr/sbin/ipset -N china hash:net
for i in $(cat ./china_ip_list.txt );
do
/usr/sbin/ipset -A china $i;
done
# 创建一个NAT规则集`SSNAT`
/sbin/iptables -t nat -N SSNAT
# 在`SSNAT`规则中添加无需走ss流量的国内ip以及局域网ip
/sbin/iptables -t nat -A SSNAT -p all -m set --match-set china dst -j RETURN
/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
/sbin/iptables -t nat -A SSNAT -d $remote_ip -j RETURN
# `SSNAT`的网络代理包括udp、icmp、udp(请按需)
/sbin/iptables -t nat -A SSNAT -p tcp -j REDIRECT --to-port $local_port
/sbin/iptables -t nat -A SSNAT -p udp -j REDIRECT --to-port $local_port
/sbin/iptables -t nat -A SSNAT -p icmp -j REDIRECT --to-port $local_port
# 将所有数据包转入`SSNAT`规则集合中
/sbin/iptables -t nat -A PREROUTING -p tcp -j SSNAT
# 将出站数据包的源地址进行NAT,否则在部分网络场景下会100%丢包
/sbin/iptables -t nat -I POSTROUTING -j MASQUERADE
脚本说明:
ipset
导入china_ip_list.txt文件并命名集合名为china
SSNAT
的nat链,在此链中设置国外走代理,国内直连的处理策略SSNAT
链中处理并进行NAT伪装运行上面的脚本缺少china_ip_list.txt
文件,china_ip_list.txt
文件在github.com可以下载,地址如下
wget https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt
脚本目录如下
Alpine:~# ls -l /root/iptables
total 13212
-rw-r--r-- 1 root root 1526 Aug 2 10:13 iptables.sh
-rw-r--r-- 1 root root 95316 Jul 27 11:51 china_ip_list.txt
执行脚本查看结果
sh /root/iptables/iptables.sh
没有错误输出的话,就可以将这个脚本设置为开机执行
crontab -e
设置开机执行
# do daily/weekly/monthly maintenance
# min hour day month weekday command
...
@reboot /bin/sh /root/iptables/iptables.sh
我的NAT表规则如下(可供参考)
chancel@j3455 ~$ sudo iptables -L -n -v -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
508 31917 SSNAT 6 -- * * 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
2283 183K MASQUERADE 0 -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN 0 -- docker0 * 0.0.0.0/0 0.0.0.0/0
Chain SSNAT (1 references)
pkts bytes target prot opt in out source destination
47 4192 RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/0 match-set china dst
0 0 RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/8
0 0 RETURN 0 -- * * 0.0.0.0/0 10.0.0.0/8
0 0 RETURN 0 -- * * 0.0.0.0/0 127.0.0.0/8
0 0 RETURN 0 -- * * 0.0.0.0/0 169.254.0.0/16
2 185 RETURN 0 -- * * 0.0.0.0/0 172.16.0.0/12
2 120 RETURN 0 -- * * 0.0.0.0/0 192.168.0.0/16
0 0 RETURN 0 -- * * 0.0.0.0/0 224.0.0.0/4
449 26940 REDIRECT 6 -- * * 0.0.0.0/0 0.0.0.0/0 redir ports 28080
在局域网内,找任意机器将网关设置成192.168.10.2
,此时应该可以正常访问国内网站
到上一步为止,可以访问国内网站,但仍然无法正常访问国外网站
即使SSNAT
内能根据国内外IP自动分流,由于DNS污染没有解决,google.com
等域名的ip是错误的,分流是无法正常生效的
注:DNS污染指通过国内DNS解析去解析国外的IP地址时,DNS服务器返回一个错误的IP(或者根本不存在的IP)
所以我们需要自建一个正常的DNS解析服务,overture
是一个基于Go语言开发的DNS解析服务程序
比起经典的ChinaDNS的优点是设置更加丰富,也更简单一些
安装方法如下
mkdir -p /root/overture && cd /root/overture
wget https://github.com/shawn1m/overture/releases/download/v1.8/overture-linux-amd64.zip
unzip overture-linux-amd64.zip
# 保留默认配置文件用于恢复(可选)
cp config.yml config.yml.bak
编辑文件:/root/overture/config.yml
bindAddress: :53
debugHTTPAddress: 127.0.0.1:55555
dohEnabled: false
primaryDNS:
- name: DNS114
address: 114.114.114.114:53
protocol: udp
socks5Address:
timeout: 6
ednsClientSubnet:
policy: disable
externalIP:
noCookie: true
- name: AliDNS
address: 223.5.5.5:53
protocol: udp
socks5Address:
timeout: 6
ednsClientSubnet:
policy: disable
externalIP:
noCookie: true
onlyPrimaryDNS: false
alternativeDNS:
- name: GoogleDNS
address: 8.8.8.8:53
protocol: tcp
# 使用一开始设置的`gost`直连代理来访问Google的DNS服务
socks5Address: 127.0.0.1:18080
timeout: 6
ednsClientSubnet:
policy: disable
externalIP:
noCookie: true
ipv6UseAlternativeDNS: false
alternativeDNSConcurrent: false
whenPrimaryDNSAnswerNoneUse: primaryDNS
ipNetworkFile:
# 如在`primary`2个DNS服务中匹配到`china_ip_list.txt`中的ip则直接返回
primary: /root/overture/china_ip_list.txt
alternative: /root/overture/ip_network_alternative_sample
domainFile:
primary: /root/overture/domain_primary_sample
# 如在`alternative`2个DNS服务中匹配到`gfw_all_domain.txt`中的域名则直接返回
alternative: /root/overture/gfw_all_domain.txt
matcher: full-map
hostsFile:
hostsFile: /etc/hosts
finder: full-map
minimumTTL: 0
domainTTLFile: /root/overture/domain_ttl_sample
cacheSize: 100
cacheRedisUrl:
cacheRedisConnectionPoolSize:
rejectQType:
- 255
配置文件解释如下:
ipNetworkFile
和domainFile
,一个确定匹配到国内ip段将直接返回,一个确定匹配到国外域名就直接返回配置中使用到的国内IP与国外域名来源:
由于china_ip_list.txt
与gfw_all_domain.txt
文件都是需要定期更新,所以写一个脚本来实现
编辑文件:/root/overture/ip_and_domain_update.sh
#/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
执行脚本,然后查看目录下是否包含了china_ip_list.txt
和gfw_all_domain.txt
cd /root/overture && bash ip_and_domain_update.sh
运行overture
./overture-linux-amd64 -c ./config.yml
确保执行结果中没有error
提示,然后用局域网内其他机器测试解析github.com的网址
➜ dig @192.168.10.2 www.github.com
; <<>> DiG 9.18.4 <<>> @192.168.10.2 www.github.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8772
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;www.github.com. IN A
;; ANSWER SECTION:
www.github.com. 1552 IN CNAME github.com.
github.com. 60 IN A 192.30.255.113
;; Query time: 243 msec
;; SERVER: 192.168.10.2#53(192.168.10.2) (UDP)
;; WHEN: Tue Jul 26 18:46:44 CST 2022
;; MSG SIZE rcvd: 107
最后使用supervisor将overture
设置为daemon程序运行
编辑文件:/etc/supervisor.d/overture.ini
[program:overture]
directory=/root/overture
command=/root/overture/overture-linux-amd64 -c /root/overture/config.yml
autostart=true
autorestart=true
user=root
更新supervisor运行
supervisorctl update
检查运行状态
supervisorctl status
最后修改Alpine
网络配置中dns服务地址
编辑文件:/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.10.2
netmask 255.255.255.0
gateway 192.168.10.1
nameservers 127.0.0.1
重启网络
service networking restart
将局域网其他设备设置为192.168.10.2
,并验证透明网关是否正确分流国内外ip
本文仅抛砖引玉,若对透明网关有兴趣还需要多多查阅资料
资料参考