FRP TCP 穿透下,利用 mmproxy 让 SSH 获取真实客户端 IP
FRP TCP 穿透下,利用 mmproxy 让 SSH 获取真实客户端 IP
在 FRP TCP 端口转发场景下,流量经过 frpc 代理后,后端服务看到的来源 IP 永远是 127.0.0.1,无法获取真实访问者 IP。
虽然 FRP 支持通过 PROXY protocol 透传真实 IP,但大量常规服务(如默认配置的 SSH 等)并不原生支持 PROXY protocol,直接开启会导致服务无法建立连接。
此时可以使用 mmproxy 做一层中转:识别 PROXY protocol 报文,还原真实源 IP,再将干净的普通 TCP 流量转发给后端服务,从而让不支持 PROXY protocol 的服务直接拿到真实客户端 IP。
mmproxy:解析 PROXY protocol,通过底层路由机制保留真实源 IP 的透明代理工具。
本文以 SSH 服务为例,实现 FRP 穿透后正常显示公网真实 IP。
前提条件
- FRP 服务端/客户端已正常工作
- frpc 已开启
proxy_protocol_version = v2
1. frpc 配置(关键)
frpc 不再直接转发到本地 22 端口,而是先转发到 2222 端口(交给 mmproxy 处理)。
# frpc.ini
[common]
server_addr = xxxx
server_port = 7000
tls_enable = false
user = xxxxxxxxxxxxxxxxxx
token = xxxxxxxxxxxxx
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 2222 # 流量先发给 mmproxy
remote_port = 47574
proxy_protocol_version = v2 # 必须开启 PROXY protocol v2
2. 安装 mmproxy(Rust 版本)
我使用更轻量、稳定的 Rust 实现:saiko-tech/mmproxy-rs
# 克隆代码(国内可替换加速地址) https://github.akams.cn/
git clone --depth 1 https://github.com/saiko-tech/mmproxy-rs
cd mmproxy-rs
# 静态编译(不依赖 libc,方便迁移)
cargo build --release --target x86_64-unknown-linux-musl
# 分发二进制文件
cp target/x86_64-unknown-linux-musl/release/mmproxy /usr/local/bin/
chmod +x /usr/local/bin/mmproxy
3. 系统底层配置(必须!)
mmproxy 要伪造源 IP,需要 CAP_NET_ADMIN 权限 + 一套本地策略路由,保证流量完全在本机回环,不会流出物理网卡。
3.1 添加能力权限
sudo setcap cap_net_admin+ep /usr/local/bin/mmproxy
3.2 配置策略路由
# 来源为 127.0.0.0/8、从 lo 进入的流量,使用路由表 123
sudo ip rule add from 127.0.0.0/8 iif lo table 123
# 路由表 123:所有流量强制走 lo 回环(保证不逃逸到外网)
sudo ip route add local 0.0.0.0/0 dev lo table 123
简单理解:
- mmproxy 发出的包源 IP 是公网真实 IP
- 系统默认会认为这是外网包,试图从网卡发出
- 这两条规则强制让它走本地回环 lo,到达本机 SSH 服务
4. mmproxy 启动命令
mmproxy -m 123 \ # 使用上面的路由表 123
-l 127.0.0.1:2222 \ # 监听 frpc 发来的 PROXY protocol 流量
-4 127.0.0.1:22 \ # 转发 IPv4 到本地 SSH
-6 [::1]:22 \ # 转发 IPv6 到本地 SSH
-p tcp
5. systemd 服务(推荐托管)
/etc/systemd/system/mmproxy.service
[Unit]
Description=mmproxy TCP Proxy Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/mmproxy -m 123 -l 127.0.0.1:2222 -4 127.0.0.1:22 -6 [::1]:22 -p tcp
Restart=on-failure
RestartSec=2
User=caddy # 已 setcap,无需 root 运行,更安全
[Install]
WantedBy=multi-user.target
启动并开机自启:
sudo systemctl daemon-reload
sudo systemctl enable --now mmproxy
sudo systemctl status mmproxy
6. 验证真实 IP 是否生效
通过 FRP 公网 IP 端口 SSH 登录,查看 SSH 日志:
journalctl -u ssh -f
成功效果:
Accepted publickey for foxer from 47.94.151.63 port 65482 ssh2:...
已显示真实公网 IP,而非 127.0.0.1。
7. 配合 sshguard 防爆力破解(Debian 13)
获取真实 IP 后,sshguard 才能正常封禁恶意 IP,否则只能封禁 127.0.0.1 毫无意义。
配置文件 /etc/sshguard/sshguard.conf:
#### REQUIRED ####
BACKEND="/usr/libexec/sshguard/sshg-fw-nft-sets"
LOGREADER="LANG=C journalctl -afb -p info -n1 -o cat SYSLOG_FACILITY=4 SYSLOG_FACILITY=10"
#### PARAMS ####
THRESHOLD=20 # 攻击积分超过封禁
BLOCK_TIME=180 # 首次封禁 3分钟
DETECTION_TIME=3600 # 1小时内累计记分
IPV4_SUBNET=26 # 封禁网段,防止批量攻击
#### LIST ####
WHITELIST_FILE=/etc/sshguard/whitelist
BLACKLIST_FILE=100:/etc/sshguard/blacklist
启动 sshguard:
sudo systemctl enable --now sshguard
总结
整套流程链路:
公网用户 → frps → frpc(带 PROXY protocol)→ mmproxy(解包、伪造源IP)→ SSH(显示真实IP)
优点:
- 无需修改 SSH/任何服务源码
- 完全透明,应用无感知
- 支持 TCP 任意服务(SSH、HTTP、RDP 等)
- 配合 sshguard/fail2ban 可正常封禁真实攻击 IP

浙公网安备 33010602011771号