11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cisco Systems JapanAdvent Calendar 2024

Day 1

Batfish + bfqry で NW コンフィグから情報抽出や妥当性確認を行う

Last updated at Posted at 2024-12-02

どんな人向けの記事?

この記事は以下のような方を想定しています。

  • ネットワーク関連の業務を担当されている方
  • Batfish を使って効率良く、業務を進めたい方
  • 「プログラミングせずに済ませたい」という方!

はじめに

Batfish の利用には Python コードを書くのが一般的ですが、bfqry というツールを使うことでプログラミングせずに Batfish を利用できます。 この記事では Batfish + bfqry を組み合わせる方法を説明します。

Batfish とは

Batfish は Java で書かれた OSS です。 Batfish を使うと「ネットワーク機器のコンフィグから、設定情報を抽出したり、妥当性を確認する」ということが可能です。 公式サイトには Batfish 自身の説明として以下の記載があります。

Batfish is a network validation tool that provides correctness guarantees for security, reliability, and compliance by analyzing the configuration of network devices. It builds complete models of network behavior from device configurations and finds violations of network policies (built-in, user-defined, and best-practices).

Batfish は様々なメーカーの製品に対応しており、サポート対象の一覧は Supported Network Device and Operating System List に掲載されています。

  • A10 Networks
  • Arista
  • AWS (VPCs, Network ACLs, VPN GW, NAT GW, Internet GW, Security Groups, etc…)
  • Cisco (All Cisco NX-OS, IOS, IOS-XE, IOS-XR and ASA devices)
  • Check Point
  • Cumulus
  • F5 BIG-IP
  • Fortinet
  • Free-Range Routing (FRR)
  • iptables (on hosts)
  • Juniper (All JunOS platforms: MX, EX, QFX, SRX, T-series, PTX)
  • Palo Alto Networks
  • SONiC

Batfish のメリット・デメリット

Batfish にはメリットもあればデメリットもあります。 各々の点について簡単に触れます。

メリット

Batfish には以下のようなメリットがあります。

  1. 「コンフィグだけを対象に処理を行う」という仕組み上、実機へのアクセスが不要 (=安全)
  2. 過去であっても、コンフィグさえあれば処理が可能 (=任意の時点を対象に出来る)
  3. L3 以上の処理、特にルーティングの計算は得意
  4. 実機では再現し辛い構成を論理的に作成する機能が豊富 (=ホストを JSON で再現する、BGP のアトリビュートをファイルから注入する、等)
  5. マルチベンダー対応である

構成管理ツールを利用する場合、通常は「実機へアクセス出来ること」が利用の大前提になります。 ですが Batfish は「コンフィグさえ、あれば動作する」為、Batfish を本番環境へ接続する必要はありません。 本番環境から隔離された、普段のオフィス内から安全に Batfish を実行することが出来ます。

Batfish は任意のタイミングで取得したコンフィグを対象に出来ます。 1 年前のコンフィグであっても、まだ本番環境には存在しない「机上で作成した未来のコンフィグ」であっても対象にすることが出来ます。 その為、工事前後のコンフィグを用意して「追加したコンフィグが妥当であるか?」の確認に利用することも出来ます。

また、Layer3 以上の処理、特にルーティングの計算は非常に得意です。 各種のルーティングプロトコルに対応しており、「どの機器が・どのようなルーティングテーブルになるはずか?」を計算させることが出来ます。

一般的に BGP のテストを実施する場合、「任意のアトリビュートが付与された受信経路」の再現が手間になる場合があります。 Batfish の External BGP announcements という機能を利用することで JSON ファイルで BGP 経路を生成することが出来、任意の経路を簡単に再現出来ます。

デメリット

勿論、万能のツールでは無く、下記のような制限・デメリットもあります。

  1. OS バージョンを意識した処理は出来ない (=「OS バージョンによって振る舞いが変わる」というケースには対応が難しい)
  2. 対応していないコンフィグ構文も多い
  3. パフォーマンステストは実施出来ない
  4. L2 以下の処理は苦手
  5. (コンフィグ以外に)「show コマンドの結果などを追加して情報の精度を上げる」といった仕組みは存在しない。 あくまでコンフィグしか処理対象に出来ない

