LinuxでGRE over IPsecを構築しOSPF動的ルーティングを安全に運用する設定手順

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

背景と理由:深夜2時に素のIPsecがOSPFをダウンさせる原因

土曜日の午前2時15分、監視ダッシュボードに大量のアラートが押し寄せます。3つのリモート拠点にまたがるサブネットが、コアルーティングテーブルから一斉に消失しました。エッジルーター上では、フェーズ1およびフェーズ2のIPsec Security Association(SA)はいずれもアクティブで正常を示しています。しかし、OSPFデーモンはINIT状態のままスタックし、正常なネイバー隣接関係を確立できていません。

これが発生する理由は明確です。標準的なポリシーベースのIPsecトンネルモードは、明示的なトラフィックセレクターに一致するユニキャストIPパケットのみを暗号化します。ブロードキャストやマルチキャストのパケットは完全に破棄されてしまいます。

OSPFは、5〜10秒ごとに224.0.0.5(すべてのOSPFルーター宛て)および224.0.0.6(すべてのDesignated Router宛て)に送信されるマルチキャストのHelloパケットに依存しています。これらのマルチキャストが届かなければ、ノードはネイバーの検出、リンクステートデータベース(LSDB)の同期、あるいはダウンしたリンクの検知ができません。トポロジーが2拠点から20拠点へと拡大した場合、素のIPsecトンネル上で何十もの静的フォールバックルートを維持管理することは、運用上の悪夢と化します。

Generic Routing Encapsulation(GRE)はこのギャップを埋める技術です。GREはIPプロトコル47を使用し、任意のレイヤー3ペイロードを標準的なPoint-to-Point IPパケット内にカプセル化します。ただし、ネイティブのGREはパブリックインターネット上で全ペイロードデータを平文のまま送信してしまいます。そこでGREをIPsecのトランスポートモードでラップすることにより、両者の利点を同時に享受できます。GREがOSPFに必要なマルチキャストパケットを処理し、IPsecがWANに送出される前のすべてのバイトを暗号化します。

このアーキテクチャは、汎用ハードウェアが高価な専用ゲートウェイを代替するLinuxベースのエッジルーターやSD-WAN構築における標準的な構成となっています。

インストール:LinuxへのstrongSwanとFRRoutingの導入

各Linuxゲートウェイには2つのソフトウェアパッケージが必要です。IKEv2ネゴシエーションとIPsecカーネルポリシーを担うstrongSwan、そしてOSPFデーモンを管理するFRRouting (FRR)です。GREトンネリング自体は、Linuxカーネルがiproute2を介してネイティブに処理します。

本ガイドでは、エッジルーターとして動作する2台のDebian/Ubuntuゲートウェイを構成します:

  • 本社ゲートウェイ(ノードA): パブリックWAN 198.51.100.1(eth0上)、LAN 10.10.0.0/24、トンネルIP 172.16.1.1/30
  • 支社ゲートウェイ(ノードB): パブリックWAN 203.0.113.2(eth0上)、LAN 10.20.0.0/24、トンネルIP 172.16.1.2/30

両方のマシンに必要なパッケージをインストールします:

sudo apt-get update
sudo apt-get install -y strongswan strongswan-pki libcharon-extra-plugins frr iproute2 iptables

次に、IPv4パケット転送を有効化し、必要なカーネルモジュールをロードします。rp_filter = 0またはルーズモード(2)に設定することで、仮想インターフェースに到達したカプセル化GREパケットがカーネルによって暗黙的に破棄されるのを防ぎます:

sudo modprobe ip_gre
sudo modprobe esp4
sudo modprobe xfrm4_mode_transport

# sysctlパラメータを永続化
sudo tee /etc/sysctl.d/99-router.conf << 'EOF'
net.ipv4.ip_forward = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
EOF

sudo sysctl --system

設定:暗号化GREパイプラインの構築

ステップ1:GREトンネルインターフェースの作成

本社ゲートウェイ(ノードA)で、仮想Point-to-Point GREインターフェースを作成します:

sudo ip tunnel add gre1 mode gre remote 203.0.113.2 local 198.51.100.1 ttl 255
sudo ip addr add 172.16.1.1/30 dev gre1
sudo ip link set dev gre1 mtu 1400
sudo ip link set dev gre1 up

対向となる支社ゲートウェイ(ノードB)でも同様にインターフェースを設定します:

sudo ip tunnel add gre1 mode gre remote 198.51.100.1 local 203.0.113.2 ttl 255
sudo ip addr add 172.16.1.2/30 dev gre1
sudo ip link set dev gre1 mtu 1400
sudo ip link set dev gre1 up

インターフェースのMTUを1400に下げ(必要に応じてTCP MSSを1360にクランプし)ておくことは極めて重要です。24バイトのGREヘッダーに加え、およそ50〜73バイトのIPsec ESPオーバーヘッドを考慮することで、標準的な1500バイトのWANリンクにおけるパケットのサイレントフラグメンテーションを防止します。

ステップ2:strongSwan IPsecをトランスポートモードで設定

ここではトンネルモードではなく、トランスポートモードでIPsecを使用します。外側のGREパケットには既にルーティング可能なパブリックIPヘッダー(プロトコル47)が存在するため、トランスポートモードを使用すれば、冗長な2つ目のIPヘッダーを付加することなく、元のIPヘッダーの直後にESPヘッダーが挿入されます。

本社ゲートウェイ(ノードA)の/etc/ipsec.secretsで事前共有鍵(PSK)を定義します:

