zfl9 / chinadns-ng

chinadns 重构增强版,支持域名分流、ipset/nftset、UDP/TCP/DoT
GNU Affero General Public License v3.0
1.09k stars 184 forks source link

请教redsocks和iptables搭配chinadns-ng进行DNS分流的食用方法 #129

Closed sunlewuyou closed 1 year ago

sunlewuyou commented 1 year ago

主要信息如下: redsocks.conf

base {
    log_debug = off;
    log_info = on;
    log = stderr;
    daemon = off;
    redirector = iptables;
    user = redsocks;
    group = redsocks;
}
redsocks {
    bind = "127.0.0.1:8888";
    relay = "127.0.0.1:1080";
    type = socks5;
}

iptables自定义规则链

iptables -t nat -N REDSOCKS 
iptables -t nat -A PREROUTING -p tcp -j REDSOCKS 
iptables -t nat -A REDSOCKS -d <server_ip> -j RETURN 
iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN 
iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN 
iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN 
iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN 
iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN 
iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-port 8888
iptables -t nat -A OUTPUT -p tcp -m owner ! --uid-owner redsocks -j REDSOCKS

经过上面的步骤后,已经可以正常实现tcp全局代理了。接下来就需要代理dns了。

我尝试如下操作:

[root@centos7 ~]# iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5353
[root@centos7 ~]# dns2tcp -L "127.0.0.1#5353" -R "8.8.8.8#53" -v
# ipv4 请求成功获取页面
[root@centos7 ~]# curl -4 www.google.com
2023-05-30 04:09:35 INF: [main] udp listen addr: 127.0.0.1#5353
2023-05-30 04:09:35 INF: [main] tcp remote addr: 8.8.8.8#53
2023-05-30 04:09:35 INF: [main] verbose mode, affect performance
2023-05-30 04:09:39 INF: [udp_recvmsg_cb] recv from 192.168.10.9#48471, nrecv:32
2023-05-30 04:09:39 INF: [udp_recvmsg_cb] try to connect to 8.8.8.8#53
2023-05-30 04:09:39 INF: [tcp_connect_cb] connect to 8.8.8.8#53 succeed
2023-05-30 04:09:39 INF: [tcp_sendmsg_cb] send to 8.8.8.8#53, nsend:34
2023-05-30 04:09:39 INF: [tcp_recvmsg_cb] recv from 8.8.8.8#53, nrecv:50
2023-05-30 04:09:39 INF: [tcp_recvmsg_cb] send to 192.168.10.9#48471, nsend:48