Batfish で計算を行わせる際、対象コンフィグの OS バージョンは意識しません。 例えば「OS バージョンによって振る舞いの変わる処理」があったとしても、「Batfish のコードが再現している振る舞い」しか行いません。 その為、バージョンによって振る舞いが変わるような繊細な挙動の確認には、Batfish は向きません。

「Batfish はコンフィグだけあれば動作する」とは言っても、ありとあらゆるメーカーの・全てのコンフィグ構文に対応している… とはお世辞にも言えない状況です。 Batfish を利用する場合は「Batfish が対応しているコンフィグ構文」「対応していないコンフィグ構文」を理解した上で「Batfish が向いている、使える」というシチュエーションに限定して利用する方が安全だと思います。

また、あくまで「コンフィグを計算している」という仕組みであり、ハードウェアは存在していない為、パフォーマンステストも実施出来ません。

Batfish の利用方法

Batfish はクライアント・サーバモデルで動作します。 そのため、Batfish 用のクライアントとサーバを用意する必要があります。

Batfish サーバ

上述の通り、Batfish は Java で書かれており、勿論 JAVA アプリケーションとして動作させることが可能です。 ですが How do I get started? に書かれているようにサーバ側は Docker (Podman) コンテナとして動作させるのが簡単です。 Batfish のコンテナには以下があります。

名称 内容
batfish/batfish Batfish 本体のみ
batfish/allinone Batfish 本体 + Jupyter Notebook

後述しますが、Batfish を利用するには「Python のソースコードを書く」必要があります。 はじめて Batfish を利用する際は Python コードと格闘することになるかもしれませんので Jupyter Notebook を含む batfish/allinone イメージを使ってトライ&エラーするのが簡単かも知れません。 本番運用するのであれば Jupyter Notebook は不要になりますので batfish/batfish を利用します。

Batfish の GitHub に記載されていますが、batfish/allinone コンテナを開始するには以下のように実行します。 Batfish 自体は v1 ポート (デフォルト:TCP/9997) と v2 ポート (デフォルト:TCP/9996) という概念がありますが、v1 ポートは古いバージョンでしか利用されません。

docker pull batfish/allinone
docker run --name batfish -v batfish-data:/data -p 8888:8888 -p 9997:9997 -p 9996:9996 batfish/allinone

Batfish クライアント

Batfish を利用するには pybatfish を使って Python コードを書くのが一般的です。 Batfish 本体は Java で書かれていますが、クライアントは Python 版しかリリースされていないようです。

Batfish への問い合わせには Question を使う

Batfish クライアントから Batfish サーバへ計算処理を依頼するには Batfish Questions を用います。 Batfish Questions には以下のカテゴリーがあり、各々のカテゴリー内には幾つかの Question が存在します。

例えば「インターフェイスの設定情報を取得したい」場合、Configuration Properties カテゴリーに含まれる Interface Properties Question を利用します。

Batfish を利用する際のディレクトリ・ファイル構造

Batfish を利用する場合、対象のディレクトリ・ファイルは以下の構造にします。Batfish の処理対象とするネットワーク機器のコンフィグは configs というディレクトリ配下に保存します。 このディレクトリ名は Batfish にハードコーディングされている為、変更することは出来ません。

simple
└── configs
    ├── dev1.txt
    ├── dev2.txt
    ├── dev3.txt
    ├── dev4.txt
    └── dev5.txt

Batfish を簡単に利用できる「bfqry」

Batfish クライアントは「Python コードで pybatfish を import し、Batfish Question(s) を実行する」のが一般的な方法です。 つまり「Batfish を利用するには Python のプログラミングスキルが必要」になってしまいます。

