はじめに(この記事で分かること)#

SSHポート22をグローバルIPに公開すると、世界中のスキャナーが1日に何千回もの認証試行を送ってくる。鍵認証のみにしていても、ポートが開いていること自体がサーバーの存在を示すシグナルになる。

Shodan/Censysの記事で説明したとおり、SSHのホスト鍵フィンガープリントはIPアドレスが変わっても残る識別子だ。SSHポートを公開しない設計にすることで、この識別子自体が外に漏れることを防ぐ。

この記事では、踏み台サーバー(bastion)とWireGuardを組み合わせて、バックエンドサーバーのSSHを完全にグローバルネットワークから隠す構成を実装する。検証環境はUbuntu 26.04 LTSである。

前提: 脅威モデルの整理#

SSHを公開しない設計が有効な相手は、脅威モデル入門で整理した5種類の相手のうち、主に2者だ。

CensysやShodanのような走査エンジンは、SSHポートが開いているサーバーを走査し、ホスト鍵のフィンガープリントを収集する。ポートが閉じていれば、この走査は何も得られない。

標的型攻撃者は、公開されたSSHポートを攻撃の起点として使う。鍵認証のみであっても、認証情報の流出やOpenSSH自体の脆弱性を悪用される可能性は残る。

アーキテクチャ#

管理者 → 踏み台サーバー(bastion、公開 IP あり)
              ↓ ProxyJump または WireGuard
        バックエンドサーバー(公開 IP なし、または UFW で bastion のみ許可)

踏み台サーバーだけがグローバルIPでSSHを受ける。バックエンドサーバーのSSHは、踏み台のIPからのアクセスのみを許可するか、WireGuardトンネル内のみで受け付ける。

さらに踏み台をTorのonionサービスとして公開すれば、踏み台のIPアドレスも隠せる。

ステップ1: 踏み台サーバーの設定#

踏み台サーバーには、SSHのhardened設定を適用する。

# /etc/ssh/sshd_config のベースライン
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30
AllowUsers deploy
ForwardAgent no

ForwardAgentを踏み台で有効にしない。踏み台が侵害された場合、エージェント転送によりバックエンドの鍵にアクセスされる。

ステップ2: バックエンドサーバーのUFW設定#

バックエンドサーバーでは、SSHを踏み台のIPからのみ許可する。

ufw default deny incoming
ufw default allow outgoing
ufw allow from BASTION_PUBLIC_IP to any port 22 proto tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

BASTION_PUBLIC_IPは踏み台サーバーの公開IPアドレスに置き換える。

ステップ3: ProxyJumpでの接続#

管理者のマシンから、SSHのProxyJump機能で踏み台を経由してバックエンドに接続する。

# ~/.ssh/config
Host bastion
  HostName bastion.example.com
  User deploy

Host backend
  HostName backend.internal.ip
  User deploy
  ProxyJump bastion
ssh backend

この接続は、踏み台を経由してバックエンドのSSHに到達する。踏み台が存在しないIPからバックエンドへの接続は、UFWにより拒否される。

ステップ4: WireGuardトンネル内のみでSSHを受け付ける#

さらに厳格にするには、SSHをWireGuardトンネル内のみで受け付ける。

# WireGuard のインストール
apt install wireguard

踏み台サーバーにWireGuardのサーバー側設定を行う。

# /etc/wireguard/wg0.conf(踏み台サーバー)
[Interface]
Address = 10.50.0.1/24
PrivateKey = <bastion-private-key>
ListenPort = 51820

[Peer]
# backend
PublicKey = <backend-public-key>
AllowedIPs = 10.50.0.2/32

バックエンドサーバーにWireGuardのクライアント側設定を行う。

# /etc/wireguard/wg0.conf(バックエンドサーバー)
[Interface]
Address = 10.50.0.2/24
PrivateKey = <backend-private-key>

