📀

Postgres公式Dockerイメージのパスワードの扱いについて

2021/12/23に公開

気になったこと

こちらのPostgres公式のDockerイメージを使って色々と実験してたところ、コンテナ内からパスワードを指定せずpsqlで接続できたことが気になったので調べてみた

詳細

  1. Dockerイメージをpullしてくる
docker pull postgres
  1. Dockerコンテナを立ち上げる
docker run -p 5432:5432 --name some-postgres -e POSTGRES_PASSWORD=hogefuga postgres
  1. Dockerコンテナに接続する
docker exec -it some-postgres bash
  1. コンテナ内からPostgreSQLに接続する
psql -U postgres

あれ、パスワード要らないの?

Postgresイメージのコンテナを立ち上げる際に POSTGRES_PASSWORD=hogefuga って指定したけど、このパスワード使わないの?

公式イメージのドキュメントをちゃんと読んでみる

POSTGRES_PASSWORDは必須項目

The PostgreSQL image uses several environment variables which are easy to miss. The only variable required is POSTGRES_PASSWORD, the rest are optional.

POSTGRES_PASSWORDのみ必須のENVらしい。確かに指定せずコンテナをrunしようとすると、こちらのコードに起因するエラーが出る

Error: Database is uninitialized and superuser password is not specified.
       You must specify POSTGRES_PASSWORD to a non-empty value for the
       superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".

当該コードを読むと

if [ -z "$POSTGRES_PASSWORD" ] && [ 'trust' != "$POSTGRES_HOST_AUTH_METHOD" ]; then

と、POSTGRES_HOST_AUTH_METHODがtrustならパスワードを設定しなくても済むようだ。この値については後述

localhostに対してはtrust認証方式が適用される

Note 1: The PostgreSQL image sets up trust authentication locally so you may notice a password is not required when connecting from localhost (inside the same container). However, a password will be required if connecting from a different host/container.

ここで核心について触れていた。localhost(同じコンテナ内)からの接続ならパスワードは必要ないけど、他のホストやコンテナからの接続なら求めるから心配しないでね、とのこと。とはいえ心配なのでコンテナ外からの接続を試してみる。

コンテナ外からアクセスするためにはポートフォワーディングを設定し直す必要があるので、一度コンテナを削除してから、-p 5432:5432を追加した以下のコマンドで作り直す

docker run -p 5432:5432 --name some-postgres -e POSTGRES_PASSWORD=hogefuga postgres 

コンテナが立ち上がったら以下のコマンドでホストマシンからコンテナ内のPostgreSQLに接続してみる

psql -d postgres -h localhost -p 5432 -U postgres
Password for user postgres: 

ちゃんとパスワードが求められた

認証方式をどこで設定できるのか

上記の設定はどこに記述されているの?と調べたところpg_hba.confが怪しいので、Postgresコンテナの中の設定ファイルを覗いてみる

cat var/lib/postgresql/data/pg_hba.conf
pg_hba.conf
# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust

host all all all scram-sha-256

psqlはデフォルトでunixドメインソケットを使って接続するため、psqlで接続を試みると1列目にlocalと記載された設定レコードが読み込まれ、4列目のtrustによりパスワード不要と判断するらしい。

trustじゃなくてrejectにしてみる

localのレコードをtrustではなくrejectに変えてみれば、同じコンテナ内からの接続を拒否するはず。pg_hba.confを編集する

pg_hba.conf
local   all             all                                     reject

confは即座に反映される訳では無いので、一度psqlで接続して

psql -U postgres

以下のコマンドを実行してPostgreSQLサーバに設定ファイルの読み込みを指示する。もしくはコンテナを立ち上げ直しても良いと思う。

select pg_reload_conf();

もう一度psqlで接続を試みたところ、ちゃんと弾かれた

psql -U postgres
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  pg_hba.conf rejects connection for host "[local]", user "postgres", database "postgres", no encryption

rejectじゃなくてmd5にしてみる

認証方式がrejectだと常に接続が弾かれてしまうので、パスワードを求める認証方式に変更してみる。ひとまずmd5

pg_hba.conf
local   all             all                                     md5

pg_reload_conf()を呼び出したいが、現在unixソケットを使った接続はrejectされているので、-hオプションでlocalhostを指定してTCP/IP接続に切り替える

psql -h localhost -U postgres

設定ファイルを再度読み込ませる

select pg_reload_conf();

unixソケットを使って接続を試みると、ちゃんとパスワードが求められた

psql -U postgres
Password for user postgres: 

passwordとmd5に関する注意点

ちなみにpasswordも認証方式として設定できるが平文状態でパスワードが扱われてしまうため推奨されていないし、ついでにmd5もハッシュ衝突攻撃に対して比較的弱いためmd5ではなくscram-sha-256を設定した方が良い。

ホストマシンからの接続をtrustする

Dockerコンテナから見てホストマシンのIPは標準ネットワーク設定の場合172.17.0.1なので、こちらをpg_hba.confに追加してみる

pg_hba.conf
host   all             all                172.17.0.1/32            trust

再度読み込ませた後にホストマシンから接続を試みるとパスワードを求められることなく接続できた。開発中はこの設定にしておいた方がコンテナに入る手間が省けて楽

POSTGRES_HOST_AUTH_METHODについて

この辺を読んでみた限りPOSTGRES_HOST_AUTH_METHODが指定されていると以下の1行がconfに追加される

pg_hba.conf
host all all trust

これだとあらゆるホストからの接続をパスワードなしで許可することになるのでローカル環境での開発フェーズぐらいでしか使い道はなさそう。確かにあらゆるホストからの接続を許可するのであればパスワードの設定は意味をなさないので、以下のコードの通りPOSTGRES_PASSWORDが設定されていなくても問題ない。

if [ -z "$POSTGRES_PASSWORD" ] && [ 'trust' != "$POSTGRES_HOST_AUTH_METHOD" ]; then

まとめ

  • PostgreSQLの公式イメージではPOSTGRES_PASSWORDをENVに指定しなければいけない
  • 同じコンテナ内からの接続にはパスワードは求められない
  • それはpg_hba.conf内の認証方式がtrustに設定されているため
  • 同コンテナ以外(例えばホストマシン)からの接続には上記で設定したパスワードが求められる
  • 設定を変えたければpg_hba.confをいじって、PostgreSQLサーバに設定を読み込ませるプロシージャを呼び出すか、コンテナを再起動すれば良い
PrAha

Discussion