AnsibleでInSpecを利用する


InSpec1自体はAnsibleのRoleをTDDで作成するときにkitchen-inspecでよく利用していたものの、実環境に対するテストでは使ったことがなかったのでどう使うかを整理。

環境

  • Ansible 2.7.10
  • InSpec 4.3.2

InSpecの基本的な使い方

インストール

Chefと同じくパッケージが提供されているのでChef Downloadsからダウンロードするのが手っ取り早い。
gem installする場合はCLIで利用するためにinspec-binが必要なので注意。

rpmの場合
$ yum -y localinstall /tmp/inspec-4.3.2-1.el7.x86_64.rpm
gemの場合
$ rbenv exec gem install inspec inspec-bin
$ rbenv rehash
インストール確認
$ inspec --version
4.3.2

$ inspec help
Commands:
  inspec archive PATH                # archive a profile to tar.gz (default) or zip
...

Profileの作成

knifeコマンドよろしくinspecコマンドにもテンプレートの生成機能があるのでそちらを利用。
inspec init profile PROFILE_NAME

$ inspec init profile my-profile
my-profile
├── README.md
├── controls
│   └── example.rb
├── inspec.yml
└── libraries

ProfileのDirectory Layoutの概要は以下の通り。

  • inspec.yml (required)
    • このテストに関するメタ情報を記載する
    • 実行可能なOS環境や他のテストとの依存関係などの定義が可能
  • controls (required)
    • テストを配置するディレクトリ
    • このディレクトリ配下に配置されたrbファイルがすべて実行対象になる
  • libraries (optional)
    • Custom Resourcesを配置するディレクトリ
    • 今のバージョンだと標準のResourcesだけで大体事足りるので使うことはあんまりないかも
  • files (optional)
    • inspec init profileでは作成されない
    • このディレクトリ配下のファイルはテストコードの中でinspec.profile.file('ファイル名')とすることでアクセス可能
    • テストで利用するパラメータを定義するために使うが、環境依存で変更されるファイルを置く場所ではないことに注意

リモートサーバへのテスト実行

サンプルで作成されるexample.rbは「/tmpというディレクトリが作成されていること」のテストになっているのでテスト対象がCentOSとかであればinspec init profileで作成したプロファイルを使ってそのまま試せる。
Ansibleで管理しているのであれば、Ansible実行用のリモートユーザをそのまま使えばOK。
inspec exec PROFILE_PATH --target=ssh://REMOTE_USER@REMOTE_HOST

実行例
$ inspec exec my-profile --target=ssh://[email protected]

Profile: InSpec Profile (my-profile)
Version: 0.1.0
Target:  ssh://[email protected]:22

  ✔  tmp-1.0: Create /tmp directory
     ✔  File /tmp should be directory

  File /tmp
     ✔  should be directory

Profile Summary: 1 successful control, 0 control failures, 0 controls skipped
Test Summary: 2 successful, 0 failures, 0 skipped

Ansibleとの併用

Serverspecでもちょっと考える必要があるのがサーバ毎のパラメータを読み込む箇所。
Ansibleではansible-inventoryを利用することで実行対象で利用されるvarsをyaml形式で出力できる2
InSpecでは--input-fileオプションを使うことで外部のyamlファイルをロードすることができるので、生成したyamlファイルをロードすることでサーバ毎のパラメータを利用したテストが実行可能となる。

Direcotry Layoutについて

ローカル検証用にochiba/inspec-exampleを用意したのでこちらで解説。

├── ansible.cfg
├── inventories
│   └── stg
│       ├── group_vars
│       ├── host_vars
│       └── nodes.yml
├── plays                              # playbookの配置先(roles_pathはansible.cfgで調整)
├── roles
│   └── common
│       ├── defaults
│       ├── tasks
│       ├── templates
│       └── test
│           └── integration
│               └── default            # InSpec Profile
│                   ├── controls
│                   ├── inspec.yml
│                   └── libraries
└── test
    └── attributes                     # ansible-inventoryで出力したvarsの配置先

Alternative Directory LayoutからPlaybookの配置を少し弄った構成。
Profile自体はTDDで作成したものを使い回すのがポイント。

パラメータの呼び出し方

検証用に作成したテストは以下の通り。

roles/common/test/integration/default/controls/myprofile.rb
# encoding: utf-8
# copyright: 2019, Hiroki KUNITAKE

title 'MyProfile section'

control 'myprofile-01' do
  impact 0.7
  title 'Create myprofile.txt'
  desc 'Check contents'
  describe file('/var/tmp/myprofile.txt') do
    it { should be_file }
    its('content') { should match /^My name is #{attribute('my_name')}/ }
    its('content') { should match /^I'm in #{attribute('my_group')}/ }
  end
end

--input-fileオプションで読んだパラメータはattribute('VARS_NAME')で呼び出せる。
階層構造になっている場合は親を呼び出して一旦変数に格納してから利用する。

実行サンプル

ansible-inventoryを利用してAttributes File(vars情報が入ったyamlファイル)を作成し、そのファイルを--input-fileオプションで食わせてテストを実行する。

$ ansible-inventory --yaml --inventory=inventories/stg/nodes.yml --host=192.168.33.10 > test/attributes/192.168.33.10.yml
$ cat test/attributes/192.168.33.10.yml
my_group: Web
my_name: Web-01

$ inspec exec roles/common/test/integration/default --target=ssh://[email protected] --input-file=test/attributes/192.168.33.10.yml

Profile: Common baseline (common-baseline)
Version: 0.1.0
Target:  ssh://[email protected]:22

  ✔  myprofile-01: Create myprofile.txt
     ✔  File /var/tmp/myprofile.txt should be file
     ✔  File /var/tmp/myprofile.txt content should match /^My name is Web-01/
     ✔  File /var/tmp/myprofile.txt content should match /^I'm in Web/


Profile Summary: 1 successful control, 0 control failures, 0 controls skipped
Test Summary: 3 successful, 0 failures, 0 skipped

今のところinspec execそのものには並列実行の機能がなく1ホストずつしか実行できない。
グループ単位でテストを実行したい場合はProfileやAttirbutes Fileの配置パスについてルールを設けてクリプト化する必要がある。


  1. Serverspecの拡張として作られているテストツール。サーバの状態をチェックするのにも利用できるが、どちらかといえばある規則に従っているかどうかをスコア付けするためのツール。 

  2. Factsやvars_filesはその特性上ansible-inventoryでは出力できないので別の手立てが必要。