STUDY · NETWORK GUIDE

4-5 Zone-Based Firewall — ゾーン設計とステートフルインスペクション

ACL のステートレスな限界を起点に、インターフェースをゾーンで束ねてステートフルに検査する ZBF を扱う。ゾーン間は default deny・self は既定 permit という既定挙動と、inspect が戻りを自動許可する非対称性を csr1000v の show 出力で検証する。

1. 前節の振り返りと本節の内容

前節 4-4 L2 セキュリティ では、L2 で成立する 3 つの攻撃を起点に、それを止める防御機能を扱いました。MAC フラッディング・なりすましには Port-Security が許可 MAC を制限し、Rogue DHCP には DHCP Snooping が trust 境界で偽のサーバ応答を破棄し、ARP スプーフィングには DAI (Dynamic ARP Inspection) がその binding table を入力に ARP を突合しました。

ここまでの 4-1 ACL から 4-4 L2 セキュリティまでは、個々の要素 を制御する仕組みでした。ACL はパケットの送信元・宛先アドレスやポート番号という単位で許可・拒否を 1 本ずつ書き、Port-Security は MAC の個数、DHCP Snooping は DHCP の応答方向、DAI は ARP の中身というように、それぞれが「ある 1 つの要素」を検査する防御でした。

本節で扱う ZBF (Zone-Based Firewall) は、防御の単位が変わります。ZBF は個々のインターフェースやアドレスではなく、インターフェースをゾーン (zone) という単位でまとめ、ゾーンとゾーンの間を流れる通信をまとめて検査します。ACL が「許可・拒否のルールを 1 本ずつ書く」のに対し、ZBF は「INSIDE から OUTSIDE への通信」というゾーン単位のポリシーで通信を表現します。さらに ZBF は ステートフル (stateful) に動作します。ACL がパケット 1 つずつを独立に判定する ステートレス (stateless) なのに対し、ZBF は通した通信を覚え、その戻りを自動で許可します。この「ステートレス ACL とステートフル ZBF」の対比が本節の背骨です。CML 上の csr1000v で、ゾーンを割り当てただけでは通らない既定 deny、zone-pair と policy-map type inspect による許可、戻りの自動許可と非要求の遮断、そして self ゾーンの扱いを、実機の show 出力で検証します。

2. なぜゾーンで考えるのか — ACL の限界とステートフルの動機

ZBF が解決する問題を理解するために、まず ACL の限界を 2 点で整理します。ZBF の設計は、この 2 つの限界への回答になっています。

第一の限界は、インターフェースと方向の組み合わせで管理が膨れ上がる ことです。ACL は「どのインターフェースの、どちらの方向 (in / out) に適用するか」を 1 つずつ指定します (4-1 ACL で扱いました)。ルータのインターフェースが 2 つなら方向を含めて 4 通りですが、インターフェースが増えるとこの組み合わせが増え、どのインターフェースにどの ACL が当たっているかを追うのが難しくなります。ZBF は、複数のインターフェースを 1 つのゾーンにまとめ、「ゾーンからゾーンへ」という単位でポリシーを書くことで、この組み合わせの爆発を抑えます。同じセキュリティレベルのインターフェースを 1 つのゾーンに束ねれば、インターフェースが何本あってもゾーン間のポリシーは変わりません。

第二の限界は、戻りトラフィックの穴を手で開ける必要がある ことです。ステートレスな ACL は、パケット 1 つずつを独立に判定します。内部から外部へ出す通信を許可しても、その通信に対する外部からの応答 (戻り) は別のパケットとして扱われるため、応答を通す許可を別途書かなければなりません。この戻りを許可するために、ACL では established キーワード (TCP の応答フラグを見て通す) や reflexive ACL (出た通信を記憶して戻りを一時許可する) を使いますが、いずれも設定が煩雑で、TCP 以外のプロトコルでは扱いにくいという弱点があります。

ステートフルインスペクション (stateful inspection) は、この第二の限界を根本から解きます。ステートフルなファイアウォールは、内部から外部へ通した通信を セッション (session) として記憶し、その通信に対応する戻りだけを自動で許可します。管理者が戻りの許可を書く必要はなく、ファイアウォールが「これは先ほど通した通信の戻りだ」と判断して通します。ZBF の inspect アクションがこのステートフル検査を担い、established や reflexive ACL を書かずに戻り穴が自動で開くようにします。以降、この仕組みを構成要素から順に見ていきます。

3. ZBF の構成要素 — zone / zone-member / zone-pair / policy-map type inspect

