0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rook + Ceph & Postgres Operator で、証明書認証しか許可しない PostgresQL インスタンスを立ち上げる

Posted at

この記事は、
softdeviceのみんなでゆるく記事を書く。 Advent Calendar 2020
の12/3分の記事として公開しています。

はじめに

以前、SSL証明書接続が必要な PostgresQL サーバーを docker-compose で立ち上げるという記事を書いたのですが、その環境を丸々先日作成した Rook + Ceph & Postgres Operator な環境に移行する必要が出てきました。

Postgres Operator にはそういったケースにも対応できるコマンドが用意されており、簡単に移行が出来ましたので、ご紹介します。

前提

以前書いた記事を参照して、Rook + CephPostgres 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 では、 KubernetesConfigMap として保存されているので、そこを編集します。

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接続の設定に対応していてくれた事も大きいですね。

同じような実装が必要になる要件は少ないように思いますが、何かの参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?