こんにちは、中野です。
今回は、BGPにおけるネットワークセキュリティ技術である「ROV(Route origin validation)」について、Dockerコンテナで試す環境を作ってみましたので、記事にしてみました。
ROVの技術や仕組み等は、JPNICさんのWebサイトやJANOGの過去プログラム、その他の様々なWeb記事をご確認いただくとして、この記事では作った環境や使い方について解説します。
作った環境はこちらに置いてありますので、ご自由にお試しください。
構成
本環境は、以下のコンポーネントにて構成しています。
- GoBGP
- Goで書かれたオープンソースのBGP実装
- GitHubのmasterブランチよりビルド
- Route Views Archive Project Pageにて公開されているフルルートのRIBを注入し、BGPにてFRRoutingに対してフルルートを広告
- Routinator
- Rustで書かれたオープンソースのRPKI Relying Partyソフトウェア
- 公式Dockerイメージのv0.12.1を使用
- ROAの取得、BGPルータとのRPKI-RTRにも対応
- FRRouting
- Quaggaから派生したオープンソースのルーティングプロトコルスイート
- 公式Dockerイメージの9.0.0を使用
- Routinator、JPNAP ROAキャッシュ、BBIX ROAキャッシュから、RPKI-RTRにてVRP(Validate ROA Payload)を受信
- GoBGPからフルルートを受信し、ROV(Route origin validation)を実施
ネットワーク構成は、以下のとおりです。
- AS64513にgobgpを配置
- gobgp 〜 frr1・frr2・frr3間でeBGPピアを設定
- gobgpにフルルートのRIBをインポートし、BGPにてfrr1/frr2/frr3にベストパスを広告
- AS64512にfrr1・frr2・frr3を配置
- frr1・frr2・frr3はそれぞれ異なるROAキャッシュを参照
- frr1は、コンテナで起動したroutinatorを参照
- frr2は、JPNAP ROAキャッシュを参照
- frr3は、BBIX ROAキャッシュを参照
- frr1・frr2・frr3は、受信したフルルートをROVの状態(valid/invalid/notfound)に応じてLocal Preferenceを設定
- frr1・frr2・frr3はそれぞれ異なるROAキャッシュを参照
設定概要
docker-compose.yml
この環境は、Docker Composeを利用しています。
Routinator1つ、GoBGP1つ、FRRouting3つのコンテナと、これらのコンテナを接続する10.0.0.0/24のネットワークを作成します。
また、GoBGP、FRRoutingでは設定ファイルをコンテナから参照するため、ボリュームの設定をしています。
services: routinator: image: nlnetlabs/routinator:v0.12.1 hostname: routinator networks: network-bgp: ipv4_address: 10.0.0.2 ports: - 9556:9556 gobgp: build: context: gobgp/docker hostname: gobgp networks: network-bgp: ipv4_address: 10.0.0.3 volumes: - ./gobgp/gobgpd.conf:/gobgpd.conf - ./gobgp/ribs/:/ribs/ command: /gobgpd -f /gobgpd.conf frr1: image: quay.io/frrouting/frr:9.0.0 hostname: frr1 cap_add: - NET_ADMIN - SYS_ADMIN networks: network-bgp: ipv4_address: 10.0.0.4 volumes: - ./frr/daemons:/etc/frr/daemons - ./frr/frr1.conf:/etc/frr/frr.conf frr2: image: quay.io/frrouting/frr:9.0.0 hostname: frr2 cap_add: - NET_ADMIN - SYS_ADMIN networks: network-bgp: ipv4_address: 10.0.0.5 volumes: - ./frr/daemons:/etc/frr/daemons - ./frr/frr2.conf:/etc/frr/frr.conf frr3: image: quay.io/frrouting/frr:9.0.0 hostname: frr3 cap_add: - NET_ADMIN - SYS_ADMIN networks: network-bgp: ipv4_address: 10.0.0.6 volumes: - ./frr/daemons:/etc/frr/daemons - ./frr/frr3.conf:/etc/frr/frr.conf networks: network-bgp: driver: bridge ipam: config: - subnet: 10.0.0.0/24
GoBGPのDockerfile
GoBGPは公式のコンテナイメージがあるようですが、数年前のイメージと古かったため、GitHubのmasterブランチをビルドしています。
FROM golang:1.20.6-alpine3.18 AS builder RUN apk update && apk add git \ && git clone https://github.com/osrg/gobgp.git /go/src/github.com/osrg/gobgp \ && cd /go/src/github.com/osrg/gobgp \ && go mod download \ && cd /go/src/github.com/osrg/gobgp/cmd/gobgp \ && go build \ && cd /go/src/github.com/osrg/gobgp/cmd/gobgpd \ && go build \ && mkdir /ribs FROM scratch COPY --from=builder /go/src/github.com/osrg/gobgp/cmd/gobgp/gobgp /gobgp COPY --from=builder /go/src/github.com/osrg/gobgp/cmd/gobgpd/gobgpd /gobgpd COPY --from=builder /ribs /ribs CMD ["/gobgpd"]
frr1の設定
FRRoutingは、IPアドレス、BGPピア、ルートマップ、RPKIの設定と必要最低限のみを設定をしています。
また、ROVの結果に応じて、Local Preferenceを以下のように変えています。
ROV結果 | Local Preference |
---|---|
valid | 200 |
notfound | 100 |
invalid | 10 |
frr version 9.0_git frr defaults traditional hostname frr1 no ipv6 forwarding bgp no-rib ! interface eth0 ip address 10.0.0.4/24 exit ! router bgp 64512 neighbor 10.0.0.3 remote-as external ! address-family ipv4 unicast neighbor 10.0.0.3 soft-reconfiguration inbound neighbor 10.0.0.3 route-map ebgp-in in neighbor 10.0.0.3 route-map ebgp-out out exit-address-family exit ! route-map ebgp-in permit 10 match rpki invalid set local-preference 10 exit ! route-map ebgp-in permit 100 match rpki valid set local-preference 200 exit ! route-map ebgp-in permit 200 match rpki notfound set local-preference 100 exit ! route-map ebgp-out permit 10 exit ! rpki rpki polling_period 300 rpki retry_interval 30 rpki expire_interval 3600 rpki cache 10.0.0.2 3323 preference 1 exit !
実行方法
フルルートのRIBのダウンロード
GoBGPへフルルートを注入するために、事前にダウンロードします。
- Route Views Archive Projectより、BGPのフルルートのRIBをダウンロード
- ダウンロードした rib.yyyymmdd.hhmm.bz2 ファイルを、bunzip2コマンドで展開します
- 展開した rib.yyyymmdd.hhmm ファイルを、 gobgp/ribs/ ディレクトリに移動します
Docker Composeの起動
Docker Composeを、以下のコマンドにて起動します。
% docker compose up -d [+] Running 16/16 ✔ routinator 5 layers [⣿⣿⣿⣿⣿] 0B/0B Pulled 6.0s ✔ 6875df1f5354 Already exists 0.0s ✔ 160d0347c010 Already exists 0.0s ✔ 6eed1cee6d09 Already exists 0.0s ✔ 9d6980578a97 Already exists 0.0s ✔ 14c5217b1cfb Already exists 0.0s ✔ frr2 Pulled 10.3s ✔ frr3 Pulled 10.3s ✔ frr1 7 layers [⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 10.3s ✔ 8c6d1654570f Already exists 0.0s ✔ 7d0afa75b38d Pull complete 1.0s ✔ b53793227860 Pull complete 2.0s ✔ 0ac198d11877 Pull complete 4.5s ✔ 4f4fb700ef54 Pull complete 4.5s ✔ 83f241f7a471 Pull complete 4.5s ✔ ecb4523afdab Pull complete 4.5s [+] Building 1.0s (9/9) FINISHED => [gobgp internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 617B 0.0s => [gobgp internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [gobgp internal] load metadata for docker.io/library/golang:1.20.6-alpine3.18 0.9s => [gobgp builder 1/2] FROM docker.io/library/golang:1.20.6-alpine3.18@sha256:7839c9f01b5502d7c 0.0s => CACHED [gobgp builder 2/2] RUN apk update && apk add git && git clone https://github.com/o 0.0s => CACHED [gobgp stage-1 1/3] COPY --from=builder /go/src/github.com/osrg/gobgp/cmd/gobgp/gobgp 0.0s => CACHED [gobgp stage-1 2/3] COPY --from=builder /go/src/github.com/osrg/gobgp/cmd/gobgpd/gobg 0.0s => CACHED [gobgp stage-1 3/3] COPY --from=builder /ribs /ribs 0.0s => [gobgp] exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:ddab0141cc075ce7da75dedbb7374e1f4ff08fe9ce49a2c57ebe929b99b9b52c 0.0s => => naming to docker.io/library/try_rov_frrouting_gobgp_routinator_on_docker-gobgp 0.0s [+] Running 6/6 ✔ Network try_rov_frrouting_gobgp_routinator_on_docker_network-bgp Created 0.0s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-gobgp-1 Started 0.8s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-frr1-1 Started 0.7s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-frr3-1 Started 0.7s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-frr2-1 Started 0.8s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-routinator-1 Started 0.9s %
Routinatorのコンテナは、ROAを取得・VRP(Validate ROA Payload)の生成完了まで数分かかります(手元の環境では4分程度かかりました)。
GoBGPにフルルートをインポート
GoBGPのMRT Formatの経路注入の機能を利用して、ダウンロードしたMRTからフルルートをインポートします。
% docker compose exec gobgp /gobgp mrt inject --only-best global /ribs/rib.20230830.0000 %
BGPピアやROAキャッシュの状態確認
FRRoutingにログインして、ROAキャッシュサーバの設定や接続状態を確認します。
% docker compose exec frr1 vtysh % Can't open configuration file /etc/frr/vtysh.conf due to 'No such file or directory'. Configuration file[/etc/frr/frr.conf] processing failure: 11 Hello, this is FRRouting (version 9.0_git). Copyright 1996-2005 Kunihiro Ishiguro, et al. frr1# show bgp summary IPv4 Unicast Summary (VRF default): BGP router identifier 10.0.0.4, local AS number 64512 vrf-id 0 BGP table version 0 RIB entries 1796247, using 329 MiB of memory Peers 1, using 13 KiB of memory Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc 10.0.0.3 4 64513 986261 32 0 0 0 00:15:14 986216 0 N/A Total number of neighbors 1 frr1# show rpki cache-server host: 10.0.0.2 port: 3323, preference: 1 frr1# show rpki cache-connection Connected to group 1 rpki tcp cache 10.0.0.2 3323 pref 1 (connected) frr1#
ROAキャッシュから受信したVRP(Validate ROA Payload)の確認
ROAキャッシュから受信したVRP(Validate ROA Payload)を確認するには、以下のコマンドを実行します。
frr1# show rpki prefix-table RPKI/RTR prefix table Prefix Prefix Length Origin-AS 1.128.0.0 11 - 11 1221 1.0.0.0 24 - 24 13335 <中略> 2a01:c000:: 19 - 48 5511 2003:: 19 - 19 3320 Number of IPv4 Prefixes: 377013 Number of IPv6 Prefixes: 82276
個別経路のROVの状態の確認
個別経路のROVの状態を確認するには、以下のコマンドを実行します。
以下の例では、 rpki validation-state: valid
と ROVが valid となっている事が分かります。
frr1# show bgp ipv4 unicast 8.8.8.0/24 BGP routing table entry for 8.8.8.0/24, version 0 Paths: (1 available, no best path) Not advertised to any peer 64513 23673 15169 203.189.128.233 (inaccessible, import-check enabled) from 10.0.0.3 (10.0.0.3) Origin IGP, localpref 200, invalid, external, rpki validation-state: valid Community: 23673:10 23673:15169 23673:65101 Last update: Sat Sep 2 01:40:14 2023
ROVによる経路判定の確認
FRRoutingでは、GoBGPから受信した経路に対してROVを行いますが、その判定結果(valid, invalid, notfound)毎の経路全体を見るには、以下のコマンドを実行します。
validと判定された経路
frr1# show bgp ipv4 unicast rpki valid BGP table version is 0, local router ID is 10.0.0.4, vrf id 0 Default local pref 100, local AS 64512 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path V 1.0.0.0/24 203.189.128.233 200 0 64513 23673 13335 i V 1.0.4.0/22 203.189.128.233 200 0 64513 23673 6939 7545 2764 38803 i <中略> V 223.233.36.0/22 203.189.128.233 200 0 64513 23673 9498 45609 ? V 223.233.40.0/22 203.189.128.233 200 0 64513 23673 9498 45609 ? Displayed 449355 routes and 986216 total paths
notfoundと判定された経路
frr1# show bgp ipv4 unicast rpki notfound BGP table version is 0, local router ID is 10.0.0.4, vrf id 0 Default local pref 100, local AS 64512 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path N 0.0.0.0/0 94.156.252.18 0 100 0 64513 34224 3356 i V 1.0.0.0/24 203.189.128.233 200 0 64513 23673 13335 i <中略> V 223.233.36.0/22 203.189.128.233 200 0 64513 23673 9498 45609 ? V 223.233.40.0/22 203.189.128.233 200 0 64513 23673 9498 45609 ? Displayed 986216 routes and 986216 total paths
本来であれば先頭に N とROVがnot foundとなった経路のみ表示されるはずですが、バグによりnot foundの経路だけでなくvalid・invalidの経路も表示されてしまっています。
こちらは、正しく表示されるよう修正し、Pull Requestを送り無事にマージされましたので、次のFRRoutingのリリースより正常に表示されそうです。
invalidと判定された経路
frr1# show bgp ipv4 unicast rpki invalid BGP table version is 0, local router ID is 10.0.0.4, vrf id 0 Default local pref 100, local AS 64512 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path I 1.6.219.0/24 203.189.128.233 10 0 64513 23673 23764 6453 4755 9583 137130 i I 1.6.230.0/24 203.189.128.233 10 0 64513 23673 23764 6453 4755 i <中略> I 223.123.103.0/24 203.189.128.233 10 0 64513 23673 17557 138423 i I 223.224.38.0/24 203.189.128.233 10 0 64513 23673 9498 i Displayed 5482 routes and 986216 total paths
Docker Composeの停止
ROVの動作確認等が終わり、コンテナ環境が不要となった場合は、以下のコマンドにてDocker Composeを停止します。
% docker compose down [+] Running 6/6 ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-frr3-1 Removed 1.1s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-routinator-1 Removed 3.6s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-frr2-1 Removed 1.1s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-gobgp-1 Removed 1.1s ✔ Container try_rov_frrouting_gobgp_routinator_on_docker-frr1-1 Removed 1.1s ✔ Network try_rov_frrouting_gobgp_routinator_on_docker_network-bgp Removed 0.0s %
まとめ
実際にBGPのフルルートを受ける事ができない環境でも、Route Views Archive Project Pageにて公開されているMRTデータや、ROAキャッシュなどを活用することで、ROV(Route origin validation)を試すことができました。
現在、シナプスのネットワークではROVの本番導入ができていませんので、ROAキャッシュの運用検証などを進めた後に、ネットワークにROVを導入し、より安定した安全なネットワークを作っていきたいと思います。