作者:Chancel Yang, 更新:2022 Aug 02, 字数:15149, 已阅:381
一直以来都是用openwrt
作为旁路由来实现科学爱国的,使用起来十分爽快,只需要一个vm
或者docker
就可以欢快地跑起来
尽管openwrt
是自己编译lede大的仓库,插件也是非必需不选,但这个安全性仍然非常堪忧
于是打算自己实现一个简单的旁路由透明代理的分流方案,查阅了一些相关资料,计划实现如下
效果如下
这里假设我的网络是192.168.10.0/20
,路由是是192.168.10.1
,实现透明网关的Alpine虚拟机IP是192.168.10.100
最终希望局域网内其他设备将网关地址和DNS设置为192.168.10.100
实现设备的透明代理
以下是实践过程,过程是以proxmox新建虚拟机为例,docker方案略有差别,可以自行参考实现
alpine是一个非常迷你的操作系统,docker容器大小仅为5Mb,ISO镜像也仅有100+M,非常适合用来定制Linux透明网关
下载地址: alpinelinux下载,我下载的版本是3.16.1版本
下载后导入proxmox中,并创建虚拟机
我选择的虚拟机配置大小
开启虚拟机后,输入root
用户登录,无需密码,然后输入setup-alpine
开始安装
之后的安装过程与其他发行版安装无异,请自行安装
在系统安装完成后,使用root身份ssh远程进去,并修改网络设置,新增nameservers
vi /etc/network/interfaces
内容如下
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.10.100
netmask 255.255.255.0
gateway 192.168.10.1
nameservers 114.114.114.114
修改完成后重启网络
service networking restart
重启网络后,需要更新软件源,编辑/etc/apk/repositories
文件,取消community和edge的仓库注释
vi /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操作示例
# 搜索软件
apk search git
# 安装软件
apk add git
# 卸载软件
apk del git
接下来准备相关工具,安装以下软件,并初始化运行
apk add supervisor curl iptables iptables vim wget ipset
service iptables save
service iptables start
service supervisord start
由于alpine
没有使用较为先进的systemd
来管理系统,我们需要手动添加以上部分软件的自启动
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
允许机器转发局域网请求
vim /etc/sysctl.conf
添加如下字段
net.ipv4.ip_forward=1
更新sysctl配置
sysctl -p
到此,系统的初始化设置完毕
gost
是一款使用golang编写的网络安全隧道工具,支持大部分主流协议,目前更新到V3.0版本,本文使用v2.11版本
顺嘴提一下服务端的部署,服务端下载gost: gost v2.11.2下载
并使用gzip
解压
gzip -d gost-linux-amd64-2.11.2.gz
运行ss协议非常简单,如下
# 方式一:直接运行ss协议
./gost-linux-amd64-2.11.2 -L=ss://chacha20:password@:8338
# 方式二:运行ss+kcp协议
./gost-linux-amd64-2.11.2 -L=ss+kcp://chacha20:password@:8338
如果VPS的网络相对较差,可以考虑采用方式二来牺牲带宽降低网络延迟
下面以方式一运行为例子,方式二请自行修改部分配置以便适配
回到我们本地的alpine系统,先下载gost: gost v2.11.2下载
并使用gzip
解压
gzip -d gost-linux-amd64-2.11.2.gz
下载完成后先尝试运行
./gost -L=:18080 -F="ss://chacha20:password@server_ip:8338"
在本地使用连接192.168.10.100
的18080端口,测试网络是否正常,确保ss连接是成功的
在直连测试ss连接是正常后,我们测试一下开启gost的透明网关模式,如下
chmod +x gost-linux-amd64-2.11.2
./gost-linux-amd64-2.11.2 -L=red://:28080 -F="ss://chacha20:password@server_ip:8338"
检查以上命令运行无报错输出后结束,写入到supervisor
中作为daemon程序运行
mkdir /etc/supervisor.d
vim /etc/supervisor.d/gost.ini
输入内容如下,除了透明网关外,我们还需要保留socks5代理以方便后面DNS解析时使用
[program:gost-ss-direct]
command=/root/gost-linux-amd64-2.11.2 -L=:11080 -F="ss://chacha20:password@server_ip:8338"
autostart=true
autorestart=true
startsecs=10
user = root
[program:gost-ss-redir]
command=/root/gost-linux-amd64-2.11.2 -L=red://:28080 -F="ss://chacha20:password@server_ip:8338"
autostart=true
autorestart=true
startsecs=10
user = root
更新supervisor运行
supervisorctl update
检查运行状态
supervisorctl status
iptables是Linux中用来管理网络包的用户态工具,大部分操作都需要超级用户权限,其路径通常位于/sbin/iptables
iptables是由table、chain、rules组成的,用不同的表来处理不同类型的数据包,用链来处理不同时期的数据包,用规则来管理数据包的行为
要使用iptables,要掌握一定的基础网络知识,这里受限于篇幅,仅查阅了一些关于透明网关相关的iptables操作
iptables的基本构成是4表5链
注:还有一个不常见的security表
此外还包含以下动作(即-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网段,目前在github.com上已经有现成的 china_ip_list
按照我们要实现的透明网关目标做了以下的sh脚本,根据实际情况填写$your_server_ip
和$your_local_port
两个变量
vim /root/iptables.sh
内容如下
#/bin/bash
#author:Chancel.Yang
#date:2022-07-26
remote_ip=[your_server_ip]
local_port=[your_local_port]
# 允许使用转发服务的pc白名单
for mac in $(cat ./mac-whitelist.txt );
do
/sbin/iptables -A FORWARD -m mac --mac-source $mac -j ACCEPT
done
# 创建ipset的国内IP列表 -> https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt
/usr/sbin/ipset -N china hash:net
for i in $(cat ./china_ip_list.txt );
do
ipset -A china $i;
done
# Create nat rules
/sbin/iptables -t nat -N SSNAT
# 在这里添加无需走ss流量的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
# ss中继流量转发包括了udp、icmp、udp,看情况可以仅允许转发tcp流量
/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
# 全局规则
/sbin/iptables -t nat -A PREROUTING -p tcp -j SSNAT
/sbin/iptables -A FORWARD -j REJECT
以上脚本主要分为3部分
ipset
导入china_ip_list.txt文件并命名集合名为china
SSNAT
的nat链,排除掉本地地址与服务器地址的连接SSNAT
加入到OUTPUT
链中,同时允许SSNAT
转发局域网请求运行上面的脚本缺少china_ip_list.txt
文件,china_ip_list.txt
文件在github.com可以下载,地址如下
wget https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt
为了防止流量中转被滥用,脚本中还采用了mac
地址过滤的方法,创建一个mac-whitelist.txt
文件,其内容是允许使用转发服务PC的mac地址,如下
d2:d9:f8:1d:b4:2f
46:ba:63:03:8a:df
脚本目录如下
alpine:~# ls -l
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
-rw-r--r-- 1 root root 36 Aug 1 20:40 mac-whitelist.txt
执行脚本查看结果
sh /root/iptables.sh
没有错误输出的话,就可以将这个脚本设置为开机执行
crontab -e
# do daily/weekly/monthly maintenance
# min hour day month weekday command
...
@reboot /bin/sh /root/iptables.sh
到这一步,我们可以让局域网内其他机器将网关设置成192.168.10.100
来测试能否正常转发网络请求
因为DNS污染仍然没有解决,即便能转发请求,但仍然无法正常访问国外网站
注:DNS污染指通过国内DNS解析去解析国外的IP地址时,DNS服务器返回一个错误的IP(或者根本不存在的IP)
此时我们虽然能区分国内外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
编辑配置文件config.yaml
vim 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: CloudFlareDNS
address: 1.1.1.1:53
protocol: tcp
# 是否使用socks5代理来进行DNS查询(注意代理要支持protocol协议)
socks5Address: 127.0.0.1:11080
timeout: 6
ednsClientSubnet:
policy: disable
externalIP:
noCookie: true
- name: GoogleDNS
address: 8.8.4.4:53
protocol: tcp
# 是否使用socks5代理来进行DNS查询(注意代理要支持protocol协议)
socks5Address: 127.0.0.1:11080
timeout: 6
ednsClientSubnet:
policy: disable
externalIP:
noCookie: true
ipv6UseAlternativeDNS: false
alternativeDNSConcurrent: false
whenPrimaryDNSAnswerNoneUse: primaryDNS
ipNetworkFile: # 优先级匹配,如IP在primary文件中则直接返回primaryDNS结果,反之亦然,这里可以引入国内的IP段(如chnrouter列表),alternative默认即可
primary: /root/overture/china_ip_list.txt
alternative: /root/overture/ip_network_alternative_sample
domainFile: # 优先级匹配,如域名在primary文件中则直接返回primaryDNS结果,反之亦然,这里在alternative中引入gfwlist列表,primary默认即可
primary: /root/overture/domain_primary_sample
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
配置中比较重要的有3个点
protocol
采用tcp还是udp取决于gost配置ss时有没有配置能否支持udp转发china_ip_list.txt
与gfw_all_domain.txt
文件需要定期更新(短时间内不更新问题不大)下面是获取china_ip_list.txt
与gfw_all_domain.txt
文件的脚本
vim /root/overture/overture_file_download.sh
内容如下
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
运行脚本下载文件,然后执行overture程序
cd /root
sh /root/overture/overture_file_download.sh
./overture-linux-amd64 -c ./config.yml
如果输出中没有错误即可,用局域网内其他机器测试能否正常解析github.com的网址(可选)
➜ dig @192.168.10.100 www.github.com
; <<>> DiG 9.18.4 <<>> @192.168.10.100 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.100#53(192.168.10.100) (UDP)
;; WHEN: Tue Jul 26 18:46:44 CST 2022
;; MSG SIZE rcvd: 107
检查解析服务日志输出是否有异常以及解析结果是否正常
最后使用supervisor将overture
设置为daemon程序运行
vim /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
startsecs=10
user=root
更新supervisor运行
supervisorctl update
检查运行状态
supervisorctl status
最后修改alpine
网络配置中dns服务地址
vim /etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.10.100
netmask 255.255.240.0
gateway 192.168.2.1
nameservers 127.0.0.1
重启网络
service networking restart
现在可以将局域网其他设备设置为192.168.10.100
验证透明网关能否正常工作了
由于透明网关涉及到的知识实在过于复杂了,不太适合新手折腾,而且总体来说效果不如openwrt
来的方便
本文仅抛砖引玉,若对透明网关有兴趣还需要多多查阅资料
资料参考