脱敏说明
本文中的 IP、端口、账号、密码、Token 均为占位示例,实际使用时请替换为你自己的值。

一、业务场景

手上有一台带公网 IP 的云服务器,以及若干台没有公网 IP、只能在内网访问的机器,需要把内网里的服务暴露到公网。

装备情况:

  • 一台云服务器(示例为 Windows Server 2012),带公网 IP;
  • 三台内网服务器,其中两台 Windows Server,一台 CentOS;
  • 一个一级域名,已解析到该公网 IP。

需求:

  • 通过域名或 IP 访问三台内网机器上部署的 http 服务;
  • 通过域名或 IP 连接这三台机器的远程桌面(RDP)或 SSH。

二、为什么选择 frp

  • 开源,维护频繁;
  • 支持 TCP、UDP、HTTP、HTTPS、STCP 等协议;
  • Go 语言编写,占用内存小,代理稳定;
  • 配置简单,支持自定义插件,也方便按业务需求二次开发;
  • 提供安全暴露内网服务、加密与压缩、底层通信可选 kcp、端口复用、负载均衡、健康检查、URL 路由、范围端口映射、请求 header 处理等能力。

frp 还提供了一个浏览器端的状态与统计面板,即代理服务器上的可视化仪表盘。

仪表盘总览(查看各代理状态、代理统计信息):

frp dashboard

在线改配置(查看各穿透状态、动态在线配置、热更新配置):

frp webServer

三、服务端 frps 配置

1. 开放云服务器的端口

登录云服务平台,进入实例安全组,开放穿透代理需要的端口。示例规划如下:

协议 端口 用途
TCP 7000 frp 服务端默认通信端口
TCP 7001 - 7010 代理内网 TCP 应用,端口不够用时可继续往后延伸
TCP 7080 / 7443 HTTP 与 HTTPS 代理。云服务器的 80、443 已被其他应用占用,取带 80、443 后缀的名字方便记忆
UDP 7001 如需点对点内网穿透可开启
TCP 7500 frp 仪表盘(可选)

以上规划并非硬性要求,可按需自定义。

2. 下载 frp

进入 frp Releases,下载对应云服务器操作系统与架构的压缩包。

3. 配置 frps

解压后,服务端只需要 frps.toml;frpc 开头的文件是给内网机器用的,服务端用不到。

frps.toml 示例:

[common]
# 监听地址及通信端口
bind_addr = 0.0.0.0
bind_port = 7000

# 默认的 UDP 端口
bind_udp_port = 7001

# HTTP / HTTPS 代理端口,两者可以设成同一个端口
vhost_http_port = 7080
vhost_https_port = 7443

# 仪表盘监听地址与端口。dashboard_addr 不设置时默认与 bind_addr 相同;
# dashboard_port 不设置则不开启仪表盘服务。
dashboard_addr = 0.0.0.0
dashboard_port = 7500

# 仪表盘登录账号与密码,不设置时默认都是 admin,务必修改
dashboard_user = <your_dashboard_user>
dashboard_pwd = <your_dashboard_password>

# 云服务器使用的域名,方便用子域名方式访问各个被代理出去的服务
subdomain_host = example.com

# 鉴权 Token,建议用足够长的随机字符串,frpc 侧需保持一致
token = <your_frp_token>

# 日志
log_file = ./logs/frps.log
# 日志级别:trace / debug / info / warn / error
log_level = info
# 日志保留天数
log_max_days = 7

4. 启动 frps

./frps -c ./frps.toml

5. 配成系统服务

Linux 系统的 frp 压缩包内自带 systemd 目录,里面是现成的 service 文件,可直接参考。

四、客户端 frpc 配置

1. 下载 frp

同样从 frp Releases 下载对应内网机器系统的版本。

2. 配置 frpc

解压后只用到 frpc.toml;frps 开头的文件内网机器用不到。

frpc.toml 示例:

