【実務・中級編】 VPNゲートウェイの高可用性(HA)構成とアクティブ・スタンバイ切替 – ゼロトラスト&エンタープライズセキュリティ実践ガイド

VPNゲートウェイの「死」と「蘇生」:HA構成でセッション断を極限まで減らす技術論

VPNゲートウェイは、エンタープライズネットワークの「喉元」です。ここが詰まればWeb APIは叩けず、リモートワークの社員は路頭に迷う。そんなクリティカルなインフラにおいて、単一障害点(SPOF)を放置するのは、運頼みのギャンブルに等しい。

今日は、VPNゲートウェイのHA(High Availability)構成、特に「いかにしてシームレスにアクティブ・スタンバイを切り替えるか」という、現場泣かせのテーマに深く切り込んでいこう。教科書的な冗長化の話ではなく、パケットが落ちる瞬間の「あの嫌な沈黙」をどう防ぐか、その泥臭い現実を語る。

—

1. 冗長化の黄金律:VRRPの限界と「同期」の重要性

VPNゲートウェイの冗長化で最も一般的なのが VRRP(Virtual Router Redundancy Protocol)を用いた構成だ。仮想IP(VIP)を共有し、マスタ機がダウンしたらバックアップ機がVIPを引き継ぐ。

しかし、ここで忘れてはならないのが「セッション情報の同期」だ。

単純な VRRP 切り替えだけでは、VPNの暗号化トンネル情報(IPsec SA/IKE SA)はスタンバイ機に引き継がれない。結果、切り替わった瞬間にすべてのVPNトンネルが再ネゴシエーションを始め、通信が数秒から十数秒間、完全にブラックアウトする。APIリクエストはタイムアウトし、クライアントは接続をリセットされる。

これを防ぐためには、ゲートウェイ間で State Sync(セッション同期)を行う独自プロトコル(各ベンダーが実装しているハック)が必須だ。

—

2. 実践:HA構成の肝となる設定の勘所

例えば、Linuxベースのルーター(FRRouting等)で冗長化を考える場合、keepalived を使うのが定石だ。以下に、最低限押さえておくべき keepalived.conf のエッセンスを記す。

! VRRP設定例
vrrp_instance VI_1 {
    state BACKUP        ! 基本はBACKUPで動かし、優先度でマスタを決める
    interface eth0      ! 監視対象のインターフェース
    virtual_router_id 51
    priority 100        ! マスタは150、バックアップは100にするのが鉄則
    advert_int 1        ! 1秒間隔で死活監視(負荷と検出速度のトレードオフ)
    
    virtual_ipaddress {
        192.168.10.1    ! VPNゲートウェイのVIP
    }

    ! フェイルオーバー時に叩くスクリプト
    notify_master "/usr/local/bin/sync_session_start.sh"
    notify_backup "/usr/local/bin/sync_session_stop.sh"
}

ここで重要なのは advert_int の値だ。ネットワークが混雑している環境でこれを短くしすぎると、パケットロスによる「誤検知の嵐」が起き、フラッピング(マスタとバックアップが切り替わり続ける現象)が発生する。現場では、1秒をベースにしつつ、回線の安定度を見て微調整するのがセオリーだ。

—

3. アプリケーション視点:VPN断をどう「吸収」するか

どれほど完璧なHAを組んでも、数ミリ秒〜数百ミリ秒の瞬断は避けられない。Web API設計において、VPN経由の通信がこの瞬断を食らったとき、どう振る舞うべきか。

一番の悪手は「エラーをそのままユーザーに返す」ことだ。クライアントサイド(例えば Python の requests や Fetch API)で、リトライ(指数バックオフ)を実装しておく必要がある。

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# 瞬断を考慮したリトライロジック
def get_api_with_retry(url):
    session = requests.Session()
    # 500系エラーやコネクションエラー時に最大3回リトライ
    retries = Retry(total=3, backoff_factor=1, status_forcelist=[502, 503, 504])
    session.mount('https://', HTTPAdapter(max_retries=retries))
    
    try:
        response = session.get(url, timeout=5)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"VPNの揺らぎ?リトライしてもダメでした: {e}")

この backoff_factor=1 という設定が重要だ。VPNゲートウェイが切り替わっている最中に爆速でリトライを繰り返すと、ゲートウェイにさらに負荷をかけ、事態を悪化させる。少しずつ間隔を空けて様子を見る、この「配慮」が堅牢なインフラを作る。

—

4. 現場の教訓:デバッグ手順の極意

もしVPNの切り替えで通信が死んだら、まずは以下の順序でパケットの足跡を追え。

1. VIPの所在確認: ip addr show でVIPがどちらの機体に浮いているか確認する。
2. ARPテーブルの確認: ネットワークスイッチ側で、VIPのMACアドレスが切り替わった後のノードに正しく紐付いているか show arp で見る。これ、意外とスイッチの学習待ちで詰まることが多い。
3. SA(Security Association)の生存確認: ip xfrm state(Linuxの場合)を叩き、トンネルが確立されているか確認する。

「設定したから大丈夫」という慢心は禁物だ。深夜のメンテナンス時にフェイルオーバーを実際に発生させ、どれくらいの通信ロスが発生するか、ping を叩きながら目で追う。その泥臭い検証の積み重ねだけが、トラブル対応の自信を形作る。

—

最後に:完璧を求めすぎない設計を

ゼロトラストの時代、VPNは「いずれ廃れる技術」とも言われる。しかし、現実のエンタープライズ環境では、まだまだレガシーなシステムを繋ぐための「生命線」だ。

VPNゲートウェイの冗長化は、100%の可用性を目指すものではない。「いかに素早く、かつ安全に復旧させるか」というリカバリーの速さが勝負だ。HA構成を組む際は、切り替わった瞬間の挙動を、アプリケーション側がどう許容するか。その両面からの設計を忘れないでほしい。

現場のエンジニア諸君、パケットの行方に常に疑いの目を持ち続けよう。それが、最強の防衛線への第一歩だ。

コメント

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