【実務・中級編】 VPN接続時のパケット構造:外側IPヘッダーと内側IPヘッダーの役割 – サイバーセキュリティとプライバシー保護実践ガイド

カフェの心地よい喧騒、あるいは新幹線の座席。ノートPCを開き、私たちは日常的に公共Wi-Fiへと接続し、APIを叩き、インフラを管理する。エンジニアである私たちなら誰もが知っているはずだ。「暗号化されていないフリーWi-Fiは、ARPスプーフィングや野良APによる中間者攻撃(MitM)の格好の餌食である」という現実を。

だからこそ、私たちは個人向けVPN(あるいはリモートアクセスVPN)のスイッチを入れる。一瞬の硬直のあと、パケットは暗号化のベールに包まれ、安全なトンネルを駆け抜けていく。

だが、ここで少し立ち止まって考えてみてほしい。
そのVPNが確立された瞬間、ネットワークの深部で何が起きているのか? 私たちのPCから送り出されたパケットは、トンネルのなかでどのように姿を変え、ルーターの海を泳ぎ渡っているのか?

今回は、実務でWeb API設計やインフラ運用に深く関わるエンジニアのあなたに向けて、VPNの心臓部である「パプセル化(Encapsulation)」、そしてその極みである「外側IPヘッダーと内側IPヘッダーの二重構造」の深淵へと飛び込んでみよう。教科書には載っていない、パケットの生々しい挙動とデバッグの現場のリアルをお伝えする。

—

1. なぜ「IPヘッダー」が2つ必要なのか? 境界防御のジレンマ

VPN(Virtual Private Network)がやっていることは、一言で言えば「荷物の中に、別の荷物を丸ごと入れる(カプセル化)」ことだ。

私たちが普段、アプリケーション層から発信する通信(HTTPリクエストなど)は、送信元IPアドレス(例: 10.8.0.2:VPNが割り当てた仮想IP)と、宛先IPアドレス(例: 93.184.216.34:APIサーバーのIP)を持っている。これが「内側IPヘッダー」だ。

しかし、考えてみてほしい。あなたが今接続しているカフェのWi-Fiルーターや、途中のISPのルーティング機器は、あなたのPCが勝手に割り振ったプライベートな仮想IPアドレス(10.8.0.2など)をルーティングできるだろうか? 答えはノーだ。そんなルーティングテーブルはインターネットのどこにも存在しない。

そこで登場するのが「外側IPヘッダー」である。
VPNクライアント(あなたのPC)とVPNサーバー(クラウド上の終端装置)の間で、実際のインターネット(物理的な経路)をルーティングするための、正真正銘のグローバルIPアドレス(例: カフェのIPから見たあなたのルーターのグローバルIP、およびVPNゲートウェイのグローバルIP)が、パケットの最前列に新たに付与されるのだ。

つまり、カプセル化されたパケットの構造はこうなる。

+---------------------+---------------------+-----------------------------------+
| 外側IPヘッダー      | UDP/TCPヘッダー     | 内側IPヘッダー + ペイロード       |
| (実世界ルータ用)    | (カプセル化プロトコル)| (エンドツーエンド通信用)          |
| 送信元: 自宅/カフェ | (例: WireGuardなら  | 送信元: 10.8.0.2 (VPN仮想IP)      |
| 宛先: VPNGW         |  UDP 51820等)       | 宛先: 93.184.216.34 (実サーバー)  |
+---------------------+---------------------+-----------------------------------+

この二重構造こそが、インターネットという「信頼できない荒野」の中に、自分だけの「安全な専用道路」をブチ抜くための技術的トリックなのである。

—

2. 標準仕様とプロトコルごとのアプローチ(IPsec vs WireGuard)

このパケットの二重構造を実現する代表的なプロトコルが、歴史ある IPsec (Internet Protocol Security) と、現代の寵児である WireGuard だ。それぞれのヘッダーの扱いに目を凝らしてみよう。

