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