出力を入力へ

プログラミングに関する自分が考えた事を中心にまとめます

Rails+Vuetify+ActiveAdminでボタンのスタイルが崩れる

Vuetifyにおけるスタイル崩れ

Rails6 + Vue + Vuetify でVuetify入門してた. Vuetifyのレイアウトでいろいろ試してみたところ, ハンバーガーメニューのスタイル崩れが発生した.

style collapsed huberger menu
スタイル崩れが発生したハンバーガーメニュー

Vuetify App Barsのナビゲーションアイコンに 意図しないグレーの円が表示されている. また,ハンバーガーメニュー以外にもナビゲーションメニューのうち ラベルのEDITボタンなども表示崩れが発生している.

意図しないスタイル反映の解除

スタイル崩れの共通点として,いずれもボタンタグなので ボタンタグに関するCSSがおかしいのだろうということはわかったが, その原因をすぐに見付けることができなかった.

Rails,特にCSSなどのフロントエンドは詳しくない上に Vuetifyもよく理解していなかったので(VuetifyのGetting Startedはあまり親切ではないと思う) その設定等がおかしいのかと思って試行錯誤していた.

結果, app/assets/stylesheets/application.css の反映の有無により スタイル崩れが生じることがわかった. これは同ディレクトリに active_admin.scss があり, application.cssがこのファイルを読み込んでいるため ActiveAdmin以外のページにもActiveAdminのスタイルが適用されていた.

...
  *
  *= require_tree .
  *= require_self
  */

このため, require_tree . を削除することで解決した.

require_treeの設定

この require_tree . でいろんなスタイルが反映されて表示崩れが発生するのは Railsあるあるな挙動らしい.

app/assets/stylesheets/ にscssファイルを生成すれば (generatorでビューを作成すれば勝手に作成される) 自動的に application.cssが読み込んでくれる.

逆にページごとに異なるCSSを適用したいケースがある場合は この require_tree . の記述を削除し, 共通ファイルは個別に明示的に指定した上で 各ページのビューファイル (*.html.erbなど)にてstylesheet_link_tag で個別に読み込むCSSを指定すればよい.

なるほど?

GitHub Secretsの更新をgithub-secrets-writerで自動化する

GitHub Actionsでクレデンシャル情報を利用するためにGitHub Secretsを利用しているわけだけど, この値の設定や更新を自動化したい.

具体的には AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY の更新を自動化したい. また,現状は単一のbotユーザの権限で,複数リポジトリに同じアクセスキーを設定しているので アクセスキーを更新したら複数リポジトリをまとめて更新したい.

リポジトリが少ないうちは手動対応でもよかったけれど リポジトリ数が増えるにつれて設定忘れや更新もれが発生しそうなこと, そもそも面倒なので自動化する.

自動更新の仕組み

おおまかな手順としては以下の通り

  1. アクセスキーを更新してダウンロード
  2. cliツールを利用して特定リポジトリのsecretsを更新
  3. すべてのリポジトリに対して 2 を繰り返す

cliによるsecretesの更新

secretsの更新に githubの公式クライアントツールの cli を利用したい. ところが,cliはsecretsの更新APIには対応しておらず,要望は上がっているが まだすぐに実装されそうというものではない.

その代替としては, github-secrets-writer というのがあるのでこれを利用する. その名前の通り,github secretsの更新に特化したcliツールである.

github-secrets-writerを利用する

手順としてはREADMEにある通り.

GITHUB_TOKENはGitHub公式ヘルプなどを参考に作成する. 権限について,パブリックリポジトリのsecretesのみを更新するのであれば public_repo だけで, プライベートリポジトリのsecretsも更新するのであれば repo のフルコントロールが必要になる.

あとは更新するリポジトリとsecrets名および値を指定して実行するだけ. key=value 形式で設定値が記載されているファイルがあれば --from-file で簡単に指定できるが, 残念ながらAWSアクセスキーはcsv形式なので--from-fileで簡単に利用できる形式ではないので 諦めて --from-literal で直接指定して実行する.

出力が少しわかりにくく, secretName1: 204 No Content のように表示されていれば上手く更新されている. 新しくsecretsの項目を作った場合は secretName1: 201 Created のようになる. 一方で,権限不足等であれば以下のように表示される.

secretname3: GET https://api.github.com/repos/thaim/samplerepo/actions/secrets/public-key: 404 Not Found []
ERROR: encountered some failures, see above

github-secrets-writerによる自動更新を自動化する

アクセスキーの更新は90日で更新してねという推奨があるので, アクセスキーの発行や上記cliの操作自体も自動化したい.

アクセスキーを発行するAPI自体は存在するので lambdaでアクセスキーを発行してそのままsecretsを更新するような処理を実装して CloudWatchで定期的に呼び出してあげればよい.

が,そこそこ面倒なので止めた. まずは手元で自動化できればよさそう.

Organizationレベルで共通のSecrets

ここまで手順を整理したところで, GitHubが最近 SecretsをOrganizations レベルで共有できる Organizations Secretsを発表している ことに気が付いた.

これを設定・利用すればそもそも,リポジトリ毎のSecretsを更新してまわるなんて不要になる.

wheneverのカスタムjob_typeで月末バッチを実現する

cronジョブをRubyで書くためのgem wheneverで 月末バッチを実現するためのカスタムスクリプトを実装したい. このとき,wheneverのカスタムjob_typeで実現したのでそのまとめ.

月末バッチの実装

cronで月末バッチをスケジュールする場合,月末の判定が面倒. 月末となる日は30日や31日,もしくは28日や29日(2月のうるう年)のような条件があるので, 日付を決め打ちにすることができない. cron書式とは別に月末判定を実施して処理する仕組みが必要である.

よくある実装としてはcronで呼び出されるスクリプトの冒頭で, 翌日が1日か判定して1日でなければ処理を終了する方法である. 例えば以下のような方法で翌日の日付を判定することで月末かを確認できる.

if [ `date - d tomorrow "+%d"` == "01" ]; then
  run_batch.sh
fi

cronだったら一行で実現したいので以下みたいになる

0 0 * * * "[ `date - d tomorrow \"+\%d\"` == \"01\" ] && run_batch.sh"

cronにおける注意点として,ダブルクオテーションに対するエスケープはもちろん, % に対するエスケープ処理が必要な点にある. 詳細はman 5 crontab を参照.

wheneverにおける実装

ではRubyでcron書式を実装する wheneverではどのように実装するか. wheneverでは以下のように記載する.

every '0 0 27-31 * *' do
  rake "app_server:task"
end

これを bundle exec whenever でcron書式に変換すると以下のように変換される.

0 0 27-31 * * /bin/bash -l -c 'cd /home/thaim/work && RAILS_ENV=production bundle exec rake app_server:task --silent'

これは wheneverの仕組みによるもので, パスの変更やRAILS_ENVの指定などをユーザが考慮する必要がなくなる. 一方で,今回のような月末判定ロジックを組込むにはwheneverの仕組みと重複するので難しい.

対策として,独自の変換ロジックとして job_typeをカスタマイズする方法である. 上記 wheneverのrakeはwheneverが実装するjob_typeによる変換方式が適用されたものである. これを月末判定ありのjob_typeを定義してあげればよい. 例えば以下のような rake_lastday job_typeを定義する.

job_type :rake_lastday, "[ `date -d tomorrow \"+%d\"` == \"01\" ] && cd :path && :environment_variable=:environment bundle exec rake :task --silent :output"

これを利用すると 月末判定付きのcronが生成されるようになる.

every '0 0 27-31 * *' do
  rake_lastday "app_server:task"
end

という記述が以下のように変換される

0 0 27-31 * * /bin/bash -l -c ''[ `date -d tomorrow "+\%d"` == "01" ] && cd /home/thaim/work && RAILS_ENV=production bundle exec rake app_server:task --silent'

補足: 拡張書式としてのL

ちなみに,cron書式にて日付に 'L’ を使えば月末日を自動で判定してくれるよ, みたいな記事がStackOverflowとか英語Wikipediaに記載がある. ただし,これは一般的なcronでは実装されていない,独自方言であり基本的には使えない.