ZBF は 4 つの構成要素を組み合わせて通信ポリシーを表現します。それぞれの役割を整理します。

  • ゾーン (zone) — セキュリティレベルが同じインターフェースを束ねる論理的なグループ。zone security <名前> で定義します。「信頼できる内部 (INSIDE)」「信頼できない外部 (OUTSIDE)」のように、通信ポリシー上で同じ扱いをしたいインターフェースを 1 つのゾーンにまとめます。
  • ゾーンメンバー (zone-member) — インターフェースをゾーンに所属させる設定。インターフェース側で zone-member security <ゾーン名> を投入し、そのインターフェースがどのゾーンに属するかを決めます。
  • ゾーンペア (zone-pair)通信の向き を定義する器。zone-pair security <名前> source <送信元ゾーン> destination <宛先ゾーン> で「どのゾーンから、どのゾーンへ」を指定します。ゾーンペアは向きを持つため、INSIDE から OUTSIDE への通信と、OUTSIDE から INSIDE への通信は別々のゾーンペアになります。
  • 検査ポリシー (policy-map type inspect) — ゾーンペアを流れる通信に対して「何を、どう扱うか」を定義するポリシー。どの通信を対象にするかを class-map type inspect で分類し、その分類に対して inspect (ステートフル検査して通す) / pass (ステートレスに通す) / drop (破棄) のいずれかのアクションを policy-map type inspect で指定します。

ここで、ZBF の検査ポリシーが MPF (Modular Policy Framework) の枠組みに従う点が重要です。MPF は「class-map で分類し、policy-map でアクションを決め、それをどこかに適用する」という Cisco IOS の共通の設定パターンで、QoS などでも使われます。ZBF はこの MPF の type inspect 版 であり、class-map type inspect で通信を分類し、policy-map type inspect でアクションを決め、それを zone-pairservice-policy で適用します。ACL のように 1 本の許可・拒否行を並べるのではなく、「分類 → アクション → 適用先」という 3 段の構造でポリシーを組み立てるのが ZBF の書き方です。

class-map type inspect では、対象とする通信を プロトコル名 で分類できます。match protocol icmpmatch protocol tcp のように、アプリケーション層のプロトコルを指定して分類する点が、アドレスとポート番号で分類する ACL との違いです。ZBF はプロトコルを認識して検査するため、そのプロトコルが戻りに使うポートを自動で開けられます。

4. ZBF の既定動作 — ゾーン間は default deny、self は既定 permit

ZBF を扱う上で最も重要なのが、ゾーンを定義した瞬間に変わる 既定の通信可否 です。これを正確に理解していないと、ゾーンを割り当てただけで通信が止まる理由が分からなくなります。既定動作を 4 点で整理します。

第一に、異なるゾーン間は、ゾーンペアが無ければすべて deny です。インターフェースをゾーンに割り当てると、そのゾーンと別のゾーンの間の通信は、対応するゾーンペアが存在しない限りすべて破棄されます。ゾーンペアは「明示的に許可を書く器」であり、ゾーンペアを作って初めてその向きの通信が検査対象になります。ゾーンペアが無い向きは暗黙的に deny されるため、ZBF は「デフォルト拒否 (default deny)」のファイアウォールです。この挙動を §6 の実機で確認します。

第二に、同一ゾーン内のインターフェース間は暗黙的に permit です。同じゾーンに属するインターフェース同士の通信は、ゾーンペアを作らなくても通ります。同じセキュリティレベルの中での通信は自由、という設計です。

第三に、ゾーンに割り当てられたインターフェースと、ゾーンに割り当てられていないインターフェースの間の通信は通れません。片方だけがゾーンに属している場合、その間の通信を許可するゾーンペアを作れないため、通信は成立しません。管理用インターフェースをゾーンに入れない設計にする場合は、この点に注意が必要です (§11 で補足します)。

第四に、self ゾーン (ルータ自身) は既定で permit です。ZBF には、システムが最初から用意している特別なゾーンとして self ゾーン があります。self ゾーンはルータ自身を宛先または送信元とする通信 (ルータへの SSH・SNMP、ルータが送る OSPF など) を表します。self ゾーンとほかのゾーンの間の通信は、ゾーンペアを作らない限り既定で許可 されます。インターフェースをゾーンに割り当てても、そのインターフェースからルータ自身への通信 (self 宛) は、self へのゾーンペアが無い限り通ります。ここが誤解しやすい点で、「インターフェースをゾーンに入れると self 宛も deny になる」というのは誤りです。self ゾーンへのゾーンペアを作った瞬間に、初めてその向きが検査ポリシーの支配下に入ります。この挙動を §10 の実機で確認します。

以上をまとめると、ゾーンをまたぐ通信は原則 deny で、ゾーンペアを作った向きだけが通り、self だけは例外的に既定 permit、という 4 つの規則で ZBF の既定動作が決まります。

5. ラボトポロジ

本節の検証で使うトポロジを示します。csr1000v 3 台を直列につなぎ、中央の R2 に ZBF を適用します。

