[chef] data bag活用法

data_bagを使う

chefにはdata_bagというものがあります。
attributeとは何が違うのかなーと思っていましたが、良い使い方を見つけましたのでご紹介させていただきます。

ユーザアカウントは全てdata_bagで管理

ユーザアカウント系は全てdata_bagで管理すると楽かも!と思ってやってみました。
例えばログインアカウント。
サーバの機能や場所ごとにroleを作りますが、そのroleの中でログインアカウントを管理するrecipeを入れるとします。
data_bagを使わないとrecpieもしくはroleのattributeにユーザ情報を格納して、recipeのuser resourceでユーザを作成することになると思います。
僕の管理している環境では鍵認証が基本なので公開鍵の情報も管理をしたいです。公開鍵はユーザに紐づいていてどのサーバでも同じ鍵で入れると便利ですよね。
そこで、data_bagを活用。
data_bagはdata_bagそのものと、data_bag_itemという2階層構造になっているようです。
まず、data_bag['users']というdata_bagを作成します。  
そしてその中にdata_bag_item['login_account']というdata_bag_itemを作ります。
data_bag_item[‘login_account’]は配列でユーザ情報が格納されています。
例えばこんな感じ。

{
  "users":[
    {
      "system": [
        "role1",
        "role2"
      ],
      "key": "ssh-rsa xxx",
      "name": "user01",
      "active": true,
      "uid": 10001
    },
    {
      "system": [
        "role1",
        "role2"
      ],
      "key": "ssh-rsa xxx",
      "name": "user02",
      "active": true,
      "uid": 10002
    }
  ],
  "id": "login_account"
}

ユーザ情報は

  • uid
  • account名
  • 公開鍵
  • ユーザを作るシステム
  • 有効フラグ

という情報からなっています。

ユーザ作成のrecipeはこんな感じ。

# User add or delete
for u in data_bag_item('users','login_account')['users']
act = false

  # 適用システムかどうかを確認する
  for role in node["roles"]
    for system in  u["system"]
      if role == system
        act = true
        break
      end
    end
    if act
      break
    end
  end

  if ! act
    next
  end

 # activeがfalseなら削除
 user_act = u["active"] ? "create":"remove"

 # ホームディレクトリの文字列作成
 home_dir = "/home/" + u["name"]

 # useradd
 user u["name"] do
  uid u["uid"]
  gid u["uid"]
  shell "/bin/bash"
  home home_dir
  action user_act
 end

 # .ssh directory
 ssh_dir = home_dir + "/.ssh"
 # authorized_keys
 authorized_keys = ssh_dir + "/authorized_keys"

 # .ssh directory作成
 if u["active"]
  directory ssh_dir do
   owner u["name"]
   group "ssh_users"
   mode  0700
  end
  
  # authorized_keyの作成
  if u["key"]
   file authorized_keys do
    owner u["name"]
    mode  0600
    content u["key"] + " " + u["name"]
   end
  end
 end
end

こんな感じで、各サーバのユーザアカウントを管理しています。
もう少し大規模になればディレクトリサービスとかを使うのですが、そこまででもない規模はこれでいいかなーと。

これをやっていてchefの残念なところを発見。

 # activeがfalseなら削除
 user_act = u["active"] ? "create":"remove"

この部分はactiveを見て、trueならcreate,falseならremoveというactionのパラメータを指定しています。
理想はrecipeからユーザが消えたら自動で削除してくれるのが良いのですが、どうもそうではないようなので、不要になったユーザはactiveをfalseにして、明示的に削除処理をするようにしています。

login_accountの説明でだいぶ長くなってしまいましたが、このdata_bag["users"]はユーザ管理data_bagとして、login_accountの他に、例えばdata_bag_item['mysql']とかdata_bag_item['basic']とかを登録して、ここを見ればユーザ管理が出来るというように使いたいと思っています。

おしまい。

[chef]サーバアカウントの管理と公開鍵の登録

最近は、いよいよchefの本格的な運用に向けてrecipeを書く日々が続いています。
その中から一コマをご紹介。

Roleで登録するユーザを分ける

chefでサーバのアカウント管理をすることになって、どのようにするのが効率的かを考えました。
登録するアカウントは全てのサーバで同じというわけではなく、属性によって異なります。
そこで、属性ごとにRoleを用意して、それぞれユーザをattributeで登録することにしました。
ユーザ登録のRecipeは共通です。

Roleはこんな感じ。

{
  "json_class": "Chef::Role",
  "description": "desc",
  "default_attributes": {
    "users": [
      {
        "active": true,
        "uid": 10001,
        "key": "ssh-rsa xxxxxx",
        "name": "user1"
      },
      {
        "active": true,
        "uid": 10002,
        "key": "ssh-rsa yyyyyy",
        "name": "user2"
      }
    ]
  },
  "env_run_lists": {
  },
  "override_attributes": {
  },
  "name": "group1",
  "run_list": [
    "recipe[add_user]",
  ],
  "chef_type": "role"
}

attributeでusersという配列を用意して、その中に以下の要素を持ったオブジェクトを入れています。

  • 名前
  • UID
  • 公開鍵文字列
  • active flag

forのloopでユーザ登録

RecipeではRoleのattribute[users]を参照しながらresource[user]でユーザを登録します。
usersは配列なのでRecipeの中でforのloopを使います。
このresource[user]ですが、不要になったアカウントを削除すれば勝手にaction :removeがはしるかと期待したんですが、そううまく行かなかったので、activeフラグを用意して、不要になったユーザはそれををfalseに変更するとaction :removeになるようにRecipeを作りました。

作ったrecipeは以下のような感じです。

# User add or delete
for u in node["users"]

 # activeがfalseなら削除
 user_act = u["active"] ? "create" : "remove"

 # ホームディレクトリの文字列作成
 home_dir = "/home/" + u["name"]

 # useradd
 user u["name"] do
  uid u["uid"]
  gid "users"
  shell "/bin/bash"
  home home_dir
  action user_act
 end
end

公開鍵の登録

こんな感じでユーザを登録して、ついでにログインするための公開鍵の登録もします。
公開鍵の情報はあらかじめRoleのattributeに登録しておきます。
authorized_keysを作るresourceはfileを使いました。

 # .ssh directory
 ssh_dir = home_dir + "/.ssh"
 # authorized_keys
 authorized_keys = ssh_dir + "/authorized_keys"

 # .ssh directory作成
 if u["active"]
  directory ssh_dir do
   owner u["name"]
   group "ssh_users"
   mode  0700
  end
  
  # authorized_keyの作成
  if u["key"]
   file authorized_keys do
    owner u["name"]
    mode  0600
    content u["key"] + " " + u["name"]
   end
  end

これは実際はさっきのforの中に入っています。

これで、アカウント登録と公開鍵の登録は完了です。

やっぱりchefを使うメリットは同じ機能のサーバを同じ設定に出来るところ。
こんなのを普通にしこしこやったら絶対に設定漏れが出てしまう良い例だと思う。