SSH 通过 Tor 洋葱服务且不开放端口
将 sshd 放入 Tor 洋葱服务,让 VPS 不监听任何入站端口。本文介绍安装配置、v3 客户端授权,以及避免锁死远程主机的准确操作顺序。
通过 Tor 洋葱服务使用 SSH 的变化
通过 Tor 洋葱服务使用 SSH,可以管理一台不在任何端口接受入站连接的 VPS。服务器主动连接 Tor 网络,并保持该连接。您的 SSH 会话通过这条连接返回,因此服务器无需在公网 IP 地址上监听任何端口。
日志中的变化会立即显现。开放公网 SSH 端口的服务器每天会收到扫描器发起的数千次密码登录失败。将 sshd 放到洋葱服务后,并在防火墙处丢弃入站流量,/var/log/auth.log 随后只会记录您发起的会话。
代价是,tor 会处于每次管理员会话的通信路径中。它是用户空间守护进程,每次重启后都必须先启动并完成引导,您才能登录。关闭端口前应提前规划,因为这里的故障结果是失去对一台无法物理访问的机器的访问权限。
在进行任何操作前准备好恢复访问路径
在没有不依赖 SSH 的恢复路径前,不要开始操作。
现在打开服务商的控制台,即控制面板中的 VNC 或串行控制台,并通过它登录。如果不知道 root 密码,请先从控制面板重置 root 密码,然后确认新密码有效。未经过测试的控制台不能作为恢复路径。
以下顺序很重要。每一步都确认成功后,才执行下一步;在洋葱路由可用前,端口 22 会保持开放。
- 安装 tor,并确认其成功完成引导。
- 定义洋葱服务并读取其地址。
- 在端口 22 仍开放时,通过洋葱路由连接。
- 添加客户端授权,然后再次连接。
- 将
sshd绑定到 loopback,并关闭端口 22。 - 重启,然后再次通过洋葱路由连接。
在整个过程中保持当前 SSH 会话处于打开状态。已建立的会话可以在防火墙变更导致新连接被阻止后继续存活,因此它是你的第一道恢复保障。
在服务器上安装 tor
Ubuntu 在自带的软件仓库中提供 tor,但其中的版本通常较旧。Tor Project 的软件仓库提供其文档所述的版本。请按照其 apt 软件仓库指南 中的命令添加该仓库。
sudo apt update
sudo apt install -y apt-transport-https wget gpg
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null执行 /etc/apt/sources.list.d/tor.sources。Suites 使用您的发行版代号,该代号可通过 lsb_release -cs 输出(Ubuntu 24.04 中为 noble)。
Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: noble
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpgsudo apt update
sudo apt install -y tor deb.torproject.org-keyring
sudo journalctl -u tor@default -n 20 --no-pager日志应以 Bootstrapped 100% (done) 结尾。如果日志停留在此之前,说明 tor 无法访问网络,几乎总是因为出站防火墙规则或系统时钟严重错误。
单元名称容易误导。 即使一切正常,systemctl status tor 也会报告 Active: active (exited),因为 Debian 和 Ubuntu 将 tor 打包为多实例主单元,其唯一作用是拉起真正的实例。守护进程本身以 tor@default.service 运行。将该名称用于 status 和 journalctl。对 tor 执行启动、停止和重新加载操作仍会作用于该实例,因此 sudo systemctl reload tor 会按预期工作。
为端口 22 定义 onion 服务
向 /etc/tor/torrc 添加以下两行。
HiddenServiceDir /var/lib/tor/ssh/
HiddenServicePort 22 127.0.0.1:22第二行指示 tor 在 onion 地址上接受虚拟端口 22 的连接,并将其连接到本机的 127.0.0.1:22。Tor 通过回环接口访问 sshd,因此 sshd 随后可以停止监听公网地址。
sudo systemctl reload tor
sudo cat /var/lib/tor/ssh/hostname该命令会输出 56 个 base32 字符,后面跟着 .onion。这些字符是经过编码的服务公钥。整个过程中不涉及证书颁发机构,也不需要在任何地方注册名称。
让 tor 自行创建 /var/lib/tor/ssh/。如果手动创建时设置了错误的所有者,或将权限设得比 0700 更宽松,tor 会拒绝使用它,journal 中会报告该目录权限过于宽松。目录中的文件构成服务身份:hs_ed25519_secret_key 就是该地址。以 600 权限备份此目录,并将副本保存在本机之外,因为丢失该目录会导致地址变更,并且每个客户端都需要修改配置。
从工作站连接
工作站需要安装 Tor 客户端,完全不需要额外配置。在 Debian 或 Ubuntu 上,该客户端是 sudo apt install -y tor netcat-openbsd。随后,Tor 会在 127.0.0.1:9050 上监听,作为 SOCKS5 代理。SOCKS 是一种通用代理协议。第 5 版可以传递主机名,而不是 IP 地址,这正是这里需要的功能。
OpenSSH 本身没有 SOCKS 客户端,因此需要借助辅助程序建立连接。将以下内容添加到 ~/.ssh/config。
Host myvps
HostName xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion
User admin
ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:9050 %h %p
ServerAliveInterval 30-X 5 选择 SOCKS5,-x 127.0.0.1:9050 指向本地 Tor。%h 将 onion 地址作为主机名传递给 Tor,使 Tor 在网络内部解析该地址。这里必须使用 OpenBSD netcat。GNU netcat 不支持 -X 选项,并会以 nc: invalid option -- 'X' 退出。
ssh myvps首次连接速度较慢,因为 Tor 必须先建立链路。像处理其他连接一样接受主机密钥指纹。从这里开始,常规 SSH 密钥处理不变。改变的是传输方式,不是身份验证方式。
如果只需临时使用,可以跳过配置项:torsocks ssh admin@xxxxx.onion 可完成相同的工作。
添加 v3 客户端授权
目前,任何知道地址的人都可以连接到 SSH banner 并开始尝试猜测。Onion 地址无法从目录系统枚举,因此地址本身类似于一个秘密,但它仍可能通过普通方式泄露:shell 历史记录,以及提交到 git 仓库的配置文件。客户端授权可以消除这一风险。服务会将其 descriptor 加密后发布给客户端密钥,因此只有地址而没有密钥的人甚至无法定位该服务。
在客户端生成一对 x25519 密钥。这是 Tor Project 的客户端授权指南中的管道,但有一处修改。
openssl genpkey -algorithm x25519 -out /tmp/k1.prv.pem
grep -v " PRIVATE KEY" /tmp/k1.prv.pem | base64 -d | tail --bytes=32 | base32 | sed 's/=//g' > /tmp/k1.prv.key
openssl pkey -in /tmp/k1.prv.pem -pubout | grep -v " PUBLIC KEY" | base64 -d | tail --bytes=32 | base32 | sed 's/=//g' > /tmp/k1.pub.key该命令的已发布版本使用 base64pem -d,而标准 Ubuntu 安装不包含此工具。随后命令会因 base64pem: command not found 停止。GNU base64 -d 可以解码相同的 PEM 内容,因此改用它。
在服务器上安装公钥。
sudo install -d -m 700 -o debian-tor -g debian-tor /var/lib/tor/ssh/authorized_clients
echo "descriptor:x25519:PASTE_PUBLIC_KEY_HERE" | sudo tee /var/lib/tor/ssh/authorized_clients/laptop.auth >/dev/null
sudo chown debian-tor:debian-tor /var/lib/tor/ssh/authorized_clients/laptop.auth
sudo systemctl reload tor系统只读取以 .auth 结尾的文件。将其保存为 laptop.auth.txt 后,tor 会忽略该文件且不输出错误,服务也会继续对所有知道地址的人开放。
在客户端安装私钥。在 Ubuntu 上,tor daemon 以 debian-tor 用户身份运行,无法读取主目录中的文件,因此应将目录放在该用户可以访问的位置。
sudo install -d -m 700 -o debian-tor -g debian-tor /var/lib/tor/onion_auth
echo "ADDRESS_WITHOUT_DOT_ONION:descriptor:x25519:PASTE_PRIVATE_KEY_HERE" | sudo tee /var/lib/tor/onion_auth/myvps.auth_private >/dev/null
sudo chown debian-tor:debian-tor /var/lib/tor/onion_auth/myvps.auth_private
sudo chmod 600 /var/lib/tor/onion_auth/myvps.auth_private将 ClientOnionAuthDir /var/lib/tor/onion_auth 添加到客户端的 /etc/tor/torrc,然后重新加载 tor。如果以自己的用户身份运行 tor,例如在 macOS 上使用 Homebrew 构建版本,则将 ClientOnionAuthDir 指向 ~/.tor/onion_auth,并将权限设为 0700。
文件中的地址是 56 个字符,不包含 .onion 后缀。完成后删除 /tmp/k1.prv.pem 和 /tmp/k1.prv.key。
现在测试两个方向。ssh myvps 应仍能连接。从没有密钥的计算机访问相同地址时,应连接失败。该失败证明客户端授权已生效。
按此顺序关闭端口 22
先设置安全措施。如果您被锁在服务器外,这条命令会在十五分钟后撤销下面的两项更改。
sudo systemd-run --on-active=15m --unit=ssh-rescue \
/bin/sh -c 'ufw allow 22/tcp; rm -f /etc/systemd/system/ssh.socket.d/override.conf; systemctl daemon-reload; systemctl restart ssh.socket'确认 onion 路由仍可用后,使用 sudo systemctl stop ssh-rescue.timer 取消它。
接下来,停止 sshd 监听公网地址。Ubuntu 24.04 通过 socket unit 激活 SSH,因此 sshd_config 中的 ListenAddress 会被忽略:监听 socket 由 ssh.socket 管理,而不是由 sshd 管理。先确认您属于哪种情况。
systemctl is-enabled ssh.socket如果输出 enabled,请运行 sudo systemctl edit ssh.socket 并添加以下内容。
[Socket]
ListenStream=
ListenStream=127.0.0.1:22空的 ListenStream= 会清除从软件包提供的 unit 继承的值。如果省略这一行,您会在保留公网监听器的同时添加第二个监听器。这是此步骤静默失败的最常见原因。
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
ss -tlnp | grep ':22'ss 应显示 127.0.0.1:22,并且 0.0.0.0:22 上不应有任何内容。如果 ssh.socket 已禁用,请将 ListenAddress 127.0.0.1 写入 /etc/ssh/sshd_config.d/10-onion.conf,运行 sudo systemctl restart ssh,然后使用相同的 ss 行检查。无论哪种情况,该输出都能证明配置已生效。
然后配置防火墙。相关操作属于普通的 VPS 上的 ufw 规则管理。先运行 sudo ufw status numbered,然后删除其中列出的 SSH 规则。
sudo ufw status numbered
sudo ufw delete allow OpenSSH
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw status verbose允许出站流量。Tor 会通过 443、9001 等端口向中继建立出站连接,因此出站默认拒绝策略会阻止 Tor 完成引导,同时移除您最后的登录途径。大多数服务商还会在控制面板中提供独立的网络防火墙。也要在那里关闭 22,否则无论 ufw 报告什么,该端口仍然可以访问。
如果此服务器运行 Docker,请在完成前检查已发布的端口。Docker 会将自己的规则写入相同的规则表,并且 直接绕过 ufw 发布容器端口,因此仅设置 ufw deny 策略并不能完整反映实际情况。
重启后再确认服务正常
systemctl is-enabled tor@default
sudo reboot如果第一条命令没有报告服务已启用,请在重启前运行 sudo systemctl enable tor@default。等待 2 分钟,然后运行 ssh myvps。Tor 必须在启动后完成引导,因此 onion 地址会在机器启动后经过一段时间才开始响应。
如果服务始终没有恢复,请打开控制台并读取 sudo journalctl -u tor@default -b。其中会打印 torrc 语法错误或目录权限问题。您也可以在应用 torrc 修改前检查配置。
sudo -u debian-tor tor --verify-config将其与 WireGuard 隧道的成本进行比较
与在您自己的 VPS 上运行 WireGuard VPN相比,洋葱服务更慢,也更难预测。在决定使用之前,请如实评估这种取舍。
延迟。 客户端电路由 3 个中继组成,服务端还会增加另外 3 个中继,因此您输入的每个字符大约要经过全球范围内随机选取的 6 台机器。交互式输入会有明显延迟,文件复制速度也很慢。WireGuard 只增加 1 跳。使用 time ssh myvps 'echo ok' 测量您自己的情况,因为具体数值取决于 Tor 当时建立的电路;Tor 建立新电路后,数值也会变化。
用户空间守护进程位于关键路径中。 WireGuard 在内核中运行,并随网络一起启动。Tor 是一个进程,必须先启动、完成引导,并连接到 guard 中继,之后服务才会正常工作。服务失败时,您只能使用服务商控制台。
时钟准确性。 洋葱服务描述符会按照时间周期发布,因此时钟严重错误会导致地址查找失败,而且通常不会显示明确错误。timedatectl 应报告 System clock synchronized: yes。
您获得的好处是,暴露面不再取决于防火墙规则是否正确。没有可扫描的端口,也没有可获取的服务标识;此外,该地址本身就是公钥,因此端点会在 SSH 启动之前证明自己的身份。
实际情况下,通常应同时使用两者。日常访问使用 WireGuard,并保留洋葱服务作为 WireGuard 配置错误时仍可用的路径。这样只需开放 1 个 UDP 端口,而不是公开 SSH 端口。这些措施都不能替代加固 sshd 本身:仍应使用仅密钥认证和非 root 登录,因为洋葱服务只保护网络路径,除此之外的部分并不受其保护。
故障模式及您将看到的错误
Tor 始终无法通过 Bootstrapped 0%。 出站流量被阻止,或系统时钟偏差过大。使用 sudo ufw status verbose 检查出站策略,然后运行 timedatectl。
systemctl status tor 显示 active (exited)。 这在 Debian 和 Ubuntu 上是正常现象。请改为查看 tor@default。
找不到描述符。 Tor 返回 SOCKS 扩展错误 F0:“找不到 Onion Service 描述符”。原因可能是描述符尚未发布;重新加载后需要短暂等待。也可能是服务器上的 tor 未运行。
F4:“Onion Service 缺少客户端授权”。 客户端没有匹配的 .auth_private,供 tor 使用。请确认 torrc 中包含 ClientOnionAuthDir,目录权限为 0700,文件名以 .auth_private 结尾,并且 debian-tor 可以读取该文件。
F5:“Onion Service 客户端授权错误”。 私钥与服务器上的 .auth 文件不匹配。base32 字符串末尾多出 = 或其中包含多余换行符,都会导致此错误。
nc: invalid option -- 'X'。 安装的是 GNU netcat,而不是 OpenBSD 版本。运行 sudo apt install -y netcat-openbsd。
Could not resolve hostname。 ssh 尝试执行普通 DNS 查询,但 .onion 没有 DNS 响应,因此 ProxyCommand 从未运行。~/.ssh/config 中的 Host 模式与您输入的名称不匹配。
Permission denied (publickey)。 隧道工作正常,tor 已完成任务。请将其视为普通的 permission denied publickey 问题,不要将原因归咎于 tor。
FAQ
onion service 是否意味着 VPS 上确实没有开放端口?
是。只要 sshd 绑定到 127.0.0.1,并且防火墙丢弃入站流量即可。Tor 会向中继建立出站 TCP 连接,您的会话再通过该连接返回,因此服务器不会在公网地址上接受任何连接。您可以在服务器上使用 ss -tlnp,再从其他位置执行端口扫描来验证。不要忘记服务商控制面板中的网络防火墙。它与 ufw 是独立的控制项,也必须关闭相应的入站访问。
仅使用 .onion 地址作为 SSH 安全措施是否足够?
不够。该地址包含 56 个字符,无法通过目录系统猜测或枚举,因此其行为类似于机密信息,但它可能泄露到 shell 历史记录和配置文件中。请添加 v3 客户端授权。启用后,服务描述符会使用您的客户端密钥加密,因此仅持有该地址的人会收到扩展错误 F4,根本无法访问 sshd。
重启后 tor 无法启动会发生什么?
您会完全失去 SSH 访问,因为此时 onion 地址是唯一的访问途径。因此,在关闭 22 端口前,必须先测试服务商控制台。Tor 在启动后也需要时间完成引导,因此该地址的响应会晚于机器对 ping 的响应。如果始终没有响应,请通过控制台登录并查看 sudo journalctl -u tor@default -b;其中会记录 torrc 语法错误,或 /var/lib/tor/ssh 上的权限问题。
通过 Tor 使用 SSH 是否比 WireGuard 更慢?
是,慢很多。连接 onion service 时,流量会经过随机选择的约 6 个中继;而 WireGuard 只需通过一条加密跳转直接连接到服务器。输入命令时会明显延迟,传输速度也较慢。常见做法是日常工作使用 WireGuard,同时保留 onion service 作为紧急访问路径,以便在 VPN 配置损坏时仍可使用。