4-5 Zone-Based Firewall ラボトポロジ。R2 (csr1000v) が ZBF 適用点で、R1 方向の Gi2 を INSIDE ゾーン (信頼側)、R3 方向の Gi3 を OUTSIDE ゾーン (非信頼側) に割り当て、ルータ自身は self ゾーンになる。R1 は内部ホスト (Lo10 10.0.10.1)、R3 は外部サーバ (Lo20 10.0.20.1) を持つ。OSPF area 0 で全 Loopback / リンクを広告し、ZBF 適用前は双方向に到達する。INSIDE→OUTSIDE の zone-pair に policy-map type inspect を当て、inside 発トラフィックの戻りをステートフルに自動許可する一方、outside 発の非要求トラフィックは OUT→IN の zone-pair が無いため既定 deny で落ちる、という非対称を検証する

トポロジの構成は以下のとおりです。

  • R1 (INSIDE 側の内部ホスト) — Loopback10 に 10.0.10.1、R2 への Gi2 に 10.12.0.1 を持ちます。信頼側の送信元として使います。
  • R2 (ZBF firewall) — R1 への Gi2 に 10.12.0.2、R3 への Gi3 に 10.23.0.1 を持ちます。Gi2 を INSIDE ゾーン、Gi3 を OUTSIDE ゾーンに割り当て、ルータ自身が self ゾーンになります。ここに ZBF のポリシーを構築します。
  • R3 (OUTSIDE 側の外部サーバ) — Loopback20 に 10.0.20.1、R2 への Gi2 に 10.23.0.2 を持ちます。非信頼側の宛先として使います。

3 台は OSPF area 0 で全 Loopback とリンクを広告しており、ZBF を適用する前は双方向に到達できます。R1 が OSPF で学習した経路を確認します。

snippet
show ip route ospf
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2, m - OMP
       n - NAT, Ni - NAT inside, No - NAT outside, Nd - NAT DIA
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       H - NHRP, G - NHRP registered, g - NHRP registration summary
       o - ODR, P - periodic downloaded static route, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR
       & - replicated local route overrides by connected

Gateway of last resort is not set

      10.0.0.0/8 is variably subnetted, 9 subnets, 3 masks
O        10.0.2.1/32 [110/2] via 10.12.0.2, 00:02:40, GigabitEthernet2
O        10.0.3.1/32 [110/3] via 10.12.0.2, 00:02:40, GigabitEthernet2
O        10.0.20.1/32 [110/3] via 10.12.0.2, 00:02:40, GigabitEthernet2
O        10.23.0.0/30 [110/2] via 10.12.0.2, 00:02:40, GigabitEthernet2
R1#

R1 は外部サーバ R3 の Loopback 10.0.20.1/32 を、R2 の 10.12.0.2 経由で学習しています。経路が届いているため、この時点では R1 から R3 へ通信できます。ZBF を適用する前のベースラインとして、R1 (INSIDE) から R3 (OUTSIDE) の 10.0.20.1 へ ping します。

snippet
ping 10.0.20.1 source Loopback10 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.20.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.10.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
R1#

逆向きに、R3 (OUTSIDE) から R1 (INSIDE) の 10.0.10.1 へも ping します。

snippet
ping 10.0.10.1 source Loopback20 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.10.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.20.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/2 ms
R3#

どちらの向きも Success rate is 100 percent (5/5) で、ZBF を適用する前は inside↔outside が双方向に疎通しています。この双方向到達が、ゾーンを割り当てた後にどう変わるかを §6 以降で追います。

6. ゾーンを定義しインターフェースに割り当てる — まだ通らない

R2 でゾーンを 2 つ定義し、インターフェースを割り当てます。INSIDE ゾーンに Gi2 (R1 方向)、OUTSIDE ゾーンに Gi3 (R3 方向) を所属させます。この時点ではゾーンペアをまだ作りません。ゾーンを割り当てただけの状態から、R1 (INSIDE) から R3 (OUTSIDE) へ ping します。

snippet
ping 10.0.20.1 source Loopback10 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.20.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.10.1 
.....
Success rate is 0 percent (0/5)
R1#

§5 では 100 percent だった同じ ping が、Success rate is 0 percent (0/5) に変わりました。ゾーンペアを 1 つも作っていないため、INSIDE と OUTSIDE の間の通信が §4 の第一の規則 (異なるゾーン間はゾーンペアが無ければ deny) によってすべて破棄されています。OSPF の経路は §5 のとおり届いているにもかかわらず、ZBF がゾーン境界で ICMP を落としています。ゾーンにインターフェースを入れた瞬間、ゾーン間は塞がる ことがこの結果に表れています。

ゾーンの状態を show zone security で確認します。

snippet
show zone security
zone self 65535
  Description: System defined zone

zone service 65534
  Description: System defined zone

zone INSIDE 1
  Description: trusted internal zone
  Member Interfaces:
    GigabitEthernet2

