本文へ
ハクソク

世界を動かす技術を、日本語で。

SSHとNginxを用いた自己ホスト型HTTPトンネル

元記事: 原文(vincent.bernat.ch)

概要

  • OpenSSH と nginx のみで安全な自己ホスト型トンネルを構築する方法の解説
  • ngrok や Cloudflare Quick Tunnels などの商用サービス不要
  • ポート転送 と nginxリバースプロキシ の設定手順
  • セキュリティ強化 のための ngx_http_secure_link_module 活用法
  • 自動URL生成スクリプト の紹介と活用

OpenSSHとnginxで自己ホスト型HTTPトンネルを構築する

  • ローカル開発環境 (localhost:8080)を外部からアクセス可能にする自己ホスト型ソリューション
  • 商用サービス (ngrok, Cloudflare Quick Tunnels)や 専用クライアント (frp, localtunnel)を使わない構成
  • OpenSSH のリモートポートフォワード機能を利用
    • コマンド例:ssh -N -R 0:localhost:8080 web02.luffy.cx
    • ポート番号0指定で、サーバー側が空きポートを自動割当
  • nginx でリバースプロキシ設定
    • server_name ~^p(?<port>\d\d\d\d\d)\.ssh\.luffy\.cx$; で動的にポート抽出
    • proxy_pass http://127.0.0.1:$port; で該当ポートへ転送
  • DNS 設定・ワイルドカード証明書発行
    • *.ssh.luffy.cxにCNAME、CAA、ACMEチャレンジ用レコードを追加
    • Let’s EncryptのDNS-01チャレンジで自動証明書取得(NixOS環境で自動化可能)

セキュリティ強化:ngx_http_secure_link_moduleの活用

  • 転送先ポート番号 のみが「秘密」状態だとセキュリティが不十分
  • ngx_http_secure_link_module で認証付きURLを生成
    • ハッシュ値+有効期限+ポート番号を組み合わせてURLのユーザー名部分に埋め込み
    • 例:https://[email protected]/en/blog
  • nginx設定例
    • $remote_userからハッシュと有効期限を抽出し、secure_linkで検証
    • ハッシュ不一致時は401エラー、期限切れ時は410エラーを返却
    • Authorizationヘッダーを削除し、WebSocket対応も追加
  • ハッシュ生成例
    • printf '%s %s %s' "$expires" "$port" "$secret" | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
    • ハッシュ値・有効期限・ポート番号・シークレットを組み合わせて安全なワンタイムURLを作成

自動URL生成スクリプトの作成

  • OpenSSH が割り当てるエフェメラルポート番号の自動検出が課題
  • ssコマンド と psコマンド でsshd-sessionプロセスに紐づくポートを抽出
  • forループ で各ポートごとに安全なURLを生成し、表示
  • スクリプト例
    • http-over-sshとしてサーバーに設置
    • ~/.ssh/configにエントリ追加で、ワンコマンド運用可能
    • NixOS向けにはhttp-over-ssh.nixも提供

まとめ:自己ホスト型トンネルのメリット

  • OpenSSH と nginx のみで完結、追加サービス不要
  • ワンコマンド で安全なトンネルと共有用URLを即時発行
  • 商用サービスに依存せず、セキュリティも強化可能
  • NixOS 環境なら証明書管理も自動化可能
  • スクリプト を活用することで運用も簡単、友人との安全なプレビュー共有が実現

提供: ハクソク · 要約更新:

AIによる日本語要約です。内容は原文とあわせてご確認ください。

Hacker Newsの議論を読む

Hackerたちの意見

これはDNTLSで取り組んでいるコアな部分の一つだよ。トンネルのアイデアが大好きで、特にプライベートなやり取りに使えるのがいいね。SaaSプロバイダー(TailscaleやCloudflareなど)は、インフラをすごく簡単にしてくれてるけど、"セルフホスティング"の境界が曖昧になっちゃうんだよね。理想としては、仲介なしで完全に動かせるようなソリューションができればいいな。

└

つまりWireGuardみたいな感じ?

セルフホスティングなら、なんで第三者サービスの *.ssh.luffy.cx が必要なの?

└

自分のドメインに置き換えればいいよ。

この手のことには、irohを使ったhttpsが一番ワクワクしてる。 - https://github.com/aflin/iroh-webproxy - https://github.com/n0-computer/iroh-proxy-utils ポートフォワーディングもいらないし、パブリックIPも不要。特別なプロキシを設定する必要もない。Irohはすでにパブリックリレーを運営してるから、2台のコンピュータはそれを通じて信号を送り合って、ポートノックして直接接続するんだ。しかも完全に暗号化されてる。新しいhttps://のURLを定義する必要があるね、例えば「irohttps://」って呼んで、そうすれば「irohttps:///path?query」で僕のノートパソコンに連絡できる。

└

ポートノックして直接接続するんだ、しかも完全に暗号化されてる。 それって…ICE/TURN/STUNのこと? どれが実際に機能するか忘れちゃったけど、ちゃんと動くよ。

└

iroh-ssh、今は動いてるよ!: https://github.com/rustonbsd/iroh-ssh 完璧ではないけど、cgnatを通じてウェブインターフェースのポートフォワーディングには十分な感じ。

└

「Irohはすでに公開リレーを運営してる。」それは「セルフホスティング」じゃないよ。前に確認した時、このプロジェクトは直接接続が失敗した時に、自動的に彼らのサーバーを通してトラフィックを転送するんだ。ピアツーピアですらないし。

ユーザースペースのWireGuardで似たようなことを進めてるから、近いうちにシェアするよ。

コードは手元にないけど、特定の証明書がブラウザにインストールされている場合だけコンテンツを提供するnginxサーバーを持つ方がいいと思う。そうすれば、キーのペアを生成して、コンテンツを共有したい相手に公開鍵を渡すだけで済むから。欠点は、いくつかのブラウザが証明書を正しく処理できないこと(特にスマホでは)。

└

HTPSサーバーは常に公開鍵を配布するんだよね。クライアントの秘密とかのことを言ってるの?

これ、めちゃくちゃ複雑で、いろんな罠やセキュリティリスクがあるよ。例えば、最初のNGINXのセクションでは、攻撃者がローカルホストの任意のリスニングポートにトラフィックを向けることができちゃう。bashでcurlを使っていろんなポートをテストする簡単なforループも書けるしね。これ、外部からのトラフィックをブロックするファイアウォールのルールも無視しちゃうから、この記事の指示にはめっちゃ注意が必要だよ。SSHのmanページを読んで、特にリモートフォワードのセクションをチェックしてね。 https://man7.org/linux/man-pages/man1/ssh.1.html

Hacker Newsで議論の続きを見る