ばうあーろぐ TOP へ戻る

Chef の Data Bag を使ってユーザー作成の自動化をしてみる

この記事は書かれてから1年以上が経過しており、最新の情報とは異なる可能性があります

Chef のユーザー管理を突き詰めてみるだけでも、色々と勉強になっていますね。

前回までで、Chef の Cookbook にユーザー作成やグループ作成のレシピを記述することで、 ユーザー作成の自動化を試してみましたが、今回はもうちょっと複雑なことをやりたいので、 別の方法を試してみようと思います。

『入門 Chef Solo』によると、

システムに追加されるべきユーザーの各種データなどはノードの「属性」でもないし Resource の「属性」でもないデータと見ることができます。 こういったドメインモデル的なデータは Chef のデータ管理の仕組みである Data Bag を使うほうが扱いやすい。

(『入門 Chef Solo』 #19 Attribute と Data Bag より引用)

とのことで、データを扱うための仕組みが存在するようです。

また、

Data Bag はクックブック単位ではなく、レポジトリ全体にグローバルなスコープのデータです。

(『入門 Chef Solo』 #19 Attribute と Data Bag より引用)

ともありますね。

ということで、今回は Data Bag の仕組みを使って、もうちょっと凝ったユーザー作成の自動化をやってみようと思います。

まずは Data Bag を使ってみる

この Data Bag、Cookbook とは別のところに書いて、Cookbook 側から読み込んで使う類のものらしいです。 対象ディレクトリの下に、種別のサブディレクトリを区切って、その中に JSON ファイルをデータごとに用意するようです。

であれば、まずは自分で作った cookbooks ディレクトリと並列なところに、 data_bags というディレクトリでも作り、users というサブディレクトリを作っておきます。

$ mkdir -p data_bags/users
$ cd data_bags/users

で、それぞれのユーザーごとに [ユーザー名].json のファイルを作ります。 ちなみにこの JSON ファイルは、あくまで任意のデータを扱うためのもので、あとで Cookbook 側から読み込むために用意するので、 前回設定した項目がこちらに用意できればオーケーですね。前回と同じことが出来るところまで設定しましょう。

$ cat alice.json
// alice
{
  "id": "alice",
  "shell": "/bin/bash",
  "password": "megckhCt3LluU"
}

そして、default.rb のレシピはこんな感じ。

# Cookbook ユーザーとグループのテスト

data_ids = data_bag('users')
data_ids.each do |id|
  u = data_bag_item('users', id)
  user u['id'] do
    shell    u['shell']
    password u['password']
    supports :manage_home => true, :non_unique => false
    action   [:create]
  end
end

group 'samplegroup' do
  group_name 'samplegroup'
  members    ['alice', 'bob']
  action     [:create]
end

Data Bag の機能を使っているところとしては、

ですね。この辺は Ruby のスクリプトであることを意識すればそれほど難しいことではないかなーと思います。

また、書いてるときにちょっと気づいたのですが、 Ruby で色々書こうとするときに、変数名とかに配慮が必要ですね。

というのも、user とか group とか directory とか書くと、実際はメソッド呼び出しになっている部分の上書きになると思うので、 きっとこの辺の名前は使えません。

『入門 Chef Solo』にも、u とか使ってたのですが、これは user だとかぶってしまうからなのではないかなーと邪推してます。

Vagrantfile の編集

Vagrant から、Cookbook のファイルは見えていますが、Data Bag のファイルはまだ見えてません。

Vagrant の公式のドキュメントの中の、Chef Solo のページにちゃんと書いてあるので見てみます。

http://docs.vagrantup.com/v2/provisioning/chef_solo.html

Cookbook の場所の指定と同じように、Data Bag の場所も指定してやります。

今回は、cookbook ディレクトリと同列に data_bags ディレクトリを作ったので、以下のようになるはずです。

vmclient.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = "cookbooks"
  chef.data_bags_path = "data_bags"
  chef.run_list = "recipe[vmclient1]"
end

vagrant up!

さて、ここまで書けたら vagrant up なり vagrant reload なりで起動して確認してみます。 一度まっさらな状態にして確認してみるのもいいかもしれません。

[vmclient1] -- /tmp/vagrant-chef-1/chef-solo-1/cookbooks
[vmclient1] -- /tmp/vagrant-chef-1/chef-solo-2/data_bags

お、Cookbook だけじゃなく、Data Bag も何らか作用していることがログから確認できますね。

さらにさらに、

[2013-05-11T19:16:29+00:00] INFO: Processing user[alice] action create (vmclient1::default line 6)
[2013-05-11T19:16:29+00:00] INFO: Processing user[bob] action create (vmclient1::default line 6)
[2013-05-11T19:16:29+00:00] INFO: Processing user[carol] action create (vmclient1::default line 6)
[2013-05-11T19:16:29+00:00] INFO: user[carol] created
[2013-05-11T19:16:29+00:00] INFO: Processing group[samplegroup] action create (vmclient1::default line 14)
[2013-05-11T19:16:29+00:00] INFO: Chef Run complete in 0.071730458 seconds
[2013-05-11T19:16:29+00:00] INFO: Running report handlers
[2013-05-11T19:16:29+00:00] INFO: Report handlers complete

僕は前回のところから立て続けにやっているので、carol だけが削除されていない状態でした。

alice, bob はもう作られていたので、carol だけが作られているのが、user[carol] created というログから予想できます。

さて、ログインして見てみます。

[vagrant@vmclient1 ~]$ su - alice
パスワード:
[alice@vmclient1 ~]$ su - bob
Password:
[bob@vmclient1 ~]$ su - carol
Password:
[carol@vmclient1 ~]$

成功しました!

前回までと同じく、パスワード認証ではありますが、それぞれ3人のユーザーを作ることが出来ていますね。

ただ、前回の検証時にもあったように、単にCookbook から記述を消すだけではユーザーの削除は行われないので、http://tech.blog.piyo.org/2012/06/19/chef-data-bag%E6%B4%BB%E7%94%A8%E6%B3%95/ にもあるように、ユーザーの有効/無効を Data Bag のパラメータとして管理する方法が良いのかもしれません。

普通にパスワード認証で SSH までしてみる

ちょっと脇道それます。これまでは、ゲストOS内で su - alice などで確認をしてましたが、 実際仮想マシン作っているので、ちゃんとIPアドレスも割り振られていて、普通に SSH で入れるはずですよね。

ホストOS(つまり今触ってるPC)から、ゲストOSへ SSH で接続確認をしてみます。

まずは始めから用意されていて、vagrant ssh vmclient1 とかでいつも SSH でログインしているであろう vagrant ユーザーで試します。

$ ssh vagrant@192.168.100.11
The authenticity of host '192.168.100.11 (192.168.100.11)' can't be established.
RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.100.11' (RSA) to the list of known hosts.
vagrant@192.168.100.11's password:
Last login: Sat May 11 19:20:10 2013 from xxx.xxx.xxx.xxx
Welcome to your Vagrant-built virtual machine.
[vagrant@vmclient1 ~]$ logout

はい、これは出来て当然ですね。 いつも ‘default’ というホスト名でログインしちゃってたので、IPアドレス直打ちだと最初に「初めて接続するけどいい?」って聞かれますが、問題ないので yes ですね。

お次は alice ユーザーです。

$ ssh alice@192.168.100.11
alice@192.168.100.11's password:
Welcome to your Vagrant-built virtual machine.
[alice@vmclient1 ~]$

やったー、SSH で入れました!

鍵認証したくてたまらない

今度は、パスワード認証以外の方法を自動化できるようにしていきたいなーと思います。

パスワードなしで鍵認証が出来るようにするには、この辺の設定が自動化出来ればいいんじゃないかと思います。

実際の運用フローを考えるのなら、秘密鍵は本人以外誰にも渡すべきではないものなので、 本人が作成した鍵のうち、公開鍵のみをサーバ運用担当に「これで登録しておいてー」と渡す感じになるのかなと思います。

今回は検証用なので、各ユーザーごとに秘密鍵・公開鍵を用意して、公開鍵のみをさっきの Data Bag の方に記載してみることにします。

公開鍵認証の仕組みについて

これは完全に余談で、ググったらたくさん情報が載っているのですが、触れずに進めるのもアレなので さらっと鍵認証の仕組みについて触れておきます。

鍵を作るとき

  1. 秘密鍵、公開鍵という2種類の鍵が存在(今から作る)
  2. なんかの文字列に対して、暗号化だけできるのが公開鍵、それを元に戻せる(復号化)できるのが秘密鍵
  3. 手元には、誰にも見せない秘密鍵を置いておく
  4. 接続対象のサーバには、公開鍵を置いておく

接続するとき

  1. ユーザーがサーバに対して「接続したいですー」とアクセス
  2. サーバ側は、持っている公開鍵でなんかの文字を暗号化してユーザーに返す
  3. ユーザーは、持っている秘密鍵で復号化して、なんかの文字をサーバに再度送る
  4. 秘密鍵でしか復号化できない=秘密鍵をもってる正規のユーザー であることが確認できる

という流れですね。

つまり今からやろうとしているのは、ホストOS上で鍵を作り、公開鍵のみを Data Bag に書き込み、ゲストOSの公開鍵として設定しようとしているわけです。

その後、ホストOSで一緒に作った秘密鍵を使って、ゲストOSに鍵認証で接続確認をする流れです。

公開鍵と秘密鍵を用意する

alice だけだと複数人のときでも上手くいってる保証にならないので、2人分の alice と bob の鍵を用意しておきます。

検証用なので作る場所はどこでも良いのですが、ホームディレクトリに作った Vagrant ディレクトリの直下に、 alice, bob のディレクトリを作って、そこに入れておくことにします。

$ mkdir alice
$ mkdir bob
$ cd alice

ssh-keygen というコマンドを用いるので、先に help を読み漁りましょう。

SSH-KEYGEN(1)             BSD General Commands Manual            SSH-KEYGEN(1)

NAME
     ssh-keygen -- authentication key generation, management and conversion

SYNOPSIS
     ssh-keygen [-q] [-b bits] -t type [-N new_passphrase] [-C comment] [-f output_keyfile]

(中略)

     -t type
             Specifies the type of key to create.  The possible values are ``rsa1'' for protocol version 1 and ``dsa'', ``ecdsa'' or ``rsa'' for protocol version
             2.

長いので使うところだけ抜粋します。

-t オプションで鍵の種類を指定できて、rsa1, dsa, ecdsa, rsa の4種類が指定できるようです。 今回は rsa を使います。

ssh-keygen -t rsa のコマンドを実行すると、後は対話的に進みます。

$ ssh-keygen -t rsa
Generating public/private rsa key pair.

公開鍵・秘密鍵のセットを作りますよー、と。

Enter file in which to save the key (/Users/[ユーザー名]/.ssh/id_rsa): /Users/[ユーザー名]/Vagrant/alice/id_rsa

ファイルをどこに保存するか聞かれていますが、デフォルトのままだとホストOSのホームディレクトリ直下の .ssh 以下に入っちゃうので、 ここは Vagrant/alice になるようにします。

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

認証するときのパスフレーズですが、ここは空にしておきます。2回聞かれます。

Your identification has been saved in /Users/[ユーザー名]/Vagrant/alice/id_rsa.
Your public key has been saved in /Users/[ユーザー名]/Vagrant/alice/id_rsa.pub.
The key fingerprint is:
(略)
The key's randomart image is:
(略)

なんかずらずらーと出ます。これで鍵は出来てます。(検証用なのでさらしても問題はないのですが、臆病なのでやめておきます・・・)

$ ls
id_rsa     id_rsa.pub

id_rsa が秘密鍵で、id_rsa.pub の方が公開鍵ですね。

サーバに登録するのは公開鍵なので、公開鍵の中身を Data Bag の方へ “key” という項目を新たに設けて追加しておきます。

$ cat data_bags/users/alice.json
// alice
{
  "id": "alice",
  "shell": "/bin/bash",
  "password": "megckhCt3LluU",
  "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE......(略)"
}

bob も同様に作ります。

$ cat data_bags/users/bob.json
// bob
{
  "id": "bob",
  "shell": "/bin/bash",
  "password": "8Q14e7OP5wroc",
  "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCg......(略)"
}

これで鍵ファイル自体の用意はできました。

ユーザーの公開鍵における Cookbook 見直し

Cookbook 内でディレクトリとファイルを作る必要があります。opscode からドキュメント探します。

http://docs.opscode.com/resource_directory.html http://docs.opscode.com/resource_file.html

こちらを参考にして、各ユーザーごとのホームディレクトリに .ssh ディレクトリと、authorized_keys ファイルを権限など変えつつ 自動的に作るレシピを書きます。

# Cookbook ユーザーとグループのテスト

data_ids = data_bag('users')
data_ids.each do |id|
  u = data_bag_item('users', id)
  user u['id'] do
    shell    u['shell']
    password u['password']
    supports :manage_home => true, :non_unique => false
    action   [:create]
  end

  directory "/home/#{id}/.ssh" do
    owner u['id']
    group u['id']
    mode 0700
    action :create
  end

  file "/home/#{id}/.ssh/authorized_keys" do
    owner u['id']
    mode  0600
    content u['key']
    action :create_if_missing
  end
end

group 'samplegroup' do
  group_name 'samplegroup'
  members    ['alice', 'bob']
  action     [:create]
end

ディレクトリは、本人だけが読み書き実行 (0700)、ファイルについても、本人だけが読み書き (0600) という権限のものを作ります。

鍵認証の確認の前に、実際にディレクトリやファイルが出来ているかどうかだけでも確認しておきましょう。

[vagrant@vmclient1 ~]$ su - alice
パスワード:
[alice@vmclient1 ~]$ ls -la
total 28
drwx------  3 alice alice 4096 May 11 21:49 .
drwxr-xr-x. 7 root  root  4096 May 11 19:16 ..
-rw-------  1 alice alice   23 May 11 21:45 .bash_history
-rw-r--r--  1 alice alice   18 Feb 21 21:09 .bash_logout
-rw-r--r--  1 alice alice  176 Feb 21 21:09 .bash_profile
-rw-r--r--  1 alice alice  124 Feb 21 21:09 .bashrc
drwx------  2 alice alice 4096 May 11 21:49 .ssh

おおー、.ssh ディレクトリは出来ておりますね。

[alice@vmclient1 ~]$ cd .ssh/
[alice@vmclient1 .ssh]$ ls
authorized_keys
[alice@vmclient1 .ssh]$ cat authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE...(略)[alice@vmclient1 .ssh]$

authorized_keys というファイルも出来ていて、中身も反映されておりました。ここまではオーケーですね!

Template を使って設定ファイルを配置する

さあ、あと残っているのは『/etc/ssh/sshd_config の設定項目編集』と『sshd サービスの再起動』だけです。

※sshd サービスの再起動に関しては、パッケージのインストールを詳しくやるタイミングで併せてやった方がいいので、今回は仮想マシンの再起動で代用してしまおうと思います。

今まで Cookbook の中に user, group, file, directory, package という記述を色々書いてきましたが、 これらは Resource と呼ばれています。

今度はこういったサーバの設定ファイルを配置するための Resource である、template を使います。

まずはテンプレートのファイルを用意する

レシピの方からテンプレートを指定するわけですが、先にテンプレートファイルを設置します。

Cookbook の中に recipes ディレクトリを作り、その中に default.rb というレシピファイルを作って今までいじってきましたが、 この recipes ディレクトリと並列に templates ディレクトリ を作ります。

$ mkdir cookbooks/vmclient1/templates

※実はここ、置き場所間違ってますが後で説明します・・・

その下に、sshd_config というファイルであれば、sshd_config.erb というファイルを作り、実際にゲストOSで使われている /etc/ssh/sshd_config というファイルの中身をごっそりもってきて、以下の必要最低限のところだけ修正します。

$ cat cookbooks/vmclient1/templates/sshd_config.erb
(長いので略)
PubkeyAuthentication yes
AuthorizedKeysFile     .ssh/authorized_keys
(長いので略)

公開鍵認証を有効にする設定のコメントアウトを外し、認証鍵ファイルの置き場所の設定もコメントアウトします。置き場所はさっきの通りですね。

これだけだと、変数とかの埋め込みがなくてテンプレート使ってる意味がないかもしれませんが、ここから先は erb の記法と同じく変数の埋め込みが可能です。(機会があれば後日行いたいと思います。)

※あとで調べたら、変数使わずに単にファイルを設置するときは、Template じゃなくて、File を使えば良かったっぽいですね。。。 http://docs.opscode.com/resource_file.html

テンプレートをレシピから読み込む

レシピが徐々に長くなってきたので、追記部分のみ記載しておきます。

template '/etc/ssh/sshd_config' do
  owner 'root'
  group 'root'
  mode 0600
end

元々置いてあったファイルが上記の設定だったので、合わせて root:root, 0600 にしてあります。

さて、さっき作ったテンプレートファイルは、ファイル名を合わせておけばこれだけで動くとのことですが、 ホントにこれだけで問題ないのか試してみます。

vagrant up! vagrant up!

[2013-05-11T22:35:06+00:00] FATAL: Chef::Exceptions::FileNotFound: template[/etc/ssh/sshd_config] (vmclient1::default line 28) had an error: Chef::Exceptions::FileNotFound: Cookbook 'vmclient1' (0.0.0) does not contain a file at any of these locations:
  templates/centos-6.4/sshd_config.erb
  templates/centos/sshd_config.erb
  templates/default/sshd_config.erb

なんかエラー出ました。これによると、テンプレートファイルが見つからなかったようです。

テンプレートファイルは、‘Linuxディストリビューション名’-‘バージョン’, ‘Linuxディストリビューション名’, ‘default’ を それぞれ自動で探してくれるようになっているようです。素敵ですねー。

ということで、正しいパスは templates/default/sshd_config.erb でしたー。移動させて再度試します。

[2013-05-11T22:41:13+00:00] INFO: Processing template[/etc/ssh/sshd_config] action create (vmclient1::default line 28)
[2013-05-11T22:41:13+00:00] INFO: template[/etc/ssh/sshd_config] backed up to /var/chef/backup/etc/ssh/sshd_config.chef-20130511224113
[2013-05-11T22:41:13+00:00] INFO: template[/etc/ssh/sshd_config] updated content
[2013-05-11T22:41:13+00:00] INFO: template[/etc/ssh/sshd_config] owner changed to 0
[2013-05-11T22:41:13+00:00] INFO: template[/etc/ssh/sshd_config] group changed to 0
[2013-05-11T22:41:13+00:00] INFO: template[/etc/ssh/sshd_config] mode changed to 600
[2013-05-11T22:41:13+00:00] INFO: Chef Run complete in 0.121144117 seconds
[2013-05-11T22:41:13+00:00] INFO: Running report handlers
[2013-05-11T22:41:13+00:00] INFO: Report handlers complete

ログを見る限りだと、上手いこと出来たっぽいですね!

さて、ログインして見てみましょう!

[vagrant@vmclient1 ~]$ su
パスワード:(vagrant といれた)
[root@vmclient1 vagrant]# cat /etc/ssh/sshd_config
(略)
PubkeyAuthentication yes
AuthorizedKeysFile     .ssh/authorized_keys
(略)

おおー、ちゃんとテンプレートが反映されておりました!

ということは、これで一通り SSH 周りの設定が完了したことになります。(sshd の再起動を除く)

最後に、再度 vangrant reload しておきます。sshd の再起動もそうなんですが、サービスの起動、再起動についてはまた後日やりたいと思います。

鍵認証でログイン

ここから先はホストOS上での作業です。

ssh のオプションいろいろつけて、鍵ファイル指定するとかめんどくさいので、~/.ssh/config に alice, bob 用の設定を追加しておきましょう。

$ vi ~/.ssh/config

ここに Host default の設定項目があって、 vagrant ユーザーが接続するための設定がずらずら書いてあるのでこれを流用して、 alice ユーザー、bob ユーザーでログインできるようにしてみます。

Host vmclient1_by_alice
  HostName 192.168.100.11
  User alice
  Port 22
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile "/Users/[ユーザー名]/Vagrant/alice/id_rsa"
  IdentitiesOnly yes
  LogLevel FATAL

Host vmclient1_by_bob
  HostName 192.168.100.11
  User bob
  Port 22
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile "/Users/[ユーザー名]/Vagrant/bob/id_rsa"
  IdentitiesOnly yes
  LogLevel FATAL

そして、ssh を実行してみます!

$ ssh vmclient1_by_alice
Welcome to your Vagrant-built virtual machine.
[alice@vmclient1 ~]$

alice 鍵認証きたーーー!! ええ、もちろんパスワード入力しておりません。

さあ、bob はどうでしょう?

$ ssh vmclient1_by_bob
Welcome to your Vagrant-built virtual machine.
[bob@vmclient1 ~]$

bob も鍵認証きたーーー!!

まとめ

これで最低限のユーザー管理の自動化が出来るようになったと言ってもいいのかもしれません。

また、汎用的に以下のことが少し出来るようになりました。

次はアプリケーションのインストールを色々やってみて、 この辺がある程度慣れてくれば、レシピも比較的自由に書けるようになるのではないかなーと思います。 がんばります!

参考URL

この記事は書かれてから1年以上が経過しており、最新の情報とは異なる可能性があります

もし記事内に誤りなどございましたら、 @girigiribauer までご一報いただけると助かります。