Netfilter/iptables/TPROXY

Netfilter/iptables/TPROXY

TPROXY 常用于透明代理,其作用在某种程度上类似“REDIRECT”。与 REDIRECT / DNAT 不同,TPROXY 透明地拦截(intercept) incoming 流量而不会改变其 destination address。

TPROXY 只适用于 -t mangle PREROUTING。

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.)

注:理论上,可以通过读取 netfliter conntrack tables 获取 REDIRECT / DNAT 前的原始 destination address,但这种方式非常不 robust 所以实际没有程序会这样做。

内核支持:

To use tproxy you'll need to have the following modules compiled for iptables:
 - NETFILTER_XT_MATCH_SOCKET
 - NETFILTER_XT_TARGET_TPROXY

Or the floowing modules for nf_tables:
 - NFT_SOCKET
 - NFT_TPROXY

TPROXY 透明代理

借助 TPROXY 实现透明代理:(以后示例来源于 kernel TPROXY 文档)

client <-> load balancer <-> server

透明代理:客户端没有意识到代理的存在,认为直接与服务器端通信。

  • client send IP package: (tcp, client ip, client port, server ip, server port)

以下均为 load balancer 上的配置

Kernel: /etc/sysctl.conf

net.ipv4.ip_forward=1
net.ipv4.ip_nonlocal_bind=1

注:

ip_nonlocal_bind 显然对于创建 non-local socket必须。需要 ip_forward 因为如果没有这个 kernel 会直接 drop 所有 dst ip 不是本机 IP 地址的数据包而不再经过 netfliter chains 处理.

sysctl -f

Making non-local sockets work

iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT

ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

Redirecting traffic (拦截 incoming TCP 请求)

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
  --tproxy-mark 0x1/0x1 --on-port 50080

load balancer 应用程序

fd = socket(AF_INET, SOCK_STREAM, 0);
/* - 8< -*/
int value = 1;
setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));
/* - 8< -*/
name.sin_family = AF_INET;
name.sin_port = htons(0xCAFE);
name.sin_addr.s_addr = htonl(0xDEADBEEF);
bind(fd, &name, sizeof(name));

说明

 IP_TRANSPARENT (since Linux 2.6.24)
              Setting this boolean option enables transparent proxying
              on this socket.  This socket option allows the calling
              application to bind to a nonlocal IP address and operate
              both as a client and a server with the foreign address as
              the local endpoint.  NOTE: this requires that routing be
              set up in a way that packets going to the foreign address
              are routed through the TProxy box (i.e., the system
              hosting the application that employs the IP_TRANSPARENT
              socket option).  Enabling this socket option requires
              superuser privileges (the CAP_NET_ADMIN capability).

              TProxy redirection with the iptables TPROXY target also
              requires that this option be set on the redirected socket.

注:

  • 以上 load balancer 应用程序代码为 load balancer 上接收 client 请求的 socket (监听非本地的 server IP)。对于代理,通常还需要还需要与真正的 server 之间建立通信,这个就是普通的 socket。
  • Making non-local sockets work 部分的 iptables 规则目的是拦截 client -> load balancer 的流量(匹配non-local sockets)然后由 lo 本地网卡发出。
  • Redirecting traffic 部分的 iptables TPROXY 规则将 client -> load balancer 的 流量透明地 REDIRECT 到应用程序监听的 (non-local) socket port(因为client发起连接时使用的 server IP/port 可能与 load balancer 里应用程序non-local socket 监听的IP/PORT 不同。如果相同,这个部分应该不需要)。
  • Making non-local sockets work 和 Redirecting traffic 两个部分里都设置了 mark。

TPROXY 透明反向代理

对于透明反向代理(Transparent Reverse Proxy),基本原理互通。

透明是指 server 收到的 IP 包的 source ip 是原始的 client ip。(所以,还可能需要配置 server 的路由表使到 client 的路由指向或经过 load balancer)。

HAProxy 通过 TPROXY 透明反向代理文档

  • 只需要 non-local sockets(用于接收server -> client 的 reply traffic)。不需要 iptables TPROXY 规则(因为反向代理的入口是代理自身的固定 IP)。
  • server 的路由表里到 client 的路由必须指向或经过反向代理。

Last update: 2022-08-18 01:51:40 UTC