没有公网 IP,怎么从外面 SSH 登进家里那台机器
家里有台 Linux 机器,平时挂着跑点东西。人在外面想登进去看一眼、改个配置,结果它在路由器后面,没有公网 IP,SSH 直接连不上。
我这里用 frp 做反向隧道。云服务器跑 frps,家里机器跑 frpc,外面的 Mac 或 Windows 再跑一个 frpc visitor。以后在外面连自己电脑的 127.0.0.1:6000,流量会被转到家里机器的 22 端口。
这篇主线只用 stcp visitor,云服务器只需要放行 7000。这样不用把家里 SSH 映射成公网端口,少挨一些扫描。
开始前准备
先把东西备齐:
- 一台带公网 IP 的云服务器,本文用
203.0.113.42代替,系统示例用 Ubuntu。 - 家里那台 Linux 机器,SSH 服务已经在 22 端口跑着。
- 外面要连接的电脑,macOS 或 Windows 都行。
- 云服务器安全组放行 TCP
7000。 - 一段
auth.token,云服务器、家里机器、外部电脑三边都要一致。 - 一段
secretKey,家里机器和外部电脑的 visitor 要一致,别和auth.token混用。
先在云服务器和家里机器上确认 SSH 服务正常:
bash
sudo systemctl status ssh
看到 active (running) 就行。Ubuntu 服务名是 ssh,CentOS / RHEL / Rocky 系列是 sshd,本文后面所有 ssh 服务名都相应换成 sshd。
再生成两段密钥。找一台可信机器执行:
bash
openssl rand -hex 32
openssl rand -hex 32
第一段后面填到 auth.token,第二段填到 secretKey。
步骤 1 · 云服务器和家里机器安装 frp
frps 和 frpc 在同一个发布包里。云服务器和家里机器都要装。
先看机器架构:
bash
uname -m
# x86_64 -> 用 amd64
# aarch64 -> 用 arm64
下面示例用 0.61.1,安装时可以去 frp 的 GitHub release 页把版本号换成新的。云服务器和家里机器都执行:
bash
cd /tmp
wget https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz
tar -xzf frp_0.61.1_linux_amd64.tar.gz
cd frp_0.61.1_linux_amd64
sudo cp frps frpc /usr/local/bin/
sudo mkdir -p /etc/frp
检查一下:
bash
frps -v
frpc -v
能看到 0.61.1,这步就过了。
步骤 2 · 云服务器配置 frps
云服务器上写 /etc/frp/frps.toml:
toml
bindAddr = "0.0.0.0"
bindPort = 7000
auth.method = "token"
auth.token = "your-frps-token-here"
transport.tls.force = true
log.to = "/var/log/frps/frps.log"
log.level = "info"
log.maxDays = 7
your-frps-token-here 换成刚才生成的第一段密钥。
建日志目录,再用 systemd 拉起:
bash
sudo mkdir -p /var/log/frps
sudo tee /etc/systemd/system/frps.service > /dev/null <<'EOF'
[Unit]
Description=frp server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now frps
sudo systemctl status frps
预期是 active (running)。日志里能看到服务启动成功:
bash
sudo tail -n 50 /var/log/frps/frps.log
步骤 3 · 云服务器放行 7000
云厂商控制台的安全组,入站放行 TCP 7000。这一步别只改系统防火墙,云厂商安全组不放,外面一样连不进来。
如果服务器本机开了 ufw,再放一次:
bash
sudo ufw allow 7000/tcp
sudo ufw status
预期能看到 7000/tcp ALLOW。
到这里,云服务器只负责接收 frp 控制连接,不直接暴露家里 SSH。
步骤 4 · 家里机器配置 frpc
家里 Linux 上写 /etc/frp/frpc.toml:
toml
serverAddr = "203.0.113.42"
serverPort = 7000
auth.token = "your-frps-token-here"
transport.tls.enable = true
transport.poolCount = 3
[[proxies]]
name = "home-ssh-stcp"
type = "stcp"
secretKey = "your-stcp-secret-here"
localIP = "127.0.0.1"
localPort = 22
这里有三个值要换:
serverAddr换成你的云服务器公网 IP。auth.token和云服务器frps.toml保持一字不差。secretKey换成刚才生成的第二段密钥。
transport.poolCount = 3 是为了多预建几条 TCP 连接。家用宽带 NAT 有时会清理空闲长连接,多几条能缓一下。
然后拉起家里机器上的 frpc:
bash
sudo tee /etc/systemd/system/frpc.service > /dev/null <<'EOF'
[Unit]
Description=frp client
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now frpc
sudo systemctl status frpc
看日志:
bash
journalctl -u frpc -n 50
看到 start proxy success,并且带着 home-ssh-stcp,说明家里机器已经把 SSH 注册到云服务器。
顺手把 SSH 安全项改掉。这个建议别跳过:
bash
sudo nano /etc/ssh/sshd_config
确认这两行是这样:
text
PasswordAuthentication no
PermitRootLogin no
改之前要先确认你的公钥已经在家里机器的 ~/.ssh/authorized_keys 里。否则重启 SSH 后,自己可能进不去。
确认没问题再重启:
bash
sudo systemctl restart ssh
步骤 5 · macOS 配置 visitor
Mac 这边不用开公网端口,只需要跑一个本地 frpc,把本机 127.0.0.1:6000 接到家里机器。
Intel Mac 下载 darwin_amd64:
bash
cd ~/Downloads
curl -LO https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_darwin_amd64.tar.gz
tar -xzf frp_0.61.1_darwin_amd64.tar.gz
mkdir -p ~/.frp
cp frp_0.61.1_darwin_amd64/frpc ~/.frp/frpc
chmod +x ~/.frp/frpc
Apple 芯片 Mac 把包名里的 darwin_amd64 换成 darwin_arm64。
写配置文件 ~/.frp/frpc-home-ssh.toml:
toml
serverAddr = "203.0.113.42"
serverPort = 7000
auth.token = "your-frps-token-here"
transport.tls.enable = true
[[visitors]]
name = "home-ssh-mac"
type = "stcp"
serverName = "home-ssh-stcp"
secretKey = "your-stcp-secret-here"
bindAddr = "127.0.0.1"
bindPort = 6000
启动 visitor:
bash
~/.frp/frpc -c ~/.frp/frpc-home-ssh.toml
终端会停在前台,先别关。另开一个终端连家里机器:
bash
ssh -p 6000 youruser@127.0.0.1
youruser 是家里 Linux 的用户名,不是云服务器用户名,也不是 Mac 用户名。登进去后执行:
bash
hostname
显示家里那台机器的主机名,就通了。
步骤 6 · Windows 配置 visitor
Windows 下载 frp_0.61.1_windows_amd64.zip,解压到 C:\frp。ARM64 Windows 下载 windows_arm64 包。
在 C:\frp\frpc-home-ssh.toml 写入:
toml
serverAddr = "203.0.113.42"
serverPort = 7000
auth.token = "your-frps-token-here"
transport.tls.enable = true
[[visitors]]
name = "home-ssh-windows"
type = "stcp"
serverName = "home-ssh-stcp"
secretKey = "your-stcp-secret-here"
bindAddr = "127.0.0.1"
bindPort = 6000
PowerShell 启动 visitor:
powershell
cd C:\frp
.\frpc.exe -c .\frpc-home-ssh.toml
同样让这个窗口开着。另开一个 PowerShell:
powershell
ssh -p 6000 youruser@127.0.0.1
这里的 youruser 还是家里 Linux 的用户名。Windows 只是在本机开了 127.0.0.1:6000,真正认证发生在家里那台 Linux 上。
踩坑提醒
frpc 日志报 auth failed。 auth.token 不一致。云服务器、家里机器、Mac/Windows 三边都要填同一段,复制粘贴,别手敲。
日志里有 tls handshake。 云服务器开了 transport.tls.force = true,客户端就得走 TLS。frp 从 v0.50.0 起客户端默认已开 TLS,transport.tls.enable = true 写不写都会走,配置里显式写出来是图个清楚。家里机器和 Mac/Windows 三边确认都开着就行。
家里机器日志没有 start proxy success。 先看 serverAddr 和 serverPort,再看云服务器安全组是否放行 7000。这一步没通,后面的 Mac/Windows 都不用看。
Mac/Windows visitor 连上了,但 SSH 到 127.0.0.1:6000 失败。 看 visitor 配置里的 serverName,必须等于家里机器 [[proxies]] 里的 name。这篇里两边都是 home-ssh-stcp。再看 secretKey,也必须一字不差。
连一会儿就断,偶发 connection reset。 家用宽带 NAT 会清理空闲连接。transport.poolCount = 3 能缓解,真要稳再加 SSH keepalive:
bash
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -p 6000 youruser@127.0.0.1
改完 SSH 配置后把自己锁外面。 这是最烦的坑。关密码登录前,先用密钥从另一台机器成功登录一次,再重启 ssh。没有确认公钥可用,不要急着改 PasswordAuthentication no。
验证
云服务器验证 frps:
bash
sudo systemctl status frps
sudo tail -n 50 /var/log/frps/frps.log
家里机器验证 frpc:
bash
sudo systemctl status frpc
journalctl -u frpc -n 50
Mac 或 Windows 验证本地端口:
bash
nc -zv 127.0.0.1 6000
ssh -p 6000 youruser@127.0.0.1
Windows 没有 nc 的话,用 PowerShell:
powershell
Test-NetConnection 127.0.0.1 -Port 6000
ssh -p 6000 youruser@127.0.0.1
登进去后 hostname 是家里那台机器,整条链路就通了。
可选:不跑本地 frpc 的直连方式
如果你不想在 Mac/Windows 上跑 visitor,也可以用普通 tcp 映射,把家里机器的 22 映射到云服务器的 6022:
toml
[[proxies]]
name = "home-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6022
云服务器 frps.toml 里建议限制允许的远端端口:
toml
allowPorts = [
{ single = 6022 },
]
然后云厂商安全组和 ufw 都放行 6022,外面直接连:
bash
ssh -p 6022 youruser@203.0.113.42
这个方案少一个本地 visitor,连起来更省事。但代价也明显:家里 SSH 等于挂到了公网 6022,扫描器迟早会扫到。我自己更倾向于上面那套 stcp visitor,多跑一个客户端,换来云服务器不暴露业务端口。
到这里,外面用 Mac 或 Windows 都能 SSH 进家里那台 Linux。临时调试、用完即走,Cloudflare Tunnel 或 ngrok 更省事;长期挂着,又刚好有台云服务器,frp 这套更划算。