# 服务端地址(填公网 IP 或域名)
serverAddr = "<your_server_ip_or_domain>"
# 服务端通信端口
serverPort = 7000

# 传输协议
transport.protocol = "tcp"

# 鉴权方式
auth.method = "token"
# 与服务端配置保持一致
auth.token = "<your_frp_token>"

# ===== SSH:公网 <ssh_remote_port> -> 本机 22 =====
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = <ssh_remote_port>

# ===== 游戏服务器(以 Java 版 Minecraft 为例):公网 25565 -> 本机 25565 =====
[[proxies]]
name = "game-tcp"
type = "tcp"
localIP = "127.0.0.1"
localPort = 25565
remotePort = 25565

[[proxies]]
name = "game-udp"
type = "udp"
localIP = "127.0.0.1"
localPort = 25565
remotePort = 25565

# ===== 远程桌面 xrdp:公网 <rdp_remote_port> -> 本机 3389 =====
[[proxies]]
name = "rdp"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3389
remotePort = <rdp_remote_port>

# ===== 远程桌面 GNOME:公网 <rdp_gnome_remote_port> -> 本机 3390 =====
[[proxies]]
name = "rdp-gnome"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3390
remotePort = <rdp_gnome_remote_port>

3. 启动 frpc

./frpc -c ./frpc.toml

启动成功后就可以从公网测试穿透出去的 SSH、RDP 和 Web 服务。

4. 配成系统服务

与配置 frps 为系统服务同理,不再赘述。

五、HTTPS 配置

把穿透出去的 HTTP 服务变成 HTTPS,主要有两种思路:

  1. 用 frpc 的 https2http 插件在本地直接启用 HTTPS。配置简单、可控,但如果要代理的 HTTPS 服务很多,就得为每个服务单独生成并配置证书,比较繁琐。
  2. 在公网服务器上申请泛域名证书,配 Nginx 反向代理转发到穿透出来的 Web 服务。

本文采用第二种方案。

六、连接方式

先约定几个占位符,后文命令都复用它们:

占位符 含义
<public_ip> 云服务器的公网 IP,如 203.0.113.10(RFC 5737 文档保留地址)
<intranet_ip> 校内/内网直连地址,如 192.168.1.100
<ssh_port> frp 映射出来的 SSH 端口
<rdp_port> frp 映射出来的 RDP 端口
<user> 登录用的服务器账号
<password> 登录密码(仅在提示时手动输入,不要落盘)

方式 1:终端命令行连接

适合快速查看系统状态、跑点简单命令。

  1. 打开本地终端

    • Windows:按 Win + R,输入 powershell 或 cmd 回车;
    • macOS / Linux:打开自带的 Terminal。
  2. 执行连接命令

    # 走公网入口
    ssh -p <ssh_port> <user>@<public_ip>
    
    # 备用:校内网络下可直连内网地址
    ssh -p <ssh_port> <user>@<intranet_ip>
  3. 输入密码

    • 提示 password: 时输入你的服务器密码。
    • Linux 终端下输入密码不会显示任何字符或星号(光标不动),这是正常的,直接盲敲完回车即可。

方式 2:VS Code 远程连接(推荐)

在服务器上跑代码、调试项目时,用 VS Code 最顺手:本地编辑远程代码,用远程的环境运行调试,体验和本地开发一致。

  1. 本地安装 Visual Studio Code,在扩展市场安装 Remote - SSH 插件。

  2. 配置 SSH 配置文件

    1. 在 VS Code 中按 F1(或 Ctrl + Shift + P),输入并选择 Remote-SSH: Open SSH Configuration File...;

    2. 选择第一项(通常是 C:\Users\你的用户名\.ssh\config);

    3. 在文件末尾追加并保存:

      Host myserver
        HostName <public_ip>
        Port <ssh_port>
        User <user>
        ServerAliveInterval 60        # 每 60 秒发一次心跳,防止空闲掉线
        ServerAliveCountMax 3
  3. 发起连接

    1. 点击 VS Code 左下角的远程连接图标(><),或按 F1 选择 Remote-SSH: Connect to Host...;
    2. 在列表中点击 myserver;
    3. 按提示选择服务器平台(Linux);
    4. 输入密码并回车;
    5. 连接成功后,从「资源管理器 -> 打开文件夹」选择项目目录即可开始开发。

