Quick Start: Chạy TPROXY trong 5 phút
Trước khi đi vào lý thuyết, mình sẽ cho bạn có một transparent proxy chạy được ngay. Môi trường: Ubuntu 22.04, kernel 5.15+, Squid làm proxy daemon.
Bước 1: Cài Squid với hỗ trợ TPROXY
sudo apt update && sudo apt install squid
# Kiểm tra Squid có hỗ trợ TPROXY không
squid -v | grep -i netfilter
Output phải có --enable-linux-netfilter. Package mặc định trên Ubuntu đã include flag này rồi.
Bước 2: Cấu hình Squid lắng nghe TPROXY port
Thêm vào /etc/squid/squid.conf:
# Port cho TPROXY (khác với port proxy thường 3128)
http_port 3129 tproxy
# Tạm thời cho phép tất cả để test
http_access allow all
Bước 3: Cấu hình kernel routing + iptables
Tạo file /usr/local/bin/tproxy_setup.sh:
#!/bin/bash
# Tạo routing table riêng cho traffic có fwmark=1
ip rule add fwmark 1 lookup 100 2>/dev/null || true
ip route add local default dev lo table 100 2>/dev/null || true
# Load kernel module
modprobe xt_TPROXY
# Chain riêng để dễ quản lý
iptables -t mangle -N TPROXY_MARK 2>/dev/null || iptables -t mangle -F TPROXY_MARK
# Redirect TCP port 80 và 443 từ LAN về Squid
iptables -t mangle -A TPROXY_MARK -p tcp -m multiport --dports 80,443 \
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 3129
# Không redirect traffic từ proxy daemon (tránh loop)
iptables -t mangle -A PREROUTING -m owner --uid-owner proxy -j RETURN
# Áp dụng cho traffic từ LAN (thay subnet cho đúng môi trường)
iptables -t mangle -A PREROUTING -s 192.168.1.0/24 -j TPROXY_MARK
sudo chmod +x /usr/local/bin/tproxy_setup.sh
sudo bash /usr/local/bin/tproxy_setup.sh
sudo systemctl restart squid
Test ngay từ một client trong LAN — không cấu hình proxy trên browser — truy cập bất kỳ trang HTTP nào rồi kiểm tra:
sudo tail -f /var/log/squid/access.log
Nếu thấy request của client xuất hiện trong log, TPROXY đã hoạt động đúng.
Hiểu rõ tại sao cần TPROXY
Vấn đề với proxy truyền thống
Proxy thông thường có một điểm yếu cố hữu: phải cấu hình từng thiết bị. Công ty 200 máy tính — phải push setting qua GPO hoặc MDM. Smart TV, game console, thiết bị IoT — đa số không có tùy chọn proxy. Camera IP, máy in network — gần như không bao giờ hỗ trợ proxy.
Giải pháp nằm ở kernel level. Traffic bị chặn ngay tại gateway trước khi rời khỏi mạng nội bộ — client không cần biết, không cần cấu hình. Smart TV vẫn duyệt web bình thường, nhưng mọi request đều đi qua proxy của bạn.
Cách TPROXY khác với redirect NAT thông thường
Dùng NAT redirect thông thường (REDIRECT target), proxy nhận packet nhưng chỉ thấy địa chỉ của chính nó — thông tin về đích thật bị mất hoàn toàn. Giống như nhận thư mà không biết người gửi định giao cho ai.
TPROXY khác ở điểm mấu chốt: địa chỉ đích gốc được giữ nguyên khi deliver packet lên userspace. Proxy đọc đích thật qua socket option IP_TRANSPARENT, rồi kết nối ra internet với source IP của client. Server đích thấy request đến từ 192.168.1.100, không biết proxy tồn tại.
Flow xử lý packet đầy đủ
- Client gửi TCP SYN đến
93.184.216.34:80 - Packet vào interface
eth0, đi qua PREROUTING chain bảng mangle - Rule TPROXY match, đánh dấu packet với fwmark=1, chuyển đến port 3129
- Kernel tra cứu socket cục bộ — tìm thấy Squid đang lắng nghe trên port 3129
- Packet được deliver lên Squid, nhưng giữ nguyên địa chỉ đích gốc
- Squid đọc địa chỉ đích
93.184.216.34:80quagetsockname() - Squid mở connection ra internet, bind source IP thành
192.168.1.100(IP của client)
Cấu hình nâng cao
Xử lý UDP và DNS trong suốt
UDP không có handshake nên cơ chế khác TCP, nhưng TPROXY vẫn handle được. Một use case hay gặp là intercept DNS để filter hoặc ghi log toàn bộ query:
# Cần module xt_socket cho UDP transparency
modprobe xt_socket
# Thêm rule UDP DNS
iptables -t mangle -A TPROXY_MARK -p udp --dport 53 \
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 5353
Viết UDP proxy với Python cần set socket option đặc biệt:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Cho phép bind địa chỉ không thuộc về máy
sock.setsockopt(socket.SOL_IP, socket.IP_TRANSPARENT, 1)
# Nhận địa chỉ đích gốc của mỗi packet
sock.setsockopt(socket.SOL_IP, socket.IP_RECVORIGDSTADDR, 1)
sock.bind(('0.0.0.0', 5353))
Chuyển sang nftables (khuyến nghị cho kernel mới)
Từ Debian 11 và Ubuntu 22.04+, nftables là default. Syntax gọn hơn iptables nhiều, và kernel team đang ưu tiên phát triển nftables thay vì tiếp tục mở rộng iptables:
table ip tproxy_table {
chain prerouting {
type filter hook prerouting priority mangle; policy accept;
# Bỏ qua traffic từ proxy daemon
meta skuid proxy return
# Redirect TCP 80/443 từ LAN
ip saddr 192.168.1.0/24 tcp dport { 80, 443 } \
tproxy ip to 127.0.0.1:3129 meta mark set 0x1
}
chain divert {
type filter hook prerouting priority mangle;
# Traffic đã có socket established thì không cần TPROXY nữa
meta l4proto tcp socket transparent 1 meta mark set 0x1 accept
}
}
sudo nft -f /etc/nftables-tproxy.conf
# Kiểm tra
sudo nft list ruleset
SSL Bump để inspect HTTPS
Trong môi trường enterprise thường phải inspect cả HTTPS. Squid handle được với SSL bump — nhưng cần deploy CA certificate xuống toàn bộ client trước, không thì browser sẽ báo lỗi certificate:
# Tạo CA certificate trước
openssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 \
-keyout /etc/squid/ssl_cert/myCA.key \
-out /etc/squid/ssl_cert/myCA.pem
# squid.conf
https_port 3130 tproxy ssl-bump \
cert=/etc/squid/ssl_cert/myCA.pem \
key=/etc/squid/ssl_cert/myCA.key
# Chỉ peek SNI, không decrypt (ít xâm phạm hơn)
ssl_bump peek step1
ssl_bump splice all
Troubleshooting và tips vận hành
Debug khi traffic không bị redirect
Checklist mình dùng khi TPROXY không hoạt động:
# 1. Kiểm tra packet có vào rule không
iptables -t mangle -L TPROXY_MARK -v -n
# 2. Kiểm tra routing table 100
ip route show table 100
ip rule show
# 3. Kiểm tra Squid có bind đúng port không
ss -tlnp | grep 3129
# 4. Kiểm tra capability của process Squid
cat /proc/$(pgrep squid | head -1)/status | grep -i cap
Lỗi phổ biến nhất: Squid không có CAP_NET_ADMIN. Fix bằng cách thêm vào systemd service:
# /etc/systemd/system/squid.service.d/tproxy.conf
[Service]
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
# Reload và restart
sudo systemctl daemon-reload && sudo systemctl restart squid
Tính subnet khi setup cho nhiều VLAN
Khi cần thêm rule cho nhiều subnet cùng lúc, mình hay dùng toolcraft.app/vi/tools/developer/ip-subnet-calculator — nhập CIDR là ra ngay network range, broadcast, số host. Tiện hơn nhiều so với tính tay khi phải add rule cho từng VLAN.
Tối ưu performance với nhiều connection
# Tăng connection tracking table
sysctl -w net.netfilter.nf_conntrack_max=262144
# Tăng socket buffer
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
# Disable conntrack cho traffic đã được TPROXY xử lý (giảm overhead)
iptables -t raw -A PREROUTING -p tcp -m multiport --dports 80,443 \
-m mark --mark 0x1 -j NOTRACK
Auto-start sau reboot với systemd
# /etc/systemd/system/tproxy-setup.service
[Unit]
Description=TPROXY iptables setup
Before=squid.service
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/tproxy_setup.sh
ExecStop=/usr/local/bin/tproxy_cleanup.sh
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now tproxy-setup.service
Setup xong là hết việc. Mọi thiết bị trong LAN — smart TV, camera IP, thiết bị IoT — đều đi qua proxy trong suốt. Không cần động vào cấu hình từng máy.

