これは、なにをしたくて書いたもの?
CockroachDBを使うにあたって、SSL/TLS証明書を扱うセキュアなクラスタを作るのは少し面倒そうかな?と思って
あとで見ようと思っていたのですが。
先にこちらをクリアしておいた方が良さそうだなぁと思いまして。
たとえばCluster APIを使うためにはセッショントークンを作る必要があるのですが、
All endpoints except /health and /login require authentication using a session token.
Cluster API v2.0 / Requirements
セッショントークンを得るためにはユーザーID、パスワードで認証できる必要があります。
ですが、そもそもユーザーを作成するためにはセキュアなクラスタである必要があります。
CREATE USER | CockroachDB Docs
デフォルトで作成されるroot
ユーザーにはパスワードがないため、そのままでは使えません。また、非セキュアな
クラスタではroot
ユーザーのパスワードを変更することすらできません。
この一方でDockerでのGetting Startedなどを見ていると、非セキュアなクラスタになっていたりもするので。
Start a Cluster in Docker (Insecure) | CockroachDB Docs
ここは、最初のうちにクリアしておいた方が良さそうだなと思いまして。
お題
やりたいことは、こちら。
また、以下はやりません。
- データのホスト側への保存は考慮しない(使い捨てを前提にする)
- DB Consoleの証明書についてはカバーしない
あくまで、勉強目的ですね。
環境
今回の環境は、こちらです。
$ docker version Client: Docker Engine - Community Version: 20.10.8 API version: 1.41 Go version: go1.16.6 Git commit: 3967b7d Built: Fri Jul 30 19:54:27 2021 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.8 API version: 1.41 (minimum version 1.12) Go version: go1.16.6 Git commit: 75249d8 Built: Fri Jul 30 19:52:33 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.4.9 GitCommit: e25210fe30a0a703442421b0f60afac609f950a3 runc: Version: 1.0.1 GitCommit: v1.0.1-0-g4144b63 docker-init: Version: 0.19.0 GitCommit: de40ad0 $ docker-compose version docker-compose version 1.29.2, build 5becea4c docker-py version: 5.0.0 CPython version: 3.7.10 OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019
CockroachDBは21.1.7を使用します。
セキュアなクラスタを作る
Docker Compose…の前に、そもそもセキュアなクラスタをどうやって作るのか?について見ておいた方が良い気がします。
セキュアなクラスタとは、CockroachDBのノード間の通信やクライアントとの通信をSSL/TLSで行うことがベースになります。
そのためにSSL/TLS証明書を作成します。
非セキュアなクラスタには、cockroach
コマンドでアクセスする際に--insecure
オプションがつきまとうことになります。
…といっても、セキュアなクラスタにすると今度は--certs-dir
オプションがつきまとうのですが。
作り方自体は、Get Startedに書いています。
また、セキュリティの認証に関するページにもセキュアなクラスタで使うSSL/TLS証明書についての記載があります。
Authentication | CockroachDB Docs
ですが、Get Startedの内容はひとつのノードに3つのCockroachDBプロセスを起動してクラスタを構築しているので、
ノードがホストごとに分かれた手順でも確認したいな、と。
こんなことをする必要があります。
- CA証明書と鍵を作成する
- サーバー証明書と鍵を作成する
- クライアント証明書(接続ユーザーごとに作成)と鍵を作成する
Authentication / Using digital certificates with CockroachDB
いずれもcockroach cert [コマンド]
で作成でき、クラスタ内の各ノードで参照できる必要があります。
こんな感じです。
## CA証明書と鍵を作成する $ cockroach cert create-ca \ --certs-dir=[証明書を作成するディレクトリ] \ --ca-key=[CAキーを配置するディレクトリ]/ca.key ## サーバー証明書と鍵を作成する $ cockroach cert create-node \ localhost \ [ホスト名(複数可)] \ --certs-dir=[証明書を作成するディレクトリ] \ --ca-key=[CAキーを配置するディレクトリ]/ca.key ## クライアント証明書(接続ユーザーごとに作成)と鍵を作成する $ cockroach cert create-client \ [ユーザー名] \ --certs-dir=[証明書を作成するディレクトリ] \ --ca-key=[CAキーを配置するディレクトリ]/ca.key
たとえば、まとめて/cockroach/cockroach-certs
ディレクトリ内に証明書とCA鍵を作成するようにしてみましょう。
$ sudo mkdir -p /cockroach/cockroach-certs/{certs,ca} $ sudo chown -R `whoami`:`whoami` /cockroach/cockroach-certs
ノードはnode1、node2、node3の3つ分、ユーザーはroot
とmyuser
の2つとします。
CA証明書と鍵の作成。
$ cockroach cert create-ca \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key
作成されたファイル。
$ find /cockroach/cockroach-certs -type f /cockroach/cockroach-certs/certs/ca.crt /cockroach/cockroach-certs/ca/ca.key
次に、ノード証明書と鍵を作成します。--certs-dir
と--ca-key
は、CA証明書と鍵を作った時と同じものを指定します。
$ cockroach cert create-node \ localhost node1 node2 node3 \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key
node.crt
とnode.key
が作成されます。
$ find /cockroach/cockroach-certs -type f /cockroach/cockroach-certs/certs/node.key /cockroach/cockroach-certs/certs/ca.crt /cockroach/cockroach-certs/certs/node.crt /cockroach/cockroach-certs/ca/ca.key
localhost
を含め4つのホスト名を指定したのですが、できあがったファイルはひとつです。
これは、SAN(Subject Alternative Name)を使っているからですね。
Authentication / Using cockroach cert or openssl commands
ワイルドカードにも対応していそうではあります。
node.crt must have CN=node and the list of IP addresses and DNS names listed in the Subject Alternative Name field. CockroachDB also supports wildcard notation in DNS names
最後にクライアント証明書と鍵を作成します。こちらは、ユーザー単位に作成します。
$ cockroach cert create-client \ root \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key $ cockroach cert create-client \ myuser \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key
最終的には、これだけのファイルができあがります。
$ find /cockroach/cockroach-certs -type f /cockroach/cockroach-certs/certs/client.root.key /cockroach/cockroach-certs/certs/client.root.crt /cockroach/cockroach-certs/certs/client.myuser.key /cockroach/cockroach-certs/certs/client.myuser.crt /cockroach/cockroach-certs/certs/node.key /cockroach/cockroach-certs/certs/ca.crt /cockroach/cockroach-certs/certs/node.crt /cockroach/cockroach-certs/ca/ca.key
あとは、これらのファイルをクラスタを構成する各ノードに配って--certs-dir
で指定してCockroachDBプロセスを
起動すればOKです。
$ cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=[データの保存ディレクトリ] \ --join=node1 \ --join=node2 \ --join=node3
起動後はinit
でクラスタを初期化します。
$ cockroach init --certs-dir=/cockroach/cockroach-certs/certs
Docker Composeで書く
と、ここまで書いた内容をDocker Composeで一気に行うようにします。
定義は、このようになりました。
docker-compose.yml
version: "3.9" services: cockroach_seed_node: image: cockroachdb/cockroach:v21.1.7 ports: - "26257:26257" - "8080:8080" volumes: - certs_volume:/cockroach/cockroach-certs:rw # hostname: node1 networks: cockroach_cluster_network: aliases: - node1 entrypoint: >- bash -c " test -d /cockroach/cockroach-certs/certs || mkdir /cockroach/cockroach-certs/certs && test -d /cockroach/cockroach-certs/ca || mkdir /cockroach/cockroach-certs/ca && rm -rf /cockroach/cockroach-certs/certs/* /cockroach/cockroach-certs/ca/* && cockroach cert create-ca \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach cert create-node \ localhost node1 node2 node3 \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach cert create-client \ root \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach cert create-client \ myuser \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=/cockroach/cockroach-data \ --advertise-addr=node1 \ --join=node1 \ --join=node2 \ --join=node3 " cockroach_node2: image: cockroachdb/cockroach:v21.1.7 depends_on: - cockroach_seed_node volumes: - certs_volume:/cockroach/cockroach-certs:ro # hostname: node2 networks: cockroach_cluster_network: aliases: - node2 entrypoint: >- bash -c " sleep 3 && cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=/cockroach/cockroach-data \ --advertise-addr=node2 \ --join=node1 \ --join=node2 \ --join=node3 " cockroach_node3: image: cockroachdb/cockroach:v21.1.7 depends_on: - cockroach_seed_node volumes: - certs_volume:/cockroach/cockroach-certs:ro # hostname: node3 networks: cockroach_cluster_network: aliases: - node3 entrypoint: >- bash -c " sleep 3 && cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=/cockroach/cockroach-data \ --advertise-addr=node3 \ --join=node1 \ --join=node2 \ --join=node3 " networks: cockroach_cluster_network: driver: bridge volumes: certs_volume: {}
証明書は、ボリュームで共有するようにします。
volumes: certs_volume: {}
最初に、証明書を作るノードを起動します。このノードだけが、証明書を共有するためのボリュームに書き込みます。
cockroach_seed_node: image: cockroachdb/cockroach:v21.1.7 ports: - "26257:26257" - "8080:8080" volumes: - certs_volume:/cockroach/cockroach-certs:rw # hostname: node1 networks: cockroach_cluster_network: aliases: - node1 entrypoint: >- bash -c " test -d /cockroach/cockroach-certs/certs || mkdir /cockroach/cockroach-certs/certs && test -d /cockroach/cockroach-certs/ca || mkdir /cockroach/cockroach-certs/ca && rm -rf /cockroach/cockroach-certs/certs/* /cockroach/cockroach-certs/ca/* && cockroach cert create-ca \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach cert create-node \ localhost node1 node2 node3 \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach cert create-client \ root \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach cert create-client \ myuser \ --certs-dir=/cockroach/cockroach-certs/certs \ --ca-key=/cockroach/cockroach-certs/ca/ca.key \ && cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=/cockroach/cockroach-data \ --advertise-addr=node1 \ --join=node1 \ --join=node2 \ --join=node3 "
ENTRYPOINT
で、証明書を作成してcockroach start
まで行います…。
証明書は、毎回作成し直しています(残っていたら、削除します)。
CockroachDBのデータは、コンテナ内にしか持っていません。ホスト側に残したい場合は、ホスト側のディレクトリを
ボリュームとしてマウントするようにしてください。
ノード数やノード名、ユーザー名も固定なので、こちらもお好みで…。
ノードのホスト名は、networks
のエイリアスで指定しました。
networks: cockroach_cluster_network: aliases: - node1
この場合、CockroachDBノード間で通信する時に--advertise-addr
で明示的にホスト名を指定してあげないと
うまくいかないので注意してください。
cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=/cockroach/cockroach-data \ --advertise-addr=node1 \ --join=node1 \ --join=node2 \ --join=node3
なお、hostname
でホスト名を指定した場合は--advertise-addr
の指定は不要です…。
このノードに関しては、ホスト側からlocalhost
で接続できた方が良いかなと思い、ports
も指定しています。
ports: - "26257:26257" - "8080:8080"
create-node
の時にlocalhost
を含めているのは、ホスト側からの利用も意図しています。
その他のノードは、最初のノードが起動した後に証明書の作成を待ってからcockroach start
させます。
※3秒固定で待っているだけですが
cockroach_node2: image: cockroachdb/cockroach:v21.1.7 depends_on: - cockroach_seed_node volumes: - certs_volume:/cockroach/cockroach-certs:ro # hostname: node2 networks: cockroach_cluster_network: aliases: - node2 entrypoint: >- bash -c " sleep 3 && cockroach start \ --certs-dir=/cockroach/cockroach-certs/certs \ --store=/cockroach/cockroach-data \ --advertise-addr=node2 \ --join=node1 \ --join=node2 \ --join=node3 "
確認してみる
では、作成したDocker Composeの定義で動作確認してみます。
起動。
$ docker-compose up
起動したら、シードノードに入ってみましょう。
$ docker-compose exec cockroach_seed_node bash
クラスタを初期化します。
# cockroach init --certs-dir=/cockroach/cockroach-certs/certs --host=node1
次に、ユーザーを作成しましょう。
# cockroach sql --certs-dir=/cockroach/cockroach-certs/certs --host=node1
ユーザーを作成して、DB Consoleを操作できるようにadmin
権限も与えておきます。ここで作成するユーザーの名前は、
クライアント証明書を作った時のユーザー名と合わせるようにしてください。
root@node1:26257/defaultdb> create user myuser password 'password'; CREATE ROLE Time: 151ms total (execution 150ms / network 1ms) root@node1:26257/defaultdb> grant admin to myuser; GRANT Time: 200ms total (execution 200ms / network 0ms)
1度SQL Shellを抜けて
root@node1:26257/defaultdb> exit
作成したユーザーで接続。
# cockroach sql --certs-dir=/cockroach/cockroach-certs/certs --host=node1 --user=myuser # # Welcome to the CockroachDB SQL shell. # All statements must be terminated by a semicolon. # To exit, type: \q. # # Server version: CockroachDB CCL v21.1.7 (x86_64-unknown-linux-gnu, built 2021/08/09 17:55:28, go1.15.14) (same version as client) # Cluster ID: abe5df0f-9f5d-4d0c-a41b-34b6d3d1446d # # Enter \? for a brief introduction. # myuser@node1:26257/defaultdb>
セッショントークンを作って、Cluster APIも使ってみましょう。
CA証明書と、作成したユーザー名・パスワードでログインAPIを呼び出すと
# curl -d 'username=myuser&password=password' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --cacert /cockroach/cockroach-certs/certs/ca.crt \ https://node1:8080/api/v2/login/
セッショントークンが得られます。
{"session":"[セッショントークン]"}
あとは、こちらを使ってCluster APIにアクセス。
# curl -H 'X-Cockroach-API-Session: [セッショントークン]' \ --cacert /cockroach/cockroach-certs/certs/ca.crt \ https://node1:8080/api/v2/nodes/
最後に、DB Consoleにもアクセスしてみましょう。https://localhost:8080
で、自己署名証明書の警告を無視すれば繋がります。
なお、DB Consoleの証明書を作りたい場合は、パブリックCAの証明書を使ってくださいとのことで…。
Authentication / Using a public CA certificate to access the DB Console for a secure cluster
DB Consoleへのログインは、先ほど作成したユーザー名、パスワードでログイン可能です。
OKですね。
そういえば、クライアント証明書を使っていませんね。こちらは、JDBCなどで接続する際に別途利用しましょう。
まとめ
簡単な内容ですが、CockroachDBでセキュアなクラスタをDocker Composeで作ってみました。
このまま実際の環境で使えるようなものではないですが、CockroachDBの勉強用としては良いかなと思います。
今後はこれを使って進めていきましょう。