这篇只记录 frp 相关的反向代理方案。目标是:
- GPU 服务器不直接暴露 SSH 端口。
- 公网 VPS 只暴露 frp 服务端口。
- 本地电脑通过 visitor 连接到 GPU 服务器的 SSH。
- 认证同时依赖 frp token、STCP secret key 和 SSH key。
Topology
Local Mac / PC
-> frpc visitor
-> Public VPS: frps
-> Private GPU server: frpc stcp
-> 127.0.0.1:22
code Local Mac / PC
-> frpc visitor
-> Public VPS: frps
-> Private GPU server: frpc stcp
-> 127.0.0.1:22
code 这里使用 stcp,而不是直接把 GPU 服务器的 SSH 映射到公网端口。这样公网只能看到 VPS 上的 frps 端口,看不到 GPU 服务器的 SSH 服务。
Shared Parameters
三端配置需要保持一致:
VPS address: your_vps_ip
frps bind port: 7000
auth token: your_auth_token
stcp secret key: your_secret_key
local visitor port: 6000
code VPS address: your_vps_ip
frps bind port: 7000
auth token: your_auth_token
stcp secret key: your_secret_key
local visitor port: 6000
code auth.token 用于 frpc/frps 之间认证,secretKey 用于 STCP visitor 和 server proxy 匹配。二者不要复用同一个值。
VPS: frps
在公网 VPS 上运行 frps。
frps.toml:
bindPort = 7000
auth.method = "token"
auth.token = "your_auth_token"
code bindPort = 7000
auth.method = "token"
auth.token = "your_auth_token"
code 启动:
./frps -c frps.toml
code ./frps -c frps.toml
code 云厂商安全组或防火墙只需要放行 7000/tcp:
sudo ufw allow 7000/tcp
code sudo ufw allow 7000/tcp
code GPU Server: frpc STCP Server
在内网 GPU 服务器上运行 frpc,把本机 SSH 暴露给 STCP 隧道。
frpc.toml:
serverAddr = "your_vps_ip"
serverPort = 7000
auth.method = "token"
auth.token = "your_auth_token"
[[proxies]]
name = "gpu_ssh"
type = "stcp"
secretKey = "your_secret_key"
localIP = "127.0.0.1"
localPort = 22
code serverAddr = "your_vps_ip"
serverPort = 7000
auth.method = "token"
auth.token = "your_auth_token"
[[proxies]]
name = "gpu_ssh"
type = "stcp"
secretKey = "your_secret_key"
localIP = "127.0.0.1"
localPort = 22
code 启动:
./frpc -c frpc.toml
code ./frpc -c frpc.toml
code Local Machine: frpc Visitor
在本地 Mac/PC 上运行 visitor,把 STCP 隧道绑定到本地端口。
frpc-visitor.toml:
serverAddr = "your_vps_ip"
serverPort = 7000
auth.method = "token"
auth.token = "your_auth_token"
[[visitors]]
name = "gpu_ssh_visitor"
type = "stcp"
serverName = "gpu_ssh"
secretKey = "your_secret_key"
bindAddr = "127.0.0.1"
bindPort = 6000
code serverAddr = "your_vps_ip"
serverPort = 7000
auth.method = "token"
auth.token = "your_auth_token"
[[visitors]]
name = "gpu_ssh_visitor"
type = "stcp"
serverName = "gpu_ssh"
secretKey = "your_secret_key"
bindAddr = "127.0.0.1"
bindPort = 6000
code 启动:
./frpc -c frpc-visitor.toml
code ./frpc -c frpc-visitor.toml
code 连接 GPU 服务器:
ssh -p 6000 your_username@127.0.0.1
code ssh -p 6000 your_username@127.0.0.1
code SSH Hardening
frp 只解决网络可达性,不替代 SSH 安全配置。GPU 服务器建议只允许密钥登录。
在本地生成专用密钥:
ssh-keygen -t ed25519 -f ~/.ssh/id_gpu_ed25519
code ssh-keygen -t ed25519 -f ~/.ssh/id_gpu_ed25519
code visitor 启动后,把公钥复制到 GPU 服务器:
ssh-copy-id -i ~/.ssh/id_gpu_ed25519.pub -p 6000 your_username@127.0.0.1
code ssh-copy-id -i ~/.ssh/id_gpu_ed25519.pub -p 6000 your_username@127.0.0.1
code 修改 GPU 服务器 /etc/ssh/sshd_config:
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
code PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
code 重启 SSH:
sudo systemctl restart ssh
code sudo systemctl restart ssh
code 之后用密钥连接:
ssh -p 6000 -i ~/.ssh/id_gpu_ed25519 your_username@127.0.0.1
code ssh -p 6000 -i ~/.ssh/id_gpu_ed25519 your_username@127.0.0.1
code Run In Tmux
调试阶段推荐先用 tmux 跑三端进程,方便随时查看日志。
tmux new -s frp
./frps -c frps.toml
code tmux new -s frp
./frps -c frps.toml
code 后台挂起:
Ctrl+b, d
code Ctrl+b, d
code 恢复:
tmux attach -t frp
code tmux attach -t frp
code 确认稳定后,再考虑把 frps 和 frpc 写成 systemd service。
Troubleshooting
| Symptom | Common cause | Check |
|---|---|---|
nc -vz your_vps_ip 7000 fails | VPS firewall or security group blocks traffic | Open 7000/tcp |
token doesn't match | auth.token differs between frps and frpc | Compare all config files |
| visitor connects but SSH fails | serverName or secretKey mismatch | Match visitor serverName with proxy name |
session shutdown | frp versions differ or system time is off | Use same frp version and sync time |
| macOS says binary is blocked | Gatekeeper quarantine | xattr -d com.apple.quarantine ./frpc |
Minimal Checklist
- VPS runs
frpsand exposes only7000/tcp. - GPU server runs
frpcwithtype = "stcp"andlocalPort = 22. - Local machine runs
frpcvisitor and binds127.0.0.1:6000. - SSH connects through
ssh -p 6000 your_username@127.0.0.1. - GPU server disables password login after SSH key login is confirmed.