zone OUTSIDE 2
  Description: untrusted external zone
  Member Interfaces:
    GigabitEthernet3

R2#

上部の zone self 65535zone service 65534 は、いずれも System defined zone で、システムが最初から用意している既定のゾーンです。self ゾーンがルータ自身を表すゾーンで、§4 の第四の規則および §10 で扱います。その下の zone INSIDE 1 に Member Interfaces として GigabitEthernet2zone OUTSIDE 2GigabitEthernet3 が所属しており、管理者が定義した 2 つのゾーンにインターフェースが割り当てられています。ゾーンとメンバーは設定されているものの、ゾーン間を通すゾーンペアがまだ無いため、§4 の既定 deny が働いて ping が落ちた、という状態です。

7. zone-pair と policy-map type inspect — inside 発を検査して通す

INSIDE から OUTSIDE への通信を許可します。§3 の 3 段構造 (分類 → アクション → 適用先) に従い、class-map で通信を分類し、policy-map で inspect アクションを指定し、それを INSIDE→OUTSIDE のゾーンペアに適用します。R2 に投入する設定は以下のとおりです。

cisco
class-map type inspect match-any CM-IN-OUT
 match protocol icmp
 match protocol tcp
policy-map type inspect PM-IN-OUT
 class type inspect CM-IN-OUT
  inspect
 class class-default
  drop
zone-pair security IN-OUT source INSIDE destination OUTSIDE
 service-policy type inspect PM-IN-OUT

設定の意味を段に分けて整理します。class-map type inspect match-any CM-IN-OUT は、ICMP または TCP を対象とする分類です。match-any なので、ICMP か TCP のいずれかに一致すればこの class-map にヒットします。policy-map type inspect PM-IN-OUT は、その CM-IN-OUT に一致した通信を inspect (ステートフル検査して通す) し、それ以外のすべて (class-default) を drop します。最後の zone-pair security IN-OUT source INSIDE destination OUTSIDE が INSIDE から OUTSIDE への向きを定義するゾーンペアで、そこに service-policy type inspect PM-IN-OUT でポリシーを適用しています。

この設定を投入した後、R1 (INSIDE) から R3 (OUTSIDE) へ ping します。

snippet
ping 10.0.20.1 source Loopback10 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.20.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.10.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/8 ms
R1#

§6 で 0 percent だった ping が、Success rate is 100 percent (5/5) に戻りました。IN-OUT ゾーンペアの inspect が ICMP を検査して通し、その戻り (R3 からの echo reply) も自動で許可されたため、往復が成立しています。ここで注目すべきは、R3 から R1 への戻りを許可するゾーンペア (OUTSIDE→INSIDE) を作っていない にもかかわらず、戻りが通っている点です。これがステートフルインスペクションの効果で、§8・§9 で詳しく確認します。

TCP も同じゾーンペアで検査されます。R1 から R3 の TCP ポート 80 へ telnet して、TCP セッションが確立するかを確認します。

snippet
telnet 10.0.20.1 80 /source-interface Loopback10
Trying 10.0.20.1, 80 ... Open

Trying 10.0.20.1, 80 ... OpenOpen は、TCP の 3 ウェイハンドシェイクが成立し、ポート 80 へのコネクションが確立したことを示します。ICMP と同様に、IN-OUT ゾーンペアの inspect が TCP を検査して通し、R3 からの戻り (SYN-ACK 以降) を自動で許可した結果、コネクションが Open になっています。ステートフル検査が ICMP だけでなく TCP でも働き、戻りを自動許可していることが確認できます。

8. ステートフルの証跡 — 検査統計とセッションカウンタ

inspect が「通した通信を覚えている」ことを、R2 の検査統計で確認します。まず、IN-OUT ゾーンペアに適用したポリシーの構造を show policy-map type inspect zone-pair IN-OUT sessions で確認します。

snippet
show policy-map type inspect zone-pair IN-OUT sessions
  Zone-pair: IN-OUT 
  Service-policy inspect : PM-IN-OUT

    Class-map: CM-IN-OUT (match-any)  
      Match: protocol icmp
      Match: protocol tcp
      Inspect


    Class-map: class-default (match-any)  
      Match: any 
      Drop
        0 packets, 0 bytes
R2#

IN-OUT ゾーンペアに PM-IN-OUT が適用され、CM-IN-OUT が ICMP と TCP を Inspectclass-default が残りを Drop する構造が表示されています。この出力は、§7 で投入したポリシーが正しくゾーンペアに適用されていることを確認するものです。個々のセッション行は、キャプチャ時点でセッションが短命に消えていたため表示されていません。

セッションの生存を示す証跡は、カウンタ付きの show policy-map type inspect zone-pair IN-OUT で確認できます。

