mysql
や mysqldump
コマンドを使うとき、パスワードをどう入力すればよいのか悩みました。それぞれの問題点などを挙げつつ、いくつか方法をまとめておきます。
前提条件
$ mysql --version
mysql Ver 9.1.0 for Linux on aarch64 (MySQL Community Server - GPL)
私の環境では Docker Compose で下記のMySQLコンテナを立ち上げ、そこに接続しています。特に断りがなければ docker exec -it my-database
を省略して記述します。
services:
mysql:
image: mysql:9.1
container_name: docker-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: my-database
MYSQL_USER: user
MYSQL_PASSWORD: password
今回は最新版の MySQL 9.1 で検証していますが、8.0 でも同一の挙動を確認しています。
方法1: パスワードをコマンドラインに入力
$ mysql -u user -ppassword
最も簡単でわかりやすくスクリプトとしても実行しやすい方法です。
無論、パスワードが直接見えており安全ではありません。実行時に MySQL からも警告されます。
mysql: [Warning] Using a password on the command line interface can be insecure.
方法2: インタラクティブにパスワード入力
$ mysql -u user -p
Enter password:
-p
または --password
だけを指定するとパスワード入力が求められます。手入力を行う際はこの方法が最も使われていると思われます。コマンドライン上にパスワードは入力されていないので警告も受けません。
しかしこの方法では mysqldump
の結果をパイプで出力するのには使えません。何故なら、パスワード入力を促す Enter password:
が標準出力で表示されるためです。しかも、仮にパイプで繋いでしまうと Enter password:
でさえもパイプに吸い込まれてしまうので Ctrl+D を押すまでこのプロンプトも表示されません。
$ mysqldump -u user -p my-database | cat
Enter password:
-- MySQL dump 10.13 Distrib 9.1.0, for Linux (aarch64)
--
-- Host: localhost Database: my-database
-- ------------------------------------------------------
-- Server version 9.1.0
(snip)
方法3: 環境変数にパスワードを入れる
$ MYSQL_PWD=password mysql -u user
環境変数 MYSQL_PWD
にパスワードを入れると mysql
コマンド上ではパスワードを入力せずに済みますが、この方法は 2019年の時点ですでに非推奨かつ廃止予定 とされています。
公式ドキュメントにおいても extremely insecure と記述されています。
使うべきではありません。
方法4: オプションファイルを使う
[client]
user=user
password=password
$ touch ~/.my.cnf
$ chmod 600 ~/.my.cnf
# 上記の内容を書き込む
$ nano ~/.my.cnf
$ mysql
(snip)
$ rm ~/.my.cnf
MySQLのオプションファイルを記述すると、コマンドラインで指定できる内容をファイルとして記述できます。どのファイルを読むかについては 公式ドキュメントに記載 があります。
当然ながら、オプションファイルにはパスワードをそのまま書くのでファイル権限は限定しなければなりません。必要なくなったらすぐに削除すべきです。
ところで、パスワードに特殊な文字が含まれているとオプションファイルとして正しく認識されないことがあります。その場合は 引用符で囲みましょう。
方法5: オプションファイルを明示的に指定する
$ mysql --defaults-extra-file=./my.cnf
--defaults-extra-file
を使うと、オプションファイルの場所を明示的に指定できます。
この方法の良いところは、例えば Docker Compose で MySQL コンテナを立ち上げた際、secretsとしてコンテナ外部のファイルをも参照できることです。
services:
mysql:
image: mysql:9.1
container_name: docker-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: my-database
MYSQL_USER: user
MYSQL_PASSWORD: password
secrets:
- mysql_option_file
secrets:
mysql_option_file:
file: mysql_secret.txt
[client]
user=user
password=password
$ docker exec -it docker-mysql mysql --defaults-extra-file=/run/secrets/mysql_option_file
docker-compose.yml に secrets
を指定すると、コンテナ側の /run/secrets/
以下にマウントされ、同期された状態になります。この方法の欠点として、/run/secrets/
以下にあるファイルの権限を変更できないことです。
$ docker exec docker-mysql chmod 600 /run/secrets/mysql_password
chmod: changing permissions of '/run/secrets/mysql_password': Read-only file system
方法6: 標準入力でオプションファイルを渡す
$ mysql --defaults-extra-file=/dev/stdin
オプションファイルを明示的に指定できるということは、デバイスファイルも指定できるということです。/dev/stdin
で標準入力を使えるので、コマンドラインに入力を残さず、標準出力に Enter password:
を出力させることもなく、一時的なオプションファイルを作る必要も、Docker であれば secrets の設定をする必要もありません。
この方法を使うと、スクリプトから mysqldump
コマンドを呼び出す際に極めて便利です。例えば Python から呼び出そうとすると、
import subprocess
command = [
'docker exec -i docker-mysql '
'mysqldump --defaults-extra-file=/dev/stdin my-database'
]
option_file = '[client]\nuser=user\npassword=password'
result = subprocess.run(command, input=option_file.encode(), shell=True,
capture_output=True)
print(result.stdout.decode())
とするだけで安全にコマンドを呼び出せます。
この方法の欠点は、シェルから直接実行するとオプションファイルとしてキーボード入力した内容がそのままエコーバックされ、標準出力にも出てきてしまうことです。
$ mysqldump --defaults-extra-file=/dev/stdin my-database | cat
# 以下の内容を手入力。^D は Ctrl+D
[client]⏎user=user⏎password=password⏎^D
# ここから下が標準出力で表示される内容
[client]
user=user
password=password
-- MySQL dump 10.13 Distrib 9.1.0, for Linux (aarch64)
--
-- Host: localhost Database: my-database
-- ------------------------------------------------------
-- Server version 9.1.0
(snip)
参考文献
- MySQLの公式ドキュメントです。ドキュメントでは mysql_config_editor ツール についても触れています。しかしこのツールの役割はあくまでも難読化なので、パスワードをどうやって安全に入力するかという話とは別問題です
- オプションファイルを読み込ませる方法が解説されています
- 標準入力を使うという発想はこちらから着想を得ました。ただしこちらは
echo
を使ってパイプを使って渡しているのでコマンドライン上には入力する形になっています