ですが、bfqry というツールを使うことでプログラミングを行うことなく、Batfish を利用することが出来ます。

bfqry のインストール

bfqry のパッケージは pypi で公開されており、CLI ツールとして利用することが出来ます。 他の Python パッケージと同様に pip でインストールすることが出来ます。

python -m pip install bfqry

bfqry の初期設定

bfqry の設定ファイルは以下、いずれかに配置します。 上にある程、優先度が高い方から採用されます。

優先順位 ファイルパス
1 bfqry と同じディレクトリにある settings.ini
2 ~/.config/bfqry/settings.ini
3 ~/.settings.ini

設定ファイルの記載例は以下の通りです。

batfish=10.0.0.1
port1=9997
port2=9996
timeout=5.0
https=false
insecure=true

各々の項目は以下の意味を持ちます。

項目 デフォルト値 説明
batfish 127.0.0.1 Batfish サーバの名前 / IP アドレス
port1 9996 v1 ポート。 古いバージョンの Batfish で利用されるが、現在は利用されていない
port2 9997 v2 ポート。 Batfish クライアント / サーバ間の通信で利用される
timeout 5.0 Batfish サーバ接続確立時のタイムアウト時間 (秒)
https False Batfish サーバへの接続時、HTTPS の利用有無
insecure False https フラグが True の場合にサーバ証明書の妥当性を確認するか、否か

bfqry の使い方

bfqry を利用するには実行したい Question をツールのサブコマンドとして指定します。 以下は bfqry のヘルプ表示です。

# bfqry
usage: bfqry {command} ...

bfqry (Batfish Query Utility) 2024.12.01

commands:
  {command}
    aclsearch
              Finds flows for which a filter takes a particular behavior.
    acltest   Returns how a flow is processed by a filter (ACLs, firewall rules).
    aclunreach
              Returns unreachable lines in filters (ACLs and firewall rules).
    bgpedge   Returns BGP adjacencies.
    bgprib    Returns routes in the BGP RIB.
    bireach   Searches for successfully delivered flows that can successfully receive a response.
    bitrace   Traces the path(s) for the specified flow, along with path(s) for reverse flows.
    eigrpedge
              EIGRP Edges
    hsrp      Returns configuration settings of HSRP groups.
    interface
              Returns configuration settings of interfaces.
    ip        Returns where IP addresses are attached in the network.
    l3edge    Lists all Layer 3 edges in the network.
    loop      Detects forwarding loops.
    lpm       Returns routing tables.
    ospfedge  Lists all OSPF adjacencies in the network.
    parse     Displays file parse status.
    prefix    Traces prefix propagation through the network.
    reach     Finds flows that match the specified path and header space conditions.
    route     Returns routing tables.
    trace     Traces the path(s) for the specified flow.
    vlan      Returns configuration settings of switched VLANs.
    vrrp      Returns configuration settings of VRRP groups.

前述の「Batfish を利用する際のディレクトリ・ファイル構造」を踏まえ、ip サブコマンドを実行するには以下のように実行します。 --base は必須のオプションであり、configs を配置した 親ディレクトリ を指定します。

bfqry ip --base simple/

bfqry の実行例

以下は bfqry の実行例です。

parse サブコマンド

parse サブコマンドは内部的に Snapshot Input File Parse Status Question を実行します。 Batfish は処理対象ファイルの特徴から「どのメーカーの・どの OS 種別なのか」を見分けているようです。 parse サブコマンドの File_Format 出力から「対象のコンフィグファイルがどの OS 形式として Batfish に処理されているのか?」などが分かります。

# bfqry parse --base simple/
Nodes        File_Name Status File_Format Version
 dev1 configs/dev1.txt PASSED   CISCO_IOS 16.12.1
 dev2 configs/dev2.txt PASSED   CISCO_IOS 16.12.2
 dev3 configs/dev3.txt PASSED   CISCO_IOS 16.12.3
 dev4 configs/dev4.txt PASSED   CISCO_IOS 16.12.4
 dev5 configs/dev5.txt PASSED   CISCO_IOS       ?