snippet
show policy-map type inspect zone-pair IN-OUT
  Zone-pair: IN-OUT 
  Service-policy inspect : PM-IN-OUT

    Class-map: CM-IN-OUT (match-any)  
      Match: protocol icmp
      Match: protocol tcp
      Inspect
        Packet inspection statistics [process switch:fast switch]
        tcp packets: [0:22]
        icmp packets: [0:20]

        Session creations since subsystem startup or last reset 4
        Current session counts (estab/half-open/terminating) [1:0:0]
        Maxever session counts (estab/half-open/terminating) [2:0:0]
        Last session created 00:00:09
        Last statistic reset never
        Last session creation rate 1
        Last half-open session total 0

    Class-map: class-default (match-any)  
      Match: any 
      Drop
        0 packets, 0 bytes
R2#

この出力がステートフル動作の中心的な証跡です。読み取れる点を整理します。

  • tcp packets: [0:22] / icmp packets: [0:20]inspect が実際に検査した TCP・ICMP のパケット統計です。§7 の ping と telnet で流れた通信が検査対象になっています。
  • Session creations since subsystem startup or last reset 4 — ZBF が起動以降に作成したセッションが累計 4 つあることを示します。inspect が通信ごとにセッションを作り、その通信の戻りを許可するための記憶を持ったことを表します。
  • Current session counts (estab/half-open/terminating) [1:0:0] — この瞬間に確立中 (estab) のセッションが 1 つあることを示します。inspect が「今この通信を追跡中で、戻りが来たら通す」という状態を保持しています。この確立中セッションの存在こそが、ステートフルの実体です。
  • class-default: Drop 0 packets, 0 bytes — CM-IN-OUT に一致しなかった通信を破棄するカウンタですが、0 のままです。INSIDE から OUTSIDE への通信は ICMP と TCP しか流していないため、class-default に落ちる通信が無かったことを示します。

ステートフルインスペクションの全体像を、時間を追って整理します。

ZBF のステートフルインスペクションとゾーン間 default deny を時間で追う 5 ステップ。① Gi2 を INSIDE・Gi3 を OUTSIDE に割り当てただけでは zone-pair が無く、inside 発の通信もゾーン境界で既定 deny となり落ちる。② IN→OUT zone-pair に policy-map type inspect を当てると inside 発が検査されて通る。③ inspect はその通信を (送信元・宛先・プロトコル・状態) としてセッションテーブルに記録する。④ outside からの戻りはそのセッションに一致するのでステートフルに自動許可され、OUT→IN の zone-pair は要らない。⑤ outside 発の新規通信はセッションに無く OUT→IN zone-pair も無いため既定 deny で破棄される。inside 発は戻れて outside 発は入れない非対称が ZBF の骨子

図の各ステップは、①がゾーンを割り当てただけで通らない §6、②③がゾーンペアと inspect で通る §7・§8、④が戻りの自動許可、⑤が非要求の遮断に対応します。④⑤の非対称を §9 で実機の drop として確認します。

9. 戻りは自動、非要求は遮断 — ステートフルの非対称性

ステートフルインスペクションの本質は、inside 発の戻りは通るのに、outside 発の新規通信は入れない という非対称にあります。これを実機で確認します。

まず、対照として R1 (INSIDE) から R3 (OUTSIDE) への ping がまだ通ることを確認します。

snippet
ping 10.0.20.1 source Loopback10 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.20.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.10.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/2 ms
R1#

Success rate is 100 percent (5/5) で、inside 発の通信は §7 と同じく通ります。IN-OUT ゾーンペアの inspect が inside 発を検査して通し、その戻りを自動許可しているためです。

次に、逆向きに R3 (OUTSIDE) から R1 (INSIDE) の 10.0.10.1 へ、outside 発の新規 ping を送ります。

snippet
ping 10.0.10.1 source Loopback20 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.0.10.1, timeout is 2 seconds:
Packet sent with a source address of 10.0.20.1 
.....
Success rate is 0 percent (0/5)
R3#

同じ R3→R1 の ping が、§5 のベースラインでは 100 percent だったのに、Success rate is 0 percent (0/5) に変わりました。inside 発は通り (100 percent)、outside 発は落ちる (0 percent) という非対称がここに表れています。

ここで、outside 発が落ちる 原因を正確に理解する ことが重要です。この drop は、IN-OUT ゾーンペアの class-default が破棄したものでは ありません。§8 の出力で、IN-OUT の class-default は Drop 0 packets, 0 bytes のままでした。outside 発の通信は IN-OUT ゾーンペア (INSIDE→OUTSIDE の向き) には流れないため、そもそも IN-OUT のポリシーで判定されていません。

