🎉
IPSec の検証環境をContainerlabで構築する
1. はじめに
この記事の概要です。
- IPSecおよびIKEv3の動作確認を行う
- 暗号化されたユーザー通信が送信されるまでの、パケットフローを確認する
- 暗号化されているパケットを復号化することで、どのようなやりとりをしているのかを確認する
- 環境はContainerlabを使用して仮想環境で構築する
環境構築のためのファイルはこちらにあります。
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を用いて構築します。
環境もサンプルをベースにしています。
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