interface サブコマンド

interface サブコマンドは内部的に Interface Properties Question を実行します。 オプションを指定しない場合、プライマリアドレスや Description、VRF などを表示します。

# bfqry interface --base interface/
                       Interface Primary_Address Description     VRF
                dev11[Loopback0]    10.0.0.11/16        <11> default
                dev12[Serial0/0]    10.0.0.12/16        <12> default
               dev13[BRI0/1/0:0]    10.0.0.13/16        <13> default
               dev14[BRI0/1/1:0]    10.0.0.14/16        <14> default
       dev15[GigabitEthernet0/1]            None        <14> default
       dev15[GigabitEthernet0/2]            None        <14> default
           dev15[Port-channel15]            None        <14> default
                   dev15[Vlan15]    10.0.0.15/16        <14> default
              dev16[Ethernet0/0]    10.0.0.16/16        <16> default
          dev17[FastEthernet0/0]    10.0.0.17/16        <17> default
       dev18[GigabitEthernet0/0]    10.0.0.18/16        <18> default
    dev19[TwoGigabitEthernet0/0]    10.0.0.19/16        <19> default
    dev21[TenGigabitEthernet0/0]    10.0.0.21/16        <21> default
        dev22[TwentyFiveGigE0/0]    10.0.0.22/16        <22> default
  dev23[FortyGigabitEthernet0/0]    10.0.0.23/16        <23> default
dev25[HundredGigabitEthernet0/0]    10.0.0.25/16        <25> default

interface サブコマンドには --column オプションがあります。 このオプションを指定することで表示する内容を変更出来ます。

# bfqry interface --help
usage: bfqry interface [-h] -b BASE [--batfish BATFISH] [--port1 PORT1] [--port2 PORT2] [--timeout TIMEOUT] [--https] [--insecure]
                       [--nocache] [--log {debug,info,warning,error,ciritical}] [--excel EXCEL] [--node [NODE ...]]
                       [--column {acl,all,basic,hsrp,ip,portchannel,speed,state,stp,vlan}] [--wounit]

--column speed を指定した場合、実行例は以下の通りです。 Speed や Bandwidth の値を表示します。 但し、幾つかのインターフェイス種別に関しては「想定と異なる値が表示されている」ことが分かります。

# bfqry interface --base interface/ --column speed
                       Interface Primary_Address  Speed Bandwidth
                dev11[Loopback0]    10.0.0.11/16 (None)        8G
                dev12[Serial0/0]    10.0.0.12/16 (None)        1T
               dev13[BRI0/1/0:0]    10.0.0.13/16 (None)        1T
               dev14[BRI0/1/1:0]    10.0.0.14/16 (None)        1T
       dev15[GigabitEthernet0/1]            None     1G        1G
       dev15[GigabitEthernet0/2]            None     1G        1G
           dev15[Port-channel15]            None (None)        2G
                   dev15[Vlan15]    10.0.0.15/16 (None)        1G
              dev16[Ethernet0/0]    10.0.0.16/16    10M       10M
          dev17[FastEthernet0/0]    10.0.0.17/16   100M      100M
       dev18[GigabitEthernet0/0]    10.0.0.18/16     1G        1G
    dev19[TwoGigabitEthernet0/0]    10.0.0.19/16     2G        2G
    dev21[TenGigabitEthernet0/0]    10.0.0.21/16    10G       10G
        dev22[TwentyFiveGigE0/0]    10.0.0.22/16    25G       25G
  dev23[FortyGigabitEthernet0/0]    10.0.0.23/16 (None)        1T
dev25[HundredGigabitEthernet0/0]    10.0.0.25/16 (None)        1T

ip サブコマンド

ip サブコマンドは内部的に IP Owners Question を実行します。 実行例は以下の通りです。