方式 3:Windows 图形化远程桌面(RDP)

需要图形界面时,用 Windows 自带的「远程桌面连接」。

⚠️ 同一时间同一账户只能有一人控制。 多人共用同一账号时,同时进图形化桌面会互相抢鼠标。建议日常跑代码优先用 VS Code SSH,互不影响。

  1. 按 Win + R 输入 mstsc.exe 回车。
  2. 在「远程桌面连接」窗口中:
    • 计算机:填 <public_ip>(若 RDP 映射了非默认端口,填 <public_ip>:<rdp_port>);
    • 用户名:填 <user>。
  3. 点「连接」,在密码框中输入密码即可进入图形化桌面。

七、常见问题

Q1:突然连接超时 / 提示连接失败?

  1. 先核对公网 IP 和映射端口是否发生了变更;
  2. 如果 IP 或端口改过了,记得同步修改本地 VS Code SSH 配置里的 HostName 和 Port。

Q2:长时间运行的任务怕断网中断?

SSH 或 VS Code 里跑耗时较长的脚本(如模型训练),关电脑、断网都会导致任务终止。建议用 tmux 把任务挂到后台:

# 1. 创建并进入一个名为 train_task 的后台会话
tmux new -s train_task

# 2. 在会话中启动脚本
python train.py

# 3. 暂时离开:先按 Ctrl + B,松开后再按 D(任务仍在后台跑)

# 4. 下次连上服务器后,重新接回该会话
tmux a -t train_task

Q3:提示 Host key verification failed?

内网穿透经常更换公网服务器或重装系统,本地记录的旧指纹与新节点对不上就会报这个错。在本地终端清理旧指纹后重连:

ssh-keygen -R [<public_ip>]:<ssh_port>

Q4:如何使用 SSH 密钥免密直接登录服务器?

把本地生成的公钥上传并授权到服务器后,SSH 每次连接会自动用非对称加密完成校验,无需再手动输入密码。公钥可以公开,私钥始终留在本地,不要外传。

  1. 首次绑定(把本地公钥同步到服务器,只做一次)

    ssh-copy-id -i ~/.ssh/id_ed25519.pub -p <ssh_port> <user>@<public_ip>

    按提示输入一次服务器登录密码,出现 Number of key(s) added: 1 即表示绑定成功。

  2. 日常免密登录

    ssh -p <ssh_port> <user>@<public_ip>
    
    # 若已按「方式 2」配置了别名,可以直接用别名登录
    ssh myserver

各平台的首次绑定命令:

平台 命令
Linux ssh-copy-id -i ~/.ssh/id_ed25519.pub -p <ssh_port> <user>@<public_ip>
macOS 同上(系统自带 ssh-copy-id)
Windows(PowerShell) 见下方「Windows 平台的等价写法」

Windows 平台的等价写法

Git for Windows 自带的 OpenSSH 没有 ssh-copy-id 这个脚本,用一行管道命令替代:

type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh -p <ssh_port> <user>@<public_ip> "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

如果用 WSL,或自己装了 MSYS2 的 openssh 包,直接使用上面的 ssh-copy-id 命令即可。

也可以纯手工完成:用记事本打开公钥文件并复制内容,粘贴到服务器的 ~/.ssh/authorized_keys 中(一个公钥一行):

notepad $env:USERPROFILE\.ssh\id_ed25519.pub

参数提示:公钥文件名按实际情况替换(如 id_rsa.pub);若本地还没有密钥对,先执行 ssh-keygen -t ed25519 -C "你的邮箱" 生成。

参考资料