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内のローカル変数をテンプレートで使うための記述を追記。
knife ec2でインスタンスの初期設定(RVM+Ruby1.9.2編)
前回はnodeのchef-clientを動かすためにrbelリポジトリのRubyを使っていた。特にこだわりがなかったり、動かすシステムでRubyを使うわけじゃなければ、RPMだと楽でいいですよね。
ただ、セットアップするnodeでRubyを使いたい、RVMで複数バージョンのRubyを使いたいなんてことがある場合もあると思う。
であれば、bootstrapでインストールするRubyもRVMでインストールしちゃえばいいじゃない。
という訳で、基本的には前回と同じで、bootstrapの中身を変えればOK。セットアップ対象はCentOS6で、Ruby1.9.2をインストールしてデフォルトRubyにします。
$CHEF_REPO/.chef/bootstrap/centos6-rvm.erb を以下のように作成。
bash -c ' <%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%> yum install -y gcc bison autoconf patch git curl readline-devel libxml2-devel libxslt-devel wget man if [ ! -f /usr/local/rvm/bin/rvm ]; then bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer ) source /etc/profile.d/rvm.sh rvm install 1.9.2 rvm use 1.9.2 --default fi gem install ohai --no-ri --no-rdoc gem install chef --no-ri --no-rdoc ln -nfs $(which chef-client) /usr/bin/chef-client mkdir -p /etc/chef ( cat <<'EOP' <%= validation_key %> EOP ) > /tmp/validation.pem awk NF /tmp/validation.pem > /etc/chef/validation.pem rm /tmp/validation.pem ( cat <<'EOP' <%= config_content %> EOP ) > /etc/chef/client.rb ( cat <<'EOP' <%= { "run_list" => @run_list }.to_json %> EOP ) > /etc/chef/first-boot.json <%= start_chef %>'
上記bootstrapテンプレートが出来上がったら、前回使用したknife ec2コマンドの -d オプションでの指定を今回作ったものに変えてあげる。
$ knife ec2 server create -G security_roup -I ami-12345678 -f t1.micro -S key-pair-name -i ~/.ssh/private-key.pem -x ec2-login-user -d centos6-rvm -r 'rolw[rails_server]' --region ap-northeast-1 -Z ap-northeast-1a
しばらく待てば、はい完成。(microだとちょっと時間がかかります。)