# ipv6 请求无响应
[root@centos7 ~]# curl -vvv www.google.com
* About to connect() to www.google.com port 80 (#0)
*   Trying 2607:f8b0:4007:817::2004...

2023-05-30 04:29:40 INF: [main] udp listen addr: 127.0.0.1#5353
2023-05-30 04:29:40 INF: [main] tcp remote addr: 8.8.8.8#53
2023-05-30 04:29:40 INF: [main] verbose mode, affect performance
2023-05-30 04:29:48 INF: [udp_recvmsg_cb] recv from 192.168.10.9#46396, nrecv:32
2023-05-30 04:29:48 INF: [udp_recvmsg_cb] try to connect to 8.8.8.8#53
2023-05-30 04:29:48 INF: [tcp_connect_cb] connect to 8.8.8.8#53 succeed
2023-05-30 04:29:48 INF: [tcp_sendmsg_cb] send to 8.8.8.8#53, nsend:34
2023-05-30 04:29:48 INF: [udp_recvmsg_cb] recv from 192.168.10.9#46396, nrecv:32
2023-05-30 04:29:48 INF: [udp_recvmsg_cb] try to connect to 8.8.8.8#53
2023-05-30 04:29:48 INF: [tcp_connect_cb] connect to 8.8.8.8#53 succeed
2023-05-30 04:29:48 INF: [tcp_sendmsg_cb] send to 8.8.8.8#53, nsend:34
2023-05-30 04:29:49 INF: [tcp_recvmsg_cb] recv from 8.8.8.8#53, nrecv:50
2023-05-30 04:29:49 INF: [tcp_recvmsg_cb] send to 192.168.10.9#46396, nsend:48
2023-05-30 04:29:49 INF: [tcp_recvmsg_cb] recv from 8.8.8.8#53, nrecv:62
2023-05-30 04:29:49 INF: [tcp_recvmsg_cb] send to 192.168.10.9#46396, nsend:60

如果要搭配chinadns-ng食用达到DNS分流的目的该如何做呢? @zfl9 请指点指点,在此先谢谢了~

sunlewuyou commented 1 year ago

之所以没有直接用ss-tproxy,第一个是想自己动手一步步摸清原理;第二个是在WSL2子系统部署脚本时发现其内核没有tproxy模块。

zfl9 commented 1 year ago

那你这边要代理 udp 吗?

如果要代理 udp(全局 udp 代理),就需要 tproxy 模块。如果只是代理 tcp,可以不用。


题外话:ss-tproxy 可以只代理 tcp,不需要 tproxy 模块。


如果想使用 chinadns-ng 配合 tcp 全局代理,进行分流,需要使用 tcp 访问上游,可以使用 dns2tcp,也可以使用 dnsproxy(DoH/DoT)。

  1. 修改 /etc/resolv.conf,将 nameserver 改为 127.0.0.1,指向 chinadns-ng
  2. chinadns-ng 监听 127.0.0.1:53
    • 国内上游,随便,假设为223.5.5.5,因为走udp,所以是走直连的。不用特殊处理
    • 国外上游,需要使用 dns2tcp 或 dnsproxy,假设为 dns2tcp,dns2tcp 上游设为 8.8.8.8,因为 tcp 全部都走代理了,所以访问 8.8.8.8:53/tcp 也是走代理
  3. 然后创建 ipset(chnroute数据库),判断是国内ip还是国外ip。导入项目根目录的ipset文件即可

此时,chinadns-ng 这边的国内外分流就完成了,-g gfwlist.txt -m chnlist.txt,再加上 chnroute,chnroute6 两个 ipset 集合(用于 tag:none 域名的判定)。

但是,这种情况下,也仅仅是完成了 DNS 分流,如果要真正可用,你还需要给 TCP 进行分流,也就是在 iptables 中放行国内 IP,可以使用 -m set --match-set chnroute dst -j RETURN 来完成。


最后提一句 ipv6,我看你说到 ipv6 无法代理,这是肯定的,因为 ipv6 用的是 ip6tables,iptables 只负责 ipv4 相关的。

sunlewuyou commented 1 year ago

@zfl9 感谢详细解答,又回去仔细查了ss-tproxy的文档,如果只使用ipt2socks,在WSL下又没有tproxy,iptables的配置应该怎么修改呢?我先问了下GPT,这是它给的回答:

iptables -t nat -N SSREDIR
iptables -t nat -A SSREDIR -j RETURN -m comment --comment "Don't redirect local traffic"
iptables -t nat -A SSREDIR -p tcp -m addrtype --dst-type LOCAL -j RETURN -m comment --comment "Don't redirect local traffic"
iptables -t nat -A SSREDIR -p udp -m addrtype --dst-type LOCAL -j RETURN -m comment --comment "Don't redirect local traffic"
iptables -t nat -A SSREDIR -p tcp -d MyIP --dport MyPort -j RETURN -m comment --comment "Don't redirect traffic sent to ss-server"
iptables -t nat -A SSREDIR -p udp -d MyIP --dport MyPort -j RETURN -m comment --comment "Don't redirect traffic sent to ss-server"
iptables -t nat -A SSREDIR -d 0.0.0.0/8 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 10.0.0.0/8 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 100.64.0.0/10 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 127.0.0.0/8 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 169.254.0.0/16 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 172.16.0.0/12 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 192.0.0.0/24 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 192.0.2.0/24 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 192.88.99.0/24 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 192.168.0.0/16 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 198.18.0.0/15 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables-t nat -A SSREDIR -d 198.51.100.0/24 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 203.0.113.0/24 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 224.0.0.0/4 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 240.0.0.0/4 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -d 255.255.255.255/32 -j RETURN -m comment --comment "Don't redirect reserved addresses"
iptables -t nat -A SSREDIR -p tcp -j DNAT --to-destination ProxyIP:ProxyPort
iptables -t nat -A OUTPUT -p tcp -m addrtype --src-type LOCAL ! --dst-type LOCAL -j SSREDIR
iptables -t nat -A PREROUTING -p tcp -m addrtype ! --src-type LOCAL ! --dst-type LOCAL -j SSREDIR
iptables -t nat -A PREROUTING -p tcp -m mark --mark 0x2333 -j DNAT --to-destination 127..0.1:60080
iptables -t nat -A PREROUTING -p udp -m mark --mark 0x2333 -j DNAT --to-destination 127.0.0.1:60080
zfl9 commented 1 year ago

只要不处理 UDP,有没有 TPROXY 模块都没关系。使用 redsocks/redsocks2/ipt2socks 都是可以的。


代理的分流,其实有两块内容:

DNS 分流会涉及到两组 DNS 上游,比如在 chinadns-ng 中,一组是国内DNS,一组是可信DNS(国外),访问国内DNS,请记得在 iptables 中放行,避免走代理。访问国外DNS,需要在 iptables 中确保其会走代理,避免被 gfw 干扰/污染。

另外,可以看下 ss-tproxy 的 README,加深这一块的理解。

zfl9 commented 1 year ago

关于规则这块,还是看 ss-tproxy 的代码来得快一些,就算我再讲下去,也还是重复 ss-tproxy 的内容 😂

sunlewuyou commented 1 year ago

@zfl9 谢谢,温故而知新,ss-tproxy 的 README很详细,无奈自己对这块不太熟才踩了好多坑,看到ipt2socks可以将所有tcp和udp的流量都转交给socks代理处理,还没有redsocks2这么麻烦,接下来用它做下测试,正好加强理解。 ipt2socks+iptables处理tcp和udp的流程是不是这样:将所有匹配的流量都重定向到ipt2socks处理后交给socks代理出去,即使client 端只支持 socks5 传入也是可以的。

zfl9 commented 1 year ago

redsocks/redsocks2/ipt2socks 只是负责接收 iptables 过来的 TCP/UDP 流量,然后转发给 socks5 代理服务器(比如 ss-local、trojan)。这个程序本身并不负责“分流”,只是单纯的“协议转换”。

分流由 iptables 规则来实现,所谓分流,就是将不需要走代理的流量“放行”(也就是 -j RETURN),将需要走代理的流量重定向至 redsocks/redsocks2/ipt2socks 的监听端口(-j REDIRECT),接入 socks5 代理。

上面说的分流,是指 IP 分流。除了 IP 分流,还需要 DNS 分流,这是因为我们平常上网,不是直接使用 IP,而是使用域名。

sunlewuyou commented 1 year ago

是的,我说的只使用ipt2socks是不考虑分流的情况,就只想做到全局透明代理的样子,用来创建某些编译环境时临时使用而已,不需要注重效率,简单就可以了。

zfl9 commented 1 year ago

如果只是想简单的全部走代理,那你一开始的那个例子就足够了。并且只需要dns2tcp,不需要chinadns

zfl9 commented 1 year ago

如果还想要ipv6,只需要简单复制ipv4的规则,将地址从v4改为v6就好了。

比如监听地址从127.0.0.1改为::1

sunlewuyou commented 1 year ago

嗯,是的,多尝试几种方法有助于理解,觉得使用ipt2socks更为简单。

sunlewuyou commented 1 year ago

@zfl9 奇怪,使用ipt2socks没有成功:

[root@centos7 ~]# nslookup cip.cc
;; connection timed out; no servers could be reached

[root@centos7 ~]# ipt2socks -v
2023-05-30 13:30:25 INF: [main] server address: 127.0.0.1#1080
2023-05-30 13:30:25 INF: [main] listen address: 127.0.0.1#60080
2023-05-30 13:30:25 INF: [main] listen address: ::1#60080
2023-05-30 13:30:25 INF: [main] udp cache maximum size: 256
2023-05-30 13:30:25 INF: [main] udp socket idle timeout: 60
2023-05-30 13:30:25 INF: [main] number of worker threads: 1
2023-05-30 13:30:25 INF: [main] enable tcp transparent proxy
2023-05-30 13:30:25 INF: [main] enable udp transparent proxy
2023-05-30 13:30:25 INF: [main] verbose mode (affect performance)
2023-05-30 13:30:29 INF: [udp_tproxy_recvmsg_cb] recv from 192.168.10.9#45756, nrecv:24
2023-05-30 13:30:29 INF: [udp_tproxy_recvmsg_cb] try to connect to 127.0.0.1#1080 ...
2023-05-30 13:30:29 INF: [udp_socks5_connect_cb] connect to 127.0.0.1#1080 succeeded
2023-05-30 13:30:29 INF: [udp_socks5_send_authreq_cb] send to 127.0.0.1#1080, nsend:3
2023-05-30 13:30:29 INF: [udp_socks5_recv_authresp_cb] recv from 127.0.0.1#1080, nrecv:2
2023-05-30 13:30:29 INF: [udp_socks5_recv_authresp_cb] send to 127.0.0.1#1080, nsend:10
2023-05-30 13:30:29 INF: [udp_socks5_recv_proxyresp_cb] recv from 127.0.0.1#1080, nrecv:10
2023-05-30 13:30:29 ERR: [udp_socks5_recv_proxyresp_cb] response->respcode:0x7(Command not supported) is not SUCCEEDED:0
2023-05-30 13:30:29 INF: [udp_socks5_context_timeout_cb] context will be released, reason: manual
2023-05-30 13:30:34 INF: [udp_tproxy_recvmsg_cb] recv from 192.168.10.9#45756, nrecv:24
2023-05-30 13:30:34 INF: [udp_tproxy_recvmsg_cb] try to connect to 127.0.0.1#1080 ...
2023-05-30 13:30:34 INF: [udp_socks5_connect_cb] connect to 127.0.0.1#1080 succeeded
2023-05-30 13:30:34 INF: [udp_socks5_send_authreq_cb] send to 127.0.0.1#1080, nsend:3
2023-05-30 13:30:34 INF: [udp_socks5_recv_authresp_cb] recv from 127.0.0.1#1080, nrecv:2
2023-05-30 13:30:34 INF: [udp_socks5_recv_authresp_cb] send to 127.0.0.1#1080, nsend:10
2023-05-30 13:30:34 INF: [udp_socks5_recv_proxyresp_cb] recv from 127.0.0.1#1080, nrecv:10
2023-05-30 13:30:34 ERR: [udp_socks5_recv_proxyresp_cb] response->respcode:0x7(Command not supported) is not SUCCEEDED:0
2023-05-30 13:30:34 INF: [udp_socks5_context_timeout_cb] context will be released, reason: manual
2023-05-30 13:30:39 INF: [udp_tproxy_recvmsg_cb] recv from 192.168.10.9#45756, nrecv:24
2023-05-30 13:30:39 INF: [udp_tproxy_recvmsg_cb] try to connect to 127.0.0.1#1080 ...
2023-05-30 13:30:39 INF: [udp_socks5_connect_cb] connect to 127.0.0.1#1080 succeeded
2023-05-30 13:30:39 INF: [udp_socks5_send_authreq_cb] send to 127.0.0.1#1080, nsend:3
2023-05-30 13:30:39 INF: [udp_socks5_recv_authresp_cb] recv from 127.0.0.1#1080, nrecv:2
2023-05-30 13:30:39 INF: [udp_socks5_recv_authresp_cb] send to 127.0.0.1#1080, nsend:10
2023-05-30 13:30:39 INF: [udp_socks5_recv_proxyresp_cb] recv from 127.0.0.1#1080, nrecv:10
2023-05-30 13:30:39 ERR: [udp_socks5_recv_proxyresp_cb] response->respcode:0x7(Command not supported) is not SUCCEEDED:0
2023-05-30 13:30:39 INF: [udp_socks5_context_timeout_cb] context will be released, reason: manual

按照 https://gist.github.com/zfl9/d52482118f38ce2c16195583dffc44d2 的脚本内容修改后来测试的。

zfl9 commented 1 year ago

上游不支持 udp 代理吧?用的是哪个程序的 socks5

sunlewuyou commented 1 year ago

@zfl9 我也怀疑是socks5客户端使用ipt2socks也还是不支持,是naiveproxy。可以对此客户端做下支持吗?

cattyhouse commented 1 year ago

https://github.com/klzgrad/naiveproxy/issues/425

zfl9 commented 1 year ago

@zfl9 我也怀疑是socks5客户端使用ipt2socks也还是不支持,是naiveproxy。可以对此客户端做下支持吗?

这个需要上游 socks5 支持。naive 目前不支持 socks5 的 udp 代理。

zfl9 commented 1 year ago

对于这种情况,你需要稍微修改下 iptables 规则,将 -p udp 的规则注释掉。也就是只代理 tcp。

由于 dns 查询默认走 udp 协议,你需要使用 dns2tcp -L 127.0.0.1#53 -R 8.8.8.8#53,转换为 tcp 查询。

然后 /etc/resolv.conf 改为 127.0.0.1,将 dns 交给 dns2tcp。

sunlewuyou commented 1 year ago

@zfl9 好的,谢谢悉心指导,没有打扰您午休吧,再次感谢!

zfl9 commented 1 year ago

那先关了,这个 issue。

sunlewuyou commented 1 year ago

好的,正准备自己关的。