IPsec(トンネルモード)

エンタープライズの拠点間接続や伝統的なリモートアクセスで使われる重厚長大(だが非常に堅牢)なプロトコル。
IPsecのトンネルモードでは、元のIPパケット(内側)全体の前に、新しいIPsecヘッダー(ESPヘッダーなど)と新しい外側IPヘッダーがまるごと付与される。ルーターなどの機器間で完全なトンネルを形成するため、元のパケットの送信元・宛先は完全に隠蔽される。

WireGuard

Linuxカーネルスペースに直接組み込まれ、現代のクラウドネイティブなインフラや個人向けVPNのデファクトスタンダードになりつつある次世代プロトコル。
WireGuardはシンプルさを極めており、UDPパケット(デフォルトポート: 51820)のペイロードとして、暗号化された内側IPパケットをそのまま包み込む。外側のIPヘッダーは通常のUDP/IPパケットのものと変わらないため、NATトラバーサル(NAPT越え)が極めてスムーズに動作する。

—

3. 通信フローの全体像(パケットの旅路)

ここで、クライアント(PC)からVPN経由で外部のWeb API(例: https://api.example.com/v1/data)を叩く際の、パケットのシーケンスと変容を追ってみよう。

[Client (10.8.0.2)]                  [VPN Gateway]                  [Target API Server]
       |                                   |                                   |
       |--- 1. HTTPリクエスト作成 -------->|                                   |
       |    (内側IP: 10.8.0.2 -> 93... )    |                                   |
       |                                   |                                   |
       |--- 2. カプセル化 & 暗号化 -------->|                                   |
       |    (外側IP: 203.0.113.5 -> 198.51.100.1)                              |
       |                                   |                                   |
       |====== (物理ネットを転送) ========>|                                   |
       |                                   |--- 3. 外側IP剥離 & 復号 --------->|
       |                                   |    (内側IPパケットを取り出し)     |
       |                                   |                                   |
       |                                   |--- 4. 通常ルーティングで転送 ---->|
       |                                   |    (送信元: 198.51.100.10(VPN GW) |
       |                                   |     宛先: 93.184.216.34)          |
       |                                   |                                   |
       |                                   |<-- 5. APIレスポンス返却 ----------|
       |                                   |                                   |
       |--- 6. 再びカプセル化・転送 ------->|                                   |
       |<-- 7. 復号してアプリケーションへ -|                                   |

インフラ運用者として注意すべきポイントは、「パケットが二重になることで発生するオーバーヘッド(MTU問題)」だ。

—

4. 現場の落とし穴:MTU(Maximum Transmission Unit)とパケットフラグメンテーション

実務でVPNを導入した際、最も頻繁に遭遇するトラブルが「特定のWebサイトだけが表示されない」「APIへの大容量POSTリクエストがタイムアウトする」という現象である。

原因の多くは MTU(Maximum Transmission Unit)のミスマッチ だ。

一般的なEthernetの標準MTUは 1500 バイトである。ここにVPNのカプセル化(外側IPヘッダーやESPヘッダー、UDPヘッダーなど)が加わると、パケット全体のサイズが 1500 バイトを超えてしまう。
ルーター側で「フラグメンテーション(分割)」が許可されていれば勝手に分割してくれるが、セキュリティポリシーやクラウドの仮想ルーターの設定で DF (Don't Fragment) フラグが立っていると、パケットは途中のルーターで破棄(Drop)され、ICMPの「Fragmentation Needed」が返されることになる。

もしこのICMPが途中のファイアウォールでブロックされていると、通信が完全に沈黙する(いわゆる PMTUD (Path MTU Discovery) ブラックホール問題)のだ。

対策の設定例(Linux / ipコマンド)

インフラ構築時やコンテナ環境において、仮想インターフェース(tun0など)のMTUを適切に調整するか、MSS(Maximum Segment Size)クランピングを有効にするのが定石である。

# 1. 現在のインターフェースのMTUを確認
ip link show

# 2. VPNトンネル(tun0)のMTUを安全な値(例: 1360)に手動で引き下げる
sudo ip link set dev tun0 mtu 1360

# 3. iptablesでTCP MSS Clampingを有効にし、外向きパケットのMSSを自動調整する
# (PPPoEやVPN環境でのTCPセッション確立トラブルを防ぐ特効薬)
sudo iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

実務では、VPNサーバー側の設定ファイル(例: WireGuardの wg0.conf や OpenVPNの server.conf)で MTU = 1360 などの適切な値をあらかじめ指定しておくことが、後々の不毛な障害シュミレーションを防ぐ最大の防御策となる。

—

5. 実践:Pythonとcurlで内側・外側の挙動を体感する

理屈はわかった。では、実際に開発者の手元で「VPNが正しく機能しているか」「IPアドレスがどのように書き換わっているか」をプログラムから検証してみよう。

以下のPythonスクリプトは、標準的な requests ライブラリを使用して、現在のグローバルIPアドレス(外側から見た宛先、あるいはVPNゲートウェイのIP)と、接続経路の情報を確認するものだ。

import requests
import sys

def check_ip_and_routing():
    # パblicなIPアドレスを返す無料のJSON APIを利用
    target_api = "https://httpbin.org/ip"
    
    print("[*] ネットワーク接続とIPアドレスの検証を開始します...")
    
    try:
        # タイムアウトを5秒に設定し、確実にリクエストを投げる
        response = requests.get(target_api, timeout=5)
        response.raise_for_status()
        
        data = response.json()
        print(f"[+] 接続成功: 外部APIから観測されたあなたのIPアドレス -> {data.get('origin')}")
        print("    ※ VPNが有効な場合、ここに表示されるのはプロバイダのIPではなく、")
        print("      VPNプロバイダの出口(ゲートウェイ)のグローバルIPになります。")
        
    except requests.exceptions.RequestException as e:
        print(f"[-] エラー発生: APIへのリクエストに失敗しました - {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    check_ip_and_routing()

curlによる簡易チェック

CLIでサクッと確認したい場合は、以下の curl コマンドが便利だ。

# VPN接続前後のグローバルIPの変化をワンライナーで比較する
curl -s https://ipinfo.io/json | grep -E "ip|city|org"

もしVPNのスイッチを入れた前後で ip や org の値が、自宅の回線事業者(NTTやKDDIなど)から、海外や別リージョンのクラウド事業者(DigitalOceanやAWSなど)に切り替わっていれば、内側IPパケットが安全に外側IPのトンネルに包み込まれ、別の出口からインターネットへ飛び出している証拠である。

—

6. まとめ:パケットの構造を知る者が、セキュリティを制す

今回は、個人向けVPNの背後でうごめく「外側IPヘッダー」と「内側IPヘッダー」の役割、そしてそのカプセル化構造が生み出す実務上のポイント(MTU問題やルーティングの仕組み)を紐解いた。

VPNは、単なる「怪しいWi-Fiから身を守るための魔法のボタン」ではない。
その実体は、OSのネットワークスタックとカーネルスペースを駆使し、パケットに新たな宛先と身元を纏わせ、インターネットの荒波を正確に渡り歩くための極めて緻密なエンジニアリングの産物である。

Web APIの設計や、クラウドインフラのネットワークトポロジを設計する際、「このパケットのヘッダーは今、どこでどう書き換わっているのか?」を頭の中でパケットキャプチャできるようになれば、あなたのネットワークエンジニアとしての戦闘力は一段と跳ね上がるはずだ。

さあ、今日の業務に戻ろう。次にVPNの接続ログやiptablesのエラーに直面したときは、この記事のパケット構造を思い出してほしい。トラブルの原因は、必ずその「二重のベール」のどこかに隠されている。

コメント

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