# bfqry ip --base ip/
Node     VRF        IP Mask Interface Active
dev1 default  10.0.0.1   24 Loopback0   True
dev1 default 10.0.0.99   24 Loopback0   True
dev2 default  10.0.0.2   24 Loopback0   True
dev2 default 10.0.0.92   24 Loopback0   True
dev3 default  10.0.0.3   24 Loopback0   True
dev3 default 10.0.0.93   24 Loopback0   True
dev4 default  10.0.0.4   24 Loopback0   True
dev4 default 10.0.0.99   24 Loopback0   True
dev5       A  10.0.0.5   24 Loopback0   True
dev5       A 10.0.0.99   24 Loopback0   True

--duplicate オプションを指定すると「重複している IP アドレス」を表示することが出来ます。

# bfqry ip --base ip/ --duplicate
Node     VRF        IP Mask Interface Active
dev1 default 10.0.0.99   24 Loopback0   True
dev4 default 10.0.0.99   24 Loopback0   True
dev5       A 10.0.0.99   24 Loopback0   True

route サブコマンド

route サブコマンドは内部的に Routes Question を実行します。 実行例は以下の通りです。

# bfqry route --base ospf/ --node dev1
Node      Network    Next_Hop_IP  Protocol Metric Admin_Distance
dev1 10.12.0.0/24 AUTO/NONE(-1l) connected      0              0
dev1 10.12.0.1/32 AUTO/NONE(-1l)     local      0              0
dev1 10.23.0.0/24      10.12.0.2      ospf      2            110
dev1 10.34.0.0/24      10.12.0.2      ospf      3            110
dev1 10.45.0.0/24      10.12.0.2      ospf      4            110
dev1 10.99.0.0/24 AUTO/NONE(-1l) connected      0              0
dev1 10.99.0.1/32 AUTO/NONE(-1l)     local      0              0
dev1 10.99.0.2/32      10.12.0.2      ospf      2            110
dev1 10.99.0.3/32      10.12.0.2      ospf      3            110
dev1 10.99.0.4/32      10.12.0.2      ospf      4            110
dev1 10.99.0.5/32      10.12.0.2      ospf      5            110

trace サブコマンド

trace サブコマンドは内部的に Traceroute Question を実行します。 以下のように実行することで擬似的に Traceroute を実行出来ます。 「Traceroute」といってもアプリケーションレイヤーも任意に指定することが出来ます。 下記の例では宛先に DNS (UDP/53) を指定しています。 下記は成功例ですが、失敗する場合は「どこで・どうった理由で失敗しているのか」が表示されます。

# bfqry trace --base simple/ --node dev1 --saddr 10.12.0.1 --daddr 10.45.0.5 --protocol udp --dport 53
============================================================ 1/1 Flow
start=dev1 [10.12.0.1:49152->10.45.0.5:53 UDP]
-------------------------------------------------- 1/1 Trace
ACCEPTED
1. node: dev1
  ORIGINATED(default)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1 with resolved next-hop IP: 10.12.0.2, Routes: [static (Network: 0.0.0.0/0, Next Hop: ip 10.12.0.2)])
  TRANSMITTED(GigabitEthernet0/1)
2. node: dev2
  RECEIVED(GigabitEthernet0/0)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1 with resolved next-hop IP: 10.23.0.3, Routes: [static (Network: 0.0.0.0/0, Next Hop: ip 10.23.0.3)])
  TRANSMITTED(GigabitEthernet0/1)
3. node: dev3
  RECEIVED(GigabitEthernet0/0)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1 with resolved next-hop IP: 10.34.0.4, Routes: [static (Network: 10.45.0.0/24, Next Hop: ip 10.34.0.4)])
  TRANSMITTED(GigabitEthernet0/1)
4. node: dev4
  RECEIVED(GigabitEthernet0/0)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1, Routes: [connected (Network: 10.45.0.0/24, Next Hop: interface GigabitEthernet0/1)])
  TRANSMITTED(GigabitEthernet0/1)
5. node: dev5
  RECEIVED(GigabitEthernet0/0)
  ACCEPTED(GigabitEthernet0/0)

