Linux透明代理#

支持透明代理(Transparent proxy support)#

This feature adds Linux 2.2-like transparent proxy support to current kernels.To use it, enable the socket match and the TPROXY target in your kernel config.You will need policy routing too, so be sure to enable that as well.

该功能为当前内核增加了类似于 Linux 2.2 的透明代理支持。要使用此功能,您需要在内核配置中启用 socket 匹配(socket match)和 TPROXY 目标(TPROXY target)。同时,您还需要策略路由(policy routing),因此请务必也启用该功能。

From Linux 4.18 transparent proxy support is also available in nf_tables.

从 Linux 4.18 开始,nf_tables 也支持透明代理功能。

让非本地套接字工作(Making non-local sockets work)#

The idea is that you identify packets with destination address matching a local socket on your box, set the packet mark to a certain value::

其思路是,您先识别出那些目标地址与您本机上某个本地套接字相匹配的数据包,然后将这些数据包标记(packet mark)设置为某个特定值:

    ## 在 mangle 表中创建一个名为 DIVERT 的自定义链。
    # iptables -t mangle -N DIVERT
    
    ## 在 PREROUTING 链中追加一条规则:对于 TCP 协议且目标地址匹配本机透明代理 socket 的数据包,跳转到 DIVERT 链进行处理。
    # iptables -t mangle -A PREROUTING -p tcp -m socket --transparent -j DIVERT
    
    ## 在 DIVERT 链中追加一条规则:将这些数据包标记(fwmark)设置为 1。
    # iptables -t mangle -A DIVERT -j MARK --set-mark 1
    
    ## 在 DIVERT 链中追加一条规则:接受这些数据包,允许它们继续进入协议栈。
    # iptables -t mangle -A DIVERT -j ACCEPT

Alternatively you can do this in nft with the following commands::

或者,您可以通过以下命令在 nftables 中完成此操作:

    ## 创建一个名为 filter 的 nftables 表。
    # nft add table filter
    
    ## 在 filter 表中创建一个名为 divert 的链,该链挂载在 PREROUTING 钩子点上,优先级为 -150(mangle 优先级)。
    # nft add chain filter divert "{ type filter hook prerouting priority -150; }"
    
    ## 在 divert 链中添加一条规则:对于 TCP 协议且目标地址匹配本机透明代理 socket 的数据包,将其标记(fwmark)设置为 1,然后接受该数据包。
    # nft add rule filter divert meta l4proto tcp socket transparent 1 meta mark set 1 accept

And then match on that value using policy routing to have those packets delivered locally::

然后,使用策略路由匹配该标记值,以使这些数据包被本地交付:

    ## 添加一条策略路由规则:对于标记(fwmark)为 1 的数据包,查询路由表 100 进行路由决策。
    # ip rule add fwmark 1 lookup 100
    
    ## 在路由表 100 中添加一条本地路由:将所有目标地址(0.0.0.0/0)指向本地回环接口(lo),类型为 local。
    # ip route add local 0.0.0.0/0 dev lo table 100

Because of certain restrictions in the IPv4 routing output code you’ll have to modify your application to allow it to send datagrams from non-local IP addresses. All you have to do is enable the (SOL_IP, IP_TRANSPARENT) socket option before calling bind::

由于IPv4路由输出代码中存在某些限制,您必须修改应用程序,使其能够从非本地 IP 地址发送数据报。您只需在调用 bind 之前启用 (SOL_IP, IP_TRANSPARENT) 套接字选项即可:

    ## 创建一个 IPv4 的 TCP 套接字,返回文件描述符 fd。
    fd = socket(AF_INET, SOCK_STREAM, 0);
    
    ## 设置套接字选项:启用 IP_TRANSPARENT,允许该 socket 绑定非本地 IP 地址。
    /* - 8< -*/
    int value = 1;
    setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));
    
    ## 配置 sockaddr_in 结构体并绑定:
    ## 地址族:IPv4(AF_INET)
    ## 端口:0xCAFE(十进制 51966)
    ## IP 地址:0xDEADBEEF(点分十进制为 222.173.190.239)
    ## 最后调用 bind() 将该 socket 绑定到该非本地地址
    
    /* - 8< -*/
    name.sin_family = AF_INET;
    name.sin_port = htons(0xCAFE);
    name.sin_addr.s_addr = htonl(0xDEADBEEF);
    bind(fd, &name, sizeof(name));

