Postgres公式Dockerイメージのパスワードの扱いについて
気になったこと
こちらのPostgres公式のDockerイメージを使って色々と実験してたところ、コンテナ内からパスワードを指定せずpsqlで接続できたことが気になったので調べてみた
詳細
- Dockerイメージをpullしてくる
docker pull postgres
- Dockerコンテナを立ち上げる
docker run -p 5432:5432 --name some-postgres -e POSTGRES_PASSWORD=hogefuga postgres
- Dockerコンテナに接続する
docker exec -it some-postgres bash
- コンテナ内から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
# "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を編集する
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
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に追加してみる
host all all 172.17.0.1/32 trust
再度読み込ませた後にホストマシンから接続を試みるとパスワードを求められることなく接続できた。開発中はこの設定にしておいた方がコンテナに入る手間が省けて楽
POSTGRES_HOST_AUTH_METHODについて
この辺を読んでみた限りPOSTGRES_HOST_AUTH_METHODが指定されていると以下の1行が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サーバに設定を読み込ませるプロシージャを呼び出すか、コンテナを再起動すれば良い
Discussion