onareのBlog

sslhでポート443を共有しOpenVPN・SSH・Webを1つに集約

はじめに

自宅サーバーで Web サイト以外に SSH や VPN など、複数のサービスを外部から利用したいと考えたことはありませんか? しかし、サービスごとにポートを分けると管理が煩雑になり、セキュリティの観点からも公開ポートは最小限に抑えたいものです。さらに、外出先のフリー Wi-Fi 環境では 443 番ポート(HTTPS) 以外の通信が許可されていないケースも少なくありません。

このような課題を解決するのが、今回紹介する「sslh」です。sslh は、単一のポート(ここでは 443 番)で受け取ったトラフィックを、そのプロトコルに応じて適切なバックエンドサービスに振り分けてくれる、非常に便利なプロトコルマルチプレクサです。

この記事では、sslh を活用し、OpenVPN、SSH、Web サービス(nginx)をすべて 443 番ポートに集約する方法を、Docker Compose を用いてスマートに実現する手順を解説します。

全体構成図

今回構築するシステムの全体像は以下の通りです。外部からのすべてのアクセスはまず sslh に届き、そこから各サービスへとインテリジェントに転送されます。

(インターネット)
  |
  | <--- TCP 443番ポートへのアクセス
  |
+-----------------------------+
|    ルーター / ファイアウォール    |
| (ポートフォワーディング: 443) |
+-----------------------------+
  |
  |
+-----------------------------+
|        Docker ホスト        |
|                             |
|  +-----------------------+  |
|  |   sslh コンテナ        |  |  <--- すべてのトラフィックを最初に受け取る
|  | (ポート 443 をリッスン) |  |
|  +-----------------------+  |
|      |   |             |    |
| (プロトコル判定) |   | (プロトコル判定) |
|      |   |             |    |
|  +---+   +---+         +----+
|  |         |           |    |
|  v         v           v    |
| +---------+ +---------+ +---------+
| | nginx   | |  sshd   | | OpenVPN |
| |コンテナ | |(ホストOS)| |コンテナ |
| +---------+ +---------+ +---------+
|                             |
+-----------------------------+

sslh とは? - プロトコルマルチプレクサの仕組み

sslh は「プロトコルマルチプレクサ(Protocol Multiplexer)」と呼ばれるツールの一種です。

  • マルチプレクサ (Multiplexer): 日本語で「多重化装置」。複数の信号をまとめて一つの伝送路で送るための装置や技術を指します。

クライアントからの接続時、sslh は通信パケットの最初の数バイトを検査します。例えば、SSH 接続であれば SSH-2.0... といった特徴的な文字列が、TLS/SSL(HTTPS や OpenVPN など)接続であれば固有のバイナリパターンが含まれています。sslh はこれらのシグネチャを識別し、「これは SSH」「これは TLS/SSL」といった判断を下し、あらかじめ設定されたサーバー(コンテナ)へトラフィックを透過的に転送(フォワード)します。

この仕組みにより、外部からは単一のポートにアクセスしているように見えながら、内部では全く異なるサービスを動かすことが可能になるのです。

Docker Compose による環境構築

それでは、Docker Compose を使って環境を構築しましょう。以下の 3 つのサービスを 1 つの docker-compose.yml ファイルで管理します。

  • sslh: 中核となるプロトコルマルチプレクサ
  • nginx: Web サーバー(HTTPS 通信担当)
  • openvpn: OpenVPN サーバー

なお、SSH サーバー(sshd)は、コンテナではなくホスト OS 上で直接稼働しているものを想定しています。

docker-compose.yml の作成

以下が docker-compose.yml の全体像です。各サービスについては後ほど詳しく解説します。

services:
  sslh:
    image: ghcr.io/yrutschle/sslh:latest
    container_name: sslh
    ports:
      - "443:443"
    command: >
      --foreground
      --listen=0.0.0.0:443
      --tls=nginx:443
      --ssh=host.docker.internal:22
      --openvpn=openvpn:1194
    environment:
      TZ: Asia/Tokyo
    restart: always
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      - nginx
      - openvpn

  nginx:
    container_name: nginx
    image: nginx:latest
    restart: always
    environment:
      TZ: Asia/Tokyo

  openvpn:
    container_name: openvpn
    image: openvpn/openvpn-as:latest
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - MKNOD
    devices:
      - /dev/net/tun
    environment:
      TZ: Asia/Tokyo