[Peer]
PublicKey = <bastion-public-key>
Endpoint = <bastion-public-ip>:51820
AllowedIPs = 10.50.0.1/32
PersistentKeepalive = 25
systemctl enable --now wg-quick@wg0
wg show

ステップ5: SSHをトンネルIPでのみリッスンする#

バックエンドサーバーのsshdを、WireGuardのトンネルIPでのみリッスンさせる。

# /etc/ssh/sshd_config
ListenAddress 10.50.0.2
systemctl restart sshd
ss -tulpn | grep sshd
# 10.50.0.2:22 のみが表示されることを確認

この状態では、バックエンドサーバーのSSHはWireGuardトンネル内からのみアクセス可能であり、グローバルIPのどのポートにもSSHは存在しない。

ステップ6: 発信方向の接続制限#

バックエンドサーバーは、SSHを受けないだけでなく、自分から外に向けて接続を開くときにもIPアドレスを露出する。

Shodan/Censysの記事で説明したとおり、パッケージミラー、NTP、Webhook、エラーレポーター、Git remoteなど、バックエンドが自分から接続を開く宛先は、バックエンドのIPを送信元として記録する。

ServPrivacyのガイドは、この問題を「egressリーク」として明確に定義し、二つの対策を示している。

対策A(厳格): バックエンドの全発信トラフィックをWireGuardトンネル経由にし、踏み台サーバーでNATを行う。wg-quickは0.0.0.0/0ルートでfwmarkルールを自動インストールする。

対策B(実用的): ユーザー供給URLを取得する機能(リンクプレビュー、OGPフェッチ等)の前に必ずプロキシを挟む。

どちらの対策を採ったかを明示的に決め、実際に自分のホストへリクエストしてログの送信元IPで検証することまでを手順に含める。

ステップ7: 踏み台をonionサービスとして公開する#

さらに踏み台のIPも隠すには、SSHをTorのonionサービスとして公開する。

SSD Nodesの手法では、sshdをonion serviceで公開し、グローバルポートは完全に閉じる。onionアドレス自体が公開鍵であり、ポートゼロでスキャン不能になる。

# /etc/tor/torrc
HiddenServiceDir /var/lib/tor/ssh-service/
HiddenServicePort 22 127.0.0.1:22

接続にはSSHのProxyCommandでTorを使う。

# ~/.ssh/config
Host bastion-onion
  HostName xxxxxxxxxxxx.onion
  User deploy
  ProxyCommand nc -X 5 -x 127.0.0.1:9050 %h %p

この構成では、踏み台サーバーのIPアドレスは攻撃者から見えず、ポートスキャンの対象にもならない。

限界と残るリスク#

踏み台サーバーのIPアドレスは、踏み台自身が外部のサービスに接続する際には露出する。NTP、DNS、パッケージミラー、ソフトウェアの更新リポジトリへの接続元IPは、踏み台のIPである。

踏み台が侵害されれば、バックエンドへのアクセス経路が攻撃者の手に渡る。踏み台にMFAを設定し、踏み台自体の監査ログを外部に転送することで、このリスクを軽減する。

Shodan/Censysの記事で説明した自己監査の手順を、踏み台とバックエンドの両方に対して定期的に実行する。

まとめ#

SSHをグローバルIPに公開しない設計は、3段階で構築できる。

第一段階は、UFWで踏み台のIPからのみSSHを許可する構成だ。第二段階は、SSHをWireGuardトンネル内でのみリッスンさせる構成だ。第三段階は、踏み台自身をonionサービスとして公開する構成だ。

どの段階でも、バックエンドサーバーの発信方向の接続(NTP、Webhook、SMTPなど)がIPアドレスを露出する点は変わらない。egressの制御は、踏み台とWireGuardの構成とは別の課題として扱う。

この記事で説明した構成は、IPアドレスを出さずにonionサイトを運営する方法と組み合わせることで、サーバー全体の匿名化をさらに進めることができる。

出典#