復号パケットで学ぶSEIL IKE/IPsec

2020年12月22日 火曜日


【この記事を書いた人】
芦原 渉

ルータのファームウェア開発をしています。河原でプログラミングするのが好きです。

「復号パケットで学ぶSEIL IKE/IPsec」のイメージ

IIJ 2020 TECHアドベントカレンダー 12/22(火)の記事です】

ネットワークプロトコルを学ぶ一番の方法は、実際のパケットを見ることだ、と私は考えています。ルータ・サーバの設定を変えて、コマンドを打って、パケットやログの変化を見てプロトコルの雰囲気をつかむことが、そのプロトコルへの入門の第一歩となります。

IPsec VPNはIIJ独自開発ルータSEILの人気機能の一つで、IPsecの鍵交換を行うのがIKE (Internet Key Exchange) プロトコルです。私の部署では配属後「SEILをさわってみよう研修」というのがあるのですが、最も難しい機能の一つがIKE/IPsecでした。馴染みの無い概念・用語が多く登場する上に、パケットキャプチャを見ても暗号化されていて何をしているのか分からなかったためです。

ようやく少し分かり始めたのは1年後のことで、WiresharkでIKE/IPsecパケットを復号できることが分かってからでした。

SEILの機種は

  • seil3: SEIL/B1、SEIL/X1、SEIL/X2、SEIL/BPV4、SEIL/x86 Fuji
  • seil8: SEIL/X4、SEIL/x86 Ayame

と分類されます。この記事ではseil8 静的IPv4アドレスのルーティングベースIPsecでのIKEを、復号済パケットキャプチャを見ながら解説します。

本記事の検証中のパケットキャプチャ

本記事の検証で採取したパケットキャプチャはGitHubにアップロードしました。

Wiresharkで開いたら、Edit -> Preferences -> Protocols -> ISAKMP -> IKEv1 Decryption Table Edit… で以下のエントリを追加してください。

  • Initiator’s COOKIE: 80f5f36ce1766d90
  • Encryption Key: 4fab73ae45d51536fc833c695acb8ea75a7e59af2cc5b49eaeb7b83ccb1f4953
IKEv1 Decryption Tableエントリの追加

IKEv1 Decryption Tableエントリの追加

また、Edit -> Preferences -> Protocols -> ESP の “Attempt to detect/decode encrypted ESP payloads” にチェックを入れて、ESP SAs Edit… で以下のエントリを追加してください。

  • Protocol: IPv4
  • Src IP: 10.0.0.1
  • Dest IP: 10.0.0.1
  • SPI: 0xced62df9
  • Encryption: AES-CBC [RFC3602]
  • Encryption Key: 0x1bc9cc7a1ffc85bb3825e4d0c17d5980a6ff776a8849d9090f1cfa2db9dbb51a
  • Authentication: HMAC-SHA-256-128 [RFC4868]
  • Authentication Key: 0x539b9eab02eac2dda3f41bb0da3778d9963129b916eee10ef4ca5b3db3fe7e2c
ESP SAsエントリの追加

ESP SAsエントリの追加

OKを押してPreferenceウィンドウを閉じたら、Analyze -> Reload Lua Plugins (Ctrl+Shift+L) で設定を反映してください。(Lua PluginがバンドルされていないWiresharkには “Reload Lua Plugins” がありません。その場合Wiresharkを再起動してください)これでパケットキャプチャの #978-1005(Wiresharkのパケット番号は #<number> と表記することにします)の一部が復号されます。

全てのパケットを復号するにはこの調子で数十エントリ追加する必要がありますが、WiresharkのGUIで編集するのはとても大変です。以下のファイルを直接編集することにします。

  • IKEv1 Decryption Table (Windows): C:\Users\<user>\AppData\wireshark\esp_sa (%APPDATA%\Wireshark\ikev1_decryption_table)
  • ESP SAs (Windows): C:\Users\<user>\AppData\wireshark\esp_sa (%APPDATA%\Wireshark\esp_sa)
  • IKEv1 Decryption Table (Linux, macOS): ~/.config/wireshark/ikev1_decryption_table
  • ESP SAs (Linux, macOS): ~/.config/wireshark/esp_sa

これらのファイルに以下のテキストを追加・保存して、WiresharkでCtrl+Shift+Lで反映してください。

