【実務・中級編】 IPsecプロトコルスイートにおけるトランスポートモードとトンネルモード – サイバーセキュリティとプライバシー保護実践ガイド

カフェのフリーWi-Fiで「よし、本番環境のAPIエンドポイントを叩いてデバッグするか」なんて、冷や汗が出るような危なっかしい真似をしていないだろうか。

こんにちは。数々の修羅場をくぐり抜けてきたインフラエンジニアなら、パケットキャプチャの画面を開かなくても、暗号化されていないWi-Fi空間を流れる平文のTLSハンドシェイクや、HTTPヘッダーがどれほど無防備に晒されているか想像がつくはずだ。私たちの仕事は、コードを書くだけではない。そのパケットが流れる足元、つまりネットワーク層の安全性を担保することまでがセットだ。

今回は、ネットワーク層(L3)のセキュリティにおける絶対王者、IPsecプロトコルスイートを取り上げる。特に、Web API設計やクラウド基盤のインフラ運用に携わるエンジニアなら避けて通れない、「トランスポートモード」と「トンネルモード」の構造的な違いを、パケットの挙動と実践的な設定から紐解いていこう。

—

1. なぜ今、IPsecなのか? アプリケーション層からネットワーク層への回帰

現代の開発現場では、HTTPS(TLS)による暗号化がデファクトスタンダードだ。API通信であれWebブラウジングであれ、上位層(L7)での保護は万全に見える。しかし、オンプレミス環境とクラウド(AWS VPCやAzure VNet)を安全に接続するサイト間VPNや、リモートワーカーが社内ネットワークにダイレクトインする環境を構築する際、TLSだけでは要件を満たせないケースに直面する。

ここで登場するのが、OSI参照モデルの第3層(ネットワーク層)で動作するIPsecだ。
IPsecの最大の特徴は、「アプリケーション層に一切の変更を強いることなく、IPパケットそのものを保護する」という点にある。Web APIを叩くPythonスクリプトやcurlコマンドは、背後でIPsecが動いていることなど露知らず、ただ宛先IPを指定してパケットを送り出すだけでいい。

IPsecの中核をなすプロトコルには、ペイロードの完全性と機密性を保証する ESP (Encapsulating Security Payload) と、認証と完全性のみを提供する AH (Authentication Header) がある。今回は、現代の実務でほぼ100%選択されるESPに絞って話を進めよう。

—

2. 構造解剖:トランスポートモード vs トンネルモード

IPsecの挙動を理解する上で最も重要な分岐点が、この「モード」の選択だ。パケットのどこを保護し、どうカプセル化するのか。図解の代わりに、その構造的な違いを頭の中に焼き付けてほしい。

トランスポートモード:エンドツーエンドのミニマリスト

トランスポートモードは、文字通り「ホスト間(End-to-End)」の通信を保護するために設計されたモードだ。

  • 対象: IPパケットの「ペイロード(上位層のデータ)」のみ
  • IPヘッダー: 元のIPヘッダーがそのまま維持される
  • 主な用途: ホストAとホストBが直接IPsecアソシエーション(SA)を結び、その間の通信を暗号化する場合(例:サーバー管理用のSSHや、特定のセキュアなAPI通信)
[ 元のIPヘッダー ] + [ ESPヘッダー ] + [ ペイロード(TCP/UDP + データ) ] + [ ESPトレーラー ]

元のIPアドレスがそのまま露出するため、NAT(Network Address Translation)を挟む環境ではパケット改変によって暗号チェックサムが破綻し、そのままではルーティングが泥沼化するという弱点がある。

トンネルモード:ネットワークの盾、カプセル化の要

インフラエンジニアが日常的に「VPNを張る」と言う場合、99%はこのトンネルモードを指している。ルーター間や、クライアント端末とクラウドのゲートウェイ間を結ぶために使われる。

  • 対象: IPパケット「全体」
  • IPヘッダー: 元のIPパケット全体がESPで丸ごと暗号化され、その外側に「新たなIPsec用のIPヘッダー」が冠される
  • 主な用途: サイト間VPN(拠点間接続)、リモートアクセスVPN
[ 新しいIPヘッダー(VPNGW間) ] + [ ESPヘッダー ] + [ 元のIPヘッダー ] + [ ペイロード ] + [ ESPトレーラー ]

外側のIPヘッダーには、VPNゲートウェイ同士のIPアドレスが記載される。内部のパケット(元のIPヘッダーとデータ)は完全に隠蔽されるため、プライベートIPアドレス同士の通信を、インターネットという荒海を越えて安全にルーティングすることが可能になる。

—

3. 通信フロー:IKEとESPが織りなすハンドシェイクの裏側

「ボタン一つでVPNがつながる」のは、裏側で複雑なネゴシエーションが行われているからだ。IPsecの通信は、大きく分けて2つのフェーズで成り立っている。

1. IKE (Internet Key Exchange) フェーズ:
暗号化のアルゴリズム(AES-256など)やハッシュ関数、鍵交換の方式(Diffie-Hellmanグループ)を合意し、セキュアな管理チャネル(ISAKMP SA)を確立する。
2. IPsec SA (Security Association) の確立:
実際にデータ(ESP)を流すための具体的な鍵とパラメータを生成する。

