Dockerの公式MySQLイメージでは接続元ホスト名によるアクセス制御ができない( `skip-name-resolve`)
はじめに
MySQLでは、接続元ホスト名ごとに「接続できる」「接続できない」の制御が可能ですが、設定方法を正しく把握していなかったのでDocker環境を使って検証したところハマってしまいました。
検証環境
$ docker --version
Docker version 18.09.2, build 6247962
$ docker-compose --version
docker-compose version 1.23.2, build 1110ad01
環境
「Docker」と「Docker Compose」を使って以下のような環境を構築し検証を行いたいと思いました。
- サーバは2台。
- 「MySQLサーバ」である
db-server
- 「Apache+PHPサーバ」である
web-server
- 「MySQLサーバ」である
- 「MySQLサーバ」には「Apache+PHPサーバ」からのみアクセス可能なユーザ
app_user
を用意する。
検証環境の用意
「Webサーバ」用のDockerコンテナ、「MySQLサーバ」用のDockerコンテナを立ち上げるために、「Docker Compose」を利用しました。
docker-compose.yml
の設定は以下のようになりました。
docker-compose.yml
version: "2"
services:
db-server:
image: mysql:latest
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_ROOT_PASSWORD=testpass
volumes:
- "$PWD:/docker-entrypoint-initdb.d"
web-server:
image: php:7.3-apache
volumes:
- "$PWD:/var/www/html"
カレントディレクトリに配置します。
「MySQLサーバ」コンテナ内の /docker-entrypoint-initdb.d
ディレクトリにカレントディレクトリをマウントしていますが、
これはこのコンテナが /docker-entrypoint-initdb.d
ディレクトリ配下にある *.sh
や *.sql
を初回起動時に実行し、初期化する特性があるためです。
初期化のようスクリプトとして、 createdb.sql
も作成しておきます。
createdb.sql
drop database if exists testdb;
create database testdb default character set utf8;
drop user if exists 'app_user'@'web-server';
create user 'app_user'@'web-server' identified WITH mysql_native_password by 'app_p@ssw0rd';
grant all privileges on testdb.* to 'app_user'@'web-server';
これで「Webサーバ」(ホスト名= web-server
)から app_user
というユーザでの接続が可能となる想定です。
コンテナ環境の起動
以下のコマンドでコンテナをdeamon起動します。
# 起動
$ docker-compose up -d
# コンテナログを監視し、DBが完全に起動し終わるまで待つ
$ docker-compose logs -f
「Webサーバ」( web-server
)からの接続
「Webサーバ」コンテナに接続して db-server
ホスト名を利用した接続を試してみます。
# コンテナ内に接続
$ docker-compose exec web-server bash
mysql
コマンドで接続したいのですが、この環境には mysql
コマンドが存在しないためインストールします。
$ apt-get update -y && apt-get install mysql-client -y
2つのユーザ( root
/ app_user
)でMySQLにログインを試みてみます。
両ユーザともログインができる想定でした。
# rootユーザ
$ mysql -h db-server -P 3306 -u root -ptestpass
# app_userユーザ
$ mysql -h db-server -P 3306 -u app_user -papp_p@ssw0rd testdb
app_user
ユーザではなぜか接続できない
接続できたのは root
ユーザのみ。 app_user
ユーザはログインに失敗してしまいます。
原因は公式のMySQLコンテナの設定
root
ユーザでMySQLにログインし、あるグローバル変数の値を確認してみます。
mysql> show global variables like '%skip%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| skip_external_locking | ON |
| skip_name_resolve | ON |
| skip_networking | OFF |
| skip_show_database | OFF |
| slave_skip_errors | OFF |
| sql_slave_skip_counter | 0 |
+------------------------+-------+
6 rows in set (0.02 sec)
上から2つ目の skip_name_resolve
グローバル変数の値が ON
となっています。
これはユーザのアクセス制御を行う際にホスト名が利用できないことを表しています。
設定を変更するには、MySQLコンテナの /etc/mysql/conf.d/docker.cnf
を変更しないといけません。
つまり一度コンテナをビルドし直す必要があります。
# コンテナ内に接続
$ docker-compose exec db-server bash
# 現在の設定内容を確認
root@d711c2a0404b:/# cat /etc/mysql/conf.d/docker.cnf
[mysqld]
skip-host-cache
skip-name-resolve # ※※※←こちらの設定が悪さをしている。
接続元ホスト名指定をやめて、接続元IPアドレス指定をすれば対応可能
結局、 createdb.sql
を書き換えて接続元ホスト名を指定している箇所を接続先IPアドレスに変更してやれば問題が解消します。
drop database if exists testdb;
create database testdb default character set utf8;
drop user if exists 'app_user'@'192.168.176.%'; -- IPアドレス指定
create user 'app_user'@'192.168.176.%' identified WITH mysql_native_password by 'app_p@ssw0rd';
-- IPアドレス指定
grant all privileges on testdb.* to 'app_user'@'192.168.176.%'; -- IPアドレス指定
※ホストIPアドレスやサブネットIPアドレスを調べるには、 docker inspect
コマンドでコンテナの詳細情報を確認します。
# WebサーバのコンテナIDを特定する(9dfb145b27cc)
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d711c2a0404b mysql:latest "docker-entrypoint.s…" 26 minutes ago Up 26 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp haldata-security-design_db-server_1
9dfb145b27cc php:7.3-apache "docker-php-entrypoi…" 26 minutes ago Up 26 minutes 80/tcp haldata-security-design_web-server_1
b43b4bd655ac mysql:latest "docker-entrypoint.s…" 7 hours ago Up 7 hours 3306/tcp, 33060/tcp relaxed_diffie
# GatewayとIPAddressからサブネットIPは "192.168.176.0" であるとわかる
$ docker inspect 9dfb145 | jq '.[].NetworkSettings.Networks'
{
"haldata-security-design_default": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"9dfb145b27cc",
"web-server"
],
"NetworkID": "91e47977a22feff67f1bf9f5d3ab891f3913cdbf78dbcb32f88ad5f66ea3c7c8",
"EndpointID": "73cb4b511ea9577b695f80d8e4a271f561dbcd43f3427837525b3f249b87fe6d",
"Gateway": "192.168.176.1",
"IPAddress": "192.168.176.3",
"IPPrefixLen": 20,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:c0:a8:b0:03",
"DriverOpts": null
}
}
ひとこと
Docker Composeで構成されたコンテナ郡以外からDBサーバコンテナに接続市に来るケースは殆ど無いでしょうし、MySQLユーザ作成時に接続元を細かく制御するのはバッドプラクティスなんでしょうか?
ディスカッション
コメント一覧
まだ、コメントがありません