outside 発が落ちる本当の原因は、OUTSIDE→INSIDE のゾーンペアが存在しない ことです。§4 の第一の規則のとおり、ゾーン間の通信はゾーンペアが無ければ既定 deny です。R3 (OUTSIDE) から R1 (INSIDE) への新規通信は OUTSIDE→INSIDE の向きであり、この向きのゾーンペアを作っていないため、class-default 以前に「ゾーンペア不在の暗黙 deny」で破棄されています。つまり、outside 発が落ちる主体は class-default の drop アクションではなく、ゾーンペアそのものの不在 です。

一方で、inside 発に対する R3 からの 戻り (echo reply) は、なぜ通るのでしょうか。戻りは新規通信ではなく、§8 でセッションテーブルに記録された「inside 発 ICMP の戻り」に一致するため、inspect がステートフルに自動許可します。同じ「outside から inside への向き」でも、セッションに一致する戻りは自動許可され、セッションに無い新規通信は既定 deny で落ちる、という区別が働いています。

この非対称こそが、4-1 ACL との決定的な違いです。ステートレスな ACL では、inside 発の戻りを通すために established や reflexive ACL で戻り穴を手で開ける必要がありました (§2)。ZBF の inspect は、通した通信のセッションを記憶し、その戻りだけを自動で開けます。戻り穴を書かずに戻りが通り、書いていない新規通信は自動的に塞がる、というのがステートフルファイアウォールが守る境界の形です。

10. self ゾーン — ルータ自身への通信を制御する

ここまでは INSIDE と OUTSIDE という 2 つのゾーンの間の通信を扱いました。ここでは、ルータ自身を表す self ゾーン への通信を制御します。self ゾーンは、ルータへの SSH・SNMP のような管理アクセスや、ルータ自身が終端する通信を表す特別なゾーンです。

まず、§4 の第四の規則 (self ゾーンは既定 permit) を実機で確認します。§7 の時点で INSIDE→SELF のゾーンペアはまだ作っていません。この状態で、R1 (INSIDE) から R2 自身の Gi2 アドレス 10.12.0.2 へ ping します。

snippet
ping 10.12.0.2 source Loopback10 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.12.0.2, timeout is 2 seconds:
Packet sent with a source address of 10.0.10.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
R1#

Success rate is 100 percent (5/5) で、INSIDE→SELF のゾーンペアが無い状態でも、R1 からルータ自身への ping は通っています。Gi2 は INSIDE ゾーンに割り当て済みですが、そのインターフェースからルータ自身 (self) への通信は、self へのゾーンペアが無い限り既定 permit で通る、という §4 の規則が確認できます。「インターフェースをゾーンに入れると self 宛も deny になる」という誤解が、この実機結果で否定されます。

次に、self ゾーンへのゾーンペアを作ります。INSIDE から self への ICMP だけを許可する設定を R2 に投入します。

cisco
class-map type inspect match-any CM-IN-SELF
 match protocol icmp
policy-map type inspect PM-IN-SELF
 class type inspect CM-IN-SELF
  inspect
 class class-default
  drop
zone-pair security IN-SELF source INSIDE destination self
 service-policy type inspect PM-IN-SELF

INSIDE→self のゾーンペア IN-SELF を作り、ICMP を inspect し、それ以外を drop するポリシーを適用しています。この設定を投入した後、再び R1 から R2 自身の 10.12.0.2 へ ping します。

snippet
ping 10.12.0.2 source Loopback10 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.12.0.2, timeout is 2 seconds:
Packet sent with a source address of 10.0.10.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
R1#

ICMP を inspect しているため、ping は Success rate is 100 percent (5/5) で通ります。ここまでは前と同じ結果ですが、ゾーンペアを作ったことで挙動が変わった部分は、次の検査統計に表れます。

作成したゾーンペアが 2 つあることを show zone-pair security で確認します。

snippet
show zone-pair security
Zone-pair name IN-OUT 1
    Source-Zone INSIDE  Destination-Zone OUTSIDE 
    service-policy PM-IN-OUT
Zone-pair name IN-SELF 2
    Source-Zone INSIDE  Destination-Zone self 
    service-policy PM-IN-SELF

R2#

IN-OUT (INSIDE→OUTSIDE) と IN-SELF (INSIDE→self) の 2 つのゾーンペアが表示されています。Destination-Zone self が、宛先がルータ自身であることを示します。

IN-SELF ゾーンペアの検査統計を show policy-map type inspect zone-pair IN-SELF で確認します。ここに self ゾーン制御の核心が現れます。

snippet
show policy-map type inspect zone-pair IN-SELF
  Zone-pair: IN-SELF 
  Service-policy inspect : PM-IN-SELF

    Class-map: CM-IN-SELF (match-any)  
      Match: protocol icmp
      Inspect
        Packet inspection statistics [process switch:fast switch]
        icmp packets: [0:10]

        Session creations since subsystem startup or last reset 1
        Current session counts (estab/half-open/terminating) [0:0:0]
        Maxever session counts (estab/half-open/terminating) [1:0:0]
        Last session created 00:00:19
        Last statistic reset never
        Last session creation rate 1
        Last half-open session total 0

    Class-map: class-default (match-any)  
      Match: any 
      Drop
        5 packets, 460 bytes