ikev1_decryption_table:

esp_sa:

これらのパラメータはstrongSwanのsave-keys plugin で取得しました。

ネットワーク構成

seil8の機種としてはQEMU (qemu-system-x86_64) 上で動作するSEIL/x86 Ayameを利用しました。 対向機器はQEMU上で動作するUbuntu 20.04のstrongSwanです。

最小設定でのIKE折衝

IKE/IPsecを確立するためのパケットのやりとりを「折衝」と呼びます。 静的IPv4ルーティングベースIPsecでstrongSwanと折衝するための最小設定は以下になります。 念のためstrongSwanのコンフィグも貼っておきます。

このコンフィグでのパケットは #978-1005 です。

#978-1005 最小設定でのIKE折衝

#978-1005 最小設定でのIKE折衝

SEILで “IKE” と呼んでいるのは、version 1の “IKEv1” です。 IKEv1/IPsecの折衝はIKEv1プロトコルで行われますが、Wiresharkには “ISAKMP” と出ています。 RFC 2408 ISAKMPは抽象的なプロトコルになっていて、 その具体的なプロトコルの1つがRFC 2409 IKEv1です。 この記事では ISAKMP = IKE = IKEv1 とします。

IKE/IPsecの折衝は基本的に

の9パケットで確立します。ここで、--> はSEILからの送信、<-- はSEILの受信を表します (RFC 2409の表記)。 その後、IPsecによる暗号通信が可能になります (No.988-)。

“IPsec” はRFC 4301等で定義される、 IPの暗号化のアーキテクチャを指す広い意味を持った言葉ですが、 暗号化プロトコルとしてはESP (Encapsulating Security Payload) と呼ばれるプロトコルが使われます。 (ESPと並んでAH (Authentication Headers) というプロトコルもありますが、 こちらは暗号化は行わずにパケットの認証だけを行うプロトコルです。 AHはseil8に対応していません。 この記事ではESPのみ取り扱います。)

パケット #988 はLinux 192.168.2.1 からSEIL 192.168.1.1 にpingを送信した際のものです。

#988 Linux 192.168.2.1 からSEIL 192.168.1.1 へのping(ESP復号済)

#988 Linux 192.168.2.1 からSEIL 192.168.1.1 へのping(ESP復号済)

Ethernet / IPv4 (10.0.0.2 -> 10.0.0.1) / ESP / IPv4 (192.168.2.1 -> 192.168.1.1) / ICMP という構造になっています。ここで注意して頂きたいのが、ESPの下の IPv4(192.168)/ICMP は暗号化されていることです。 ESPの設定の “Attempt to detect/decode encrypted ESP payloads” のチェックを外すと “Data” と表示され中身が分からなくなります。

#988 暗号化されたping

#988 暗号化されたping

192.168.1.1 <–> 192.168.2.1 の通信は、10.0.0.1 <–> 10.0.0.2 の暗号トンネルを経由して行われて、VPNとして機能していることが分かります。

実際にSEILでIKE/IPsecの検証を行う際は、

    • show log
    • show status ike
    • show status ipsec
    • show status ipsec.security-policy
    • show status interface.ipsec0
    • 対向機器のログ・ステータス
    • マニュアル

も参照してください。全て載せると長くなりすぎるので、この記事では割愛します。

他の細かいことは以下で解説していきます。

Initiator/Responder

SEILに interface.ipsec0.responder-only : enable を追加して、 strongSwanの auto=addauto=start に変更します。

最小設定ではSEILから折衝を開始していましたが、responder-only : enable を設定することでSEILからは折衝を開始しなくなります。strongSwanはその逆で、自ら折衝を開始するように設定変更しました。

パケットは #1010-1021 で、strongSwan (10.0.0.2) から折衝を始めていることが分かります。ここからはWiresharkのスクリーンショットの代わりにテキストを貼っていきます。

折衝を始めた側は “Initiator”、その対向は “Responder” と呼ばれます。 SEILのコンフィグの responder-only という名前はこの用語から来ています。

設定によっては「SEILがinitiator (or responder) のときだけIPsecが確立する」のようなことがよくあります。 トラブルシューティングの際は、responder-only を使ってこの事象の有無をチェックすることも問題の切り分けポイントになります。

