透明代理,可以使得在代理后面的所有流量都经过代理,而发出流量的软件本身并不知道代理的存在。但网上大多配置教程都以iptables为主,由于本人用的是nftables,就尝试依葫芦画瓢搞了一个,同时也大概弄清除了其中的原理,供参考。
首先要准备一个透明代理客户端,如ss_redir,在PROXY_PORT进行监听。然后针对TCP和UDP设置不同的规则。
TCP部分
TCP是比较容易进行转发的,只需要在nftables里面进行设置forward就行了。
为了让配置文件看起来更直观,首先将需要绕过不走代理的ip(本地地址、远程代理服务器的地址等)放到一个文件里,这里以/etc/whitelist.ips为例子,文件结构如下所示。
PS:这里非常重要,必须配置对,否则会形成环路循环转发
/etc/whitelist.ips
1 | define whitelist = { |
注意这里不要出现有交集的ip区间,否则会报错。
然后开始编辑nftables的配置文件(使用nft
命令时,当priority为负值时会出错,遇到这种情况时需要直接编辑/etc/nftables.conf以后使用nft flush ruleset && nft -f /etc/nftables.conf
导入配置文件。
首先在配置文件开头插入这一句,导入白名单文件
1 | include "/etc/whitelist.ips" |
然后对应iptables的nat表,配置文件添加如下的一个nat表:
1 | table ip nat { |
上面的PROXY_PORT换成你实际的透明代理的端口,可以看到这样会将这台机器上产生的流量(由output链控制)和经过这台机器路由的流量(比如这台机器是路由器,由prerouting链控制)转发到本地的PROXY_PORT端口。
在linux中,代理程序可以通过调用一些内核提供的API来获取TCP包转发前的目的地址和端口,这样的话直接转发给透明代理就可以很好的处理。但是UDP不能这样做,内核并不允许获取原来的这些信息。所以对于UDP,我们就需要用到另外一种方法。
UDP部分
UDP的透明代理,需要借助一个内核模块,叫做TPROXY,他可以在不改变数据包的目的地址的情况下进行路由转发,nftables中可以调用这个模块,在很多iptables配置的文章中也已经介绍过,这里不再展开做介绍。
同样的,编辑/etc/nftables.conf,对应iptables中的mangle表,添加如下配置的一个表:
1 | table ip mangle{ |
上面我们做的,在mangle表中创建了两条链,output链的type为route,这里的route就是对应了iptables的mangle表,但是route type只能hook output点,所以prerouting链的type就变成了filter。
由于TPROXY只能在prerouting链处理,在nftables的流向图中(文后附),output链之后,还跟了一个reroute的流程,要触发这个reroute,我们就需要给数据包头部打上标签(任意一个数值,这里是0x233)。这样在reroute阶段,系统会检测到数据包的头部发生了改变,于是数据包被打到prerouting的地方重新进行路由。
而在prerouting阶段,被reroute的数据包和经过本机路由的数据包一起,如过目的地址不在白名单中,就tproxy打到透明代理去。整个过程非常清晰明了。
PS:在网上的许多教程中,都提到需要进行这样的两条操作
1 | ip rule add fwmark 0x233 lookup 100 |
其作用是将标记为0x233,并且目标为本地地址的数据包经过回环网口送出,但是经过本人实际测试,加和不加并没有什么区别,我认为即使不配置,默认情况系统也应该已经能够知道这条规则,所以并不需要添加。如果有不对的地方,希望读者不吝赐教。