R2#

読み取るべき点は 2 つです。第一に、CM-IN-SELFicmp packets: [0:10]Session creations ... 1 で、inspect した ICMP (先ほどの ping) がセッションを作って通ったことが分かります。第二に、注目すべきは class-default: Drop 5 packets, 460 bytes です。これは、ICMP 以外の self 宛通信が 5 パケット破棄された実績を示します。

この class-default の drop が、self ゾーン制御の 諸刃の性質 を表しています。INSIDE→self のゾーンペアを作った瞬間、INSIDE ゾーンからルータ自身への通信は、そのゾーンペアのポリシー支配下に入ります。inspect した ICMP は通りますが、class-map に書かれていないほかのプロトコル (SSH・SNMP など) は class-default に落ちて drop されます。§4 では self 宛は既定 permit でしたが、INSIDE→self のゾーンペアを作った瞬間に、その向きは default deny (class-default drop) に切り替わる のです。self ゾーンへのゾーンペアは「ルータ自身を守る」ために使いますが、同時に「許可を明示しない管理プロトコルを意図せず塞ぐ」ことになります。管理アクセスを維持したい場合は、class-map に SSH などのプロトコルを明示的に追加する必要があります。

なお、本ラボでは R2 への管理 SSH は Mgmt-vrf の管理インターフェース (ゾーン未所属) を経由しているため、この IN-SELF ゾーンペアの影響を受けず、ロックアウトは起きていません。ゾーンに割り当てたデータ用インターフェース経由の管理アクセスを ZBF で制御する場合は、この諸刃の性質に特に注意します。

11. 落とし穴・補足

本節で扱った ZBF の実機挙動と、設計上の注意点を整理します。

  1. inspectpass の違いpolicy-map type inspect で指定できる通過アクションには inspectpass の 2 つがあります。inspect はステートフルに検査し、通した通信の戻りを自動で許可します (§8・§9)。これに対し pass はステートレスに通すだけで、戻りは許可しません。pass を使った向きの戻りを通すには、逆向きのゾーンペアを別途作る必要があります。戻りの自動許可が欲しい通常の通信には inspect を使い、戻りを持たない一方向の通信 (一部の UDP など) や、明示的に両方向を制御したい場合に pass を使い分けます。

  2. drop は既定なので明示不要だが log で可視化できる — ゾーンペアの class-default は既定で drop されるため、拒否そのものを書く必要はありません (§7)。ただし、破棄された通信を記録したい場合は drop log とすることで、drop を syslog に出力して可視化できます。既定 deny は静かに落とすため、意図せぬ通信断のトラブルシューティングでは log 付き drop が役立ちます。

  3. ゾーン未所属インターフェースとの通信は成立しない — §4 の第三の規則のとおり、ゾーンに割り当てたインターフェースと、ゾーンに割り当てていないインターフェースの間の通信は、ゾーンペアを作れないため通りません。管理用インターフェースをゾーンに入れない設計にすると、そのインターフェースからゾーン所属インターフェースへの通信が塞がれます。管理系を独立させたい場合は、Mgmt-vrf の管理インターフェース (§10 の R2 がこの構成) を使い、データプレーンのゾーンと分離するのが定石です。

  4. outside 発の drop 主体はゾーンペア不在 — §9 で確認したとおり、outside 発の新規通信が落ちる主体は、IN-OUT ゾーンペアの class-default ではなく、OUTSIDE→INSIDE のゾーンペアそのものの不在 です。class-default の drop カウンタが 0 のままでも、ゾーン間の通信は既定 deny で落ちます。ZBF のトラブルシューティングでは、「どのゾーンペアが存在し、どのゾーンペアが存在しないか」を先に確認することが、通信可否を判断する起点になります。

  5. self ゾーンペアは管理アクセスを巻き込む — §10 で確認したとおり、INSIDE→self のゾーンペアを作ると、その向きの self 宛通信は class-default で default deny になります。ICMP だけを許可すると SSH・SNMP が落ちるため (class-default drop 5 packets, 460 bytes)、管理アクセスを維持するには class-map に管理プロトコルを明示追加します。self ゾーンの制御は「ルータを守る」利点と「管理を塞ぐ」リスクが表裏の関係にあります。

  6. CBAC は ZBF の前身 — ZBF の前に使われていたステートフル検査の方式が CBAC (Context-Based Access Control) で、ip inspect コマンドでインターフェース単位に検査を設定するものでした。CBAC はインターフェースごとに検査ルールを当てるため、ゾーンという抽象がなく、インターフェースが増えると管理が煩雑でした。ZBF は CBAC の後継であり、ゾーンという単位で検査ポリシーを整理できる現行の推奨方式です。新規に IOS ルータでステートフルファイアウォールを構成する場合は ZBF を使います。

