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
を用意する。 - 「MySQL サーバ」には「Apache+PHP サーバ」から のみ アクセス可能な追加のユーザー
app_user_additional
を用意する。
検証環境の用意
「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
- MYSQL_DATABASE=testdb
- MYSQL_USER=app_user
- MYSQL_PASSWORD=app_p@ssw0rd
volumes:
- "$PWD:/docker-entrypoint-initdb.d"
web-server:
image: php:7.3-apache
volumes:
- "$PWD:/var/www/html"
上記の YAML ファイルから Docker Compose を起動すれば、以下のユーザーで接続できます。
root
( testpass )app_user
( app_p@ssw0rd )
YAMLファイルはカレントディレクトリに配置します。
「MySQL サーバ」コンテナ内の /docker-entrypoint-initdb.d
ディレクトリにカレントディレクトリをマウントしていますが、
これはこのコンテナが /docker-entrypoint-initdb.d
ディレクトリ配下にある *.sh
や *.sql
を初回起動時に実行し、初期化する仕組みがあるためです。
この仕組を利用した初期化用スクリプトとして、 createdb.sql
も作成しておきます。
createdb.sql
create user 'app_user_additional'@'web-server' identified WITH mysql_native_password by 'app_p@ssw0rd';
grant all privileges on testdb.* to 'app_user_additional'@'web-server';
これで、先の root
、 app_user
に加えて、「Web サーバ」(ホスト名= web-server
)から以下のユーザーで接続できる想定でした。
app_user_additional
( app_p@ssw0rd )
コンテナ環境の起動
以下のコマンドでコンテナを 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 default-mysql-client -y
3 つのユーザー( root
/ app_user
/ app_user_additional
)で 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_additionalユーザー
$ mysql -h db-server -P 3306 -u app_user_additional -papp_p@ssw0rd testdb
app_user_additional
ユーザーではなぜか接続できない
結果は以下のとおりでした。
# root ===> 失敗
$ /var/www/html# mysql -h db-server -P 3306 -u root -ptestpass
ERROR 1045 (28000): Access denied for user 'root'@'192.168.0.3' (using password: YES)
# app_user ===> 成功!
$ /var/www/html# mysql -h db-server -P 3306 -u app_user -papp_p@ssw0rd testdb
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.7.40 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [testdb]> \q
Bye
# app_user_additional ===> 失敗
$ /var/www/html# mysql -h db-server -P 3306 -u app_user_additional -papp_p@ssw0rd testdb
ERROR 1045 (28000): Access denied for user 'app_user_additional'@'192.168.0.3' (using password: YES)
root
ユーザーで接続できなかったのは localhost
からしか接続できない設定となっているのでしょう。
app_user_additional
ユーザーで接続できなかったのはなぜでしょう?
原因は公式の MySQL コンテナの設定
今度は db-server コンテナから root
ユーザーで MySQL にログインし、あるグローバル変数の値を確認してみます。
# こちらのコマンドだと接続できる
$ docker-compose exec db-server mysql -u root -ptestpass
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 # ※※※←こちらの設定が悪さをしている。
ちなみにユーザーの一覧を確認してみましたが、 app_user_additional
ユーザーは存在していました。 また、 root
ユーザーはローカルからの接続しか許可されていませんでした。
mysql> select Host, User, Select_priv from mysql.user;
+------------+---------------------+-------------+
| Host | User | Select_priv |
+------------+---------------------+-------------+
| localhost | root | Y |
| localhost | mysql.session | N |
| localhost | mysql.sys | N |
| localhost | healthchecker | N |
| % | app_user | N |
| web-server | app_user_additional | N |
+------------+---------------------+-------------+
6 rows in set (0.01 sec)
接続元ホスト名指定をやめて、接続元 IP アドレス指定をすれば対応可能
結局、 createdb.sql
を書き換えて接続元ホスト名を指定している箇所を接続先 IP アドレスに変更してやれば問題が解消します。
create user 'app_user_additional'@'192.168.176.%' identified WITH mysql_native_password by 'app_p@ssw0rd'; -- IPアドレス指定
grant all privileges on testdb.* to 'app_user_additional'@'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 ユーザー作成時に接続元を細かく制御するのはバッドプラクティスなんでしょうか?
ディスカッション
コメント一覧
まだ、コメントがありません