概要
- 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 環境なら証明書管理も自動化可能
- スクリプト を活用することで運用も簡単、友人との安全なプレビュー共有が実現
これはDNTLSで取り組んでいるコアな部分の一つだよ。トンネルのアイデアが大好きで、特にプライベートなやり取りに使えるのがいいね。SaaSプロバイダー(TailscaleやCloudflareなど)は、インフラをすごく簡単にしてくれてるけど、"セルフホスティング"の境界が曖昧になっちゃうんだよね。理想としては、仲介なしで完全に動かせるようなソリューションができればいいな。