198.51.100.1 203.0.113.2 : PSK "SuperSecretProductionKey2026!"

本社ゲートウェイ(ノードA)の/etc/ipsec.confに接続定義を追加します:

config setup
    charondebug="ike 1, knl 1, cfg 1"

conn %default
    keyexchange=ikev2
    ike=aes256gcm128-sha256-modp2048!
    esp=aes256gcm128-modp2048!
    dpdaction=restart
    dpddelay=10s
    dpdtimeout=30s
    auto=start

conn gre-ipsec
    type=transport
    left=198.51.100.1
    leftprotoport=gre
    right=203.0.113.2
    rightprotoport=gre
    authby=secret

支社ゲートウェイ(ノードB)にも同様の設定を適用します:

# /etc/ipsec.secrets
203.0.113.2 198.51.100.1 : PSK "SuperSecretProductionKey2026!"
# /etc/ipsec.conf
config setup
    charondebug="ike 1, knl 1, cfg 1"

conn %default
    keyexchange=ikev2
    ike=aes256gcm128-sha256-modp2048!
    esp=aes256gcm128-modp2048!
    dpdaction=restart
    dpddelay=10s
    dpdtimeout=30s
    auto=start

conn gre-ipsec
    type=transport
    left=203.0.113.2
    leftprotoport=gre
    right=198.51.100.1
    rightprotoport=gre
    authby=secret

両方のゲートウェイでstrongSwanを再起動し、トンネルを開始します:

sudo systemctl restart strongswan-starter
sudo ipsec up gre-ipsec

ステップ3:FRRoutingによるOSPFの設定

/etc/frr/daemons内でospfd=yesに設定してOSPFデーモンを有効化し、サービスを再起動します:

sudo sed -i 's/ospfd=no/ospfd=yes/' /etc/frr/daemons
sudo systemctl restart frr

本社ゲートウェイ(ノードA)でvtyshを実行してFRR統合シェルを開き、ルーティングインスタンスを設定します:

sudo vtysh

configure terminal
 router ospf
  ospf router-id 172.16.1.1
  network 172.16.1.0/30 area 0.0.0.0
  network 10.10.0.0/24 area 0.0.0.0
 exit
 interface gre1
  ip ospf network point-to-point
  ip ospf hello-interval 5
  ip ospf dead-interval 20
 exit
exit
write memory

支社ゲートウェイ(ノードB)でも対応する設定を実行します:

sudo vtysh

configure terminal
 router ospf
  ospf router-id 172.16.1.2
  network 172.16.1.0/30 area 0.0.0.0
  network 10.20.0.0/24 area 0.0.0.0
 exit
 interface gre1
  ip ospf network point-to-point
  ip ospf hello-interval 5
  ip ospf dead-interval 20
 exit
exit
write memory

ip ospf network point-to-pointを明示的に指定することで、Designated Router(DR)およびBackup Designated Router(BDR)の選出プロセスをスキップします。これにより、リンクステートトラフィックの無駄なやり取りを削減し、トンネル越しのネイバーコンバージェンスを高速化できます。

検証と監視:正常動作の確認

コマンドの終了ステータスが正常だからといって、トラフィックが安全に流れているとは限りません。ネットワークスタックの各レイヤーを必ず個別に検証してください。

1. IPsecトランスポートSAの確認

strongSwanのステータスを確認し、トランスポートモードのSAが確立されているかを検証します:

sudo ipsec statusall

GREエンドポイントと一致するアクティブなSecurity Association(SA)を確認します:

gre-ipsec{1}:  INSTALLED, TRANSPORT, reqid 1, ESP SPIs: c8f4a120_e21b5591
gre-ipsec{1}:  AES_GCM_16 256, 128450 bytes_i, 129320 bytes_o, rekeying in 45 minutes
gre-ipsec{1}:   198.51.100.1/32[gre] === 203.0.113.2/32[gre]

次に、LinuxカーネルのXFRMサブシステムにトランスポート変換ルールがインストールされていることを確認します:

sudo ip xfrm state
sudo ip xfrm policy

2. OSPFネイバー隣接関係の確認

FRR内で2台のゲートウェイがFull隣接状態に達していることを確認します:

sudo vtysh -c "show ip ospf neighbor"

期待される出力:

Neighbor ID     Pri State           Dead Time Address         Interface
172.16.1.2        0 Full/ -          00:00:18 172.16.1.2      gre1:172.16.1.1

ネイバーの状態がInitや2-Wayのままスタックしている場合は、iptablesまたはnftablesのルールを確認してください。外部インターフェースでGRE(プロトコル47)およびUDPポート500、4500がファイアウォールによって許可されていることを確認します。

3. カーネルルートの確認とパケットキャプチャ

OSPFによってリモート拠点のLANサブネットがLinuxルーティングテーブルに登録されたかを確認します:

ip route show proto ospf

仮想GREインターフェース経由でリモートサブネットへのルートが表示されるはずです:

10.20.0.0/24 via 172.16.1.2 dev gre1 proto ospf metric 20

最後に、リモートLAN上のホストに対してpingを実行しながら(例:ping -c 3 10.20.0.15)、物理WANインターフェース上でパケットキャプチャを実行します:

sudo tcpdump -ni eth0 host 203.0.113.2

eth0上を通過するパケットとしてESP(spi=0x...)のみが観測されるはずです。もし暗号化されていないGREやOSPFv2-Helloパケットが回線上に現れた場合は、直ちにトラフィックを停止し、IPsec設定のleftprotoport=greディレクティブを再確認してください。

Share: