dockerを使ってRPKI Validationを試してみる

はじめに

2017年8月25日のインターネット障害は、特に日本にとって大変なインパクトがあったと認識しています。様々な方が事象の分析や対策案の考案をされているのを見ていると、何か一つでも試してみたい気持ちになるものです。 提示される対策案の一つにRPKIが挙げられます。

リソースPKI(RPKI)は、 アドレス資源の割り振りや割り当てを証明するためのPKI(Public-Key Infrastructure:公開鍵基盤)で、 IPアドレスが正しく割り振られたものであるかどうかを確認できるほか、 BGPルータにおける誤ったインターネットの経路情報(Mis-Origination)を見つけるために使えます。

リソースPKI (RPKI; Resource Public Key Infrastructure) - JPNIC より

今回の事象に対する効果の程は議論の真っ最中であると認識していますが、まずは勉強しつつ簡単にできそうなところから手元で動かしてみようと思いました。

今回の目的

  • 楽してローカル環境にRPKIのValidator (= RPKIで言うところのRelying Party) を構築する
  • RPKIを使ってBGPの経路フィルタリングをやってみる

以下は次回以降の課題

  • 検証環境のASやPrefixを認証基盤(= RPKIで言うところのCertificate Authority)に登録し、オレオレRPKIをやってみる

Relying Partyってなに

Relying parties run local RPKI validation tools, which are pointed at the different RPKI trust anchors and using rsync gather all cryptographic objects from the different repositories used for publication. This creates a local validated cache which can be used for making BGP routing decisions.

Wikipedia: Resource Public Key Infrastructure, Validationの項 より

訳すとRelying PartyにはRPKIのValidationツールを動作させ、BGPルーティングに影響するValidationキャッシュを生成する役目があるそうです。"Validationキャッシュ"とは多少抽象的な表現のような気もします。具体的にはROA (Route Origin Authorization) のようなデータのことを指すと理解しています。

OSSでもいくつかツールはあるようですが、今回はValidationツールとしてRIPE-NCCのRPKI-Validatorを試してみます。

github.com

RPKI-Validatorを動かす

前置きが長くなりました。RPKI-Validatorは大変親切なことにDockerfileを用意してくれているので、ビルドして実行するだけでよさそう。有り難し。

環境

  • macOS High Sierra
  • docker for MAC (v17.9.0)
  • rpki-validator (v2.22-dist)

rpki-validatorコンテナ起動

% git clone git@github.com:RIPE-NCC/rpki-validator.git
% cd rpki-validator/rpki-validator-app/docker
% wget http://central02.maven.org/maven2/net/ripe/rpki/rpki-validator-app/2.22/rpki-validator-app-2.22-dist.tar.gz
% docker build -t rpki-validator:0.1 --no-cache .
% docker run -id --name rpki --privileged=true -p 8080:8080 rpki-validator:0.1

ブラウザで http://127.0.0.1:8080 にアクセス。 ただしこのままではトラストアンカーが指定されておらず、すっからかんの状態で見えます。ので、本体にあるtalファイルをdockerディレクトリ以下に置き、コンテナにvolumeでくっつけてみました。

% pwd
/YOUR/DIR/rpki-validator/rpki-validator-app/docker
% mkdir tal
% cp ../conf/tal/*.tal tal/
% docker run -id --name rpki --privileged=true -v `pwd`/tal:/opt/docker/conf/tal -p 8080:8080 rpki-validator:0.1

ブラウザで先程のページを更新してみると、いくつかのトラストアンカーが読み込み中になっています。 しばらくすると読み込みが完了し、なんとなくそれっぽく見えます。ROAsのタブを見るとROAがインストールされているのが分かります。

f:id:goto_infamous:20171009170813p:plain

外部からRPKI-RTRでアクセス

RPKI-Validatorでは TCP 8282 でRTRを待ち受けるみたいです。 コンテナの外からアクセスできるように EXPOSE して再度ビルドし、コンテナを起動します。

% emacs Dockerfile
% git diff Dockerfile
diff --git a/rpki-validator-app/docker/Dockerfile b/rpki-validator-app/docker/Dockerfile
index 264d043..9073586 100644
--- a/rpki-validator-app/docker/Dockerfile
+++ b/rpki-validator-app/docker/Dockerfile
@@ -8,5 +8,6 @@ COPY *.conf conf/
 COPY docker-startup.sh ./

 EXPOSE 8080
+EXPOSE 8282

 CMD /opt/docker/docker-startup.sh
\ No newline at end of file
% docker build -t rpki-validator:0.2 --no-cache .
% docker run -id --name rpki --privileged=true -v `pwd`/tal:/opt/docker/conf/tal -p 8080:8080 -p 8282:8282 rpki-validator:0.2

BGPフィルタリングをやる前に、RPKI-RTRクライアントでちゃんと動いているか確認しておきたいです。RTRのクライアントツールをまず入れました。 github.com

% cd YOUR_DEV_DIR
% git clone git@github.com:rtrlib/rtrlib.git
% cd rtrlib
% cmake -D CMAKE_BUILD_TYPE=Release .
% make
% make install
% rtrclient -h
Usage:
 rtrclient tcp [options] <host> <port>

Options:
-k  Print information about SPKI updates.  
-p  Print information about PFX updates.

Examples:
 rtrclient tcp rpki-validator.realmv6.org 8282
 rtrclient tcp -k -p rpki-validator.realmv6.org 8282

先程動かしたコンテナに対して実行します。RTRはまだちゃんと勉強してないので、現時点ではログは流し見するとして、ROA情報が取れてきているのが分かります。

% rtrclient tcp -p 127.0.0.1 8282 | head -n 20
Prefix                                     Prefix Length         ASN
RTR-Socket changed connection status to: RTR_RESET, Mgr Status: RTR_MGR_CONNECTING
RTR-Socket changed connection status to: RTR_SYNC, Mgr Status: RTR_MGR_CONNECTING
RTR-Socket changed connection status to: RTR_FAST_RECONNECT, Mgr Status: RTR_MGR_CONNECTING
RTR-Socket changed connection status to: RTR_CONNECTING, Mgr Status: RTR_MGR_CONNECTING
RTR-Socket changed connection status to: RTR_RESET, Mgr Status: RTR_MGR_CONNECTING
RTR-Socket changed connection status to: RTR_SYNC, Mgr Status: RTR_MGR_CONNECTING
+ 88.199.162.0                                24 -  24        42923
+ 89.107.224.0                                24 -  24        43260
+ 109.132.0.0                                 14 -  14         5432
+ 77.138.12.0                                 22 -  22        12849
+ 37.148.0.0                                  17 -  18        31549
+ 185.176.33.0                                24 -  24        64413
+ 80.86.239.0                                 24 -  24        21104
+ 193.48.137.0                                24 -  24         1945
+ 111.119.172.0                               24 -  24       132165
+ 195.130.64.0                                18 -  24         5408
+ 178.20.80.0                                 21 -  21        39449
+ 193.54.45.0                                 24 -  24          781
+ 185.38.172.0                                23 -  23       205938

RPKI Validationによる経路フィルタリングをやってみる

ローカルで立てたRPKI-Validatorを使ってBGPの経路のフィルタリングをやってみます

f:id:goto_infamous:20171016014104p:plain

今回はBGPデーモンにGoBGPを使いました。もちろんコンテナ化されており、簡単便利に使えます。 RPKI-Validatorのコンテナ1つと、2つのGoBGPコンテナを立ち上げ、図のような環境を作っていきます。

まずはコンテナネットワークを用意する。

% docker network create --driver=bridge --subnet=192.168.100.0/24 --gateway=192.168.100.1 bgpnet

続いてRPKI Validatorのコンテナを起動しておく。ブラウザでhttp://127.0.0.1:8080にアクセスしてROAが見えることを確認。

## talファイルが必要なのでrpki-validatorのところに移動
% cd /YOUR/DIR/rpki-validator/rpki-validator-app/docker

## RPKI-Validatorコンテナを起動
% docker run -id --name rpki --privileged=true -v `pwd`/tal:/opt/docker/conf/tal -p 8080:8080 --net=bgpnet --ip=192.168.100.100 rpki-validator:0.2

GoBGPのコンテナを用意する。今回はgobgp1(AS65001)でRPKI Validationに基づく経路フィルタリングをやりました。

## 適当なディレクトリに移動
% cd YOUR_TEST_DIR

## gobgpのコンテナを入手
% docker pull osrg/gobgp

## gobgp1用のconfigファイルを作成する
## rpki-servers:にてRPKI-RTRの接続先情報を記載
## policy-definitions:にてInvalidだった経路をRejectするようなポリシー"AS65002-IMPORT-RPKI"を書く
% mkdir gobgp1
% emacs gobgp1/gobgp.yml
global:
  config:
    as:                           65001
    router-id:                    192.168.100.10
  apply-policy:
    config:
      import-policy-list:         ["AS65002-IMPORT-RPKI"]
      default-import-policy:      accept-route
rpki-servers:
  - config:
      address:                    192.168.100.100
      port:                       8282
neighbors:
  - config:
      neighbor-address:           192.168.100.20
      peer-as:                    65002
policy-definitions:
  - name:                         AS65002-IMPORT-RPKI
    statements:
    - conditions:
        bgp-conditions:
          rpki-validation-result: "invalid"
      actions:
        route-disposition:        reject-route

## gobgp2用のconfigファイルを作成
% mkdir gobgp2
% emacs gobgp2/gobgp.yml
global:
  config:
    as:                           65002
    router-id:                    192.168.100.20
neighbors:
  - config:
      neighbor-address:           192.168.100.10
      peer-as:                    65001

コンテナを起動していきます。

## GoBGPコンテナを起動
% docker run -id --name gobgp1 --privileged=true -v `pwd`/gobgp1:/etc/gobgp --net=bgpnet --ip=192.168.100.10 osrg/gobgp
% docker run -id --name gobgp2 --privileged=true -v `pwd`/gobgp2:/etc/gobgp --net=bgpnet --ip=192.168.100.20 osrg/gobgp

コンテナ内のGoBGPを起動する。

% docker exec gobgp1 gobgpd -f /etc/gobgp/gobgp.yml &
% docker exec gobgp2 gobgpd -f /etc/gobgp/gobgp.yml &

## Peer状態を確認
% docker exec gobgp1 gobgp neighbor
Peer              AS  Up/Down State       |#Received  Accepted
192.168.100.20 65002 00:03:57 Establ      |        0         0
% docker exec gobgp2 gobgp neighbor
Peer              AS  Up/Down State       |#Received  Accepted
192.168.100.10 65001 00:04:22 Establ      |        0         0

gobgp1にてRPKIの状態を確認する。

RPKI-Validatorに接続し、ROA情報を取得できてる。

## RPKI-Validatorとのコネクション状態を確認
% docker exec gobgp1 gobgp rpki server
Session                 State  Uptime     #IPv4/IPv6 records
192.168.100.100:8282    Up     00:02:11   2621/166

# ROA情報を確認
% docker exec gobgp1 gobgp rpki table | head -n10
Network                                    Maxlen AS         Server
1.9.0.0/16                                 24     4788       192.168.100.100:8282
1.9.12.0/24                                24     65037      192.168.100.100:8282
1.9.21.0/24                                24     24514      192.168.100.100:8282
1.9.23.0/24                                24     65120      192.168.100.100:8282
1.9.31.0/24                                24     65077      192.168.100.100:8282
1.9.65.0/24                                24     24514      192.168.100.100:8282
1.37.0.0/16                                17     4775       192.168.100.100:8282
1.37.2.0/23                                24     4775       192.168.100.100:8282
1.37.4.0/22                                24     4775       192.168.100.100:8282

gobgp2から経路広報する。

今回はPrivate空間の10.0.0.0/8と、gobgp rpki tableの一番上にエントリされている1.9.0.0/16を広告してみます。後者は実際のリアルな経路なので試す環境によってはご注意ください。

% docker exec gobgp2 gobgp global rib add -a ipv4 10.0.0.0/8 origin igp
% docker exec gobgp2 gobgp global rib add -a ipv4 1.9.0.0/16 origin igp

gobgp1でフィルタリング結果を確認。

1.9.0.0/16は本来のOrigin ASであるAS4788からの広告ではもちろんないので、Validation結果はInvalidとなり、Global RIBにインストールされていません。 一方10.0.0.0/8はValidation結果がUnknownであるため、今回のAS65001のポリシー上ではAcceptされ、Global RIBにインストールされました。

% docker exec gobgp1 gobgp neighbor 192.168.100.20 adj-in
   ID  Network              Next Hop             AS_PATH              Age        Attrs
I  0   1.9.0.0/16           192.168.100.20       65002                00:00:43   [{Origin: i}]
N  0   10.0.0.0/8           192.168.100.20       65002                00:01:17   [{Origin: i}]
% docker exec gobgp1 gobgp global rib
   Network              Next Hop             AS_PATH              Age        Attrs
N*>10.0.0.0/8           192.168.100.20       65002                00:01:49   [{Origin: i}]

おわりに

dockerを使うことで結構簡単にRPKIを試すことができました。 もちろん、わざわざローカルにValidatorを立てなくても、インターネット上に公開されたROAキャッシュを使うこともできます。

ROAパブリックキャッシュサーバの利用方法 - JPNIC

ただし、パブリックなROAキャッシュを使う方法や、今回のように実際のNIRの認証情報を使う方法では、自分の好きなPrefixを登録し認証の試験をすることが難しいです。今回の内容を前段として、次はCertificate Authorityを検証環境に立てるところをやってみたいと思います。