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
posted @ 2026-04-17 16:22  nsfoxer  阅读(146)  评论(0)    收藏  举报