X-Roadを手軽に試せるSecurity Server Docker Imageが出たので試してみる1

## はじめに

X-Roadは、エストニアの政府機関や、政府機関と連携を行う企業(銀行など)の間で使われている情報連携を行うための技術です。

X-Roadはソースコードは[github.com/nordic-institute/X-Road](https://github.com/nordic-institute/X-Road)にあるものの、Docker Imageやダウンロードしてすぐ動くパッケージがなかったので使ってみた系の記事があまりありませんでした。

最近、[Planetway社でX-Roadを手軽に試せるDocker Imageを公開しました\[hub.docker.com\]](https://hub.docker.com/r/planetway/xroad-securityserver)。
この記事では、X-Roadを知らない方向けに、事前知識を端折りながらまずはX-Roadを使ってみることから始めます。

\# full disclosure 私はPlanetway社の中の人です

## docker-compose upでX-Road Security Serverを立ち上げる

Docker Imageが公開されているのです。
何はともあれローカルで立ち上げてみましょう。

作業用のディレクトリを作り、[Docker Hub](https://hub.docker.com/r/planetway/xroad-securityserver)のドキュメントにある内容をコピーして`docker-compose.yml`ファイルを作成します。

```yaml

% cat docker-compose.yml
version: '3.7'

services:
  ss01:
    image: planetway/xroad-securityserver:6.24.1-1
    command: bash -c "/files/initdb.sh && /files/cmd.sh"
    depends_on:
      - postgres
    environment:
      # JP-TEST or JP
      - PX_INSTANCE=JP-TEST
      - PX_MEMBER_CLASS=COM
      - PX_MEMBER_CODE=0170121212121
      - PX_SS_CODE=qiita-demo-01
      - PX_TSA_NAME=TEST of Planetway Timestamping Authority 2020
      - PX_TSA_URL=https://tsa.test.planetcross.net
      - PX_TOKEN_PIN=jXq+rlg2VS
      - PX_ADMINUI_USER=admin
      - PX_ADMINUI_PASSWORD=Secret222
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - PX_SERVERCONF_PASSWORD=serverconf
      - PX_MESSAGELOG_PASSWORD=messagelog
      - PX_OPMONITOR_PASSWORD=opmonitor
      - PX_POPULATE_DATABASE=true
      - PX_ENROLL=true
    ports:
      - "2080:2080"
      - "4000:4000"
      - "5500:5500"
      - "5577:5577"
      - "5588:5588"
      - "8000:80"
      - "8443:443"
    volumes:
      # .p12 files and keyconf.xml
      - "px-ss-signer:/etc/xroad/signer"
      # mlog.zip files are stored here, and ./backup contains backups
      - "px-ss-xroad:/var/lib/xroad"

  postgres:
    image: postgres:10
    environment:
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    volumes:
      - "px-ss-postgres:/var/lib/postgresql/data"

volumes:
  px-ss-postgres:
  px-ss-signer:
  px-ss-xroad:
```

Docker Hubのドキュメントでは環境変数を...と省略しているところはとりあえず適当に埋めます。

`PX_SS_CODE`はX-Roadメンバー(X-Roadに参加する組織)の中のSecurity Serverを表すユニークな文字列です。ここからコピペする際には、ユニークな文字列に変更してください。

そして`docker-compose up`してSecurity ServerとPostgreSQLをまとめて起動します。

```bash

% docker-compose up
Creating network "px-ss-docker_default" with the default driver
Creating px-ss-docker_postgres_1 ... done
Creating px-ss-docker_ss01_1     ... done
Attaching to px-ss-docker_postgres_1, px-ss-docker_ss01_1
postgres_1  | The files belonging to this database system will be owned by user "postgres".
postgres_1  | This user must also own the server process.
postgres_1  |
postgres_1  | The database cluster will be initialized with locale "en_US.utf8".
postgres_1  | The default database encoding has accordingly been set to "UTF8".
postgres_1  | The default text search configuration will be set to "english".
postgres_1  |
postgres_1  | Data page checksums are disabled.
postgres_1  |
postgres_1  | fixing permissions on existing directory /var/lib/postgresql/data ... ok
postgres_1  | creating subdirectories ... ok
postgres_1  | selecting default max_connections ... 100
postgres_1  | selecting default shared_buffers ... 128MB
postgres_1  | selecting default timezone ... Etc/UTC
postgres_1  | selecting dynamic shared memory implementation ... posix
postgres_1  | creating configuration files ... ok
ss01_1      | 2021-01-11 09:00:06,566 Creating /var/lib/xroad/backup
ss01_1      | 2021-01-11 09:00:06,655 Generating new internal.[crt|key|p12] files
ss01_1      | /CN=0874c65254b7 -subj
ss01_1      | Generating a RSA private key
ss01_1      | ............................................................+++++
ss01_1      | ............+++++
ss01_1      | writing new private key to '/etc/xroad/ssl/internal.key'
ss01_1      | -----
ss01_1      | 2021-01-11 09:00:07,004 Generating new proxy-ui-api.[crt|key|p12] files
...
# しばらく待つと
ss01_1      | {"timestamp":"2021-01-11T09:03:10.183Z","level":"INFO","thread":"main","logger":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer","message":"Tomcat started on port(s): 4000 (https) with context path ''","context":"X-Road Proxy Admin REST API"}
```

↑の最後の行が出てきたら、
https://localhost:4000/ をブラウザで開いてみしょう。
**X-Road Security Serverの管理画面**です。

Safariでは"接続はプライベートではありません", Chromeでは"この接続ではプライバシーが保護されません"と表示されますが、理解しているので先に進みます。

ログイン画面では環境変数の`PX_ADMINUI_USER`と`PX_ADMINUI_PASSWORD`でログインします。

![スクリーンショット 2021-01-11 11.54.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/e353c586-40e5-dc61-b3ea-4c8d31fc575d.png)

## X-Roadとは

今一体何を立ち上げたのでしょうか。
Security Serverとは何でしょうか。

![arc-g_deployment_view_of_x_road.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/3fa9cc4c-6fba-4e99-d078-af7607cdafb5.png)
図1 [Deployment view of X-Road](https://github.com/nordic-institute/X-Road/blob/develop/doc/Architecture/arc-g_x-road_arhitecture.md#4-deployment-view) (赤い線は筆者による強調)

`docker-compose.yml`に書いた`planetway/xroad-securityserver` Docker Imageは、上図のClient Security Server, Service Security Serverの青い箱に当たるソフトウェアです。

X-Roadの情報連携とは、Service Clientという組織内のClient Information Systemから、Service Providerという組織内のService Information Systemが提供する機能を使うことを表します。

Security Serverは、Client Information Systemから見るとForward Proxyの役割を果たし、Service Information Systemから見るとReverse Proxyの役割を果たします。Security ServerはClient Information SystemとService Information Systemをインターネットを通してつなげるセキュアなトンネルを構築します。

Client Information SystemとClient Security Serverの間、そしてService Information SystemとService Security Serverの間は、SOAPとRESTプロトコルを話します。

X-Roadの情報連携をやってみましょう。

## 認証用証明書のActivate、Security Serverの登録とSubsystemの登録

`planetway/xroad-securityserver` Docker Imageは、Security Server立ち上げ時のセットアップ手順をいくつか自動的に実行しますが、情報連携を行う＝HTTPリクエストをSecurity Serverに送る、までにはまだいくつかステップがあります。

認証用証明書をActivateしましょう。

https://localhost:4000/ を開き、ログインします。
"KEYS AND CERTIFICATES" をクリック -> "Token: softToken-0"の左の ">" をクリック -> "TEST of Planetway Intermediate L1 Organizations CA 2020 ..." をクリック -> "ACTIVATE" をクリックします。

![スクリーンショット 2021-01-12 16.55.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/bfdec6fd-1de9-ada4-f8e2-ad72d1399046.png)

![スクリーンショット 2021-01-12 16.52.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/42586744-7d6a-d7b2-6f4b-06169833029a.png)

認証用証明書をActivateしました。
Security Serverの登録リクエストを送りましょう。

バツを押してその画面を閉じ、"KEYS AND CERTIFICATES" をクリック -> "Register" ボタンを押します。

![スクリーンショット 2021-01-12 16.34.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/5ec06333-4c1a-413c-af19-9695d1b40421.png)

"127.0.0.1"と入力し "ADD" ボタンを押します。
これでSecurity Serverの登録リクエストを送信したことになります。

図１のCentral Serverでは、X-Roadに参加する組織と、それぞれの組織が運用するSecurity Serverのカタログを管理しています。Security Serverの登録リクエストは、Central ServerのカタログにこのSecurity Serverを登録してもらうためのリクエストです。

次にSubsystemの登録を行います。
"CLIENTS" をクリック -> "Add Subsystem" ボタンを押します。

"Subsystem Code"には"democlient"と入力し、"ADD SUBSYSTEM"ボタンを押します。

![スクリーンショット 2021-01-15 12.01.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/ac85f4e1-c008-941f-f166-feb2bc44d39e.png)


"CLIENTS"を押し以下のように表示されていれば(Docker Demo Company (Owner)とdemoclientの行に緑の円と"REGISTERED"が表示されていれば)Subsystemの登録は完了です。

![スクリーンショット 2021-01-12 19.25.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/76377d7d-b987-af74-e4aa-af7ea11e6dda.png)

図1におけるService Client組織に所属するClient Information System情報システムをdemoclientと言う名前で登録しました。

## Security ServerのConnection TypeをHTTPに変更する

もうそろそろです。

Client Information SystemとClient Security Serverの間は、デフォルトではHTTPSを使用します。この記事では簡略化するためHTTPに変更します。

同管理画面で、"CLIENTS" をクリック -> "democlient" の文字のところをクリック -> "INTERNAL SERVERS" をクリックします。下図のように、"CONNECTION TYPE"を"HTTP"に変更してください。

![スクリーンショット 2021-01-13 14.26.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18055/b92fe98d-b292-51ed-01de-866f3b0c38c9.png)

## HTTPリクエストをSecurity Serverに送る

Service Provider側は、この記事では、予めPlanetway社が提供するものを利用しましょう。Planetway社では、現在時刻を返すtimeというX-Roadサービスをデモとして提供しています。

以下のようにcurlのコマンドを実行してください。

```bash

% curl -v "http://localhost:8000/r1/JP-TEST/COM/0170368015672/demoprovider/time" -H "X-Road-Client: JP-TEST/COM/0170121212121/democlient"
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /r1/JP-TEST/COM/0170368015672/demoprovider/time HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.64.1
> Accept: */*
> X-Road-Client: JP-TEST/COM/0170121212121/democlient
>
< HTTP/1.1 200 OK
< Date: Wed, 13 Jan 2021 12:33:54 GMT
< Content-Type: text/plain;charset=utf-8
< x-road-id: JP-TEST-b3fcf843-4d50-4108-b25a-22b5993ad0c9
< x-road-client: JP-TEST/COM/0170121212121/democlient
< x-road-service: JP-TEST/COM/0170368015672/demoprovider/time
< x-road-request-id: f13d39d1-c5a7-4fc5-bd2f-ac002e2dc2e7
< x-road-request-hash: N02MYtDKDzpT1QN7uUQlgUa+I5Vu5ImpX1WKxj/fByjKCUITbWgAlySJwfSt0xQU1AzaPG0RiEqoONQBxXD0jQ==
< Content-Length: 41
<
{"now":"2021-01-13T12:33:54.692995982Z"}
```

と表示されれば成功です！

このcurlコマンドを紐解いていきます。

`localhost:8000` 私の環境では、DockerはmacOS用のDocker Desktopで動作させています。この記事冒頭の`docker-compose.yml`ではports以下に`- 8000:80`とありました。これは`localhost:8000`をDocker containerの80番ポートに転送します。Docker container内のSecurity Serverはポート80番でlistenしています。

`/r1/JP-TEST/COM/0170368015672/demoprovider/time` Information SystemとSecurity Serverの間のプロトコルは、[X-Road: Message Protocol for REST](https://github.com/nordic-institute/X-Road/blob/develop/doc/Protocols/pr-rest_x-road_message_protocol_for_rest.md)と言うプロトコルにしたがいます。URLのpath部分はMessage Protocol for REST文書の中で`serviceId`として記載があります。
`r1`はプロトコルのバージョン。
`JP-TEST`はX-RoadインスタンスのIDです。X-Roadのインスタンスとは、図1に表される箱全部を含んだまとまりで、あるGoverning Authority/運営機関と、運営機関が提供する環境で情報連携を行う組織群を含みます。`JP-TEST`はPlanetway社が運営機関を務めています。他に`EE`(エストニア), `FI`(フィンランド)などがあります。
`COM`はX-Roadに参加する組織が法人であることを表します。他に`GOV`などがあります。
`0170368015672`はPlanetway社を表すX-Road Member IDです。`0170121212121`はDocker Imageの使用者が使えるテスト用のX-Road Member IDです。
`demoprovider`はPlanetway社の情報システムを表すSubsystem IDです。
`time`はX-Road Serviceコードです。

`X-Road-Client: JP-TEST/COM/0170121212121/democlient` というヘッダの値は、Client Information Systemを表しています。

Security Serverはアクセスコントロールの機能も含みます。予めtimeサービスは、`JP-TEST/COM/0170121212121/democlient`から呼び出せるよう設定済みです。

## time X-Road Serviceの実体

timeサービスは以下のようなGolangのコードとして実装しています。

```go

package main

import (
	"encoding/json"
	"net/http"
	"os"
	"time"
)

func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "application/json")

	t := time.Now()
	enc := json.NewEncoder(w)
	err := enc.Encode(map[string]interface{}{
		"now": t,
	})
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+port, nil)
}
```

普通のJSONを返すAPIサーバですね。`http.HandleFunc`の第一引数が`/`であることに注意。これが図1のService Information Systemに当たります。

いくつか私が面白いと思う点を書きます。

1. Security ServerがInformation Systemから見るとシンプルなトンネルであること

    このGolangのサーバをローカルで実行し、立ち上がったサーバにcurlでHTTPリクエストを送る手順と、インターネットのどこかでPlanetway社が提供するtimeサービスを呼び出す手順の違いが小さいこと。`X-Road-Client`リクエストヘッダ、URLのpath部分だけです。

1. X-Roadのセキュリティ面はInformation Systemには見えない

    よくある外部APIと比較するとどうでしょう。Bearer tokenのようなものもクライアント証明書も何も見えません。認証や認可はSecurity Serverが隠蔽しています。

1. curlのリクエスト送り先はSecurity Server

    知る必要があるのは、組織のID、その中のサービスIDからなる`serviceId`だけです。相手のURLは知る必要がありません。インターネットのレイヤの上に綺麗にRPCのレイヤをのっけています。

## まとめ

DockerでX-Road Security Serverを立ち上げ、それを通して時刻を返すデモサービスを呼び出すcurlコマンドを実行しました。

続く