例えば JavaのジョブスケジューラライブラリQuartzなんかには 拡張書式として月末日のLがサポートされている. ただし,一般的なcronでは扱われているような書式ではないので この書式がサポートされていることを期待しない方がよさそう.

Terraformでリソースをインポートしたり削除したり

TerraformでTerraform管理外の既存のリソースを管理下に入れる方法と外す方法. 管理下に入れるコマンドが import なら管理から外すのは export だろ,と思ったけどそんなコマンドはなかった.

リソースをインポートする

まずはインポートする方法から. Terraform importコマンドを利用することでリソースをTerraformの管理下に置く.

実行コマンドは terraform import [options] ADDRESS ID の書式の通り. ADDRESSは Resource Adressingにある通りで, tfファイルでリソースを指定するときに利用する書式. aws_s3_bucket.my_bucket_name とかそういうやつ.

IDはTerraformのドキュメントに記載されたIDで,リソースドキュメントの最後に記載されている. 例えばS3ならS3 bucketリソースに記載の通りバケット名. EC2インスタンスならインスタンスリソースに記載の通りインスタンスID.

このIDがリソースによってフォーマットが大きく異なり,基本的には上記の通りリソース名だが, SQSのようにIDがURLだと https://queue.amazonaws.com/80398EXAMPLE/MyQueue のように記載したり, ECSタスク定義のようにIDが存在しないとARNとして arn:aws:ecs:us-east-1:012345678910:task-definition/mytaskfamily:123 のように記載したりと たまに例外があるので注意が必要.

$ AWS_DEFAULT_REGION=ap-northeast-1 terraform import aws_s3_bucket.sample sample-bucket-name
aws_s3_bucket.sample: Importing from ID "sample-bucket-name"...
aws_s3_bucket.sample: Import prepared!
  Prepared aws_s3_bucket for import
  Prepared aws_s3_bucket_policy for import
aws_s3_bucket_policy.sample: Refreshing state... [id=sample-bucket-name]
aws_s3_bucket.sample: Refreshing state... [id=sample-bucket-name]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

リソースを除外する

importを取り消したいときなどの逆操作について. この操作がないと既存リソースを一度削除する必要が出てくるので リソースを除外するコマンドがないわけないだろうと思ったが Terraform Command一覧を見てもそれらしいものが無いので困っていた.

結論としては terraform state rm サブコマンドが目的の Terraformの管理下から除外するコマンドだった. サブコマンドが存在するのは terraform state コマンドと terraform workspace コマンドだけで,これに注意する必要があった.

実行コマンドは terraform state rm [options] ADDRESS... の書式通り.

$ AWS_DEFAULT_REGION=ap-northeast-1 terraform state rm aws_s3_bucket.sample
Removed aws_s3_bucket.sample
Successfully removed 1 resource instance(s).

Terraform backendがTerraform Cloudのような外部管理であってもリソースの追加/削除は可能なので いろんなところで利用することになりそう.

そして再度確認したら,きちんと 実践Terraformに記載されていた. あらためて見ると24章のリファクタリングは重要な操作なので再確認が必要.

Ubuntu on WSL2 でsnapを利用する

Ubuntu20.04 LTS と WSL2を試してみたところ snapコマンドが上手く利用できなかったのでそのまとめ

WSL2

もともとWSL2もInsider Previewを利用して試していたけど, とうとうGA版がリリースされたということで改めて試してみた.

WSL2の利用方法は Insider Previewの頃と変わらず. Insider Previewを利用せずともWSL2が利用できるようになった.

WSL2におけるsnapdとsystemd

Ubuntuの新しいパッケージ管理システムであるsnap. ところが WSL2上のUbuntuではsnapコマンドが実行できない.

$snap install hello-world
error: cannot communicate with server: Post http://localhost/v2/snaps/hello-world: dial unix /run/snapd.socket: connect: no such file or directory

原因は snapコマンドが systemdに依存しており,WSL2ではsystemdが起動していないこと. snapコマンドはsnapdと通信して動作するが,snapdはsystemdサービスとして動作するので systemdが動作していないとsnapdも動作せず,snapコマンドは正常に実行できない.