bitrace サブコマンド

bitrace サブコマンドは内部的に Bi-directional Traceroute Question を実行します。 trace サブコマンドと異なり、往復をテストすることが出来ます。

# bfqry bitrace --base simple/ --node dev1 --saddr 10.12.0.1 --daddr 10.45.0.5 --protocol udp --dport 53
============================================================ 1/1 Flow(s)
start=dev1 [10.12.0.1:49152->10.45.0.5:53 UDP]
start=dev5 [10.45.0.5:53->10.12.0.1:49152 UDP]
-------------------------------------------------- 1 Forward Trace(s)
---------------------------------------- 1/1 Forward Trace
ACCEPTED
1. node: dev1
  ORIGINATED(default)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1 with resolved next-hop IP: 10.12.0.2, Routes: [static (Network: 0.0.0.0/0, Next Hop: ip 10.12.0.2)])
  TRANSMITTED(GigabitEthernet0/1)
2. node: dev2
  RECEIVED(GigabitEthernet0/0)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1 with resolved next-hop IP: 10.23.0.3, Routes: [static (Network: 0.0.0.0/0, Next Hop: ip 10.23.0.3)])
  TRANSMITTED(GigabitEthernet0/1)
3. node: dev3
  RECEIVED(GigabitEthernet0/0)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1 with resolved next-hop IP: 10.34.0.4, Routes: [static (Network: 10.45.0.0/24, Next Hop: ip 10.34.0.4)])
  TRANSMITTED(GigabitEthernet0/1)
4. node: dev4
  RECEIVED(GigabitEthernet0/0)
  FORWARDED(Forwarded out interface: GigabitEthernet0/1, Routes: [connected (Network: 10.45.0.0/24, Next Hop: interface GigabitEthernet0/1)])
  TRANSMITTED(GigabitEthernet0/1)
5. node: dev5
  RECEIVED(GigabitEthernet0/0)
  ACCEPTED(GigabitEthernet0/0)
-------------------------------------------------- 1 Reverse Trace(s)
---------------------------------------- 1/1 Reverse Trace
ACCEPTED
1. node: dev5
  ORIGINATED(default)
  FORWARDED(Forwarded out interface: GigabitEthernet0/0 with resolved next-hop IP: 10.45.0.4, Routes: [static (Network: 0.0.0.0/0, Next Hop: ip 10.45.0.4)])
  TRANSMITTED(GigabitEthernet0/0)
2. node: dev4
  RECEIVED(GigabitEthernet0/1)
  FORWARDED(Forwarded out interface: GigabitEthernet0/0 with resolved next-hop IP: 10.34.0.3, Routes: [static (Network: 0.0.0.0/0, Next Hop: ip 10.34.0.3)])
  TRANSMITTED(GigabitEthernet0/0)
3. node: dev3
  RECEIVED(GigabitEthernet0/1)
  FORWARDED(Forwarded out interface: GigabitEthernet0/0 with resolved next-hop IP: 10.23.0.2, Routes: [static (Network: 10.12.0.0/24, Next Hop: ip 10.23.0.2)])
  TRANSMITTED(GigabitEthernet0/0)
4. node: dev2
  RECEIVED(GigabitEthernet0/1)
  FORWARDED(Forwarded out interface: GigabitEthernet0/0, Routes: [connected (Network: 10.12.0.0/24, Next Hop: interface GigabitEthernet0/0)])
  TRANSMITTED(GigabitEthernet0/0)
5. node: dev1
  RECEIVED(GigabitEthernet0/1)
  ACCEPTED(GigabitEthernet0/1)

まとめ

Batfish を利用することで手軽に・安全に、ネットワーク機器のコンフィグから情報抽出や妥当性確認を行うことが出来ます。 「Batfish は便利そうだけど、Python コードを書くのはチョット…」という方は是非、bfqry の利用をご検討頂ければと思います。

それでは皆様、素晴らしい年末年始をお過ごしください!

11
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?