本文记录一次真实的内网穿透部署过程:用一台云服务器作为 frp 服务端,把家里 Windows 电脑的远程桌面(RDP)安全地暴露到公网。服务端与客户端均已实际部署、长期运行,文内所有踩坑均为实录。
一、为什么需要内网穿透?
家里电脑通常在路由器后面,只有运营商分配的内网 IP(如 192.168.x.x),公网无法直接访问。想在外部连回家里的电脑,常见方案有:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 路由器端口映射 | 零成本 | 需要公网 IP,多数家宽没有;且运营商可能封 80/443 |
| TeamViewer / 向日葵 | 零配置 | 免费版限速、限商业用途、需信任第三方 |
| Tailscale / ZeroTier | 组网优雅、P2P 直连 | 两端都要装客户端,手机端支持一般 |
| frp 自建 | 完全可控、带宽自建、不限速 | 需要一台有公网 IP 的服务器 |
frp(Fast Reverse Proxy)是一个高性能的反向代理工具,采用 Go 编写,单一二进制文件,部署极其简单。本次选择它,理由是:已有云服务器、需要完全掌控数据、不想受第三方限速。
二、架构设计

关键设计决策:
| 决策 | 选择 | 理由 |
|---|---|---|
| 服务端部署方式 | 宿主机 systemd(非 Docker) | frps 仅 20MB 二进制,省一层网络转换,资源占用更低 |
| RDP 对外端口 | 33390 |
避开默认 3389 的扫描;低于 49152(Windows 动态端口范围)不冲突 |
| 认证方式 | token | 简单可靠,够用 |
| 面板 | 仅监听 127.0.0.1 |
不占公网端口,通过 SSH 隧道访问,零暴露面 |
| 端口白名单 | 精确单端口 {33390} |
纵深防御,token 泄露时限制破坏面 |
三、前置条件
服务端(云服务器):
- 一台有公网 IP 的 Linux 服务器(本文以 Ubuntu 22.04 为例)
- 建议配置:1 核 1GB 起够用,frps 本身极轻量
- 能放行端口的防火墙(云厂商安全组 + 系统防火墙)
客户端(家里 Windows):
- Windows 10/11 专业版(家庭版默认不支持 RDP 被控)
- 远程桌面功能——别假设它是开着的,本文实机就是关的,见 5.2 节
四、服务端部署(frps)
4.1 放行防火墙端口
在云厂商控制台(安全组/防火墙)放行以下端口:
| 端口 | 用途 | 是否必须对公网 |
|---|---|---|
7000 |
frps 控制端口(frpc 连接用) | ✅ 是 |
33390 |
RDP 对外代理端口 | ✅ 是 |
💡 面板
7500不需要放行,它只监听127.0.0.1。
4.2 下载并安装 frp
# 下载最新版(本文使用 v0.61.2)
cd /tmp
wget https://github.com/fatedier/frp/releases/download/v0.61.2/frp_0.61.2_linux_amd64.tar.gz -O frp.tar.gz
# 解压
tar -xzf frp.tar.gz
# 安装到 /opt/frp
sudo mkdir -p /opt/frp /etc/frp
sudo cp frp_0.61.2_linux_amd64/frps /opt/frp/
sudo cp frp_0.61.2_linux_amd64/frpc /opt/frp/ # frpc 也留着,调试有用
sudo chmod +x /opt/frp/frps /opt/frp/frpc
# 验证
/opt/frp/frps --version
# 输出:0.61.2
4.3 生成安全凭证
# 生成 frp token(64 位十六进制)
openssl rand -hex 32
# 生成面板密码(32 位十六进制)
openssl rand -hex 16
把输出保存好,后面配置要用到。这两个值一旦泄露,等于把内网通道交给别人。
4.4 编写配置文件
创建 /etc/frp/frps.toml:
# ===== 基础绑定 =====
bindAddr = "0.0.0.0"
bindPort = 7000
# ===== 鉴权 =====
auth.method = "token"
auth.token = "在此填入你生成的64位token"
# ===== 传输优化(RDP 友好) =====
transport.tcpMux = true
transport.tcpMuxKeepaliveInterval = 30
transport.tcpKeepalive = 7200
# ===== 对外代理端口白名单(精确收紧,纵深防御) =====
allowPorts = [
{ single = 33390 }
]
# ===== Web 管理面板(仅本地监听,不暴露公网) =====
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "在此填入面板密码"
# ===== 日志 =====
log.to = "/var/log/frps.log"
log.level = "info"
log.maxDays = 14
# ===== 连接限制 =====
transport.maxPoolCount = 10
设置权限并验证语法:
sudo chmod 640 /etc/frp/frps.toml
sudo chown root:root /etc/frp/frps.toml
# 语法验证(很实用,能提前发现配置错误)
sudo /opt/frp/frps verify -c /etc/frp/frps.toml
# 输出:frps: the configuration file /etc/frp/frps.toml syntax is ok
关于 allowPorts:为什么不用端口范围?
很多人图省事写成 { start = 10000, end = 20000 },留一万多个端口备用。但这有安全隐患:
allowPorts 拦的是已经通过 token 认证的 frpc。正常情况下 frpc 只开配置里写的端口,白名单形同虚设;但一旦 token 泄露,攻击者就能连上来开任意端口做内网横向移动——一万端口等于送他一万个入口。
收紧成精确单端口后,即使 token 泄露,攻击者也只能开这一个端口。成本几乎为零(以后加代理改一次配置重启即可),收益明显。
4.5 创建 systemd 服务
创建 /etc/systemd/system/frps.service:
[Unit]
Description=frp server (frps)
After=network.target network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/opt/frp/frps -c /etc/frp/frps.toml
Restart=on-failure
RestartSec=5s
LimitNOFILE=1048576
# 安全加固
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log /etc/frp
[Install]
WantedBy=multi-user.target
启动并设置开机自启:
sudo systemctl daemon-reload
sudo systemctl enable --now frps
sudo systemctl status frps
看到 Active: active (running) 即成功。
4.6 验证服务端
# 检查端口监听
sudo ss -tlnp | grep -E "(7000|7500)"
# 期望:
# LISTEN ... 127.0.0.1:7500 ... (面板,仅本地)
# LISTEN ... *:7000 ... (控制端口,公网)
# 测试本地连通性
nc -zv 127.0.0.1 7000
nc -zv 127.0.0.1 7500
# 测试面板 API
curl -s -u admin:你的面板密码 http://127.0.0.1:7500/api/serverinfo
API 返回 JSON 且包含 "version":"0.61.2" 说明一切正常。
查看实际资源占用:
ps -o pid,rss,cmd -p $(pgrep frps)
实测:约 17~20 MB 内存,CPU 接近 0%。frps 极其轻量,1GB 内存的机器也毫无压力。
五、客户端部署(Windows 上的 frpc)
以下为在家用 Windows 电脑上实际执行过的完整流程,按顺序照做即可复现。全程使用「以管理员身份」打开的 PowerShell(Win → 输入 powershell → Ctrl+Shift+Enter)。
5.0 目录规划
C:\frp\
├── bin\frpc.exe ← 客户端主程序
├── nssm.exe ← 服务托管工具(后面用)
└── frpc.toml ← 配置文件
5.1 下载 frpc
cd C:\
Invoke-WebRequest -Uri "https://github.com/fatedier/frp/releases/download/v0.61.2/frp_0.61.2_windows_amd64.zip" -OutFile frp.zip
Expand-Archive frp.zip -DestinationPath C:\frp
Rename-Item C:\frp\frp_0.61.2_windows_amd64 C:\frp\bin
国内网络直连 GitHub 大概率超时,先挂好代理再下载(或用镜像源)。下载完确认 C:\frp\bin\frpc.exe 存在;包里的 frps.exe 是服务端程序,Windows 上用不上,可删。
5.2 第一步:先确认远程桌面功能本身是开着的
实机(Windows 10 专业版)远程桌面默认是关闭的:注册表 fDenyTSConnections = 1,3389 无监听。如果跳过这步直接往下配,frpc 隧道打通了也连不上——这是最靠后才发现、排查最烦的一类问题,所以放在最前面。
# 图形界面方式:设置 → 系统 → 远程桌面 → 启用(专业版)
# 或注册表方式:
Set-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -Value 0
# 同时开启 NLA(网络级别身份验证,强烈推荐)
Set-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name UserAuthentication -Value 1
注意:用注册表方式开启后,GUI 里”远程桌面”开关同步生效,但防火墙规则要自己确认;GUI 开关则会自动创建。验证方式:
Get-NetTCPConnection -LocalPort 3389 -State Listen有输出即监听成功。建议先确认 3389 通了,再进入改端口的步骤——一次只改一个变量,排错面小。
5.3 修改 Windows RDP 监听端口(可选但推荐)
⚠️ 务必在电脑本机操作,不要用远程桌面连着改——改端口会重启远程桌面服务,远程连接会立刻断开。
# 1. 修改注册表(3389 → 33390)
reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v PortNumber /t REG_DWORD /d 33390 /f
# 2. 验证
Get-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name PortNumber
# 应输出:PortNumber : 33390
# 3. 防火墙放行新端口(见第七节:更推荐收成 LocalOnly 版本)
New-NetFirewallRule -DisplayName "RDP-33390" -Direction Inbound -Protocol TCP -LocalPort 33390 -Action Allow
# 4. 删除旧规则(按系统语言,规则名可能不同)
Remove-NetFirewallRule -DisplayName "远程桌面(TCP-In)" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "Remote Desktop (TCP-In)" -ErrorAction SilentlyContinue
💥 实录大坑:改完端口执行
Restart-Service TermService -Force后,服务状态显示 Running,但新端口不监听(3389 也没了)。改端口后的 RDP 监听器只有在服务真正冷启动时才会用新值重建,原地重启服务大概率不生效。实测结论:改端口这类操作,做完注册表直接重启电脑最稳。重启后再netstat -ano | findstr 33390确认监听,再继续后面的步骤。
端口选择避坑:
| 端口 | 是否可用 | 原因 |
|---|---|---|
3389 |
❌ | 默认端口,扫描器必扫 |
3390/33890/13389/23389 |
⚠️ | 热门”替代端口”,扫描器同样会试 |
49152–65535 |
❌ | Windows 动态端口范围,系统出站会占用,会随机冲突 |
20000–49151 内的随机值 |
✅ | 推荐 |
老实说:改端口只挡盲扫,挡不了定向扫描。真正的主力防线是后面的强密码 + NLA。别把安全权重放反了。
5.4 编写 frpc.toml
在 C:\frp\frpc.toml:
# ===== 服务器连接信息 =====
serverAddr = "你的服务器公网IP"
serverPort = 7000
# ===== 鉴权(必须与服务端一致) =====
auth.method = "token"
auth.token = "服务端生成的64位token"
# ===== 传输优化 =====
transport.tcpMux = true
transport.tcpMuxKeepaliveInterval = 30
transport.heartbeatInterval = 30
transport.heartbeatTimeout = 90
log.to = "C:\\frp\\frpc.log"
log.level = "info"
log.maxDays = 7
# ===== 本地面板(可选,查看代理状态) =====
webServer.addr = "127.0.0.1"
webServer.port = 7400
webServer.user = "admin"
webServer.password = "本地面板密码"
# ===== RDP 代理 =====
# localPort 必须与 Windows 注册表改的端口一致
[[proxies]]
name = "rdp"
type = "tcp"
localIP = "127.0.0.1"
localPort = 33390
remotePort = 33390
5.5 首次手动测试
先别急着注册服务,手动跑一次确认能连上:
cd C:\frp
.\bin\frpc.exe -c frpc.toml
成功输出类似:
[I] [client/service.go:295] try to connect to server...
[I] [client/service.go:287] login to server success, get run id [xxxx]
[I] [proxy/proxy_manager.go:173] [xxxx] proxy added: [rdp]
[I] [client/control.go:170] [xxxx] [rdp] start proxy success
看到 start proxy success 就成功了,按 Ctrl+C 停止。
常见失败原因:
| 报错 | 原因 / 解决 |
|---|---|
connection refused |
服务器防火墙没放行 7000;或本地网络屏蔽了非标准端口 |
auth failed |
token 不匹配,重新核对 |
port not allowed |
服务端 allowPorts 没包含你的 remotePort |
connect to local 127.0.0.1:33390 failed |
Windows RDP 端口还没改好(或没重启),检查 5.2 / 5.3 步 |
5.6 注册为 Windows 服务(开机自启)
用 NSSM(Non-Sucking Service Manager)把 frpc 注册为服务,比计划任务稳定得多。下载:https://nssm.cc/release/nssm-2.24.zip,取 win64\nssm.exe 放到 C:\frp\。
cd C:\frp
# 注册服务
.\nssm.exe install frpc "C:\frp\bin\frpc.exe" "-c C:\frp\frpc.toml"
.\nssm.exe set frpc DisplayName "frp Client (RDP Tunnel)"
.\nssm.exe set frpc Start SERVICE_AUTO_START
# 日志轮转(防止日志无限增长)
.\nssm.exe set frpc AppStdout "C:\frp\frpc_stdout.log"
.\nssm.exe set frpc AppStderr "C:\frp\frpc_stderr.log"
.\nssm.exe set frpc AppRotateFiles 1
.\nssm.exe set frpc AppRotateBytes 1048576
# 启动
.\nssm.exe start frpc
.\nssm.exe status frpc # 应为 SERVICE_RUNNING
5.7 重启电脑 + 端到端验证(部署自检清单)
所有配置就绪后重启一次电脑(顺带解决 5.3 的端口重建问题),frpc 是自动启动,开机后无需任何操作。然后过一遍自检清单:
# ① 本机端口监听:RDP(33390) / frpc面板(7400)
Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -in 33390,7400 }
# ② frpc 服务状态 + 日志
Get-Service frpc # 应为 Running
Get-Content C:\frp\frpc.log -Tail 10
# 应看到:login to server success + [rdp] start proxy success
# ③ 端到端回环测试:在本机连"服务器的代理端口"
# 流量会走完整隧道(服务器 → frpc → 回到本机),能 TCP 握手成功即全链路通
Test-NetConnection 你的服务器公网IP -Port 33390
# ④ 浏览器打开本地面板看代理在线状态:http://127.0.0.1:7400
四关全过 = 链路就绪,接下来只剩安全加固(第七节)。
六、连接测试
在外部网络(手机热点、公司电脑)打开远程桌面连接:
电脑: 你的服务器公网IP:33390
用户名: Windows 登录用户名
密码: Windows 登录密码
提醒:RDP 走的也是 Windows 账户验证,同样受账户锁定策略约束(见第七节)——外部输错密码的次数和坐在电脑前输错是同一个计数器。
卡顿优化:在远程桌面客户端的”显示”设置里,把颜色深度降到 16 位、关闭桌面背景和字体平滑,能显著降低带宽占用。
七、安全加固(RDP 暴露公网必做)
按实际防护力排序(这点很重要,别把精力放错地方):
🥇 第一优先级:强密码 + NLA
# 开启 NLA(网络级别身份验证)
sysdm.cpl → 远程 → 勾选"仅允许运行使用网络级别身份验证的远程桌面的计算机连接(推荐)"
NLA 不会影响 frp 工作——frp 只转发 TCP 字节流,不解析 RDP 协议,NLA 握手是「客户端 ↔ Windows」端到端进行的。所有现代 RDP 客户端都支持 NLA。
密码要求:≥12 位,含大小写 + 数字 + 符号。
🥈 第二优先级:账户锁定策略(含两个实测坑)
路径:secpol.msc → 账户策略 → 账户锁定策略。三个参数的含义:锁定阈值(连续错几次锁)、锁定时间(锁多久)、锁定计数器重置(多久没输错就清零重算)。
坑一:两个工具显示的值可能对不上。全新工作站上 secpol.msc 显示阈值”0 次无效登录”(未配置),而 net accounts 显示阈值 10。以 net accounts 的输出为准判断当前生效值。
坑二(更重要):管理员账户默认豁免锁定!策略列表里有一项「允许管理员帐户锁定」,默认禁用——不启用它,你平时用来登录的管理员账户无论输错多少次都不会被锁,整套策略对最常见的账户形同虚设。必须手动改成「已启用」。
参数怎么选:网传”5 次锁 30 分钟”偏严——自己远程时输错几次就真被锁住干瞪眼;设 0(禁用)则等于给公网爆破开门。本文最终采用折中参数,把爆破限速到约 1.4 万次/天(无实用价值),而正常使用几乎无感:
# 管理员 PowerShell(secpol.msc 改也行)
net accounts /lockoutthreshold:50 # 50 次无效登录触发锁定
net accounts /lockoutduration:5 # 锁定 5 分钟自动解锁
net accounts /lockoutwindow:15 # 15 分钟无失败则计数清零
# "允许管理员帐户锁定" 在 secpol.msc 中改为已启用
🥈 补充:两条密码策略命令
# 1. 强制密码最小长度(防止以后改密码手滑设弱)
net accounts /minpwlen:12
# 2. 检查并修复账户的"需要密码"标志
# 用 net user 用户名 查看输出,若"需要密码"显示 No,
# 说明账户标志位允许空密码(隐患),执行:
net user 用户名 /passwordreq:yes
💡 想知道有没有人敲门:事件查看器 → Windows 日志 → 安全,筛选事件 ID 4625(登录失败),可看到每次失败的来源和账户名。
🥉 第三优先级:专用账户,禁用 Administrator 远程登录
# 新建专用远程账户
$pwd = ConvertTo-SecureString "你的强密码" -AsPlainText -Force
New-LocalUser -Name "remote" -Password $pwd
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "remote"
补充:Windows 防火墙限制来源 IP
既然只有 frpc 需要连 RDP,可以限制为仅允许本机访问:
Remove-NetFirewallRule -DisplayName "RDP-33390"
New-NetFirewallRule -DisplayName "RDP-33390-LocalOnly" -Direction Inbound -Protocol TCP -LocalPort 33390 -RemoteAddress 127.0.0.1 -Action Allow
这样连同一 WiFi 下的其他设备都摸不到你的 RDP。
安全措施真实性价比参考
| 措施 | 防护力 | 说明 |
|---|---|---|
| 强密码 + NLA | ★★★★★ | 挡住 99% 爆破,主力防线 |
| token 不泄露 | ★★★★★ | frp 根本,泄露即全线崩溃 |
| 云平台防火墙最小化 | ★★★★☆ | 第一道关口 |
| 防火墙限制来源 IP | ★★★☆☆ | 成本低收益高 |
| 账户锁定 + 管理员锁定启用 | ★★★☆☆ | 限速爆破;注意管理员默认豁免,必须手动启用 |
| 改非标端口 | ★★☆☆☆ | 防盲扫,防不了定向 |
| allowPorts 收紧 | ★★☆☆☆ | 仅 token 泄露时才有意义 |
八、日常运维
服务端(Linux)
sudo systemctl status frps
sudo systemctl restart frps
sudo tail -f /var/log/frps.log
# 通过 SSH 隧道访问面板(在你自己的电脑上执行)
ssh -L 7500:127.0.0.1:7500 ubuntu@你的服务器IP
# 然后浏览器打开 http://127.0.0.1:7500
客户端(Windows)
Get-Service frpc # 查看状态
Restart-Service frpc # 重启
Get-Content C:\frp\frpc.log -Wait -Tail 30 # 实时日志
# 卸载服务(如需重装)
cd C:\frp
.\nssm.exe stop frpc
.\nssm.exe remove frpc confirm
九、带宽与性能实测
frps 资源占用(实测):内存 17~20 MB;CPU 接近 0%;磁盘 <50 MB。
真正的瓶颈是带宽:frp 的所有流量都要经过服务器的公网带宽弯一下。无论家里宽带上行多快,外部访问速度上限 ≈ 服务器带宽。
| 场景 | 体验 |
|---|---|
| SSH / 命令行 | ✅ 流畅 |
| RDP 远程桌面(1080P) | ✅ 可用,建议降画质到 720P/16位色 |
| 大文件传输 | ⚠️ 受服务器带宽限制 |
| 视频串流 / 游戏 | ❌ 不适合 |
结论:frp 适合远程桌面和命令行这类低带宽场景。NAS 大文件传输或视频串流需要更高带宽的服务器,或改用支持 P2P 打洞的方案(如 Tailscale)。
十、排错清单
| 现象 | 排查方向 |
|---|---|
| frpc 连不上服务器 | 服务器防火墙 7000 没放行;本地网络屏蔽非标端口 |
auth failed |
token 前后不一致(注意别复制时带空格) |
port not allowed |
服务端 allowPorts 未包含该端口 |
| frpc 连上了但 RDP 连不上 | Windows RDP 功能没开(5.2);端口没改对;改端口后只 Restart-Service 没重启电脑(监听器不会用新端口重建) |
| RDP 能连但黑屏 | Windows 锁屏;NVIDIA 独显可能需要虚拟显示器驱动 |
| 重启后没自动连上 | Get-Service frpc 看状态;查 C:\frp\frpc_stdout.log |
| 连接频繁断开 | 检查 heartbeatInterval/tcpKeepalive;家用 NAT 超时可能需调小 |
| 锁定策略改了不生效 | ① 用 net accounts 看生效值(secpol 未配置时显示 0)② 管理员账户默认豁免锁定,需启用「允许管理员帐户锁定」 |
十一、总结
整个部署的核心步骤:
- 服务端:放行端口 → 装 frps → 写
frps.toml(token + 精确白名单 + 本地面板)→ systemd 托管 - 客户端:开 RDP + NLA → 改端口(改完重启电脑)→ 写
frpc.toml→ NSSM 注册服务 → 端到端自检 - 安全:强密码 + NLA + 锁定策略(记得启用管理员锁定)+ minpwlen=12 + 专用账户——这几项比改端口重要得多
- 验证:本机端口监听(33390/7400)→ frpc 日志
[rdp] start proxy success→ 回环测试 → 外部真连
资源开销几乎可以忽略(20MB 内存),真正的限制是服务器带宽。只要需求是远程桌面这类低带宽场景,这套方案稳定且完全自主可控。
本文基于 frp v0.61.2 实测部署整理,服务端与客户端流程均已实机全流程跑通。