如何通过Tor Onion Service使用SSH且不开放端口
将sshd置于Tor Onion Service后,让VPS不监听任何公网入站端口。涵盖Tor安装、v3客户端授权、恢复入口,以及避免锁死自己的正确操作顺序。
通过 Tor onion service 使用 SSH 的变化
通过 Tor onion service 使用 SSH,可以管理一台不接受任何端口入站连接的 VPS。服务器主动连接 Tor 网络,并保持该连接。SSH 会话通过这条连接返回服务器,因此无需在公网 IP 地址上监听任何端口。
日志中的变化会立即出现。开放公网 SSH 端口的服务器每天会收到扫描器发起的数千次失败密码尝试。将 sshd 放到 onion service 后面,并在防火墙丢弃入站流量,/var/log/auth.log 之后只会记录您主动建立的会话。
代价是,每个管理会话都必须经过 tor。它是一个用户空间 daemon,每次重启后都必须先启动并完成引导,您才能登录。关闭端口前应规划好这一点,因为这里的故障结果是失去对一台无法物理接触的机器的访问权限。
开始修改前先准备好恢复入口
在没有不依赖 SSH 的恢复路径之前,不要开始操作。
现在打开云服务商的控制台,即控制面板中的 VNC 或串行控制台,并使用它登录。如果不知道 root 密码,请先通过控制面板重置 root 密码,然后确认新密码有效。未经测试的控制台不能作为恢复路径。
以下顺序很重要。每一步都必须确认成功后,才能执行下一步。在 onion 路由正常工作前,端口 22 必须保持开放。
- 安装 tor,并确认它已完成引导。
- 定义 onion 服务,并读取其地址。
- 在端口 22 仍开放时,通过 onion 连接。
- 添加客户端授权,然后再次连接。
- 将
sshd绑定到 loopback,并关闭端口 22。 - 重启,然后再次通过 onion 连接。
整个过程中保持当前 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 随后可以停止监听公网地址的原因。将第二行指向 127.0.0.1:80 上的 Web 服务器,即可使用相同的两条指令 在 Onion 地址上发布网站。安装 tor 后,这是运行第二项服务的实用方式。
sudo systemctl reload tor
sudo cat /var/lib/tor/ssh/hostname该命令会输出 56 个 base32 字符,后跟 .onion。这些字符是该服务经过编码的公钥。其中没有证书颁发机构,也不需要在任何地方注册名称。
让 tor 自行创建 /var/lib/tor/ssh/。如果手动创建该目录、设置了错误的所有者,或将权限设置得比 0700 更宽松,tor 会拒绝使用它,日志中会报告该目录权限过宽。目录中的文件构成服务身份: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 并开始猜测。洋葱地址无法通过目录系统枚举,因此地址本身类似于机密,但它仍可能通过常见途径泄露:shell 历史记录,以及提交到 git 仓库的配置文件。客户端授权可以弥补这一漏洞。服务会将其描述符加密后发布给客户端密钥,因此只持有地址而没有密钥的人甚至无法定位该服务。
在客户端生成一对 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 的拒绝策略并不能完整反映实际情况。
重启后再验证
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。
换来的好处是,暴露面不再取决于防火墙规则是否配置正确。没有端口可供扫描,也没有 banner 可供获取;地址本身就是公钥,因此端点会在 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 查询,但 DNS 无法解析 .onion,因此 ProxyCommand 从未运行。~/.ssh/config 中的 Host 模式与您输入的名称不匹配。
Permission denied (publickey)。 隧道已正常工作,tor 的处理也已完成。请将此问题视为普通的权限被拒绝 publickey 问题,不要再从 tor 方面排查。
FAQ
Onion 服务是否真的意味着我的 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 服务时,流量会经过约 6 个随机选择的中继;而 WireGuard 只需通过一条加密链路直接连接服务器。输入命令时会明显延迟,文件传输速度也较慢。常见做法是日常工作使用 WireGuard,同时保留 Onion 服务作为应急路径,以便在 VPN 配置损坏时仍能访问服务器。