Phase1プロポーザル

interface.ipsec0.ike.proposal.phase1.** を追加してみます。

このときの折衝は #3005-3006 です。

折衝が

  • –> Identity Protection
  • <– Informational

の1往復で終わってしまいました。これはstrongSwanに折衝が拒否されています。まずSEILが送ったIdentity Protectionから見ていきましょう。左に最小設定でのパケット (#1010)、右に interface.ipsec0.ike.proposal.phase1.** を設定したパケット (#3005) を貼ります。

Payload: Security Association (1) がスッキリしました。内容は以下のようになっています。

#3005 には、

が反映されていることが分かります。これはISAKMPで使う暗号方式とISAKMP SAのライフタイム(後述)の提案 (proposals) になっています。以下の図の 3-9. で使う暗号方式の提案です。

6. が終わると「Phase 1の折衝が完了した」「ISAKMP SA(ISAKMPの暗号化セッション)が確立した」などと言います。このISAKMP SAを使って 7-9. のIKE Phase 2を行う訳ですが、これは次節で解説します。

最小設定 (#1010) ではphase 1プロポーザルが6つありました。これはSEILのデフォルト設定が、

  • interface.ipsec0.ike.proposal.phase1.lifetime : 86400
  • interface.ipsec[].ike.proposal.phase1.encryption.[].algorithm : aes256, aes128, 3des
  • interface.ipsec[].ike.proposal.phase1.hash.[].algorithm : sha256, sha1
  • interface.ipsec[].ike.proposal.phase1.dh-group : modp1536

となっているためです。全ての組み合わせを提案するので 1×3×2×1 = 6通り になっています。

と設定すれば 4×4×1 = 16通り提案します (#3371)。

今回はstrongSwanからの返りの ISAKMP 2. のパケット (#3006) が、Identity Protection ではなく Informational というパケットになっていました。Identity Protection、Informationalは「ISAKMP Exchange Types」と呼ばれるものです。ここでは、

  • Identity Protection Exchange はphase 1の折衝
  • Informational Exchange は汎用的な情報通知

として使われています。ISAKMP Exchange Typesのより正確な意味はRFC 2408 4 ISAKMP Exchangesを参照してください。

#3006 の内容を見てみます。

Notify Message Type: NO-PROPOSAL-CHOSEN (14) がポイントです。SEILは

という1つのphase 1プロポーザルを提案しましたが、strongSwanは「この提案は受け入れられない」と言って拒否しています。(プロポーザルentryが2つ以上ある場合は「これら提案は1つも受け入れられない」という意味です)なぜ拒否されたのかは、strongSwanのログに書いてあります。

重要な部分は vvvvv ^^^^^ で示しました。MODP_1024 を提案されたけれど、MODP_1536 じゃないとだめだ、と言っていると解釈できます。これはInformational Exchangeでは教えてくれませんが、ike-scanを使えば対向機器が対応しているphase 1をスキャンできます。

strongswan.confike=aes256-sha256-modp1536!ike=aes256-sha256-modp1024! に変更すると折衝できるようになります (#3593-3601)。

Identification

前節までのISAKMPは平文だったので、普通にパケットキャプチャすれば解析できる内容でした。ここからは復号パケットを見ていくので、本記事のメインパートです。

下図の 5. 6. のパケットを見ていきます (#982-983)。

Payload: Identification には自分のIPアドレスが入っています。これにより「誰が折衝しているか」を識別できるようになります (RFC 2407 4.6.2RFC 2408 3.8)。

Payload: Hash は、1. 2. で折衝したhashと 3. 4. 5. 6. のデータを使ったハッシュで、パケットの認証をしています。とても複雑な計算で長くなるので割愛します (RFC 2409 5.2)。(注:prfを折衝していればhashではなくそちらを使う)以降に登場する Payload: Hash も同様に認証を行っています。

ここまでで、Phase 1の折衝が完了してISAKMP SAが確立し、ISAKMPの暗号経路ができました。

Phase 2

ここからは暗号セッションであるISAKMP SAの中で、IPsec (ESP) の設定を行っていきます。これをphase 2と呼びます。

上でPhase 1のプロポーザルを変更したのと同様に、今度はPhase 2のプロポーザルを変更してみます。

するとPhase 1の設定を変えたときと同様に、strongSwanのQuick Modeの応答が NO-PROPOSAL-CHOSEN になります。

strongSwanのログを見ると、hmac-md5を拒否していることが分かります。

これを解消するには ipsec.confesp=aes256-md5! を追加してやります (#4326-4337)。

最小設定でスルーしていた proxy-id ですが、これを消してみます (#4371-4381)。

そうするとQuick ModeのIdentifacation Payloadが 192.168.1.0/24、192.168.2.0/24 だった (#985) のが 0.0.0.0/0、0.0.0.0/0 になりました (#4378)。

このIdentificationはポリシーベースのIPsecでのセキュリティポリシーとして使用するものですが、SEILのルーティングベースIPsecではPhase 2の折衝以外では使わず、デフォルトで 0.0.0.0/0 という値を入れています。一方でstrongSwanはポリシーベースのIPsec(192.168.1.0/0 <–> 192.168.2.0 をIPsecに通すポリシー)として設定しているため、leftsubnet=192.168.2.0/24rightsubnet=192.168.1.0/24 の値を使用しています:

そしてstrongSwanは 0.0.0.0/0 を受け取ると無視して折衝を継続します。しかしSEILの方ではIdentificationの不整合により折衝に失敗しています。

これを解消するために、最小設定でも proxy-id の設定を入れていたのです。

ちなみにstrongSwanの方で leftsubnet=0.0.0.0/0rightsubnet=0.0.0.0/0 を設定するとphase 2の折衝は成功しますが、全てのパケットがESPを通るようになりおかしなことになります。例えばsshログイン中だとセッションが切れます(TCPの返りがIPsecを通っていく)。

proxy-id に関してはseil3のマニュアルに解説があります。

ここまででIKE折衝のおおまかな流れを理解することができました!

ISAKMP SA/IPsec SAの削除

SEILから interface.ipsec0.** の設定を削除 (#1008-1009)、あるいはstrongSwanをシャットダウンすると (#4748-4750)、Informational Exchangeが送信され、中にはDelete Payloadが含まれます。

Protocol ID: IPSEC_ESP (3) はIPsec SAを、Protocol ID: ISAKMP (1) はISAKMP SAを削除しています。これによりIPsecが切断され、ESPが観測されなくなります。

Dead Peer Detection (DPD)

RFC 2409 IKEv1IKEv1には対向機器の死活監視機能が含まれませんが、この機能はRFC 3706で定義されています。Phase 2の折衝完了後にSEILが20秒おきに送っているInformational ExchangeがDPDパケットです (#994-1003)。

SEILの Notify Message Type: R-U-THERE (are you there?) に対しstrongSwanが Notify Message Type: R-U-THERE-ACK を返しています。一定時間、または一定回数R-U-THERE-ACKが返ってこないと対向機器は死んだ (dead)とみなされ、ISAKMP SAが削除されInformational Deleteが送信されます。

INITIAL-CONTACT

SEILがinitiatorのときに、Phase 1折衝完了後に送信しているInformational ExchangeはINITIAL-CONTACTメッセージです。

これは「今あなたと1本目のISAKMP SAを張りましたよ(今までISAKMP SAは1本もありませんでしたよ)」というメッセージです (RFC 2407 4.6.3.3)。IPsecを繋いでいた機器の一方がInformational Deleteを送信せずに突然再起動してしまった場合、対向機器の再起動前のISAKMP SAが残り続けます(DPDで削除したりしない限り)。再起動後にISAKMP SAを貼り直した後INITIAL-CONTACTを送ってあげると、対向機器は前のISAKMP SAはもう使われていないと判断でき、削除することができます。

ライフタイム

SEILのコンフィグに interface.ipsec0.ike.proposal.phase1.lifetime : 60 を追加すると、phase 1プロポーザルの Life-Duration に反映されます。

そしてPhase 1折衝完了の47秒後に、新たにPhase 1を張り直します (#4890-#4895)。この「60秒」はPhase 1 (ISAKMP SA) の「ライフタイム」と呼ばれ、ISAKMP SAの有効期限になっています。Phase 1完了から60秒が経過するとISAKMP SAが無効になってしまうため、無効になる前に張り直しを行っています。

Phase 2 (IPsec SA) のライフタイムを60秒に設定した場合 (interface.ipsec0.ike.proposal.phase2.lifetime-of-time: 60) も同様です (#4985-4987)。lifetime-of-time という名前になっているのは、IPsec SAのライフタイムは時間的な有効期限の他に通信量 (lifetime of byte) の有効リミットも定義できるためです。

NAT traversal

SEILまたはstrongSwanがNAT配下にいる場合、ESPが通りません。ESPにはポート番号が無いので、NAPTが使えないためです。strongSwanを10.0.0.2のNAT配下に置いて折衝をしてみましょう。

(検証の簡略化のため、実際にはstrongSwanはNAT配下に置かず、forceencaps=yes を設定してSEILにNAT検知させています。実際にNAT配下にあった場合は、10.0.0.2 のポートは500、4500 でなくエフェメラルポートになり、#1065 のIdentification Payloadは 10.0.0.2 ではなくNAT配下のローカルのIPアドレスになります)

3往復目のIdentity Protectionからポート番号が4500に変わりました。また、ICMPのポート番号が空だったのが4500という番号がついています。NAT配下でない場合/NAT配下の場合のICMPを見比べると、

#988 はIPv4の下に直接ESPがついているのに対し、#1070 では IPv4/UDP/ESP と間にUDPが入っています。これを “UDP-Encapsulated ESP” と呼びます (RFC 3948)。UDPはポート番号を持つので、UDP-Encapsulated ESPはNAT配下からでも通信可能になります。

Main Mode/Aggressive Mode

今まで見てきたphase 1の折衝は、全て “Main Mode”と呼ばれるものでした。Phase 1の折衝で使えるもう1つのモードが、”Aggressive Mode” です (RFC 2409 5.4)。

SEILのコンフィグに interface.ipsec0.ike.exchange-mode : aggressive を追加します。

SEILがAggressive Modeでphase 1の折衝を始めるようになりましたが、strongSwanはInformationalで “AUTHENTICATION-FAILED” メッセージを返しています。strongSwanのログには、

Aggressive Mode PSK disabled for security reasons と出力されています。これはseil3のマニュアルにも書いてあります:

IKEのAggressive modeかつ事前共有鍵での認証は、Main modeと比較してセキュリティレベルが低くなるため、NAT配下の装置との接続が必要でないのであれば、アドレス書き換えの検知は無効化してご利用ください。

strongSwanのFAQにも解説があります。

(Aggressive ModeでのPSKを用いた折衝では、2つ(1往復)の折衝パケットが1つ手に入るとPSKのオフライン辞書攻撃が可能になります (VU#857035)。ただしMain Modeにおいても、中間者攻撃によりPSKのオフライン辞書攻撃が可能になる手法が2018年に発見されています (JVNVU#93409761)。)

Linuxの /etc/ipsec.confaggressive=yes を、/etc/strongswan.confcharon { i_dont_care_about_security_and_use_aggressive_mode_psk = yes } を入れて無理やり折衝させます。

折衝できました。Main Modeはphase 1の折衝に6パケット必要なのに対し、Aggressive Modeは3パケットで終わります。アグレッシブです。

まとめ

SEILの設定を変えながらIKEのパケットの変化を見て、それぞれの役割を解説しました。

この記事の内容が理解できれば、IKEのエラーログの多くが理解できるようになると思います。例えば、racoon: mismatched identification returned というログが出たときには何をすれば良いでしょう?…「Identification」で解説した機器の識別子のペアが対向と一致していないですね。interface.ipsec0.ike.{my-identifier,peers-identifier} の設定が間違っていると疑うことができます。

SEILのIKE/IPsecを使っていてエラーログの用語がよく分からなくなったら、この記事に同じ用語が載っていないかチェックしてみてください。

IIJ Engineers blog読者プレゼントキャンペーン
  • Twitterフォロー&条件付きツイートで「IoT米」を抽選で20名にプレゼント!
    応募期間は2020/12/01~2020/12/31まで。詳細はこちらをご覧ください。
    今すぐツイートするならこちら→ フォローもお忘れなく!

芦原 渉

2020年12月22日 火曜日

ルータのファームウェア開発をしています。河原でプログラミングするのが好きです。

Related
関連記事