ChefでのMySQLパスワードの扱い
opscodeのリポジトリにあるMySQLのcookbookでは、rootユーザやレプリケーション用のユーザのパスワードをランダムに生成して設定している。
opscode の recipe の特徴
このランダムという点をカバーするべく、うまい仕組みが組み込まれている。
パスワードを設定するところは
node.set_unless['mysql']['server_root_password'] = secure_password
といった形で、attributeに設定されていない場合はランダムに生成するという事をして、2度目以降も同じパスワードとなるようになっている。
2回目以降も同じパスワードを保証するために、もうひとつの技が
unless Chef::Config[:solo] ruby_block "save node data" do block do node.save end action :create end end
ここの node.save というやつ。
普通であれば、recipe(run_list)の実行がすべて完了するまではattributeがサーバに保存されないんだけど、このメソッドを使うことで、即時に保存される。これで、万が一rootのパスワードが設定されたあとのどこかでコケても、同じパスワードで設定を続けることができる(上のコードにあるようにchef-soloのときは実行されないけど)。また、masterは途中でコケたけど、スレーブはそこで設定された(であろう)であろうパスワードを使ってセットアップだけは続けることができる。
これはよく考えられた仕組みですよね。
Encrypted Data Bag
ただ、ランダムであれば推測されづらくていいかもしれないけど、好きなパスワードを設定したいし、アプリからのアクセスを考えるとすべてのサーバでアプリ用ユーザは共通にしておきたいといった要望もある。また、提供されているrecipeのままだったりattributeにそのまま設定してしまうと、平文のパスワードがattributeの一覧から確認できてしまう。
そんな時に便利なのが、Encrypted Data Bagという平文で保存したくないデータをChefに保持しておくための機構。Encrypted Data Bag を使うために必要なのはencrepted_data_bag_secretと呼ばれる共通鍵だけ。
※やってることはOpscodeのサイトに書かれていることと全く同じです。
共通鍵の作成
# openssl rand -base64 512 | tr -d '\r\n' > /etc/chef/encrypted_data_bag_secret
これをEncrypted Data Bags利用する各クライアントと共有する。共有方法は初回セットアップであれば、knifeコマンドのbootstrapで指定してあげれば良い。その方法は後ほど。
knifeコマンドで暗号化されたデータをdata bagsに保存
passwordというdata bagにmysqlという項目を作成します。ここで指定するmysqlというのは、data bagのIDとなり、これは暗号化されません。
# knife data bag create --secret-file /etc/chef/encrypted_data_bag_secret passwords mysql [editorが開くので以下のように入力して保存] { "id": "mysql", "user": "root", "pass": "your_password" }
※EDITOR環境変数を設定していないとErrorになります。
作成したdata bagを確認してみましょう。
# knife data bag show passwords mysql { "id": "mysql", "pass": "trywgFA6R70NO28PNhMpGhEvKBZuxouemnbnAUQsUyo=\n", "user": "e/p+8WJYVHY9fHcEgAAReg==\n" }
userとpassが暗号化されて保存されています。
では、復号化して表示してみます。
# knife data bag show --secret-file /etc/chef/encrypted_data_bag_secret passwords mysql { "id": "mysql", "user": "root", "pass": "your_password" }
先程入力したものが表示されました。
Recipeから Encrypted Data Bagのデータを呼び出す
実際に recipe からdata bagの値を呼ぶには、以下のような感じにしてあげます。
共通鍵を直接指定する場合
mysql_data = Chef::EncryptedDataBagItem.load("passwords", "mysql", secret) user = mysql_data["user"] password =mysql_data["pass"]
共通鍵ファイルがデフォルトの /etc/chef/encrypted_data_bag_secret に配置されている場合はsecretを指定しなくてもOK
mysql_data = Chef::EncryptedDataBagItem.load("passwords", "mysql") user = mysql_data["user"] password =mysql_data["pass"]
ここで、ユーザ名やパスワードを node['mysql']['user'] とやってしまうと、せっかく暗号化して保存されているユーザ名とパスワードが平文でattributeに登録されてしまいますので、やめたほうがいいと思います。data bagに保存されているので、次回以降に変わるという事も無いですしね。
recipe内で定義したローカル変数のままだとテンプレートに渡せないので、templateリソースの中で以下のように定義すればテンプレート内で呼び出せる。
template "hoge" do source "hoge.erb" ... variables( :user => user, :pass => pass ) end
これでめでたくattributeに平文のパスワードが登録されることなく、自分の好みのパスワードが設定出来ました。
bootstrap で encrypted_data_bag_secret を渡す
初回セットアップのサーバの場合には、validation.pemやchef_server_urlを新規クライアントに設定するために bootstrap の仕組みを利用する。この中で、encrypted data bagの共通鍵をセットアップするサーバに配置することができる。
以下のようにbootstrapファイルに書く。
( cat <<'EOP' <%= encrypted_data_bag_secret %> EOP ) > /etc/chef/encrypted_data_bag_secret
encrypted_data_bag_secret など、bootstrap で使用される値などは、knifeを実行するノードのknife.rbに設定されているものが利用される。そのため、encrypted_data_bag_secretファイルの場所もknife.rbに指定しておく必要がある。
encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret"
これで、knifeコマンドからbootstrapファイルをして初期実行をすれば、recipeを実行するクライアントでも問題なくdata bagを復号化できる。
(追記:2012-02-12 16:25) recipe内のローカル変数をテンプレートで使うための記述を追記。