$ cat /etc/systemd/system/multi-user.target.wants/snapd.service
[Unit]
Description=Snap Daemon
Requires=snapd.socket
OnFailure=snapd.failure.service
# This is handled by snapd
# X-Snapd-Snap: do-not-start

[Service]
# Disabled because it breaks lxd
# (https://bugs.launchpad.net/snapd/+bug/1709536)
#Nice=-5
OOMScoreAdjust=-900
ExecStart=/usr/lib/snapd/snapd
EnvironmentFile=-/etc/environment
Restart=always
WatchdogSec=5m
Type=notify
SuccessExitStatus=42
RestartPreventExitStatus=42
KillMode=process

[Install]
WantedBy=multi-user.target

WSL2ではsystemdはinitシステムとして動作していない. WSL2ではWindowsとのを実現するために独自のセットアップを実行しており, Ubuntuなどで採用されているsystemdは利用されていない. このため,systemctlコマンドなどは動作しない.

$ systemctl status snapd
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

WSL2上でsystemdを実行する

無理矢理systemdを起動してあげることで対処する. この記事に従いsystemdを起動すると systemctlが利用できるようになるし,snapdも動作するようになる.

$ systemctl status snapd
● snapd.service - Snap Daemon
     Loaded: loaded (/lib/systemd/system/snapd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2020-05-24 13:46:21 JST; 5min ago
TriggeredBy: ● snapd.socket
   Main PID: 444
      Tasks: 27 (limit: 30645)
     Memory: 170.6M
     CGroup: /user.slice/user-1000.slice/session-c2.scope/system.slice/snapd.service
             └─444 /usr/lib/snapd/snapd

 5月 24 13:50:47 DESKTOP-JCB3AFB snapd[801]: rip    0x5580ad845031
 5月 24 13:50:47 DESKTOP-JCB3AFB snapd[801]: rflags 0x286
 5月 24 13:50:47 DESKTOP-JCB3AFB snapd[801]: cs     0x33
 5月 24 13:50:47 DESKTOP-JCB3AFB snapd[801]: fs     0x0
 5月 24 13:50:47 DESKTOP-JCB3AFB snapd[801]: gs     0x0
 5月 24 13:50:47 DESKTOP-JCB3AFB systemd[1]: snapd.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
 5月 24 13:50:47 DESKTOP-JCB3AFB systemd[1]: snapd.service: Failed with result 'watchdog'.
 5月 24 13:50:48 DESKTOP-JCB3AFB systemd[1]: snapd.service: Scheduled restart job, restart counter is at 2.
 5月 24 13:50:48 DESKTOP-JCB3AFB systemd[1]: Stopped Snap Daemon.
 5月 24 13:50:48 DESKTOP-JCB3AFB systemd[1]: Starting Snap Daemon...

あとは 通常通りsnapコマンドを実行すればいいだけ. ただし,当然ながらsystemdはあまり正常な状態ではないようで, systemctl restartなどでsnapdを含む適当サービスを再起動するとエラーになる.

$ sudo systemctl restart snapd
[sudo] thaim のパスワード:
Warning! D-Bus connection terminated.
Failed to restart snapd.service: 接続が相手からリセットされました
See system logs and 'systemctl status snapd.service' for details.

一応snapコマンドでhello-worldをインストール・実行できたのである程度は動作する,はず. ただし,現状では可能な限りsnapコマンドは利用しない方がよさそう.

参考

書評: 新 企業の研究者を目指す皆さんへ

もともと自分が学生だった頃に読んだ本の新版. 当時はまだ企業における研究とは何か理解しておらず,その後の自分の企業研究者人生に大きな影響を与えてくれた. その新版が出たということで読んでみた.

企業の研究者を目指す人という書き方ではあるが, 企業の研究者ではなくなった今の自分でも読んでよかったと思えた.

概要

企業の研究者や,研究者を目指す学生に向けて著者のキャリアの中で感じた 企業において必要なこと,研究者において必要なことなどを記載している. 著者はソフトウェアに関する研究者ではあるが,分野に依存しない 一般的な研究者へのメッセージ・考え方を示している.

新版での追加内容として,旧版が出版されてからの著者のキャリアとして 数理研やPFNなどの経験を踏まえた内容となっている. 旧版のIBMにおける研究に対する考え方を知る本という立ち位置から, 企業という枠を超えて研究者とは何かについて知ることができる.

よかったところ

新版で追加された内容では,2章におけるソリューションを設計するが深く刺さった. 設計型の研究を上手く成果に結び付けることが難しいことは,自分も多く経験しており, 論文にすることの難しさや,成果としての評価の難しさを今でこそ理解している. さらっと書かれており,学生が読むとしたらスルーされるだろうけれど, もっと早く出会えていたら自分の悩みにもっと向き合えていたかもと思う.

旧版からあった内容で,改めて深く考えたのが6章のインテグリティについてである. もちろん,研究成果の捏造といった重大な事件はやっていないし,実際に見たことはなかったけど, 進捗のごまかしという点では大なり小なり見てきたし手を出してしまったこともある. あとはやるだけだからとか,ここのバグを直せば完了だからと,成果をごまかして報告したり, 逆に毎週良い成果を報告したいから,まだ今週は上手くいっていないことにして次週に報告しよう, といって報告しなかったりという場面に多々出会ってきた. これはコミュニケーションの問題でもあるが,社内だからこそ包み隠さず報告することが大切だし, 過剰にアピールせず等身大に伝える,ということの大切さと難しさを再度考えてしまった.

よくなかったところ

特に疑問に思ったところはなく,基本的に非常に満足できる内容だった.

あえて言うのであれば企業という視点が軽くなったように感じた. タイトルの「企業の」ということが本質ではなく,アカデミックの研究者を目指す人にも読んで欲しい内容だと思った. 「企業の研究者」と述べているからにはもっと企業特有の話を記載して欲しいとも感じた. 例えば2章の研究の出口はまさに企業とアカデミックで大きく異なるところだと思っているので, もう少し掘り下げて欲しかった(技術移転はそれだけで1冊本になりそうなほど難しいトピックだと思う).

まとめ

企業で研究するならぜひとも一度は読んで欲しい本だと思うし, 研究でなく開発よりでも非常に参考になる. 自分も読んでいてまた研究したいなと思ってしまった.

新 企業の研究者をめざす皆さんへ

新 企業の研究者をめざす皆さんへ

MySQL 5.7.30 でvalidate_password プラグインを回避する

いつも Docker での動作検証しかしていなかったので, Docker以外の環境とかMySQLの初期化処理とか知らなかった.

事の始まり

AWS EC2 / Amazon Linux2 に MySQL をインストールして利用したい. このとき,開発・検証環境だから弱いパスワードを許可したかった(もちろん本当はよくないけれど).

ところが,弱いパスワードを設定しようとするとエラーになる. 軽く検索すると設定ファイルにパスワードポリシーの変更を追記するといいよという記事がたくさんヒットする. ただし,設定項目が記事によって異なる,タイポ?バージョンで違う? しかも設定を反映するとMySQLがエラーで起動しなくなる. とりあえず非公式情報は参考にならなそう.

ということで,なぜパスワードが変更できないのか,なぜ設定変更するとMySQLが起動しなくなるのか, その設定を変更する正しい手順はどうすればいいのか について調査した.

したいこと

  • Amazon Linux2 に MySQL 5.7.30 をインストールする
  • validate_passwordプラグインの制限を緩和する
  • この設定のセットアップ手順を確立する

なぜ上手くいかなかったのか

validate_passwordプラグインによるパスワードポリシー設定

弱いパスワードが設定できなかったのはデフォルトで validate_passwordプラグインが有効になっているため. このプラグインにより,弱いパスワードを設定することができなくなっている.

dev.mysql.com

このプラグインがデフォルトで有効になるのはyumリポジトリからMySQLをインストールした場合などで, 今回のAmazon Linux2ではYumリポジトリからインストールしていたのでこのプラグインが有効化されていた.

dev.mysql.com

Note

If you installed MySQL 5.7 using the MySQL Yum repository, MySQL SLES Repository, or RPM packages provided by Oracle, validate_password is enabled by default after you start your MySQL Server for the first time.

実際に Dockerの公式イメージ(mysql:5.7.30)で確認するとプラグインは有効になっておらず, validate_passwod プラグインのみ差異があることがわかる. 公式DockerイメージはDebianベースなので確かに公式ドキュメントの通りvalidate_passwordプラグインは有効にならない.

Yumリポジトリからインストールした場合

mysql> show plugins;
+----------------------------+----------+--------------------+----------------------+---------+
| Name                       | Status   | Type               | Library              | License |
+----------------------------+----------+--------------------+----------------------+---------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| mysql_native_password      | ACTIVE   | AUTHENTICATION     | NULL                 | GPL     |
| sha256_password            | ACTIVE   | AUTHENTICATION     | NULL                 | GPL     |
| CSV                        | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| MEMORY                     | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| InnoDB                     | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| INNODB_TRX                 | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_LOCKS               | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_LOCK_WAITS          | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_CMP                 | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_CMP_RESET           | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_CMPMEM              | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_CMPMEM_RESET        | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_CMP_PER_INDEX       | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_CMP_PER_INDEX_RESET | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_BUFFER_PAGE         | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_BUFFER_PAGE_LRU     | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_BUFFER_POOL_STATS   | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_TEMP_TABLE_INFO     | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_METRICS             | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_FT_DEFAULT_STOPWORD | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_FT_DELETED          | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_FT_BEING_DELETED    | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_FT_CONFIG           | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_FT_INDEX_CACHE      | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_FT_INDEX_TABLE      | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_TABLES          | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_TABLESTATS      | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_INDEXES         | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_COLUMNS         | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_FIELDS          | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_FOREIGN         | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_FOREIGN_COLS    | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_TABLESPACES     | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_DATAFILES       | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| INNODB_SYS_VIRTUAL         | ACTIVE   | INFORMATION SCHEMA | NULL                 | GPL     |
| MyISAM                     | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| MRG_MYISAM                 | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| PERFORMANCE_SCHEMA         | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| ARCHIVE                    | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| BLACKHOLE                  | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| FEDERATED                  | DISABLED | STORAGE ENGINE     | NULL                 | GPL     |
| partition                  | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |
| ngram                      | ACTIVE   | FTPARSER           | NULL                 | GPL     |
| validate_password          | ACTIVE   | VALIDATE PASSWORD  | validate_password.so | GPL     |
+----------------------------+----------+--------------------+----------------------+---------+
45 rows in set (0.00 sec)

Dockerで起動した場合

mysql> show plugins;
+----------------------------+----------+--------------------+---------+---------+
| Name                       | Status   | Type               | Library | License |
+----------------------------+----------+--------------------+---------+---------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| mysql_native_password      | ACTIVE   | AUTHENTICATION     | NULL    | GPL     |
| sha256_password            | ACTIVE   | AUTHENTICATION     | NULL    | GPL     |
| CSV                        | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MEMORY                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| InnoDB                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| INNODB_TRX                 | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_LOCKS               | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_LOCK_WAITS          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP                 | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_RESET           | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMPMEM              | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMPMEM_RESET        | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_PER_INDEX       | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_CMP_PER_INDEX_RESET | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_PAGE         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_PAGE_LRU     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_BUFFER_POOL_STATS   | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_TEMP_TABLE_INFO     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_METRICS             | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_DEFAULT_STOPWORD | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_DELETED          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_BEING_DELETED    | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_CONFIG           | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_INDEX_CACHE      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_FT_INDEX_TABLE      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLES          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLESTATS      | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_INDEXES         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_COLUMNS         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FIELDS          | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FOREIGN         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_FOREIGN_COLS    | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_TABLESPACES     | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_DATAFILES       | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| INNODB_SYS_VIRTUAL         | ACTIVE   | INFORMATION SCHEMA | NULL    | GPL     |
| MyISAM                     | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| MRG_MYISAM                 | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| PERFORMANCE_SCHEMA         | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| ARCHIVE                    | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| BLACKHOLE                  | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| FEDERATED                  | DISABLED | STORAGE ENGINE     | NULL    | GPL     |
| partition                  | ACTIVE   | STORAGE ENGINE     | NULL    | GPL     |
| ngram                      | ACTIVE   | FTPARSER           | NULL    | GPL     |
+----------------------------+----------+--------------------+---------+---------+
44 rows in set (0.01 sec)

対策としては,セキュアなパスワードを利用するか,ポリシーを変更するか,プラグインをアンインストールするか.

プラグインの反映タイミング

パスワードの制限を緩和するためには,ポリシーとして validate_password_policy = OFF を設定すればよい. これにより利用できるパスワードの制限を変更できる. もう少しパスワードポリシーをカスタマイズしたい場合は, 以下のプラグインオプションが参考になる.

dev.mysql.com

一方で初回起動前にこの設定を my.cnf に記載すると,validate_password プラグインがインストールされる前に反映しようとして, 無効な設定としてエラーになってしまう.

2020-05-04T10:31:49.970600Z 0 [ERROR] unknown variable 'validate_password_policy=LOW'

これは公式ドキュメントにも記載されている.

This option is available only if the validate_password plugin has been previously registered with INSTALL PLUGIN or is loaded with --plugin-load-add. See Section 6.4.3.1, “Password Validation Plugin Installation”.

ではこのプラグイン自体を最初から無効化すればいいかというとそれは難しい. プラグインの設定反映はMySQLをインストールしたときの systemdの設定ファイルによるもので, /usr/lib/systemd/system/mysqld.service には

# Needed to create system tables
ExecStartPre=/usr/bin/mysqld_pre_systemd

の記載があり, この /usr/bin/mysqld_pre_systemd で validate_passwordプラグインのインストールを含む 証明書の作成やシステムテーブルの作成などの処理(mysqld --initialize)を実行している.

validate_passwordプラグインを有効にしないオプションなどは存在せず, このpreスクリプトを実行しないと自分で初期化処理が必要になるので このスクリプトを実行しない選択肢は基本的にない. このスクリプトを自前でカスタマイズするのもリスクが高いので, Yumリポジトリからインストールした場合は基本的にvalidate_passwordプラグインは有効になっている想定がよさそう.

初回起動直後にプラグインをアンインストールしようとしても, 初期パスワードの変更前にパスワード変更以外の操作は受け付けないので, 初回起動→複雑なパスワード設定→プラグインアンインストール→簡易なパスワードの設定 といった無意味な手順が生じてしまう.

以上から,一度MySQLを起動してプラグインを反映した上でパスワードポリシーを変更して再起動, という手順がよさそう.

validate_passwordプラグインを回避するセットアップ手順

MySQLのインストール

Amazon Linux2ではMariaDBが有効になっている. MySQL5.7を利用したい場合はMySQLリポジトリを追加し, MySQL8を無効化して 明示的に5.7を有効化・インストールする必要がある.

dev.classmethod.jp

このタイミングでパスワードポリシーも変更したいが,それは実行しない. 自動起動の設定は反映されていないので sudo systemctl enable mysqld自動起動を有効にだけしておく.

MySQLのセットアップ

MySQLの初回起動後に /etc/my.cnf にパスワードポリシーの変更設定を記載する. 設定ファイルの末尾に validate_password_policy = OFF を追記だけしてあげればよい.

その後MySQLを再起動すればパスワードポリシーが変更される. あとは初期パスワードを弱いパスワードでも自由に設定できる.

初期パスワードはログ(/var/log/mysql.log)に出力されているので, これを利用してパスワードを変更する.

パスワード変更時にpassword関数を利用する方法はDeprecatedなのでpassword関数は利用しない. またSET PASSWORD FOR root@localhost = 'password' よりはALTER USERの方が推奨のようで, ALTER USER root@localhost IDENTIFIED BY 'password' でよい.

dev.mysql.com

Note

SET PASSWORD ... = PASSWORD('auth_string') syntax is deprecated as of MySQL 5.7.6 and will be removed in a future MySQL release.

SET PASSWORD ... = 'auth_string' syntax is not deprecated, but ALTER USER is the preferred statement for account alterations, including assigning passwords. For example:

参考

dev.mysql.com

hub.docker.com

hirose31.hatenablog.jp

qiita.com