A trivial patch for netcat is available here: http://people.netfilter.org/hidden/tproxy/netcat-ip_transparent-support.patch

这里提供了一个针对 netcat 的简单补丁:http://people.netfilter.org/hidden/tproxy/netcat-ip_transparent-support.patch

重定向流量(Redirecting traffic)#

Transparent proxying often involves “intercepting” traffic on a router. This is usually done with the iptables REDIRECT target; however, there are serious limitations of that method. One of the major issues is that it actually modifies the packets to change the destination address – which might not be acceptable in certain situations. (Think of proxying UDP for example: you won’t be able to find out the original destination address. Even in case of TCP getting the original destination address is racy.)

透明代理通常涉及在路由器上“拦截”流量。这通常使用 iptables 的 REDIRECT 目标来完成;然而,该方法存在严重的局限性。其中一个主要问题是,它实际上会修改数据包以改变目标地址——这在某些情况下可能是不可接受的。(以代理 UDP 为例:您将无法获知原始目标地址。即使在 TCP 的情况下,获取原始目标地址也存在竞态条件问题。)

The ‘TPROXY’ target provides similar functionality without relying on NAT. Simply add rules like this to the iptables ruleset above::

TPROXY目标提供了类似的功能,且不依赖 NAT。只需将如下规则添加到上述 iptables 规则集中即可:

    ## 在 mangle 表的 PREROUTING 链中追加一条规则:对于目标端口为 80 的 TCP 数据包,使用 TPROXY 目标进行处理,将其标记为 0x1/0x1,并重定向到本地 50080 端口。
    # iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
      --tproxy-mark 0x1/0x1 --on-port 50080

Or the following rule to nft::

或者在 nftables 中使用以下规则:

    ## 在 filter 表的 divert 链中添加一条规则:对于目标端口为 80 的 TCP 数据包,使用 TPROXY 将其重定向到本机的 50080 端口,同时将数据包标记(fwmark)设置为 1,最后接受该数据包。
    # nft add rule filter divert tcp dport 80 tproxy to :50080 meta mark set 1 accept

Note that for this to work you’ll have to modify the proxy to enable (SOL_IP,IP_TRANSPARENT) for the listening socket.

请注意,要使其正常工作,您必须修改代理程序,为监听套接字启用 (SOL_IP, IP_TRANSPARENT) 选项。

As an example implementation, tcprdr is available here:https://git.breakpoint.cc/cgit/fw/tcprdr.git/ This tool is written by Florian Westphal and it was used for testing during the nf_tables implementation.

作为一个示例实现,tcprdr 可以在以下地址获取:https://git.breakpoint.cc/cgit/fw/tcprdr.git/ 这个工具由 Florian Westphal 编写,在 nf_tables 实现期间被用于测试。

iptables和 nf_tables扩展(Iptables and nf_tables extensions)#

To use tproxy you’ll need to have the following modules compiled for iptables:

要使用 tproxy,您需要为 iptables 编译以下模块:

 - NETFILTER_XT_MATCH_SOCKET
 - NETFILTER_XT_TARGET_TPROXY

Or the floowing modules for nf_tables:

或者为 nf_tables 编译以下模块:

 - NFT_SOCKET
 - NFT_TPROXY

应用程序支持(Application support)#

Squid#

Squid 3.HEAD has support built-in. To use it, pass ‘–enable-linux-netfilter’ to configure and set the ’tproxy’ option on the HTTP listener you redirect traffic to with the TPROXY iptables target.

Squid 3.HEAD 版本内置了该支持。要使用它,请在配置时传递 –enable-linux-netfilter 参数,并在您通过 TPROXY iptables 目标将流量重定向到的 HTTP 监听器上设置 tproxy 选项。

For more information please consult the following page on the Squid wiki: http://wiki.squid-cache.org/Features/Tproxy4

更多信息请查阅 Squid Wiki 上的以下页面:http://wiki.squid-cache.org/Features/Tproxy4

https://github.com/torvalds/linux/blob/master/Documentation/networking/tproxy.rst