Itamaeでサーバーの公開鍵や設定ファイルを管理する
今まではchefを使っていたんですが、小規模のプロジェクトをいくつか担当することになりました。
chefのdata_bagを使っていて、こんな感じのことができてなおかつお手軽そうなツールを…ということでitamaeを試してみました。
※使い方や書き方が正しいかどうかはあまり考慮していないです^^;
とりあえず動かすことを目標に…
環境
ローカル
OS X EI Capitan 10.11.4
サーバー
Ubuntu 14.04.3 LTS
やりたいこと
- いろんなサーバーの複数ユーザーの複数の公開鍵(authorized_keys)を管理したい
- ユーザーと鍵を保持しておいて、レシピ実行時に自動生成したい
- (おまけ)ミドルウェアの設定ファイルなんかもちょっと管理してみたい
Itamaeインストール
これを参考にします。
Getting Started · itamae-kitchen/itamae Wiki · GitHub
$ mkdir itamae $ cd itamae $ vi Gemfile source 'https://rubygems.org' gem 'json' gem 'itamae' gem 'itamae-secrets' $ bundle install
鍵管理で使用するitamae-secretsも入れておきます。
やってみる
参考URL
[itamae]ユーザ作成と公開鍵追加のレシピを作ったった – ADACHIN SERVER Lab
Chef-soloからItamaeに完全移行した話 – Qiita
Chef SoloからItamaeに完全移行した話+
Best Practice · itamae-kitchen/itamae Wiki · GitHub
GitHub – sorah/itamae-secrets: Encrypted Data Bag for Itamae
仮定
- とあるプロジェクトのフロントサーバーを立てることになった
- 例えばfront1.project.domainというホスト名で接続できるものとする
- サーバーで一般ユーザーと管理ユーザーを分けて作成する
- 管理ユーザーはパスワードなしでsudo可能にする
- apacheをインストールして設定ファイルも配置する
公開鍵登録
とりあえずitamae-secrets自体のベースディレクトリと鍵を登録して使えるようにします。
$ echo 'base: ./secret' >> .itamae-secrets.yml $ itamae-secrets newkey --method=aes-passphrase (パスフレーズ入力)
itamae-secretsを使ってキーと値を登録します。
社員が4人いるということにして4個登録します。
$ itamae-secrets set userA 'ssh-rsa hogehoge userA@localhost' $ itamae-secrets set userB 'ssh-rsa hogehoge userB@localhost' $ itamae-secrets set userC 'ssh-rsa hogehoge userC@localhost' $ itamae-secrets set userD 'ssh-rsa hogehoge userD@localhost'
getしてみて、setした値が取れればOKです。
$ itamae-secrets get userA ssh-rsa hogehoge userA@localhost
entrypoint.rb
参考URLの通りですが、nodeでrecipe指定をするためのスクリプトを準備します。
nodeにenvironmentが定義されていたら読み込むように追記しました。
./entrypoint.rb
if node[:environment] then include_recipe node[:environment] end node[:recipes] = node[:recipes] || [] node[:recipes].each do |recipe| include_recipe recipe end
ノードを作成する
nodeはサーバー単位に作るようにします。hostnameは後で使えそうなので定義しておきます。
enteypoint.rbがrecipeをくるくる読んでくれるはずです。
./nodes/front1.project.domain.json
{ "base": { "hostname": "front1.project.domain" }, "environment": "./environments/production.rb", "recipes": [ "./roles/project-front.rb", "./roles/user-testuser.rb", "./roles/user-testuseradmin.rb" ] }
ロールを作成する
一般ユーザー用
userを追加するところは後からユーザーの追加・削除がしやすいように改行で記述しておきます。
./roles/user-testuser.rb
user_name = "testuser" node.reverse_merge!( ssh_keys: { "#{user_name}": [ "userA", "userB", ], }, ) include_recipe "../cookbooks/user/#{user_name}.rb"
管理ユーザー用
一般ユーザー用とほぼ同じですがsudoレシピをincludeし、attributeをセットします。
./roles/user-testuseradmin.rb
user_name = "testuseradmin" node.reverse_merge!( ssh_keys: { "#{user_name}": [ "userC", "userD", ], }, authorization: { sudo: { include_sudoers_d: "true", user: "#{user_name}", host: "ALL", runas: "ALL", nopasswd: "true", commands: ["ALL"], }, }, ) include_recipe "../cookbooks/sudo/recipe.rb" include_recipe "../cookbooks/user/#{user_name}.rb"
フロントサーバー汎用(apacheなど)
ミドルウェアなどのレシピをincludeするroleを作成します。
サンプルということでapacheのrecipeだけincludeします。
./roles/project-front.rb
include_recipe "../cookbooks/project-front/apache2.rb"
レシピを作成する
一般ユーザー用
こちらもほぼ参考URLの通りです。
roleで定義したuser_nameを回して改行をくっつけて変数に突っ込んでいます。
./cookbooks/user/testuser.rb
require 'rubygems' require 'itamae/secrets' include_recipe "./attribute.rb" user_name = "testuser" ssh_keys = [node[:ssh_keys][:header]] node[:secrets] = Itamae::Secrets(File.join(__dir__,'../../secret')) node[:ssh_keys][:"#{user_name}"].each do |user| ssh_keys.push(node[:secrets][:"#{user}"] << "\n") end ssh_key = ssh_keys.join user "#{user_name}" do action :create username user_name create_home true home "/home/#{user_name}" shell "/bin/bash" end directory "/home/#{user_name}" do owner user_name group user_name mode "755" end directory "/home/#{user_name}/.ssh" do owner user_name group user_name mode "700" end file "/home/#{user_name}/.ssh/authorized_keys" do content ssh_key owner user_name group user_name mode "600" end
管理ユーザー用
./cookbooks/user/testuseradmin.rb
require 'rubygems' require 'itamae/secrets' include_recipe "./attribute.rb" user_name = "testuseradmin" ssh_keys = [node[:ssh_keys][:header]] node[:secrets] = Itamae::Secrets(File.join(__dir__,'../../secret')) node[:ssh_keys][:"#{user_name}"].each do |user| ssh_keys.push(node[:secrets][:"#{user}"] << "\n") end ssh_key = ssh_keys.join user "#{user_name}" do action :create username user_name create_home true home "/home/#{user_name}" shell "/bin/bash" end directory "/home/#{user_name}" do owner user_name group user_name mode "700" end directory "/home/#{user_name}/.ssh" do owner user_name group user_name mode "700" end file "/home/#{user_name}/.ssh/authorized_keys" do content ssh_key owner user_name group user_name mode "600" end
sudo用
variablesはもう少しやりようがある気がしますが、とりあえずこんな感じで。
あとでsudoerファイルのtemplateにて使用します。
./cookbooks/sudo/recipe.rb
include_recipe "./attribute.rb" user = node[:authorization][:sudo][:user] package 'sudo' do not_if 'which sudo' end if node[:authorization][:sudo][:include_sudoers_d] directory "/etc/sudoers.d" do mode '750' owner 'root' group 'root' end template "/etc/sudoers.d/#{user}" do source 'templates/sudoer.erb' mode '440' owner 'root' group 'root' variables( :defaults => [], :sudoer => node[:authorization][:sudo][:user], :host => node[:authorization][:sudo][:host], :runas => node[:authorization][:sudo][:runas], :nopasswd => node[:authorization][:sudo][:nopasswd], :commands => node[:authorization][:sudo][:commands] ) end end
apache用
基本ファイルだけtemplateを使って配置するレシピです。
とりあえずactionはnothingで。。
./cookbooks/project-front/apache2.rb
include_recipe "./attribute.rb" package 'apache2' template '/etc/apache2/apache2.conf' do source 'templates/etc/apache2/apache2.conf.erb' owner 'root' group 'root' mode '644' end template '/etc/apache2/sites-available/000-default.conf' do source 'templates/etc/apache2/sites-available/000-default.conf.erb' owner 'root' group 'root' mode '644' end service 'apache2' do action [:nothing] end
テンプレート作成
sudo用
nodeのattributeを使ってsudoerファイルの中身を埋めています。
./cookbooks/sudo/templates/sudoer.erb
<% @commands.each do |command| -%> <%= @sudoer %> <%= @host %>=(<%= @runas %>) <%= 'NOPASSWD:' if @nopasswd %><%= command %> <% end -%>
apache用
nodeのattributeを使ってServerNameを埋めています。
./cookbooks/project-front/templates/etc/apache2/sites-available/000-default.conf.erb
<VirtualHost *:80> ServerName <%= node[:base][:hostname] %> DocumentRoot "/home/testuser/www" ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined <Directory /home/testuser/www> AllowOverride all Require all granted </Directory> </VirtualHost>
その他
例えば、共通で入れたいヘッダーをattiribute.rbに入れてみたり…。
下の例ではauthorized_keysの先頭行にコメントを入れています。
./cookbooks/user/attribute.rb;
node.reverse_merge!( ssh_keys: { header: "# Created by Itamae\n", }, )
今回はステージごとの設定は空にしていますが、プロダクション環境やテスト環境を分けたければenvironmentsディレクトリ内のファイルに固有情報を記載すれば環境分けが可能かと思います。
実行
ドライラン
$ itamae ssh -u ubuntu -h front1.project.com -j nodes/front1.project.com.json entrypoint.rb -n
問題なければ本実行
$ itamae ssh -u ubuntu -h front1.project.com -j nodes/front1.project.com.json entrypoint.rb
ツリー構造
最終的にこんな構成になりました。
. ├── Gemfile ├── cookbooks │ ├── project-front │ │ ├── apache2.rb │ │ ├── attribute.rb │ │ └── templates │ │ └── etc │ │ └── apache2 │ │ ├── apache2.conf.erb │ │ └── sites-available │ │ └── 000-default.conf.erb │ ├── sudo │ │ ├── attribute.rb │ │ ├── recipe.rb │ │ └── templates │ │ └── sudoer.erb │ └── user │ ├── testuser.rb │ └── testuseradmin.rb ├── entrypoint.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── staging.rb ├── nodes │ └── front1.project.domain.json ├── roles │ ├── project-front.rb │ ├── user-testuser.rb │ └── user-testuseradmin.rb └── secret ├── keys │ └── default └── values ├── userA ├── userB ├── userC └── userD
typoありました。
–method=aws-passphrase
↓
–method=aes-passphrase
ご指摘ありがとうございます。
完全にtypoしてますね、、修正しました!
連投すみません。
./roles/user-testuseradmin.rb の
authorization: {
sudo: {
. . .
commands: “ALL”, ← commands: [“ALL”]と配列にしないと、レンダリングで落ちます
},
},
Itamaeを始めてみたのですが、まだまだドキュメントが少ない中、貴重な情報でした。
とても参考になりました!ありがとうございます。
こちらも修正しましたm(__)m
私もまとまった情報がなく探り探りやっていて、一連の流れの情報があればなあと思って書きました。
参考になって何よりです!