各サービスの詳細解説

1. sslh サービス

  • ports: ["443:443"]: これが唯一、外部(インターネット)に公開するポートです。ホストマシンの 443 番ポートに来た通信を、すべてこのコンテナが受け取ります。
  • command: sslh の挙動を決定する重要な部分です。
    • --listen 0.0.0.0:443: コンテナ内の 443 番ポートで待ち受けます。
    • --ssh host.docker.internal:22: SSH と判断した通信を、ホスト OS の 22 番ポートに転送します。host.docker.internal はコンテナからホスト自身を指すための特別な DNS 名で、extra_hosts の設定により有効化しています。
    • --tls nginx:443: SSL/TLS と判断した通信を、nginxコンテナの 443 番ポートに転送します。
    • --openvpn openvpn:1194: OpenVPN と判断した通信を、openvpnコンテナの 1194 番ポートに転送します。
  • depends_on: nginxopenvpn が起動してから sslh が起動するように、サービスの依存関係を定義します。

2. nginx サービス

Web サーバーです。ports の定義がない点に注目してください。これにより、コンテナは外部から直接アクセスできず、sslh 経由のトラフィックのみを受け付けるため、セキュリティが向上します。

3. openvpn サービス

OpenVPN サーバーです。これも ports は非公開です。OpenVPN Access Server のより詳しい設定方法はこちらの記事などを参考にしてください。

サービスの起動

設定が完了したら、以下のコマンドで全サービスをバックグラウンドで起動します。

docker-compose up -d

外部からの動作確認方法

サーバーのグローバル IP アドレスまたは設定したドメイン名を your-domain.com として、以下の方法で各サービスに接続できるか確認します。

  1. Web (HTTPS):
    ブラウザで https://your-domain.com にアクセスし、Nginx のデフォルトページが表示されれば成功です。curl コマンドでも確認できます。

    curl https://your-domain.com
    
  2. SSH:
    ssh コマンドの -p オプションでポートを 443 に指定します。

    ssh [email protected] -p 443
    
  3. OpenVPN:
    クライアント用の設定ファイル(.ovpn)を開き、remote ディレクティブを以下のように変更します。

    # 変更前
    proto tcp
    remote your-domain.com
    port 1194
    
    # 変更後
    proto tcp
    remote your-domain.com
    port 443
    

    ポートを 1194 から 443 に変更するのがポイントです。この設定ファイルを使って OpenVPN クライアントから接続できれば成功です。

セキュリティに関する考慮事項

この構成は非常に便利ですが、単一のポートにサービスを集約するからこそ、セキュリティには一層の注意が必要です。

  • SSH のパスワード認証無効化: 必ず公開鍵認証のみを許可するように sshd_config を設定してください。
  • Fail2Ban の導入: SSH へのブルートフォースアタック(総当たり攻撃)を防ぐため、fail2ban のような侵入防止ソフトウェアの導入を強く推奨します。
  • 強力なパスワードと鍵: OpenVPN の証明書や SSH の鍵、その他サービスで利用するパスワードは、すべて推測困難で強力なものを利用してください。
  • 定期的なアップデート: docker-compose pull コマンドを定期的に実行し、各コンテナのイメージを最新の状態に保ち、既知の脆弱性からシステムを保護してください。

まとめ

今回は、sslh と Docker Compose を使い、443 番ポートという一般的に開放されているポートに Web、SSH、OpenVPN の 3 つのサービスを集約する方法を解説しました。これにより、どんなネットワーク環境からでも自宅サーバーに安全かつスマートにアクセスするための、強力なエントリーポイントを構築できます。

公開するポートを最小限にすることは、サーバーセキュリティの基本です。ぜひこのテクニックを活用して、より安全で便利な自宅サーバー環境を構築してみてください。

関連記事