IPsec は IP レベルでポリシーに基づきトラフィックを保護し、IKEv2 はピアの認証、アルゴリズムの交渉、セキュリティ関連付け(SA)の維持を担当します。本社 10.10.0.0/24 から支社 10.20.0.0/24 へのアクセスを例に挙げます:業務ホストはパケットをゲートウェイに渡し、ゲートウェイは保護が必要かどうかを判断し、確立済みの送信用 SA でカプセル化します。相手側ゲートウェイは着信用 SA を検索し、認証・復号してポリシーを確認した後、内部ネットワークへ転送します。
ここでは3つの独立した事柄があります:システムルーティングがパケットをどこに送るか、ポリシーがどのように保護を要求するか、SA が利用可能なキーと状態を持っているかどうか です。IKE が接続済みと表示するのは、その一部が成立していることを示すだけです。
迅速な特定 確認すべき項目
IKE SA と Child SA コントロールメッセージの状態と業務トラフィックの状態
ESP と AH 暗号化の有無、どのフィールドが認証されるか
Tunnel と Transport 完全な元の IP パケットがカプセル化されるか
IKE_AUTH でヘッダが見えるか 保護されたペイロードと外側ヘッダの境界
UDP 4500 に2つの形式がある理由 IKE のゼロマーカと ESP の非ゼロ SPI
再鍵時にユーザーを再検証するか Rekey と Reauthentication
SA はあるが業務カウントがない ルーティング、セレクタ、転送と戻り経路
ポリシーとセキュリティ関連付け:なぜ別々に保存するのか
セキュリティポリシーデータベース(SPD)は、トラフィックを破棄するか、バイパスするか、IPsec で保護するべきかを表現します。セキュリティ関連付けデータベース(SAD)は、具体的なアルゴリズム、キー、SPI、シーケンス番号、ライフサイクルなどの状態を保存します。1つのポリシーは、異なる時期に異なる世代の SA を指すことができ、再鍵時には新旧の SA が短時間併存する場合もあります。IPsec アーキテクチャ RFC 4301
ESP SA は単方向です:A→B と B→A はそれぞれ独自の SPI、キー、シーケンス番号を使用します。IKEv2 の1回の Child SA 交渉では通常、このようなペアの SA が作成され、管理ツールはこれを1つの CHILD_SA として全体表示することがあります。IKE SA は双方のその後のコントロールメッセージを保護するため、その SPI と ESP の SPI を同じ番号空間として扱ってはなりません。strongSwan IPsec 入門
着信 ESP は、受信側が承認した SPI を使用してセキュリティ関連付けを検索します。SPI はパスワードでもユーザーIDでもありません。たとえパケットが SA に見つかったとしても、認証、リプレイチェック、ポリシー制約を通過する必要があります。正しい SPI が見えただけで、パケットを合法な業務として扱うことはできません。
Traffic Selector によってトンネルがどのように制限されるか
Traffic Selector には、アドレス範囲、プロトコル、ポート範囲が含まれます。発信側は広い範囲を提案でき、応答側はポリシーに基づいてより狭い範囲を受諾します。例えば、本社が 10.10.0.0/24 から支社 10.20.0.0/24 へのアクセスを提案した場合、最終的に支社のサーバー 10.20.0.8 の TCP 443 のみが許可されるのであれば、交渉成功を「支社サブネット全体の任意のポートがアクセス可能」と解釈してはいけません。
以下は、1方向で既に決定されたセレクタのマッチングのみを示しており、完全な IKE 交渉やカーネル SPD の優先順位はシミュレートしません。
from ipaddress import ip_address , ip_network
def protected_flow ( src , dst , protocol , dport ) :
return ( ip_address ( src ) in ip_network ( ' 10.10.0.0/24' )
and ip_address ( dst ) == ip_address ( ' 10.20.0.8' )
and protocol == 6 and dport == 443 )
assert protected_flow ( ' 10.10.0.9' , ' 10.20.0.8' , 6 , 443 )
assert not protected_flow ( ' 10.10.0.9' , ' 10.20.0.8' , 6 , 22 )
assert not protected_flow ( ' 10.10.0.9' , ' 10.20.0.9' , 6 , 443 )
assert not protected_flow ( ' 10.11.0.9' , ' 10.20.0.8' , 6 , 443 )
print ( ' アドレス、プロトコル、ポートの共同マッチングの局所モデルが通過' )
SPI が見つかったら、内側パケットを配信できるのか?
正しい SPI を送信用 SA の SPI に置き換えても、同一デバイスがそれを保存しているとしても、それはこの方向の着信状態として扱えません。復号後に宛先ポートを変更した場合、暗号学的チェックが通過しても、セレクタによって拒否される可能性があります。SPI は通行証ではありません。
図を準備しています 再読み込み
SPI が見つかったら、内側パケットを配信できるのか? SPI は単方向のキー状態を特定し、セレクタは保護されたトラフィックを制約します。SA の検索、認証の成功、ポリシーの許可は、3つの連続する判定基準です。
{"id":"ipsec-sa-and-selector","title":"SPI が見つかったら、内側パケットを配信できるのか?","summary":"SPI は単方向のキー状態を特定し、セレクタは保護されたトラフィックを制約します。SA の検索、認証の成功、ポリシーの許可は、3つの連続する判定基準です。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"form-field\"><span data-i18n=\"spi\"></span><select id=\"spi\"><option value=\"258\">0x102 inbound</option><option value=\"513\">0x201 outbound</option><option value=\"999\">0x3e7 unknown</option></select></label><label class=\"check\"><input type=\"checkbox\" id=\"auth\" checked><span data-i18n=\"auth\"></span></label><div class=\"control\"><label for=\"dst\" data-i18n=\"dst\"></label><output for=\"dst\" id=\"dst-value\"></output><input id=\"dst\" type=\"range\" min=\"7\" max=\"9\" value=\"8\" step=\"1\"></div><label class=\"form-field\"><span data-i18n=\"port\"></span><select id=\"port\"><option value=\"443\">TCP443</option><option value=\"22\">TCP22</option></select></label><div class=\"track-row\"><span data-i18n=\"trace\"></span><div id=\"trace\" class=\"track\"></div></div><div class=\"metrics\"><div><span data-i18n=\"result\"></span><strong id=\"result\"></strong></div></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.metrics strong{font-size:17px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function draw(){const sa=+$('#spi').value===258,auth=sa&&$('#auth').checked,policy=auth&&+$('#dst').value===8&&+$('#port').value===443;set('dst-value','10.20.0.'+$('#dst').value);cells('trace',[{text:'SAD',cls:sa?'on':'error'},{text:'AEAD/replay',cls:auth?'on':'empty'},{text:'selector',cls:policy?'on':'empty'}]);set('result',!sa?'DROP: no inbound SA':!auth?'DROP: authentication':!policy?'DROP: selector':'DELIVER');$('#result').dataset.deliver=String(policy);}document.querySelectorAll('input,select').forEach(e=>e.oninput=draw);draw();","audio":false,"strings":{"auth":"認証とリプレイチェックが通過","dst":"内側宛先の最下位バイト","heading":"SPI が見つかったら、内側パケットを配信できるのか?","intro":"着信 SPI、認証結果、内側宛先を変更し、SAD 検索後に継続して実行されるチェックを追跡します。","port":"内側宛先ポート","reset":"リセット","result":"処理結果","scope":"固定着信 SA SPI0x102、送信 SA0x201;ソース 10.10.0.9 から 10.20.0.8 TCP443 のみを許可。認証スイッチには完全性とリプレイ前提が含まれる;実際の復号や SPD 優先順位は含まない。","scopeLabel":"モデル範囲と規約","spi":"受信した SPI","trace":"着信チェックチェーン"}}
ESP:可視フィールド、暗号化コンテンツ、認証
ESP は IP プロトコル番号 50 を使用し、AH は 51 を使用します。ここで言う数字はプロトコル番号であり、TCP/UDP ポートではありません。ESP は機密性と完全性を提供できますが、具体的な内容は交渉されたトランスフォームによって決まります。AH は認証を提供しますが暗号化は提供せず、選択された IP ヘッダフィールドをカバーし、NAT によるアドレス書き換えにより敏感です。すべての ESP 組み合わせを固定された1つの暗号化アルゴリズムとして記述することはできません。
部分 可視性と役割
SPI 4 B、Sequence Number 4 B プレーンテキストヘッダ、SA の特定とリプレイチェックに役立つ
明示的 IV アルゴリズム依存、AES-GCM-ESP の場合 8 B
Payload、Padding、Pad Length、Next Header 暗号化変換下で保護されるコンテンツ
ICV / Tag 完全性チェック、長さはトランスフォームによって決まる
Transport モードは通常、元の IP ヘッダの後の上位層コンテンツを保護します。Tunnel モードは、完全な元の IP パケットを内側ペイロードとして使用し、新しい外側 IP ヘッダを追加します。前者が元の IP ヘッダが完全に修正不可能であることを意味するわけではなく、後者が業務のエンドツーエンド暗号化を意味するわけではありません:ゲートウェイからゲートウェイへのトンネルは、相手側ゲートウェイで終了します。ESP RFC 4303
AES-GCM の3つの長さを混同しない
AES-GCM-ESP の nonce は、4 B の salt と 8 B の明示的 IV で構成されます。salt はキー材料の割り当て時に随伴し、各パケットで ESP ヘッダ内に 4 B のフィールドを再送信するものではありません。明示的 IV はパケット内に現れ、同一キー下では重複してはなりません。SPI とシーケンス番号は追加認証データに参加するため、任意に書き換えることはできません。
Tag のサポート長さは、具体的なトランスフォーム識別子によって決まります:RFC 4106 は 16 B のサポートを要求し、8 B または 12 B も許可しており、一律に「GCM の ICV は 12 B」と記述してはいけません。以下の例では 16 B Tag、最小4バイトアライメント、IPv4 オプションなし、UDP NAT-T を選択しています。追加のトラフィック隠蔽パディングはありません。AES-GCM-ESP RFC 4106
IPv4 トンネル + UDP カプセル化 + AES-GCM:1,000 B 内側パケットの帳簿
外側 IP
20 B
UDP
8 B
ESP ヘッダ
8 B
明示的 IV
8 B
暗号化コンテンツ
1004 B
Tag
16 B
暗号化コンテンツ = 内側パケット 1000 + パディング 2 + 尾部 2
尾部は Pad Length と Next Header;追加の4バイト Non-ESP Marker は含まない
総長 1064 B;図の枠幅は実際のバイト比率を表さない
16 B Tag を固定使用、最小4バイトアライメント、IPv4 オプションなし;実際の予算は交渉されたアルゴリズムとカプセル化に依存する。
def esp_tunnel_size ( inner , outer_ip = 20 , udp = True ) :
padding = ( - ( inner + 2 ) ) % 4
total = outer_ip + ( 8 if udp else 0 ) + 8 + 8 + inner + padding + 2 + 16
return total , padding
assert esp_tunnel_size ( 1000 ) == ( 1064 , 2 )
assert esp_tunnel_size ( 1000 , udp = False ) == ( 1056 , 2 )
assert esp_tunnel_size ( 1000 , outer_ip = 40 ) == ( 1084 , 2 )
assert max ( n for n in range ( 1501 ) if esp_tunnel_size ( n ) [ 0 ] <= 1500 ) == 1438
assert esp_tunnel_size ( 1438 ) == ( 1500 , 0 )
assert esp_tunnel_size ( 1439 ) [ 0 ] == 1504
print ( ' アルゴリズムとカプセル化の仮定が明確な ESP 長さ予算が通過' )
1438 B は、このグループの仮定における最大完全内側 IP パケットであり、TCP アプリケーションペイロードではありません。内側 IPv4/TCP には、独自のヘッダをさらに差し引く必要があります。アルゴリズム、外側 IPv6、IP オプション、および他のトンネルの変化後には、再計算する必要があります。
シーケンス番号と受信リプレイウィンドウは、ある程度の順序不同を許容しつつ、重複パケットを拒否します。ESN は拡張シーケンス番号を使用しますが、オンライン ESP のシーケンス番号フィールドは依然として下位 32 ビットであり、上位ビットは状態から推測され、認証に参加します。リプレイウィンドウは信頼できる伝送ウィンドウではありません:失われた ESP パケットは、ESP によって自動的に再送信されることはありません。
IKEv2:保護キーを先に派生させ、その後身份認証を完了する
追加のチャレンジ、EAP、または拡張交換がない場合、初期確立は通常、IKE_SA_INIT と IKE_AUTH の2つのリクエスト-レスポンス交換、つまり約2ラウンドトリップタイム(RTT)を含み、計算時間が加算されます。Cookie、エラー DH グループのリトライ、認証方法、およびパケット損失は時間を増加させる可能性があり、すべての接続が一定の4パケットであると記述してはいけません。IKEv2 初期交換
基本確立には2つのリクエスト-レスポンス交換が必要;認証拡張は往復を追加できる
発信側
応答側
IKE_SA_INIT:提案、DH 公開鍵、Ni
IKE_SA_INIT:選択、DH 公開鍵、Nr
IKE_AUTH:身份、AUTH、Child 提案とセレクタ
IKE_AUTH:身份、AUTH、承認された Child パラメータ
保護キーは既に存在するが、ピアの身份認証はまだ完了していない
IKE_AUTH の保護されたペイロードは暗号化されるが、外側 IP/UDP および IKE ヘッダは依然として可視;EAP は交換を継続できる。
IKE_SA_INIT 交換は、アルゴリズム提案、一時 DH 公開鍵、および nonce を行います。ここで共通秘密が得られても、相手の身份が証明されたわけではありません:身份検証は交渉記録にバインドされ、攻撃者と共有された暗号化チャネルのみが確立されるのを防ぎます。
IKE_AUTH の暗号化ペイロードには、身份、認証材料、および最初の Child SA に必要なパラメータが含まれます。外側 IP/UDP および IKE ヘッダは依然として可視です。AUTH は、署名または共有キーなどのメカニズムに基づいて行うことができ、「最初のメッセージに証明書秘密鍵で署名する」に限定されるものではありません。基本署名認証の入力には、相手の nonce と SK_pi/SK_pr で処理された身份ペイロードが含まれ、ハンドシェイクと身份の間のバインドを確立します。RFC 7296 §2.15
EAP 認証には複数の IKE_AUTH 往復が必要になる場合があります。IDi を宣言したからといって、EAP 身份認証が完了したわけではありません。認証が通過した後でも、その身份が対応するセレクタを要求できるかどうかをチェックする必要があります。
キー派生における順序と用途
基本 DH フローでは、g^ir を「自分の秘密鍵、相手の公開鍵」を使用して双方が計算した共有秘密とします。これは2つの公開 KE 値に対して直接 DH を行うものではありません。
段階 基本派生式
初期 IKE シード SKEYSEED = prf(Ni | Nr, g^ir)
IKE キー展開 prf+(SKEYSEED, Ni | Nr | SPIi | SPIr)
最初の Child / 新規 DH なし Child KEYMAT = prf+(SK_d, Ni | Nr)
新規 DH あり CREATE_CHILD_SA KEYMAT = prf+(SK_d, g^ir_new | Ni | Nr)
prf+ は IKE で定義された反復展開関数であり、HMAC 結果を無限に繰り返すものではありません。展開された IKE 材料は、SK_d、両方向の完全性/暗号化、および身份認証補助用途のために順次使用されます。AEAD 組み合わせには、従来の完全性アルゴリズムのキーは別途必要ありません。SK_pi/SK_pr は証明書秘密鍵ではありません。RFC 7296 §2.13–2.17
最初の Child は IKE_SA_INIT の nonce を使用します。その後の作成は、対応する新しい交換の nonce を使用します。新しい DH がある場合、共有秘密は Ni/Nr 前 にあり、末尾に自由に配置できる付加フィールドではありません。基本最初の Child には独自の追加 KE 交換がないからといって、接続全体に一時 DH がないわけではありません。
NAT-T:UDP 4500 上で IKE と ESP をどのように区別するか
ネイティブ ESP には TCP/UDP ポートがないため、多くの NAT はマルチクライアント ESP マッピングを正しく維持できません。NAT-T は ESP を UDP でカプセル化し、4500 をよく使用します。IKE は通常、UDP 500 から確立を開始し、NAT の検出または機能条件に基づいて 4500 に切り替えます。4500 が見えただけでは、必ずしも NAT があるとは断定できません。MOBIKE などの動作もポートの使用に影響を与えます。strongSwan NAT トリaversal
UDP 4500 のペイロード 判断方法
IKE メッセージ 先行する4バイトのゼロ Non-ESP マーカ、その後 IKE ヘッダ
UDP カプセル化 ESP 非ゼロ SPI から直接始まり、追加の4バイトは付加されない
NAT keepalive 特定の1バイト 0xff、マッピング維持用
したがって、前述の ESP 長さ予算には UDP ヘッダ 8 B のみが追加され、IKE 専用の 4 B マーカは追加されません。NAT keepalive はマッピングを維持し、保護された IKE アクティブチェックはピアの応答を確認し、これらは互いに代替できません。UDP カプセル化 RFC 3948
再鍵、再認証、およびアドレス移行
CREATE_CHILD_SA は、追加の Child を確立したり、既存の Child を置き換えたり、IKE SA の再鍵にも使用できます。基本交換は1つのリクエストと1つのレスポンスで、約1 RTT です。「0.5 RTT 未満で交渉が完了する」には、対応するプロトコルの保証はありません。
Rekey はキーと関連付けを更新しますが、証明書の現在の状態を再検証したり、ユーザー認証を再度実行したりするものではありません。Reauthentication は認証プロセスを再実行し、影響を受ける状態が異なります。実装は通常、新旧 SA を短時間オーバーラップさせ、進行中の旧パケットを受信させます。期限切れ、バイト/パケットカウント、リプレイシーケンス番号、および双方による同時再鍵の開始を処理する必要があります。strongSwan SA 更新
MOBIKE は、双方のサポートと交渉が必要となる IKEv2 拡張機能であり、トンネルが外側アドレスの変化に応じてパスを更新できるようにします。デバイスが「IKEv2」と記載されているからといって、移行が必ずサポートされているとは断定できません。これはアプリケーションに新しい身份を作成するものではなく、切り替え中のゼロパケットロスを保証するものでもありません。MOBIKE RFC 4555
証明書チェーンが大きい場合、IKE メッセージは MTU および断片化の制限を受ける可能性があります。IKEv2 の暗号化メッセージ断片化拡張機能は IP レベルの断片化とは異なり、ESP データパケットが PMTU を無視できることを意味するものではありません。IKE 断片化 RFC 7383
IKE 成功から業務成功へ
Linux のポリシーベース IPsec は、ポリシーに基づいてマッチングトラフィックをキャプチャします。XFRM インターフェースなどのルーティングベースのアプローチは、ルーティング管理をより直感的にしますが、依然として正しいポリシー、SA、およびインターフェースの関連付けに依存しています。インターフェースの作成やルーティングの追加自体は、暗号学的セッションを確立しません。strongSwan ルーティングベース VPN
現象 継続して確認すべき証拠
IKE_SA_INIT 応答を受信しない 外側到達可能性、UDP ポート、ファイアウォール、ピアのリッスン
NO_PROPOSAL_CHOSEN を受信 双方のアルゴリズム提案とマッチされた接続構成
AUTHENTICATION_FAILED 実際の身份、資格情報、信頼チェーンおよび認証方法
IKE SA は存在するが Child が失敗 ESP 提案、TS、アドレス割り当てと認可
Child は成立したが送信カウントが増えない 業務パケットがゲートウェイに到達しているか、ルーティングとポリシーがマッチしているか
送信は増加するが入信は増えない ピアのカウント、戻り経路、フィルタ、NAT-T および SA 状態
小さなパケットは利用可能だが、大きな転送が固まる PMTU、カプセル化予算、ICMP フィードバックおよび断片化
swanctl --list-sas から実際の交渉結果を確認し、業務フローとルーティングと照合できます。Linux ip -s xfrm policy でポリシーを観察できます。SA 状態をエクスポートする際には、一部の生状態コマンドにキー材料が含まれる可能性があることに注意し、診断共有ではキーを出力しないサマリーを優先してください。パフォーマンスを判断する際には、実際のトラフィックとオフロードが有効かどうかに基づいて行い、「エンタープライズグレード」や「カーネル実装」という言葉だけでスループットの上限を直接導出しないでください。