🎉

IPSec の検証環境をContainerlabで構築する

に公開

1. はじめに

この記事の概要です。

  • IPSecおよびIKEv3の動作確認を行う
  • 暗号化されたユーザー通信が送信されるまでの、パケットフローを確認する
  • 暗号化されているパケットを復号化することで、どのようなやりとりをしているのかを確認する
  • 環境はContainerlabを使用して仮想環境で構築する

環境構築のためのファイルはこちらにあります。
https://github.com/polaris700/IPSec-Lab

2. IPSec/IKEv2とは

IPSecはIPレイヤで通信内容を暗号化・認証するためのプロトコルスイートであり、IKEv3はその鍵交換を担うネゴシエーションプロトコルです。

IPSec

  • IP層で動作するため、アプリケーション非依存で透過的に利用可能
  • ESP(Encapsulating Security Payload)やAH(Authentication Header)を用いて機密性・完全性を確保
  • トランスポートモードとトンネルモードの2つの動作モードを提供
  • 動的な鍵交換はIKE(Internet Key Exchange)により自動化可能
  • IPv4およびIPv6の両方に対応

IKEv2

  • IPSec通信における暗号鍵やSA(Security Association)の自動交換を担うプロトコル
  • メッセージ交換の手順が簡素化され、IKEv1と比較して高速かつ堅牢なネゴシエーションが可能
  • 再送制御やエラー処理機構が強化されており、信頼性の高い通信が実現される
  • 認証方式として事前共有鍵(PSK)およびX.509証明書の両方をサポート
  • NATトラバーサル機能により、NAT越しでも利用可能

3. 構成図

  • PE01 - PE02間でIPSecトンネルを構築
  • sw01はパケットキャプチャ用に設置

4. 動作環境

Host

Host OS Docker Containerlab
Debian 11.11 24.07 0.54.2

Container

OS Version Strongswan Version
Ubuntu 20.04 6.0.0

構築するIPSec VPN

プロトコル 鍵交換プロトコル
ESP IKEv2

使用するアルゴリズム

認証方式 暗号化方式 ハッシュ関数 DH Group
Pre-Shared Key AEV-CBC SHA-2 modp2048s256

今回はStrongswanを用いて構築します。
環境もサンプルをベースにしています。
https://strongswan.org/

5. Strangswan設定

swanctl.conf
connections {
   gw-gw {
      local_addrs  = 10.0.0.1 
      remote_addrs = 10.0.0.2 

      local {
         auth = psk
         id = moon.org
      }
      remote {
         auth = psk
         id = sun.org
      }
      children {
         net-net {
            local_ts  = 192.168.1.0/24
            remote_ts = 192.168.2.0/24
            esp_proposals = aes256-sha256-modp2048s256
      	    }
      }
      version = 2
      proposals = aes256-sha256-modp2048s256
      dpd_delay = 10s
      mobike = no
   }
}

secrets {
   ike-1 {
      id-1a = moon.org
      id-1b = sun.org	
      secret = "Good Key"
   }
}
  • PE01(10.0.0.1)とPE02(10.0.0.2)の間でIKEv2によるPSK認証でのIPSecトンネルを確立
  • 双方の認証IDは moon.org と sun.org を指定
  • 子SA(CHILD_SA)では192.168.1.0/24 ⇔ 192.168.2.0/24 間の通信を保護
  • 暗号スイートには AES-256 + SHA-256 + MODP2048s256 を使用
  • DPD(Dead Peer Detection)は10秒間隔で有効、MOBIKEは無効

ロギングの設定

charon-logging.conf
charon {
  # two defined file loggers
  filelog {
    charon {
      # path to the log file, specify this as section name in versions prior to 5.7.0
      path = /var/log/charon.log
      # add a timestamp prefix
      time_format = %b %e %T
      # prepend connection name, simplifies grepping
      ike_name = yes
      # overwrite existing files
      append = no
      # increase default loglevel for all daemon subsystems
      default = 4
      # flush each line to disk
      flush_line = yes
    }
    stderr {
      # more detailed loglevel for a specific subsystem, overriding the
      # default loglevel.
      ike = 4
      knl = 1
    }
  }

}
  • 暗号化されたパケットの復号化を行うため、ログレベルを4に設定

6. 動作確認

6.1. IKE_SA_INIT

ike.log
[IKE] initiating IKE_SA gw-gw[1] to 10.0.0.2
[ENC] generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ]
[NET] sending packet: from 10.0.0.1[500] to 10.0.0.2[500] (464 bytes)
[NET] received packet: from 10.0.0.2[500] to 10.0.0.1[500] (472 bytes)
[ENC] parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(CHDLESS_SUP) N(MULT_AUTH) ]
[CFG] selected proposal: IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256
  • Initiator(10.0.0.1)側で gw-gw セッションを開始
    • Initiator: 10.0.0.1 (pe01)
    • Responder: 10.0.0.2 (pe02)
  • IKE SA INIT Request パケットを生成
  • 送信パケットのポート番号は src/dst ともに500
  • IKE SA INIT Responseパケットを受信、パース
  • 最終的に提案された暗号スイートが選択され、IKE SAが確立準備状態へ遷移
    • 使用アルゴリズム: AES-CBC 256bit, HMAC-SHA2 256, MODP 2048

6.2 IKE_AUTH

