この記事は、
softdeviceのみんなでゆるく記事を書く。 Advent Calendar 2020
の12/3分の記事として公開しています。
はじめに
以前、SSL証明書接続が必要な PostgresQL サーバーを docker-compose で立ち上げるという記事を書いたのですが、その環境を丸々先日作成した Rook + Ceph
& Postgres Operator
な環境に移行する必要が出てきました。
Postgres Operator
にはそういったケースにも対応できるコマンドが用意されており、簡単に移行が出来ましたので、ご紹介します。
前提
以前書いた記事を参照して、Rook + Ceph
と Postgres Operator
が完了しているとします。
証明書を用意
まずは、SSL証明書を作成します。
ここの手順は、丸々以前の記事と同じになります。
# サーバー証明書
mkdir cert && cd cert
openssl req -new -nodes -text -out ca.csr -keyout ca-key.pem -subj "/CN=certificate-authority"
openssl x509 -req -in ca.csr -text -extfile /etc/ssl/openssl.cnf -extensions v3_ca -signkey ca-key.pem -out ca-cert.pem
openssl req -new -nodes -text -out server.csr -keyout server-key.pem -subj "/CN=pg-server"
openssl x509 -req -in server.csr -text -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem
# クライアント証明書
openssl req -new -nodes -text -out client.csr -keyout client-key.pem -subj "/CN=postgres"
openssl x509 -req -days 36500 -in client.csr -text -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem
# 証明書一覧
ls
ca-cert.pem ca.csr client-cert.pem client-key.pem server.csr
ca-cert.srl ca-key.pem client.csr server-cert.pem server-key.pem
元記事には証明書を100年有効にする方法も記載していますので、必要であればご参照下さい。
証明書をシークレットとして登録
作成した証明書を kubernetes
内で使用するためには、一度シークレットとして登録する必要があります。
CA証明書を pg-ca
として、
kubectl create secret generic pg-ca \
--from-file=ca.crt=ca-crt.pem \
-n pgo
TLS証明書を pg-tls-keypair
として登録します。
kubectl create secret tls pg-tls-keypair \
--cert=server-cert.pem\
--key=server-key.pem \
-n pgo
Postgres Operator
で使用するため、 pgo
名前空間に作成します。
PostgresQL クラスタを立ち上げ
pgo
コマンドで新しいクラスタを立ち上げますが、pgo
コマンドにはTLS用のオプションが設けられているので、そのオプションを使用してクラスタを作成します。
(別ウィンドウで開いておく)
kubectl -n pgo port-forward svc/postgres-operator 9443:8443
pgo create cluster pg-with-ssl \
--tls-only \
--server-ca-secret=pg-tls-keypair \
--server-tls-secret=pg-ca
created cluster: pg-with-ssl
workflow id: 23116517-08fd-404f-a190-e390ab235545
database name: pg-with-ssl
users:
username: testuser password: )wDfe(jH5yqFwtA]XlGZIc?a
作成完了するまで待ちます。
pgo test pg-with-ssl
cluster : pg-with-ssl
Services
primary (10.106.120.45:5432): UP
Instances
primary (pg-with-ssl-54f6cf6df9-xffmj): UP
作成が完了したら、外部からアクセスできるように設定を変更します。
kubectl edit svc pg-with-ssl -n pgo
(省略)
spec:
clusterIP: 10.102.60.6
ports:
- name: sshd
port: 2022
protocol: TCP
targetPort: 2022
- name: postgres
nodePort: 30001 # <- 追加
port: 5432
protocol: TCP
targetPort: 5432
selector:
pg-cluster: test
role: master
sessionAffinity: None
type: NodePort # <- ClusterIP から変更
nodePortは任意ですが、30000~32767である必要があります。
証明書認証を強制
ここまでの状態で接続すると…
docker run --rm -it postgres psql postgres -h <ノードのIP> -U testuser --password -p 30001
psql (13.1 (Debian 13.1-1.pgdg100+1), server 12.4)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=>
パスワードで接続できてしまいます。
それも当然で、ここまでの設定では内容がSSL化されているだけで、認証に関しては何も設定していない状態です。
認証方式を編集するには、通常の PotgresQL
と同様に pg_hba
を編集する必要があります。 Postgres Operator
では、 Kubernetes
の ConfigMap
として保存されているので、そこを編集します。
kubectl edit cm pg-with-ssl-pgha-config -n pgo
(省略)
pg_hba:
- local all postgres peer
- local all crunchyadm peer
- hostssl replication primaryuser 0.0.0.0/0 md5
- hostssl all primaryuser 0.0.0.0/0 reject
- hostssl all all 0.0.0.0/0 cert # <- md5から変更
変更を保存してエディタを終了すると書き込まれますが、これを反映するにはクラスタの再起動が必要です。
pgo restart pg-with-ssl-pgha-config -n pgo
再度接続テストをします。
docker run --rm -it postgres psql postgres -h <ノードのIP> -U testuser --password -p 30001
psql: error: FATAL: connection requires a valid client certificate
FATAL: no pg_hba.conf entry for host "192.168.197.212", user "testuser", database "postgres", SSL off
証明書が無いので接続拒否されました。想定通りの挙動です。
証明書接続の環境用意
SSL証明書認証を使った接続テストをするのですが、ポータビリティを高めたいので、docker-compose
を使用したいと思います。
Dockerfile
を使用せずに接続出来たらスマートだったのですが、証明書ファイルの権限が 600
である必要があるため、イメージを作る事にしました。
以下の構成のディレクトリを作成します。
- postgresql
- postgresql.crt
- postgresql.key
- root.crt
- docker-compose.yaml
- Dockerfile
- postgres.env
それぞれの中身は以下の通りです。
postgresqlフォルダ
「証明書を用意」 のセクションで作ったものを、以下のようにリネームして配置します。
src | dst |
---|---|
client-cert.pem | postgresql.crt |
client-key.pem | postgresql.key |
ca-cert.pem | root.crt |
docker-compose.yaml
version: "3.5"
services:
postgres:
build: .
ports:
- 5432:5432
env_file:
- postgres.env
tty: true
user: root
Dockerfile
FROM postgres:12.2-alpine
ADD postgresql /root/.postgresql
RUN chmod -R 600 /root/.postgresql
postgres.env
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
証明書接続のテスト
ファイルの用意が出来たら、 docker-compose
でバックグラウンド起動します。
docker-compose up -d
この状態で以下のコマンドを使用すると、先程作成したクラスタに接続できるはずです。
docker-compose exec postgres psql "sslmode=require host=<ノードのIP> port=30001 dbname=postgres user=postgres"
psql (12.2, server 12.4)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=#
おわりに
終わって振り返ってみると、おおまかな作業手順は普通に PostgresQL
をSSL接続 & 証明書認証化する時とほぼ同じでした。pgo
コマンドがTLS接続の設定に対応していてくれた事も大きいですね。
同じような実装が必要になる要件は少ないように思いますが、何かの参考になれば幸いです。