12. ACL / CBAC / ZBF の位置づけ

本章で扱ったステートレスな ACL、旧方式の CBAC、本節の ZBF を対比して、それぞれの位置づけを整理します。

項目ACLCBAC (ip inspect)ZBF (Zone-Based Firewall)
検査の性質ステートレスステートフルステートフル
戻りトラフィックestablished / reflexive ACL で手動許可セッションを記憶し自動許可inspect がセッションを記憶し自動許可
適用の単位インターフェース + 方向 (in / out)インターフェース単位ゾーン単位 (zone-pair の向き)
分類の基準アドレス・ポート番号プロトコル (アプリケーション認識)プロトコル (class-map type inspect)
既定動作末尾の暗黙 deny検査ルールに従うゾーン間は default deny・self は既定 permit
設定構造許可・拒否行の列挙ip inspect ルール + ACLMPF (class-map → policy-map → zone-pair)
位置づけL3/L4 の基本フィルタ旧方式 (ZBF に置き換え)現行の推奨方式

ACL は L3/L4 のアドレスとポート番号で許可・拒否を書く基本のフィルタで、パケット単位のステートレスな判定を行います。戻りを通すには手で穴を開ける必要があり、この手間がステートフル方式を生みました。CBAC はインターフェース単位でステートフル検査を導入した旧方式で、ZBF に置き換えられています。ZBF は、複数のインターフェースをゾーンにまとめ、ゾーン間の向き (zone-pair) ごとに MPF でポリシーを組み、inspect でステートフルに戻りを自動許可する、現行の推奨方式です。ステートレスな ACL とステートフルな ZBF は排他ではなく、ACL でアドレス・ポート単位の粗いフィルタを、ZBF でゾーン単位のステートフル制御を、というように役割を分けて併用します。

13. 次節

本節では、インターフェースをゾーンにまとめてステートフルに検査する ZBF を扱いました。ゾーンにインターフェースを割り当てるとゾーン間は default deny になり (§6)、zone-pair と policy-map type inspect でその向きを開き (§7)、inspect が通した通信をセッションとして記憶することで戻りが自動許可される (§8) こと、inside 発は戻れて outside 発の新規は入れない非対称 (§9)、そして self ゾーンは既定 permit だが INSIDE→self のゾーンペアを作った瞬間に管理アクセスを巻き込む諸刃の性質 (§10) を、csr1000v の show 出力で確認しました。とりわけ、outside 発が落ちる主体は class-default の drop ではなく ゾーンペアそのものの不在 である点と、inspect4-1 ACLestablished / reflexive ACL を書かずに戻り穴を自動で開ける点が、ステートレス ACL とステートフル ZBF を分ける勘所です。

参考として、本節の検証で R2 に構築した ZBF 構成の全体を示します。

snippet
show running-config | section zone|class-map|policy-map|zone-pair
class-map type inspect match-any CM-IN-SELF
 match protocol icmp
class-map type inspect match-any CM-IN-OUT
 match protocol icmp
 match protocol tcp
policy-map type inspect PM-IN-SELF
 class type inspect CM-IN-SELF
  inspect
 class class-default
  drop
policy-map type inspect PM-IN-OUT
 class type inspect CM-IN-OUT
  inspect
 class class-default
  drop
zone security INSIDE
zone security OUTSIDE
zone-pair security IN-OUT source INSIDE destination OUTSIDE
 service-policy type inspect PM-IN-OUT
zone-pair security IN-SELF source INSIDE destination self
 service-policy type inspect PM-IN-SELF
 description to R1 (INSIDE zone member)
 zone-member security INSIDE
 description to R3 (OUTSIDE zone member)
 zone-member security OUTSIDE
R2#

class-map 2 つ・policy-map 2 つ・ゾーン 2 つ・ゾーンペア 2 つと、末尾のインターフェースの zone-member security 行で、本節の ZBF が構成されています (この出力は | section で該当行を抽出したもので、末尾の description と zone-member 行がそれぞれ Gi2・Gi3 の所属を表します)。

次節 4-6 NAT/PAT では、プライベートアドレスとグローバルアドレスを変換する NAT (Network Address Translation)PAT (Port Address Translation) を扱います。NAT でも、本節で扱った inside と outside という向きの概念 が中心になります。ZBF がゾーン間の通信可否を向きで制御したのと同じように、NAT は inside から outside へ出る通信でアドレスをどう変換するかを向きで定義します。ステートフルに通信を追跡する点も NAT と ZBF に共通しており、両者がどう連携するかを次節で見ていきます。