アルパカchef日記3日目 data bagについて / またはユーザ管理クックブックなど
3日目の目標
- ユーザ管理(data bag)
- ユーザ作成
- bash_profile管理
- sudo
- ここの段階で ec2-userのsudo権限を剥奪し、新ユーザにsudo権限を付与
- security_limit
3日目を始める前に:data bag
ユーザ情報など、クックブックを跨るグローバルな値を
cookbookにいちいち書くのは得策ではありません。
さらに生で置いておくのも気が引けますね。
そんなご要望にお答えするために「data bag」という仕組みがあります。
databagを作成しておくと、複数のクックブックにまたがっている共通の変数などを保存しておくことができます。
シークレットキーを作成する
まずはdata bagを暗号/複合するためのシークレットキーを作成しましょう。
以下のコマンドでdata bag用の鍵ファイルを作っておきます。
openssl rand -base64 512 > data_bag_key # 「unable to write 'random state'」というエラーが出る場合はsudo実行しましょう
鍵を共通ディレクトリに置く
chef-repo毎に鍵を作ってもいいのですが、毎回作るのも面倒なので
今回は共通で参照できるHOMEディレクトリ以下に置いてしまいましょう。
mv data_bag_key ~/.chef/data_bag_key # knife.rbに「encrypted_data_bag_secret」を追加 vi <chef-repo>/.chef/knife.rb ... encrypted_data_bag_secret "#{ENV['HOME']}/.chef/data_bag_key" ...
data bagを作成/編集する
knife soloコマンドでdata bagを新規作成します。
この時「EDITOR環境変数」を予め設定しておく必要があるので
未設定の場合は .bashrc などに追記しておきましょう。
vi .bashrc ... " 以下を追加 export EDITOR=vim ... # databagの作成 # 「databags/users/alpaca3.json」が作られる knife solo data bag create users alpaca3 # 編集する場合はedit knife solo data bag edit users alpaca3
この時の注意点として
databags/users/alpaca3.jsonを直接編集することはできません。*1
データを編集したい場合は knife solo data bag edit コマンドから行う必要があります。
ユーザ作成
今回は完全オリジナルの「user-origin」クックブックを作ります。
が、その前にユーザのdata bagを作っておきましょう。
「alpaca3」というユーザを作ります。
create databag
knife solo data bag create users alpaca3
/databags/users/alpaca3.json の中身
以下のように編集しましょう。
{ "id": "alpaca3", "name": "alpaca3", "password": "$1$xxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "ssh_key": "ssh-rsa xxxxxxxxxxxxxxxxxxxxxxxxx" }
passwordについて
chefに記述するパスワード文字列はshadow passwordにする必要があるようです。
以下のコマンドで標準出力された文字列をpasswordの欄に設定しましょう。
openssl passwd -1 > input > verify input $1$xxxxxxxxxxxxxxxxxxxxxxxxxxxxx <- これ!
※参考ドキュメント
http://docs.opscode.com/resource_user.html#password-shadow-hash
ssh_keyについて
今回はec2-userの鍵を拝借しましょう。
一度 ec2-userでログインし、以下のコマンドで表示された文字列を「ssh_key」にします。*2
cat ~/.ssh/authorized_keys
/site_cookbooks/user-origin/recipes/default.rb を書く
では、レシピを書いていきます。
少し長くなりそうなので小出ししながら説明します。
# 暗号化されたデータバッグの情報を取得 user = Chef::EncryptedDataBagItem.load("users", 'alpaca3') user_name = user['name'] password = user['password'] ssh_key = user['ssh_key'] home = "/home/#{user_name}" # 「alpaca3」ユーザの作成 user user_name do password password home home shell "/bin/bash" supports :manage_home => true # ホームディレクトリも管理する end # 「alpaca3」をwheelグループに追加する group "wheel" do action [:modify] members [user_name] append true end
まずはユーザの作成です。
1行めの Chef::EncryptedDataBagItem.load() を用いて databagのデータをロードしています。
その情報を用いて「alpaca3」ユーザを作成します。
その後に「alpaca3」ユーザをwheelグループに追加しています。
次にssh用のディレクトリやkeyを設定します。
「directory」リソースを使ってディレクトリを作成した後、
「file」リソースを使ってファイルをアップロードしています。
# .sshディレクトリを作ります directory "#{home}/.ssh" do owner user_name group user_name end # authorized_keysファイルを作ります authorized_keys_file ="#{home}/.ssh/authorized_keys" file authorized_keys_file do owner user_name mode 0600 content "#{ssh_key} #{user_name}" # ファイルの中身を直接指定 not_if { ::File.exists?("#{authorized_keys_file}")} # 既にファイルが存在していたらリソースを実行しない end
次にbash_profileの管理です。
まずはbash_profileをどのように管理しようとしているか説明したいと思います。
<home> ├── .bash_profile # .bash_profile。.bash_profile_inc以下のファイルを読む設定を入れておく ├── .bash_profile_inc # profile include用ディレクトリ ├── base_profile.sh # 共通profile └── ... # ミドルウェア毎にbash_profileを用意する
他の様々なリソースが .bash_profile_inc 以下に
各々のprofileを置くような運用です。
こうすることによって、.bash_profile自体をいじらなくても
リソース毎の様々なprofileを管理しようとしています。
続きのレシピは以下のようになります。
# 1. bash_profile include用のディレクトリを作ります directory "#{home}/.bash_profile_inc" do owner user_name group user_name end # 2.「base_profile.sh(静的ファイル)」をbash_profile include用のディレクトリに配置します cookbook_file "#{home}/.bash_profile_inc/base_profile.sh" do source "base_profile.sh" mode 0644 owner user_name group user_name end # 3. bash_profile本体に「.bash_profile_incディレクトリ以下のファイルを読み込む」ような処理を追加しています script "include bash_profile" do environment 'HOME' => home user user_name group user_name interpreter "bash" not_if "grep -q 'bash_profile_include' #{home}/.bash_profile" flags "-e" code <<-"EOH" cat << EOF >> #{home}/.bash_profile 2>&1 # bash_profile_include for file in \\`find ~/.bash_profile_inc -type f\\`; do source \\$file done EOF EOH end
また新しいリソースが出てきたので簡単に説明を。
2. の「cookbook_file」はfilesディレクトリ以下に置かれた静的ファイルをそのままサーバに転送します。
ちなみに 2. で使用している静的ファイルの中身は
現段階で以下のような内容です。
vi files/default/base_profile.sh # 今後育っていくはず export LANG="ja_JP.UTF-8" alias sl='ls' alias ll='ls -l'
3. の「script」は「code」に書かれた内容をサーバ上で実行します。
3. で行なっている処理は既存の.bash_profileに追記を行なっているので
重複して実行したくありません。
そのため not_if で 「bash_profile_include」という文字列が存在しない時だけ実行するようにしています*3。
cooking
ではnodes jsonに追加してcookしましょう。
vi nodes/ec2-chef-repo.json { "run_list":[ "recipe[selinux::disabled]", "recipe[openssh]", "recipe[ntp]", "recipe[sysctl]", "recipe[user-origin]" ] } # cook knife solo cook ec2-chef-test
うまくいったでしょうか?
今回は利用しませんでしたが、DataBagの内容をそのままユーザ登録に使うようなレシピもあるようです。
気になる方は試してみてはいかがでしょうか。
https://github.com/opscode-cookbooks/users
sudo
sudo管理をします。
デフォルトユーザである ec2-user に管理者権限を持たせるのはちょっと嫌なので
ec2-userのsudo権限を剥奪してalpaca3ユーザにsudo権限を付けたいと思います。
sudoクックブックはopscodeのをダウンロードします。
# Berksfileに以下を追加。berks install も忘れずに。 cookbook 'sudo'
/site_cookbooks/sudo/attributes/default.rb を書く
usersはalpaca3のみにしておきます。
default['authorization']['sudo']['groups'] = [] default['authorization']['sudo']['users'] = ['alpaca3'] # 心配であれば最初だけec2-userを残したままにしておいても良いです #default['authorization']['sudo']['users'] = ['ec2-user', 'alpaca3'] default['authorization']['sudo']['passwordless'] = true default['authorization']['sudo']['include_sudoers_d'] = false default['authorization']['sudo']['agent_forwarding'] = false default['authorization']['sudo']['sudoers_defaults'] = ['!lecture,tty_tickets,!fqdn']
cooking
ではnodes jsonにレシピを追加してcookしましょう。
vi nodes/ec2-chef-repo.json # sudo追記 ... # cook knife solo cook ec2-chef-test
このレシピが成功したあとは ec2-userでのsudoができなくなります。
すなわちknife soloの実行もできなくなります。
ssh/configでユーザの指定「alpaca3」に変えておきましょう。
# ssh config設定 vi .ssh/config Host ec2-chef-test HostName ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com IdentityFile /path/to/private.pem User alpaca3 Port 22
Caution!!!!
sudoクックブックはデフォルトのまま(site-cookbooksを用意しないまま)実行してしまうと
誰もsudoできなくなってしまいます!*4
ご注意下さい。
security_limits
security_limitsもオリジナルで作成してみます。
出来上がるファイルはこんな感じ。
security_limits ├── attributes │ └── default.rb ├── recipes │ └── default.rb └── templates └── default └── limits.conf.erb
/site_cookbooks/security_limits/attributes/default.rb
# 設定したいユーザ配列 default['security_limits']['users'] = ['alpaca3', 'nobody'] # soft / hard limitの設定 default['security_limits']['soft'] = 10240 default['security_limits']['hard'] = 10240
/site_cookbooks/security_limits/recipes/default.rb
# テンプレート読み込む template '/etc/security/limits.conf' do source "limits.conf.erb" mode "0644" variables( :users => node['security_limits']['users'], :hard_limit => node['security_limits']['hard'], :soft_limit => node['security_limits']['soft'] ) end
/site_cookbooks/security_limits/templates/default/limits.conf.erb
# Dynamically generated file dropped off by Chef! <% @users.each do |user| %> <%= user %> hard nofile <%= @hard_limit %> <%= user %> soft nofile <%= @soft_limit %> <% end %>
cooking
ではnodes jsonにレシピを追加してcookしましょう。
vi nodes/ec2-chef-repo.json # security_limits追記 ... # cook knife solo cook ec2-chef-test
うまくいくと、サーバ上に以下のようなファイルが作成されているはずです。
$ cat /etc/security/limits.conf # Dynamically generated file dropped off by Chef! alpaca3 hard nofile 10240 alpaca3 soft nofile 10240 nobody hard nofile 10240 nobody soft nofile 10240