PostgreSQL 12: TLS 接続
Tags: postgres, database, security, libressl
Published: 2023-05-05 22:55:32

## はじめに

外部にある [PostgreSQL](http://postgresql.org/) サーバーをデータベース・サーバーとして使用する場合、TLS/SSL 接続を使用するのが良いでしょう。
本記事では自己証明書を用いて実現する簡便な方法をご紹介します。

### 環境

- [OpenBSD](https://www.openbsd.org) 6.7
- PostgreSQL 12.2

### 参考

- "ssl-tcp" に関する公式ドキュメント (英語): [12](https://www.postgresql.org/docs/12/ssl-tcp.html), [current](https://www.postgresql.org/docs/current/ssl-tcp.html)

---

## 概要

手順は 3 つだけです。

1. サーバー証明書を準備する
    - 自己証明書の生成
2. サーバー設定をファイル編集によって行う
    - postgresql.conf : listen_address / ssl オプションの編集
    - pg_hba.conf : hostssl 定義の追加。さらにクライアントが正当な証明書を所持していない場合は "verify-ca=0" を auth-options (認証オプション) に含める
3. クライアントマシンから検証する
    - `$ psql "sslmode=require host=$DB_HOST user=$DB_USER dbname=$DB_NAME"`

---

## チュートリアル

### 1. サーバー証明書を準備する

_postgresql ユーザーで PostgreSQL の data ディレクトリにサーバー証明書を生成します。

```console
$ doas su _postgresql
$ cd /var/postgresql/data
```

[openssl](https://man.openbsd.org/openssl.1) コマンドを使用します。

```console
$ # 自己証明書を作成
$ openssl req -new -x509 -days 36500 -nodes -text -out server.crt -keyout server.key -subj "/CN=$DB_HOST_DOMAIN"
$ chmod 400 server.key
```

"36500" で 36500 日 = 約 100 年を有効期間として指定しています。これはもちろん変更可能です。

---

補足ですが, もしも v3_ca エクステンションを使うために /etc/ssl/openssl.cnf を編集して問題無いのであれば、クライアントから妥当なものと認識してもらえるサーバー証明書を作成できます。ルートと中間証明書を作成します。編集内容は [公式ドキュメント (英語)](https://www.postgresql.org/docs/current/ssl-tcp.html#SSL-CLIENT-CERTIFICATES) に書かれています:

```console
$ # create a certificate signing request (CSR) and a public/private key file
$ openssl req -new -nodes -text -out root.csr -keyout root.key -subj "/CN=$ROOT_CA_DOMAIN"
$ chmod 400 root.key

$ # create a root certificate authority
$ openssl x509 -req -in root.csr -text -days 36500 -extfile /etc/ssl/openssl.cnf -extensions v3_ca -signkey root.key -out root.crt

$ # create a server certificate signed by the new root certificate authority
$ openssl req -new -nodes -text -out server.csr -keyout server.key -subj "/CN=$DB_HOST_DOMAIN"
$ chmod 400 server.key

$ openssl x509 -req -in server.csr -text -days 36500 -CA root.crt -CAkey root.key -CAcreateserial -out server.crt
```

ここで v3_ca エクステンションに起因するエラーが発生するかもしれません。その場合は `/etc/ssl/openssl.cnf` を編集すれば良いです。[こちらの記事](https://obsd.solutions/ja/blog/2023/05/05/libressl-error-due-to-missing-v3_ca-in-extension/index.html) を参考にしてください。

---

上記のコマンド群を実行すると、いくつかのファイルが生成されます。server.crt と server.key が重要です。

```console
$ ls {root,server}*
root.crt   root.csr   root.key   root.srl   server.crt server.csr server.key
```

_postgresql ユーザーで /var/postgresql/data にいるままで次に進んでください。

### 2. サーバー設定をファイル編集によって行う

まず postgresql.conf を編集して外部のクライアントからのリクエストを受け付けるようにします。あわせて SSL 接続を有効にします:

```diff
  #listen_addresses = 'localhost'
  ...
+ listen_addresses = '*'
  ...
  #ssl = off
+ ssl = on
  #ssl_ca_file = ''
  #ssl_cert_file = 'server.crt'
  #ssl_crl_file = ''
  #ssl_key_file = 'server.key'
```

`ssl_ca_file` を root.crt あるいは何かとして作成済の場合、それを指定します。

次に pg_hba.conf を編集します。末尾に `hostssl` の行を追加しましょう。クライアントからの SSL 接続が許容されます:

```diff
+ hostssl all             all             0.0.0.0/0               md5 clientcert=0
```

`clientcert` は auth-options (認証オプション) です。クライアントが妥当な証明書を有していない場合、"0" をセットします。妥当なものを有している場合は、その代わりに "verify-ca" あるいは "verify-full" をセットします。(更新: 値 "0" のセットは 14 からはサポートされなくなりました。)

さて途中で何か問題が発生することがあれば、/var/postgresql/logfile が詳細を知るためにきっと役に立つでしょう。

すべて終えたら、_postgresql ユーザーとして振る舞うのをやめます。

```console
$ exit
```

最後にデータベース・サーバーを再起動します。

```console
$ doas rcctl restart postgresql
```

### 3. クライアントマシンから検証する

クライアント・マシンで、[`psql`](https://www.postgresql.org/docs/current/app-psql.html) を使います。この時、以下のように "sslmode=require" を指定します:

```console
$ psql "sslmode=require host=$DB_HOST user=$DB_USER dbname=$DB_NAME"
Password for user ...: 
```

DB ユーザーのパスワードを入力しましょう。

```console
psql (12.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

{$DB_NAME}=> 
```

TLS 接続ができました :)