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

Itamaeでサーバーの公開鍵や設定ファイルを管理する” に対して1件のコメントがあります。

  1. typoありました。

    –method=aws-passphrase
     ↓
    –method=aes-passphrase

    1. tonbi より:

      ご指摘ありがとうございます。
      完全にtypoしてますね、、修正しました!

  2. 連投すみません。

    ./roles/user-testuseradmin.rb の
    authorization: {
    sudo: {
       . . .
    commands: “ALL”, ← commands: [“ALL”]と配列にしないと、レンダリングで落ちます
    },
    },

    1. Itamaeを始めてみたのですが、まだまだドキュメントが少ない中、貴重な情報でした。
      とても参考になりました!ありがとうございます。

    2. tonbi より:

      こちらも修正しましたm(__)m
      私もまとまった情報がなく探り探りやっていて、一連の流れの情報があればなあと思って書きました。
      参考になって何よりです!

tonbi へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です