ike.log
[IKE] authentication of 'moon.org' (myself) with pre-shared key
[ENC] generating IKE_AUTH request 1 [ IDi N(INIT_CONTACT) IDr AUTH N(MULT_AUTH) N(EAP_ONLY) N(MSG_ID_SYN_SUP) ]
[NET] sending packet: from 10.0.0.1[500] to 10.0.0.2[500] (176 bytes)
[NET] received packet: from 10.0.0.2[500] to 10.0.0.1[500] (128 bytes)
[ENC] parsed IKE_AUTH response 1 [ IDr AUTH ]
[IKE] authentication of 'sun.org' with pre-shared key successful
[IKE] IKE_SA gw-gw[1] established between 10.0.0.1[moon.org]...10.0.0.2[sun.org]
[IKE] scheduling rekeying in 13850s
[IKE] maximum IKE_SA lifetime 15290s
initiate completed successfully
  • Initiator(moon.org)が自身のIDでPSK認証を実施
  • IKE_AUTH requestパケットを生成
  • ポート 500 でIKE_AUTHパケットを送信・受信
  • Responder(sun.org)側の認証に成功、IKE SAが確立
  • SAの再鍵交換(rekeying)や有効期限(lifetime)のスケジューリングも同時に設定

パケットキャプチャ結果

  • 初期ベクトルより先が暗号化されている

復号化
"Edit" -> "Preference" -> "Protocol" -> "ISAKMP" -> "IKEv2 decryption table"

パラメータ
Initiator's SPI d461d2243471b4d8
Responder's SPI 56a31e8d59b3a33e
SK_ei 2E1D7CC98451206229641579B93DA96B2BF1F01611478FA38935F3B1EB931FBE
SK_er 040AEEF87E5AC54F402A5A4BC3CFF59D6894759FB01EE46031B967D570DD8840
Encryption algorithm "AES-CBC-256 [RFC3602]"
SK_ai F18DDDA8BDB126F6FFAB0F68A744EF117CD0384A6E706ECA2974DBBC3CBB80FB
SK_ar F36789AC729B06673A7EA10E57F4543192462826F7E69BC922A0EF981F57380A
Integrity algorithm "HMAC_SHA2_256_128 [RFC4868]"

  • IDや認証方法など、大部分が暗号化されている

6.3 CHILD_SA

ike.log
[IKE] establishing CHILD_SA net-net{1}
[ENC] generating CREATE_CHILD_SA request 2 [ SA No KE TSi TSr ]
[NET] sending packet: from 10.0.0.1[500] to 10.0.0.2[500] (480 bytes)
[NET] received packet: from 10.0.0.2[500] to 10.0.0.1[500] (480 bytes)
[ENC] parsed CREATE_CHILD_SA response 2 [ SA No KE TSi TSr ]
[CFG] selected proposal: ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_2048_256/NO_EXT_SEQ
[IKE] CHILD_SA net-net{1} established with SPIs c3f6db0f_i c7bcc354_o and TS 192.168.1.0/24 === 192.168.2.0/24
initiate completed successfully
  • CHILD_SA(net-net)が10.0.0.1と10.0.0.2間で新たに確立
  • 送受信されたパケットは共にUDP 500ポートでやりとり
  • ESP用の暗号方式は、AES-CBC-256とHMAC-SHA2-256、MODP 2048を使用
  • SPI(Security Parameter Index)が生成され、192.168.1.0/24 と 192.168.2.0/24 の間でセキュアな通信路が構築

画像は複合化したパケット

  • CHILD_SAもIKE_AUTHと同様に大部分が暗号化されている。

7.4 ESP

  • host01から送信したパケットが暗号化
  • 暗号化されていない部分はpe01の SPIとSequence

復号化

"Edit" -> "Preference" -> "Protocol" -> "ESP" -> "ESP SAs Edit"

パラメータ pe01 → pe02 pe02 → pe01
Protocol "IPv4" "IPv4"
Src IP 10.0.0.1 "10.0.0.2"
Dest IP "10.0.0.2" 10.0.0.1
SPI 0xc7bcc354 0xc3f6db0f
Encryption "AES-CBC [RFC3602]" "AES-CBC [RFC3602]"
Encryption Key 0x37BF96F041698D1A76295AD0D9E63D92578E2A4D8BCA90EBF444A9F1511FDDAA 0xA1EF9B65961E15DF6B00125D4D09ED3633269F55FFCB9685832348CFD8740522
Authentication "HMAC-SHA-256-128 [RFC4868]" "HMAC-SHA-256-128 [RFC4868]"
Authentification Key 0x7368885397D0A80AB52CF144F5FC89FFBB32E6EBC5756C9C73EAC22E99249B7C 0xC736076ECB3F171B057FD75C610E43897732383BBC6565B0FBEE6111338ACCB0ee

  • 暗号化されたパケットは、host01から送信されたICMPであることがわかる

8. まとめ

  • Containerlabで構築した仮想環境でIPSecの動作確認を実施しました。
  • IKEv2を用いた鍵交換のシークエンスを確認しました。
  • 暗号化されたパケットを複合化して、IKE_AUTH以降のパケット内容を確認しました。

証明書を利用したときの動作や、冗長構成での確認もやってみたいですね。

この記事は要約や構成考案でChatGPTを使用しています。

Discussion