概述

对于我们写代码的同学来说,代理是一个再熟悉不过的名词了,但是,经常会很疑惑,有时我们需要设置代理,例如使用 Go 的时候,我们经常要这么搞:

  1. [root@liqiang.io]# HTTP_PROXY=127.0.0.1:1080 go get github.com/liuliqiang/log4go

但是,有时我们访问 google.com 的时候,却可以在浏览器里面直接访问就可以了,这到底有啥区别。这其实就是所谓的透明代理了,所谓的透明代理,简单来说就是应用程序无需知道是否存在代理服务器,例如我们常用的“基于规则的全局代理”就可以大概认为是一种透明代理,因为我们访问网络时,我们使用的应用程序例如 Chrome 是不知道是否有代理的。

既然有透明代理,那么就有不透明代理,例如你可以尝试设置一个不存在的 HTTP_PROXY 变量,然后用 Go 写一个 http client 程序,你会发现你的 http client 程序无法正常工作,那是因为 Go 的默认 HTTP Client 库是会关注你的环境变量,是否包含 HTTP_PROXY,如果有,它会主动设置为你的 http client 的代理,所以你设置一个错误的代理会导致访问不同,这是一个经常在本地开发时会遇到的问题。

TProxy

在 Linux 下,内核从 2.2 版本就开始支持透明代理了,这个特性叫 TProxy,也是基于 Netfilter 的,常见的工具有

Tproxy 的用处

不要将 TProxy 的使用局限于我们简单的日常的一些简单的翻墙需求,在工业中,其实 TProxy 也是有应用的,这里介绍几个场景。

接收不属于本机的数据包

在之前讲 iptables(iptables 深度详解) 时,我介绍过一个数据报进行网络栈后的处理流程是这样的:

图 1:iptables 数据流向图

在经过 PreRouting Filter 之后就会进行一个目标地址判断,看是否是本地的数据报,如果不是,就转发出去了,本地的应用程序则无法获知,那通过 tproxy 呢,我们就可以做一些手脚,将数据报也能够发送到我们本地的应用程序中,具体的操作为:

  1. [root@liqiang.io]# iptables -t mangle -N DIVERT
  2. [root@liqiang.io]# iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
  3. [root@liqiang.io]# iptables -t mangle -A DIVERT -j MARK --set-mark 1
  4. [root@liqiang.io]# iptables -t mangle -A DIVERT -j TPROXY --on-port 1080
  5. [root@liqiang.io]#
  6. [root@liqiang.io]# ip rule add fwmark 1 lookup 100
  7. [root@liqiang.io]# ip route add local 0.0.0.0/0 dev lo table 100
  8. [root@liqiang.io]#

ok,可以看到这里的操作过程:

  1. 添加一个 iptables 的自定义链(我之前的介绍文章
  2. 如果是 socket 就发送到自定义链中
  3. 给所有进入自定义链的数据包都打上标记
  4. 接受数据报
  5. 将所有带标记 1 的数据包都去查找 ID 为 100 的这张表
  6. 将数据包路由到本地

至此,本地的应用程序就可能接收到这个数据包了,但是要确保应用程序可以收到,还是需要特别地设置一下 socket 的选项:

  1. [root@liqiang.io]# cat main.c
  2. fd = socket(AF_INET, SOCK_STREAM, 0);
  3. int value = 1;
  4. setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));
  5. name.sin_family = AF_INET;
  6. name.sin_port = htons(0x1080);
  7. name.sin_addr.s_addr = htonl(0xDEADBEEF);
  8. bind(fd, &name, sizeof(name));

ok,那么这个有什么用呢?这是一个很容易理解的透明代理的功能,如果你想实现基于端口的透明代理的话,那么你就可以使用这一套了,例如实现 Istio 类似的功能,具体怎么实现,就看你的应用程序部分的代码怎么写了。

流量重定向

这是个类似 DNAT 的功能,效果是这样的,我有三台机器:

现在我想要通过 10.0.0.3 访问 10.0.0.4:80,但是实际上,应用程序是工作在 50080 端口的,那么可以这么操作:

  1. [root@liqiang.io]# iptables -t mangle -A PREROUTING -p tcp --dport 50080 -j TPROXY \
  2. --tproxy-mark 0x1/0x1 --on-port 80

应用程序直接支持

这个就是应用程序的特点了,例如 SquidEnvoy 都是支持的。

神秘应用

这个就不介绍了,直接看别人的文章吧:透明代理:TPROXY

Ref