実務でトラブルシューティングを行う際、「Phase 1でコケているのか、Phase 2でコケているのか」を切り分けるのがエンジニアの第一歩だ。IKEv2全盛の現在でも、プリシェアードキー(事前共有鍵)のミスや、ライフタイム(鍵の有効期限)の不一致でトンネルが落ちる事故は後を絶たない。

—

4. 実務で役立つ設定・実装の具体例

では、実際にインフラ設定やアプリケーションからこの世界にアプローチしてみよう。今回は、Linux(StrongSwanなどのIPsecデーモン)を想定した設定ファイルのイメージと、そのトンネル経由で叩かれるAPIクライアントのコードを示す。

設定ファイル例: StrongSwan (ipsec.conf) のトンネルモード定義

AWSのVPCカスタマーゲートウェイとオンプレミスルーターを接続するような、代表的なサイト間VPNの定義例だ。

# /etc/ipsec.conf
config setup
    uniqueids = yes

conn %default
    # 共通の基本設定
    keyexchange = ikev2
    ikelifetime = 28800s
    salifetime = 3600s
    dpdaction = restart
    dpddelay = 30s

conn aws-to-onpremise
    type = tunnel          # トンネルモードを指定(ホスト間なら transport)
    left = 203.0.113.10    # 自拠点(オンプレミス)のグローバルIP
    leftid = 203.0.113.10
    leftsubnet = 192.168.10.0/24 # 自拠点のプライベートサブネット
    right = 198.51.100.20  # 相手拠点(AWS VGW)のグローバルIP
    rightid = 198.51.100.20
    rightsubnet = 10.0.1.0/24    # AWS側のVPCサブネット
    auto = start           # デーモン起動時に自動で接続を開始
    authby = secret        # 認証方式(事前共有鍵)
    esp = aes256-sha256-modp2048 # 暗号化・整合性アルゴリズムの指定

この設定が有効化されると、192.168.10.0/24 から 10.0.1.0/24 宛てのパケットはすべて自動的にトンネルモードでカプセル化され、暗号化されて対向ルーターへ飛び込んでいく。

アプリケーションコード例: Python (requests) によるAPIリクエスト

IPsecトンネルが確立されていれば、アプリケーション側は「あたかも同一LAN内にあるかのように」プライベートIPへリクエストを投げればいい。

import requests
from requests.exceptions import RequestException

# IPsecトンネルの向こう側にある社内/クラウド内プライベートAPIのエンドポイント
API_ENDPOINT = "https://10.0.1.50/api/v1/internal-data"

def fetch_secure_internal_data():
    headers = {
        "Authorization": "Bearer secret_api_token_xyz",
        "Content-Type": "application/json"
    }
    
    try:
        # ネットワーク層はIPsecで暗号化されているため、
        # アプリケーション層では通常のHTTPS(またはHTTP)で通信を完結できる
        response = requests.get(API_ENDPOINT, headers=headers, timeout=5)
        
        # ステータスコードのチェック
        response.raise_for_status()
        
        print("データ取得成功:", response.json())
        return response.json()

    except RequestException as e:
        # ネットワーク断やタイムアウト、IPsecのダウン時などのハンドリング
        print(f"APIリクエストに失敗しました: {e}", file=sys.stderr)
        # ここでSlack通知やメトリクス送信のフックを入れるのが実務の作法
        raise

if __name__ == "__main__":
    fetch_secure_internal_data()

—

5. 現場のシニアからの実践的Tips・デバッグ手順

最後に、現場で「VPNがつながらない!」と夜中に叩き起こされたときのための、実践的なデバッグ手順を授けよう。

1. ステータスの確認:
まずはIPsecデーモンのステータスを確認する。StrongSwanであれば sudo ipsec status や sudo swanctl --list-sas を叩き、Phase 1(IKE_SA)とPhase 2(CHILD_SA)がどちらも ESTABLISHED になっているか確認せよ。ここが落ちていれば、いくらアプリコードを直しても無駄だ。
2. パケットキャプチャの極意:
tcpdump を使うときはインターフェースに注意しろ。

  • tcpdump -i eth0 esp (カプセル化された暗号化パケットを確認)
  • tcpdump -i ipsec0 icmp (トンネルを抜けたあとの生パケットを確認 ※インターフェース名は環境依存)

暗号化されたESPパケットが見えているのに通信できない場合は、対向とのルーティングテーブル(セキュリティグループやNACL、ルーティングポリシー)のミスマッチが9割だ。
3. MTU/MSSの罠:
トンネルモードでは、ESPヘッダーや追加のIPヘッダーが付与される分、パケットのサイズ(MTU)が大きくなる。ルーターでMSS(Maximum Segment Size)クランプを設定していないと、大きなHTTPレスポンスやAPIペイロードが途中で破棄され、「なぜか途中まで通信できて、データが大きいと固まる(ブラックホールルーター問題)」という最悪のトラブルに直面する。実務では mss 1360 などの調整を忘れないこと。

インフラの土台を支えるネットワーク層の技術は、一見地味に見えるかもしれない。しかし、その挙動を深く理解し、パケットの流れを脳内でビジュアライズできるようになれば、トラブルシューティングのスピードは劇的に変わる。
セキュアで強靭なアーキテクチャは、正確な知識の積み重ねからしか生まれない。さあ、ログを開いて、パケットを追いかけに行こう。

コメント

タイトルとURLをコピーしました