FRPS SSH 连接反复断开问题排查记录
FRPS SSH 连接反复断开问题排查记录
时间:2025-10-09 ~ 2025-10-10
服务:公网 FRPS 转发 SSH(Docker 部署)
背景与目标
- 为了在外出时也能远程控制公司电脑,几年前自建了一套 FRP 转发:公网服务器运行
frps
,公司内部电脑运行frpc
,通过端口6001
暴露 SSH。 - 方案长期稳定使用,最近一周登录总是失败,需要连续尝试很多次才能成功。
- 于是逐步排查,发现先是本地 SSH 认证受到影响,修复后又暴露出公网暴力扫描的问题。
- 目标:先恢复 SSH 登录的稳定性,再压制公网扫描带来的噪音与风险。
现象概述与调查思路
-
用户感知
- 近期外网通过
ssh -p 6001
连接公司主机时,经常连续失败,多次重试才勉强连上。
- 近期外网通过
-
FRPC 侧初检
- 在公司电脑中查看
docker logs frpc
,大量日志成对出现:
说明客户端与... start a new work connection ... ... join connections ... ... join connections closed
frps
建立了隧道,但宿主侧迅速关闭了本地连接。
- 在公司电脑中查看
-
FRPS 日志追踪
- 启用
log.to = "/tmp/frps.log"
后,tail -f tmp/frps.log
显示同一公网地址在数秒内反复发起get a user connection
,例如:
远端频繁试探,显然不是正常运维流量。2025-10-10 13:00:45.933 ... [ssh-office] get a user connection [196.251.72.***:52178] 2025-10-10 13:00:55.844 ... [ssh-office] get a user connection [193.32.162.***:43492]
- 启用
-
宿主 SSH 日志核实
- 切换到公司电脑中检查
sshd
服务的日志journalctl -u ssh -n 200
发现日志中中充斥着
源地址统一为Failed password for invalid user ... Connection closed by invalid user ...
172.25.0.3
(FRPC 容器的地址),确定攻击流量是通过 FRP 转发到宿主sshd
。
- 切换到公司电脑中检查
-
处理顺序
- 先调整 SSH 登录策略,使合法用户能稳定登录;随后针对仍在刷日志的陌生公网 IP 部署防护措施。
初始配置快照
FRPS (etc/frp/frps.toml
)
bindPort = 7000 # FRPS 控制面监听端口
auth.token = "***-redacted-token-***" # 与客户端共享的 Token
log.level = "trace" # 提升日志等级便于追踪
log.to = "/tmp/frps.log" # 将日志写入容器 /tmp,供 Fail2ban 使用
[transport]
tcpMux = false # 关闭多路复用,避免交互式会话复位
webServer.addr = "0.0.0.0" # Dashboard 监听地址
webServer.port = 7500 # Dashboard 端口
webServer.user = "***-masked-user-***" # Dashboard 账号(已脱敏)
webServer.password = "***-masked-***" # Dashboard 密码(已脱敏)
vhostHTTPPort = 80 # HTTP 虚拟主机端口
FRPC (etc/frp/frpc.toml
)
serverAddr = "***.***.***.***" # 公网 FRPS 地址(完全脱敏)
serverPort = 7000 # 对应 FRPS 控制端口
auth.token = "***-redacted-token-***" # 与服务器一致的 Token
[transport]
tcpMux = false # 关闭 TCP 多路复用
dialServerTimeout = 30 # 连接服务器超时时间
dialServerKeepalive = 7200 # WorkConn 保活
[[proxies]]
name = "ssh-office" # SSH 隧道名称
type = "tcp" # 使用 TCP 代理
localIp = "host.docker.internal" # 仍沿用 Docker 默认别名
localPort = 22 # 本地 SSH 服务端口
remotePort = 6001 # 公网映射端口(高风险点)
Docker Compose(问题发生前)
services:
frps:
restart: always
image: snowdreamtech/frps
container_name: frps
volumes:
- ./etc/frp/frps.toml:/etc/frp/frps.toml # 挂载 FRPS 配置
ports:
- 7000:7000 # 控制端口
- 6001:6001 # SSH 代理端口
排查步骤与关键发现
阶段一:先让合法用户稳定登陆
-
复现问题
- 从外网登录时,经常需要尝试 5~6 次才能成功,甚至成功后几秒就掉线。
- 查看
docker logs frpc
,几乎每次尝试都会出现start a new work connection → join connections → join connections closed
,说明frps
侧握手正常,是宿主sshd
主动断开。
-
排查宿主日志
journalctl -u ssh -n 200
:查看 SSH 服务最近 200 条记录,方便捕捉异常。- 输出中出现大量
Invalid user XXX
与pam_faillock
提示,用户名随机,源 IP 为 FRPC 所在的172.25.0.3
。可推断外部有人透过 FRP 尝试密码爆破。
-
加固 SSH 配置
- 修改
/etc/ssh/sshd_config
,核心调整:PasswordAuthentication no # 禁止密码登录,仅允许密钥 PermitRootLogin no # 禁止 root 账号远程登陆 AllowUsers safeuser # 只放行实际使用者(示例已脱敏)
- 执行
sudo systemctl restart sshd
重载配置;systemctl
会优雅重启服务,避免会话中断。 - 再次通过 FRP 隧道登录,确认凭公钥可以稳定连接,第一阶段问题解决。
- 同时核实
frpc
容器内的/etc/hosts
已包含host.docker.internal
映射,确保本地地址解析稳定。
- 修改
-
新的异常
- 登录恢复后,
tmp/frps.log
仍持续刷出连接—断开记录,而合法用户的会话并未受影响。说明表象问题虽解,但公网暴力扫描还在进行,需要进一步治理。
- 登录恢复后,
阶段二:定位并缓解公网暴力扫描
-
量化恶意流量
grep "get a user connection" tmp/frps.log | cut -d'[' -f4 | cut -d']' -f1 | sort | uniq -c | sort -nr | head
逐步解析:先筛出连接日志,再提取 IP,最后统计频次。结果发现193.32.162.*
、196.251.*.*
等地址在一分钟内尝试十几次。- 结合
whois
工具验证这些 IP 多来自海外托管网络,进一步确认是非授权访问。
-
部署 Fail2ban(第一轮)
- 自定义 Filter:
failregex = ^.*get a user connection \[(?P<host><HOST>):\d+\].*$
<HOST>
捕获源 IP,用于统计重复失败。 - 使用
fail2ban-regex tmp/frps.log config/fail2ban/filter.d/frps-proxy.conf
检验,确认 900+ 次命中,证明规则有效。 - Jail 参数设置
findtime = 120
、maxretry = 3
、bantime = 86400
,即 2 分钟内超过 3 次就封禁一天。
- 自定义 Filter:
-
解决 Docker 转发漏封
- 现实情况:Fail2ban 使用默认
iptables-multiport
时,只在宿主INPUT
链加规则;而 FRPS 容器的流量经过 Docker 的DOCKER-USER
→FORWARD
→ NAT,攻击包仍然被转发。 - 方案:编写自定义动作
iptables-docker.conf
,在启动时-I DOCKER-USER 1 -j f2b-frps
,把封禁链插在 Docker 用户链首位。 - 重新启动 Fail2ban 后执行
sudo iptables -L DOCKER-USER -n --line-numbers
,看到类似:
说明所有进入 Docker 的流量都会先经过 Fail2ban 封禁链。Chain DOCKER-USER (policy ACCEPT) num target prot opt source destination 1 f2b-frps all -- 0.0.0.0/0 0.0.0.0/0 2 RETURN all -- 0.0.0.0/0 0.0.0.0/0
- 再用
sudo iptables -L f2b-frps -n
查看链内容,可见每个被封 IP 都对应一条DROP
规则。此后tmp/frps.log
不再出现这些 IP 的新连接。
- 现实情况:Fail2ban 使用默认
最终配置摘要
SSH 服务关键配置(/etc/ssh/sshd_config
节选)
PasswordAuthentication no # 禁止密码登录,杜绝暴力破解成功可能
PermitRootLogin no # 禁止 root 直接登录,降低风险
AllowUsers safeuser # 仅允许授权账号(示例名已脱敏)
PubkeyAuthentication yes # 启用密钥认证
ClientAliveInterval 60 # 定期发送保活,保持会话稳定
Fail2ban Filter (config/fail2ban/filter.d/frps-proxy.conf
)
[Definition]
failregex = ^.*get a user connection \[(?P<host><HOST>):\d+\].*$ # 匹配 FRPS 用户连接日志并抓取源 IP
ignoreregex = # 暂无忽略规则,可按需追加
Fail2ban Jail (config/fail2ban/jail.d/frps.conf
)
[frps]
enabled = true # 启用该 jail
filter = frps-proxy # 引用上文 filter
logpath = /home/ubuntu/Workspace/WebService/frps/tmp/frps.log # FRPS 日志路径
port = 6001 # 关注的代理端口
maxretry = 3 # findtime 内最大容忍次数
findtime = 120 # 统计时间窗口(秒)
bantime = 86400 # 封禁时长(秒)
banaction = iptables-docker # 使用 Docker 专用封禁动作
自定义 Action (config/fail2ban/action.d/iptables-docker.conf
)
[INCLUDES]
before = iptables-common.conf
[Definition]
actionstart = <iptables> -N f2b-<name> # 创建自定义链
<iptables> -I DOCKER-USER 1 -j f2b-<name> # 在 DOCKER-USER 链首跳转
actionstop = <iptables> -D DOCKER-USER -j f2b-<name> # 停止时移除跳转
<iptables> -F f2b-<name> # 清空链规则
<iptables> -X f2b-<name> # 删除自定义链
actioncheck = <iptables> -C DOCKER-USER -j f2b-<name> # 检查链是否存在
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype> # 封禁 IP
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype> # 解封 IP
[Init]
blocktype = DROP # 封禁行为为丢弃
Docker Compose(调整后)
services:
frps:
restart: always
image: snowdreamtech/frps
container_name: frps
volumes:
- ./etc/frp/frps.toml:/etc/frp/frps.toml # 配置文件
- ./tmp/frps.log:/tmp/frps.log # 挂载日志方便 Fail2ban 读取
networks:
npm-default:
aliases:
- frps
ports:
- 7000:7000 # 控制端口
- 6001:6001 # SSH 代理端口
验证与结果
-
Fail2ban 状态
sudo fail2ban-client status frps
返回(样例脱敏):
Status for the jail: frps |- Filter | |- Currently failed: 0 | |- Total failed: 932 | `- File list: /home/.../frps/tmp/frps.log `- Actions |- Currently banned: 11 |- Total banned: 11 `- Banned IP list: 193.32.162.*** 196.251.*** 80.94.92.***
-
iptables 验证
sudo iptables -L DOCKER-USER -n --line-numbers
显示 Fail2ban 链位于链首,可拦截所有容器向内的流量。 -
FRPS 日志复查
连续 30 分钟未再出现已封禁 IP 的get a user connection
记录,仅保留健康连接日志。 -
SSH 可用性
多次从可信环境通过ssh -p 6001 user@***.***.***.***
登录,连接稳定;journalctl -u ssh
中仅剩合法用户成功登陆的记录。
后续建议
- 最小暴露面:考虑改用
stcp
/visitor
方案或在入口前加云防火墙,彻底隐藏固定端口。 - 密钥管理:已禁用密码登录,仅保留公钥;建议定期轮换密钥,限制跳板访问来源。
- Fail2ban 自动化:若需对子网累计计数,可扩展自定义 action 或引入
ipset
/crowdsec
等工具。 - 监控告警:结合
fail2ban
邮件/Webhook 通知,及时掌握异常封禁。 - 日志持久化:
tmp/frps.log
已挂载到宿主tmp/
,建议定期轮询并归档,或接入日志系统。
附录
常用命令
# 验证正则效果(确认 failregex 是否能命中日志)
fail2ban-regex tmp/frps.log config/fail2ban/filter.d/frps-proxy.conf
# 查看 Fail2ban 日志(实时跟踪封禁动作)
sudo tail -f /var/log/fail2ban.log
# 清理历史封禁记录(停服务后)
sudo iptables -F DOCKER-USER # 清空 DOCKER-USER 链
sudo iptables -A DOCKER-USER -j RETURN # 恢复默认返回规则
sudo rm /var/lib/fail2ban/fail2ban.sqlite3 # 删除 Fail2ban 历史数据库
# 检查 FRPS 日志中新连接频率(找出高频来源)
grep "get a user connection" tmp/frps.log | cut -d'[' -f4 | sort | uniq -c | sort -nr | head
关键日志摘录(脱敏)
Fail2ban 封禁恢复记录
2025-10-10 21:27:05,631 fail2ban.actions [1503039]: NOTICE [frps] Ban 80.94.92.62
2025-10-10 21:27:09,467 fail2ban.actions [1503039]: NOTICE [frps] Ban 196.251.115.189
2025-10-10 21:30:36,018 fail2ban.actions [1519768]: NOTICE [frps] Restore Ban 80.94.92.50
FRPS 攻击流量样例
2025-10-10 13:27:07.234 [I] [proxy/proxy.go:204] [0125a6e14fd416da] [ssh-office] get a user connection [196.251.114.***:35308]
2025-10-10 13:27:09.574 [I] [proxy/proxy.go:204] [0125a6e14fd416da] [ssh-office] get a user connection [92.118.39.***:59268]
2025-10-10 13:27:34.482 [I] [proxy/proxy.go:204] [0125a6e14fd416da] [ssh-office] get a user connection [193.32.162.***:55104]
SSH 登录失败片段
Oct 10 00:18:12 Workstation sshd-session[1474238]: Failed password for invalid user caishaobin from 172.25.0.3 port 43502 ssh2
Oct 10 00:18:13 Workstation sshd-session[1474304]: Failed password for invalid user trlian from 172.25.0.3 port 39242 ssh2
Oct 10 00:18:20 Workstation sshd-session[1474664]: Failed password for root from 172.25.0.3 port 39252 ssh2
结论:问题根因是公网暴力扫描通过 FRP 直达宿主 SSH,导致认证失败后立即断开合法连接。通过禁用密码登陆、自定义 Fail2ban Docker 封禁链、持久化日志等手段,可有效缓解攻击并恢复 SSH 稳定性。后续建议进一步缩小暴露面,例如改用 STCP 或额外的防火墙策略。***
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
翠南山!
喜欢就支持一下吧