在 CentOS 7 上部署 TProxy(透明代理)是一种强大且高效的网络流量管理技术,它能够在客户端无感知的情况下,将网络流量重定向到代理服务器,实现内容过滤、访问控制、数据缓存和安全审计等功能,与传统的代理方式不同,TProxy 最大的优势在于能够保留客户端的真实 IP 地址,这对于日志记录和基于 IP 的策略制定至关重要,本文将详细介绍如何在 CentOS 7 系统上,以 Squid 为代理软件,构建一个完整的 TProxy 环境。

核心原理与优势
TProxy 的工作原理并非基于网络地址转换(NAT),传统的 NAT 代理会修改数据包的源地址,导致后端服务器看到的全部是代理服务器的 IP,而 TProxy 通过 Linux 内核的 netfilter 框架,在 mangle 表的 PREROUTING 链上使用 TPROXY 目标,将指定的数据包“劫持”并发送到用户空间(即 Squid 进程)的套接字,Squid 处理完请求后,直接以原始客户端的 IP 地址作为源地址,将响应数据包发回给客户端,整个过程对客户端和目标服务器都是透明的。
其主要优势包括:
- 客户端零配置:无需在浏览器或操作系统中手动设置代理地址和端口。
- 保留真实 IP:后端服务器和 Squid 日志中记录的都是客户端的真实 IP,便于审计和策略控制。
- 强制流量管理:所有经过网关的指定流量都会被代理,无法绕过。
环境准备与依赖安装
在开始之前,请确保您拥有一台安装了 CentOS 7 的服务器,它将作为网关和代理服务器,假设其网络接口配置如下:
- 内网接口 (eth0): 192.168.1.1/24
- 外网接口 (eth1): 公网 IP 地址
更新系统并安装必要的软件包。
# 更新系统 yum update -y # 安装 Squid 代理软件和 iptables 服务 yum install -y squid iptables-services # 禁用 firewalld 并启用 iptables systemctl stop firewalld systemctl disable firewalld systemctl start iptables systemctl enable iptables
配置 Squid 以支持 TProxy
Squid 的配置是实现 TProxy 的核心,我们需要修改 /etc/squid/squid.conf 文件,建议先备份原文件。
cp /etc/squid/squid.conf /etc/squid/squid.conf.bak
清空或编辑 squid.conf,加入以下关键配置:
# 定义 TProxy 监听端口,关键在于 'tproxy' 标志 http_port 3129 tproxy # 定义访问控制列表 (ACL) acl localnet src 192.168.1.0/24 # 允许内网网段 acl SSL_ports port 443 acl Safe_ports port 80 # http acl Safe_ports port 21 # ftp acl Safe_ports port 443 # https acl CONNECT method CONNECT # 定义访问规则 http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow localnet http_access deny all # 关闭 forwarded_for,以保留客户端真实IP forwarded_for delete # 设置可见主机名 visible_hostname proxy.example.com
关键配置解释:
http_port 3129 tproxy:这行指令告诉 Squid 在 3129 端口上监听,并启用 TProxy 模式,所有被iptables重定向过来的流量都将由 Squid 在此端口处理。acl localnet src 192.168.1.0/24:定义了允许使用此代理的内网 IP 范围。forwarded_for delete:确保 Squid 不会在 HTTP 头中添加X-Forwarded-For字段,从而完全透明。
配置完成后,启动并设置 Squid 开机自启:
systemctl start squid systemctl enable squid
配置 iptables 与系统内核参数
这是实现流量拦截和重定向的关键步骤。
开启内核 IP 转发

编辑 /etc/sysctl.conf 文件,添加或修改以下行:
net.ipv4.ip_forward = 1
然后执行 sysctl -p 使配置立即生效。
设置 iptables 规则
我们将创建一个 iptables 脚本,以便管理和持久化规则。
vim /etc/tproxy_rules.sh
粘贴到脚本中:
#!/bin/bash # 清空 mangle 表的 PREROUTING 链 iptables -t mangle -F PREROUTING # 创建一个名为 DIVERT 的链,用于处理已经建立的连接 iptables -t mangle -N DIVERT # 将已经由 Squid 处理的连接(通过 socket 匹配)直接送往 DIVERT 链,避免重复处理 iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT # 在 DIVERT 链中,给数据包打上标记 1,并直接接受,让它正常通过 iptables -t mangle -A DIVERT -j MARK --set-mark 1 iptables -t mangle -A DIVERT -j ACCEPT # 拦截所有发往 80 (HTTP) 和 443 (HTTPS) 端口的 TCP 流量 # 将其重定向到 Squid 监听的 TProxy 端口 3129,并打上标记 1 iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129 iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 443 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129 echo "iptables TProxy rules loaded."
给脚本添加执行权限并运行:
chmod +x /etc/tproxy_rules.sh /etc/tproxy_rules.sh
配置策略路由
为了让 Squid 返回的流量能够正确地发回给客户端,而不是再次被 iptables 规则拦截,我们需要配置策略路由。
# 添加一条规则:凡是标记为 1 的数据包,都去查找路由表 100 ip rule add fwmark 1 lookup 100 # 在路由表 100 中,添加一条路由,将所有数据包都从本地回环接口 (lo) 发出 ip route add local 0.0.0.0/0 dev lo table 100
为了使这些规则在重启后依然生效,建议将它们添加到 /etc/rc.local 文件中。
echo "/etc/tproxy_rules.sh" >> /etc/rc.local echo "ip rule add fwmark 1 lookup 100" >> /etc/rc.local echo "ip route add local 0.0.0.0/0 dev lo table 100" >> /etc/rc.local chmod +x /etc/rc.local
保存 iptables 规则:

service iptables save
验证与测试
至此,TProxy 服务器已配置完成,将内网客户端的默认网关指向 TProxy 服务器的内网 IP(192.168.1.1),然后尝试访问一个网站。
在 TProxy 服务器上,实时监控 Squid 的访问日志:
tail -f /var/log/squid/access.log
如果日志中出现来自客户端真实 IP 的访问记录,并且客户端能够正常上网,则说明 TProxy 配置成功,日志格式应如下所示:
123 123 192.168.1.100 TCP_MISS/200 534 GET http://www.example.com/ - HIER_DIRECT/93.184.216.34 text/html
相关问答 FAQs
Q1:TProxy 和传统的 NAT 代理(使用 iptables -j REDIRECT)最主要的区别是什么?
A1: 最核心的区别在于对客户端真实 IP 的处理方式,NAT 代理通过修改数据包的源 IP 地址为代理服务器 IP 来实现转发,后端服务器无法获知客户端的真实 IP,而 TProxy 则是在内核层面“欺骗”应用层(Squid),让 Squid 以原始客户端的 IP 地址与目标服务器通信,从而完美保留了客户端的真实 IP,这使得基于源 IP 的访问控制、日志审计和用户行为分析变得非常精确。
Q2:为什么在配置 TProxy 时必须设置策略路由?不设置会怎么样?
A2: 策略路由是为了解决“路由环路”问题,如果不设置策略路由,当 Squid 处理完请求后,它会以客户端的 IP 作为源 IP、以服务器的 IP 作为目标 IP 来发送响应数据包,当这个数据包到达服务器的网络栈时,由于目标 IP 是外部地址,根据路由表,它会被发送到默认网关(即 eth1 接口),但 iptables 的 PREROUTING 规则会再次拦截这个数据包(因为它也符合从 eth0 进来的条件),并再次将其重定向到 Squid,形成一个无限循环,策略路由通过 fwmark 标记识别出这是 Squid 返回的流量,并指定其通过 lo(本地回环接口)直接发送回客户端,从而避免了环路。