内部サービス向け TLS 証明書: Split-Horizon DNS と ACME の実装

内部サービス向け TLS 証明書: Split-Horizon DNS と ACME の実装

内部 TLS の課題

内部サービスを TLS で保護する場合、通常、自己署名証明書を使用するか、パブリック証明書を使用するかという二者択一の選択を迫られます。自己署名証明書は内部ネットワークの詳細を漏洩させることを避けられますが、すべてのクライアントデバイスにカスタムルート CA を手動で信頼させる必要があり、管理上の負担が大きくなります。パブリック証明書はデフォルトで信頼されますが、通常、検証のためにサービスがパブリックインターネット経由で到達可能である必要があります。

内部サービスへの Split-Horizon DNS の実装

Split-horizon DNS を使用すると、単一のドメイン名が、リクエスト者の場所に応じて異なる IP アドレスに解決されるようになります。内部サービスの場合、これは grafana.tuxnet.dev のようなドメインが、外部リゾルバに対しては(CA 検証を満たすために)パブリック IP に解決され、VPN 経由で接続されたクライアントに対してはプライベートな内部 IP に解決されることを意味します。

技術アーキテクチャ

このワークフローを実装するには、以下のコンポーネントが必要です。

  1. VPN と DNS リゾルバ: NetBird のようなツールを使用して Custom Zones を管理し、VPN 接続されたクライアントが内部 IP を受信し、サーバー自体は証明書発行を容易にするためにパブリック DNS を使用するようにします。
  2. ACME クライアント: acme.sh を使用して、Let's Encrypt や ZeroSSL からの証明書発行を自動化できます。
  3. リバースプロキシ/WAF: Nginx をエントリポイントとして使用し、VPN ネットワークインターフェースにのみバインドすることで、VPN からのトラフィックのみが内部サービスにアクセスできるようにし、実質的に Web Access Firewall (WAF) として機能させます。

証明書の発行と更新

acme.sh をスタンドアロンモードで使用して http-01 チャレンジを行うことで、クライアントは発行プロセス中のみポート 80 にバインドできます。つまり、Nginx が常にポート 80 を占有する必要はありません。

更新を自動化するには、cron ジョブを使用して acme.sh --cron を実行し、その後に証明書を Nginx ディレクトリに同期してサービスをリロードするように設定できます。ACME クライアントを root として実行することを避けるために、socatsetcap CAP_NET_BIND_SERVICE=+ep を適用することで、非特権ユーザーがポート 80 にバインドできるようにします。

SANs と CNAMEs によるスケーリング

個々の内部サービスごとに個別の証明書を発行するのではなく、Subject Alternative Names (SANs) を使用できます。複数の DNS 名(例: internal.tuxnet.devgrafana.tuxnet.dev、および analytics.tuxnet.dev)を含む単一の証明書を作成することで、単一の証明書ファイルを複数の Nginx サーバーブロックで再利用できます。

代替アプローチとトレードオフ

Split-horizon DNS はクライアントの信頼問題は解決しますが、「split-brain」DNS 動作や内部ホスト名の漏洩を懸念する多くのエンジニアによって議論の対象となります。

DNS-01 検証

多くの実務者は HTTP-01 よりも DNS-01 チャレンジを推奨しています。DNS-01 検証を使用すると、CA がパブリック DNS の TXT レコードを通じてドメイン所有権を検証できるため、内部サービスがパブリックにルーティング可能であったり、パブリック IP アドレスが関連付けられていたりする必要がありません。これにより、split-horizon DNS の必要性が完全に排除されます。

ワイルドカード証明書

ワイルドカード証明書 (*.domain.com) は、特定の内部サブドメイン名を Certificate Transparency (CT) ログに漏洩させることを避けるために頻繁繁使用されます。プライベートキーが侵害された場合にワイルドカード証明書はセキュリティリスクが高まると主張する人もいますが、一方で、すべての終端処理を単一のロードバランサーが扱う内部インフラストラクチャにおいては、より実用的であると主張する人もいます。

内部 CA と mTLS

高セキュリティ環境では、内部 CA (例: step-ca) を使用することが好まれることが多いです。これにより、内部ホスト名がパブリック CT ログに表示されないようになります。さらに、相互 TLS (mTLS) を実装することで、標準的な TLS とは異なる強力なアイデンティティ保証を提供できます。これは、クライアントにサーバーへ証明書を提示させる必要があり、セキュリティをネットワーク層 (VPN) からアイデンティティ層へと移行させるものです。

比較のまとめ

メソッド クライアントの信頼 DNS の複雑さ プライバシー (CT ログ) ネットワークの露出
Split-Horizon 自動 低 (名前が漏洩する) 低 (WAF 経由)
DNS-01 自動 低 (名前が漏洩する) なし
Internal CA 手動/MDM 高 (プライベート) なし
mTLS 手動/MDM 高 (プライベート) なし

Sources