Tor onion serviceでSSH、ポート開放なしの設定方法
Tor onion serviceの背後にsshdを置き、VPSのインバウンドポートをゼロにする手順です。v3クライアント認証と、ロックアウトを防ぐ安全な設定順序を解説します。
Tor onion service 経由の SSH で変わること
Tor onion service 経由の SSH を使うと、どのポートでもインバウンド接続を受け付けない VPS を管理できます。サーバーは Tor ネットワークへ発信し、その接続を維持します。SSH セッションはその接続を通って戻ってくるため、パブリック IP アドレスで待ち受ける必要はありません。
ログへの影響はすぐに現れます。パブリック SSH ポートを持つサーバーには、スキャナーによるパスワード認証の失敗が 1 日に数千件記録されます。sshd を onion service の背後に移し、ファイアウォールでインバウンドトラフィックを遮断すると、/var/log/auth.log に記録されるのは自分で開始したセッションだけになります。
代わりに、tor がすべての管理セッションの経路上に入ります。これはユーザー空間デーモンであり、再起動のたびに起動してブートストラップを完了しなければ、ログインできません。ポートを閉じる前に、この点を考慮してください。ここでの障害は、物理的に到達できないマシンへのアクセスを失うことだからです。
何かを変更する前に復旧経路を確保する
SSH を使わない復旧経路を確保するまで、作業を開始しないでください。
今すぐプロバイダーのコンソールを開きます。コントロールパネルにある VNC コンソールまたはシリアルコンソールを使用してログインしてください。root のパスワードが不明な場合は、まず パネルから root のパスワードをリセットし、ログインできることを確認します。一度もテストしていないコンソールは、復旧経路とはいえません。
以下の順序が重要です。各手順が正常に完了したことを確認してから、次の手順を実行します。onion route が機能するまで、ポート 22 は開いたままにします。
- tor をインストールし、ブートストラップが完了することを確認します。
- onion service を定義し、アドレスを確認します。
- ポート 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 はネットワークに接続できません。ほとんどの場合、原因は外向きのファイアウォールルール、またはシステム時刻の大幅なずれです。
unit 名には注意が必要です。 すべて正常でも、systemctl status torは Active: active (exited)を報告します。Debian と Ubuntu では、tor がマルチインスタンスのマスター unit としてパッケージ化されており、その役割は実際のインスタンスを起動することだけだからです。デーモン自体は tor@default.serviceとして実行されます。statusと journalctlには、この名前を使用してください。torの起動、停止、再読み込みは引き続きインスタンスに到達するため、sudo systemctl reload torは期待どおりに動作します。
ポート 22 の onion service を定義する
/etc/tor/torrc に 2 行を追加します。
HiddenServiceDir /var/lib/tor/ssh/
HiddenServicePort 22 127.0.0.1:222 行目は、onion アドレスの仮想ポート 22 で接続を受け付け、ホスト上の 127.0.0.1:22 に接続するよう tor に指示します。Tor は loopback 経由で sshd に接続します。そのため、後で sshd が公開アドレスで待ち受ける必要がなくなります。
sudo systemctl reload tor
sudo cat /var/lib/tor/ssh/hostname56 文字の 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 バナーに到達して、推測を始められます。Onion アドレスはディレクトリシステムから列挙できないため、アドレス自体は秘密情報のように機能します。しかし、シェルの履歴や、git リポジトリにコミットした設定ファイルなど、通常の経路で漏えいします。クライアント認証を有効にすると、この問題を解消できます。サービスはクライアントキーに対して暗号化した descriptor を公開するため、アドレスだけを持ち、キーを持たない相手はサービスの場所さえ特定できません。
クライアント上で x25519 キーペアを生成します。これは Tor Project の クライアント認証ガイドにあるパイプラインに、1 か所変更を加えたものです。
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 デーモンが 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クライアントの /etc/tor/torrc に ClientOnionAuthDir /var/lib/tor/onion_auth を追加して、tor を reload します。自分のユーザーとして tor を実行する場合、たとえば macOS の Homebrew ビルドを使用する場合は、ClientOnionAuthDir が ~/.tor/onion_auth を参照するように設定し、モードを 0700 にします。
そのファイル内のアドレスは、.onion サフィックスを含まない 56 文字です。作業が終わったら /tmp/k1.prv.pem と /tmp/k1.prv.key を削除します。
最後に、双方の方向をテストします。ssh myvps は引き続き接続できるはずです。キーを持たないマシンから同じアドレスに接続すると、失敗するはずです。この失敗により、認証が有効になっていることを確認できます。
ポート 22 を、この順序で閉じます
最初に安全策を設定します。このコマンドを1つ実行すると、ロックアウトした場合に15分後に以下の2つの変更が元に戻ります。
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'オニオンルートが引き続き機能することを確認したら、sudo systemctl stop ssh-rescue.timer でキャンセルします。
次に、公開アドレスで sshd が待ち受けないようにします。Ubuntu 24.04 では socket unit を使用して SSH を有効化するため、sshd_config 内の ListenAddress は無視されます。待ち受けソケットを所有するのは sshd ではなく ssh.socket です。まず、どちらの構成かを確認します。
systemctl is-enabled ssh.socketenabled と表示された場合は、sudo systemctl edit ssh.socket を実行して次の内容を追加します。
[Socket]
ListenStream=
ListenStream=127.0.0.1:22空の ListenStream= により、パッケージ付属 unit から継承した値が消去されます。この行を省略すると、公開リスナーを残したまま2つ目のリスナーが追加されます。この手順で、エラーを表示せずに失敗する最も一般的な原因です。
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 が無効になっていた場合は、/etc/ssh/sshd_config.d/10-onion.conf に ListenAddress 127.0.0.1 を記述し、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-configWireGuard トンネルと比較したコスト
自分の VPS 上で WireGuard VPN を運用する場合と比較すると、onion service は低速で、動作も予測しにくくなります。導入を決める前に、このトレードオフを正しく把握してください。
遅延。 クライアント側の circuit は 3 つの relay を経由し、サービス側でもさらに 3 つを追加するため、入力したキーは世界中からランダムに選ばれたおよそ 6 台のマシンを通過します。対話的な入力には目に見える遅延が生じ、ファイルコピーも低速です。WireGuard で増える hop は 1 つです。実際の環境では time ssh myvps 'echo ok' を使って測定してください。値は tor が構築した circuit に依存し、tor が別の circuit を構築すると変化します。
重要な経路上で動作する userspace daemon。 WireGuard は kernel 内で動作し、ネットワークとともに起動します。Tor は、起動して bootstrap を完了し、guard relay に到達する必要がある process です。それが失敗すると、provider の console から対応することになります。
時刻の正確性。 onion service の descriptor は時間帯に基づいて公開されるため、system clock が大きくずれていると、明確なエラーメッセージが表示されないまま address lookup に失敗します。timedatectl は System clock synchronized: yes を返す必要があります。
その代わりに得られるのは、firewall rule が正しく設定されていることに依存しない露出の低減です。scan できる port も取得できる banner もありません。また、address 自体が public key であるため、SSH が開始する前に endpoint の identity を検証できます。
実際には、両方を使う構成が一般的です。日常の経路として WireGuard を使用し、WireGuard の設定が誤っている場合にも機能する経路として onion service を維持します。これにより、public SSH port ではなく 1 つの UDP port だけを開放できます。sshd 自体の hardening の代わりにはなりません。key-only authentication と non-root login は引き続き重要です。onion service が保護するのは network path であり、それより先は保護しないためです。
障害パターンと表示されるエラー
Tor が Bootstrapped 0% を通過しません。 外向きのネットワークトラフィックがブロックされているか、時刻が大きくずれています。sudo ufw status verbose で送信ポリシーを確認し、続いて timedatectl を実行します。
systemctl status tor に active (exited) と表示されます。 Debian と Ubuntu では正常です。代わりに tor@default を確認します。
ディスクリプターが見つかりません。 Tor は SOCKS 拡張エラー F0「Onion Service Descriptor Can Not be Found」を返します。ディスクリプターがまだ公開されていないか、サーバー上の tor が実行されていません。再読み込み後の公開には少し時間がかかります。
F4「Onion Service Missing Client Authorization」。 クライアントに、tor が使用できる対応する .auth_private がありません。ClientOnionAuthDir が torrc に記載されていること、ディレクトリのモードが 0700 であること、ファイル名が .auth_private で終わっていること、debian-tor がそのファイルを読み取れることを確認します。
F5「Onion Service Wrong Client Authorization」。 秘密鍵がサーバー上の .auth ファイルと一致していません。base32 文字列の末尾に = が付いているか、文字列内に余分な改行があると、このエラーになります。
nc: invalid option -- 'X'。 OpenBSD 版ではなく GNU netcat がインストールされています。sudo apt install -y netcat-openbsd を実行します。
Could not resolve hostname。 ssh は通常の DNS を試しましたが、.onion に対する応答がないため、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 とは別の制御なので、こちらでも受信ポートを閉じる必要があります。
SSH では .onion アドレスだけで十分なセキュリティを確保できますか?
いいえ。アドレスは 56 文字で、ディレクトリシステムから推測したり列挙したりできないため、秘密情報のように機能します。ただし、シェル履歴や設定ファイルから漏れる可能性があります。v3 クライアント認証を追加してください。これを有効にすると、サービス記述子はクライアントキーで暗号化されます。そのため、アドレスしか持たない相手には拡張エラー F4 が返され、sshd には到達できません。
再起動後に tor の起動に失敗すると、どうなりますか?
SSH 接続を完全に失います。onion address が唯一の接続経路になるためです。そのため、ポート 22 を閉じる前にプロバイダーのコンソールをテストする必要があります。Tor は起動後のブートストラップにも時間がかかるため、マシンが ping に応答してから onion address が応答するまで時間がかかります。応答しない場合はコンソールからログインし、sudo journalctl -u tor@default -b を確認してください。torrc の構文エラーや /var/lib/tor/ssh の権限問題が出力されています。
SSH over Tor は WireGuard より遅いですか?
はい。大幅に遅くなります。onion service への接続は、ランダムに選ばれた約 6 個のリレーを経由します。一方、WireGuard はサーバーへ直接接続する 1 ホップの暗号化通信です。入力時の遅延が大きく、ファイル転送も遅くなります。一般的な構成では、日常の作業に WireGuard を使い、VPN の設定が壊れた場合にも利用できる緊急用の経路として onion service を残します。