CL LAB

HOME > CL LAB > Chef > [和訳] InSpecへの道 #getchef

[和訳] InSpecへの道 #getchef

 ★ 3

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

本稿は The Road to InSpec (2015/11/04) の和訳です。

当社は火曜日、Chef Compliance のリリースの一環として、InSpec infrastructure testing framework のリリースを発表しました。今日は Serverspec 拡張が現在のスタンドアロン・プロジェクトに至るまで、我々がどのようにそしてなぜ InSpec をクリエイトすることにしたのかを掘り下げたいと思います。

コンプライアンステストのための Serverspec

今年初旬に Chef 社が買収したスタートアップ企業 VulcanoSec 社は、ある問題を解決するためのものでした――我々はどうすれば DevOps の最優良事例をコンプライアンスの世界にもたらすことが出来るのか。我々はすでに DevOps の世界に深く根付いており、インフラ管理に持ち込むという大転換も経験しました。我々はそれと同様の利点とワークフローをセキュリティとコンプライアンスの領域にももたらしたいと考えていました。

コンプライアンスをチェックするための既存のソリューションは、大きく分けて2つの領域に分類することができます: 静的解析と動的スクリプトベースのツールです。最初は多くの場合、期待値と結果が示される場 (例えば、XML) が移植性の高いシンタックスに付随してきます。これは移植性には有効ですが、たいてい柔軟性には欠けています。なぜならこれらのドキュメントには大々的に修正されたスキーマがあるからです。一方でスクリプト・ドリブン・ソリューションはユーザにテストを自由に定義させることで逆方向からの問題に対処します。ただ、それはより高度な複雑さと保持性を生む代償を支払うことになります。我々は最終的に革新的でありながらカスタマイズが容易で、軽量構造のコンプライアンスチェックを供給することができる何かを必要としていました。

我々は、通常はセキュリティ・アサーションの根源であるインフラ・テストを受容した Serverspec プロジェクトに感銘を受けました。Serverspec はインフラ・テスト用の記述的言語を作るために Ruby と RSpec を採用しました。Serverspec は静的な期待 (例えばリソース X が Y 状態になるという期待) とプログラムの柔軟性をバランス良く提供します。

Serverspec の核心部分においては、我々はただコンプライアンスに必要なものを作らなければなりませんでした。我々はテストがコンプライアンス・プロフィールにグループ化されるコントロール・コレクションだと表現したかったのです。これらは容易にシェアや拡張が可能でなければならず、一つのプロジェクトが次につながるのに単純な調整で済むメカニズムを提供する必要があったのです。テストの意味を説明するのに役立つ記述的メタデータと同様にクリティカリティを追加し(そうすればトラック一台分の失敗が見つかったときに最初に何を注視すべきか分かる)我々はテストがより記述的であることを望みました。

VulcanoSec の初期に我々が作成したソリューションは、DevOps に対して卓越したスタックを持っていました。表現力豊かな Ruby ドリブンのコア機能、Serverspec によって作り上げられた構造、至るところに加えられたコンプライアンスです。

現場からの学び、または InSpec への進化

コアフレームワークが準備できたので、我々はそれをコンプライアンスとセキュリティ監査の担当者の手に委ねました。最初のきっかけはしばしば DevOps の世界についての好奇心であり、それに引き続き Ruby との密着合に対する懸念、そして最後はいかにこれが日常業務やコンプライアンス維持の改善につながる可能性を持ち合わせているかということに対する大きな期待でした。

スマート・リソース

テストユーザーの手に渡ることで、我々は彼らのドメインに近づく方法を大いに学ぶことになりました。我々は自分たちの考え方を変えなくてはならないこと、監査役が実際に何を望み、一体何ができるのかについて注力しなければならないことに気づきました。そして、彼らは確実に、実際のテストのように莫大なチェック用のコードを書くことを望んでいませんでした。

if os[:family] == 'redhat' or os[:family] == 'fedora'
 
if file('/etc/ssh/sshd_config').exists? and
    file('/etc/ssh/sshd_config').readable?
 
 describe 'check ssh config values' do
 
   ...
 
 end
 
else
     describe 'check ssh config values' do
       skip "Cannot read sshd configuration file."
     end
    end
 
else
    describe 'check ssh config values' do
      skip "It is not supported on your OS #{os[:family]}"
  end
end

むしろ、彼らはリソースによって作業を終わらせることを求めていました。それにより、彼らは最小限の努力でテストを作成することだけに集中できたのです。

describe sshd_config do
  # リソースが読めないまたはサポートされてないとしても、テストを失敗しない!
  # その代わり、ここでサポートされていなければ、自動的にテストを省略する。
  ...
end

前述の例は我々が作成するすべてのテストを登録するだろうし、リソースがサポートされない、もしくは調べることができない場合は適切なメッセージと共に省略されることになります。コンプライアンスチェックはスーパーユーザーアクセスを必要とするという状態で省略され、一般ユーザーとして実行させます。

カスタム・リソース

次の段階で我々が学んだことは、ユーザーが簡単にカスタムリソースを作成したいというニーズがあることです。私が過去の記事 (訳注:英文)に書いた通り、Serverspec では既にリソースを作成することができます。我々はこの概念を次のレベルまで持って行きたかったのです。

それらを利用したいだけの人々がいる一方で、何人かの監査役はとてもコーディングが得意であり、専用のリソースを作成したがっています。彼らをサポートするために我々はカスタム・リソースを作成し、既存のカスタム・リソースを拡張する明確なインターフェイスを作成する必要がありました。バックエンドとやりとりするのは容易であるべきで、サポートするものを定義したうえで実際のテストに価値を与えなければなりません。単にリソースを利用したい一般ユーザーにとって、簡単に実施できるようになっていなければならないのです。

多くの議論および数回に及ぶ開発の後、我々は明確なバックエンドおよびインターフェースの定義を持った InSpec プラグインシステムを作成しました。次は SSH サーバー構成リソースの例です:

class SshConf < Inspec.resource(1)
  name 'ssh_config'
  ...
end

これは新しいリソースを ssh_config として登録します。これにより、テストでの使用が可能になります:

describe ssh_config do
  ...
end

なぜならこれは InSpec リソースのため、バックエンドへのアクセスおよび他のすべてのリソースにアクセスできます:

def read_file
  f = inspec.file('/etc/ssh/ssh_config')
  return skip_resource "Can't find SSH config" if !f.file?
  return skip_resource "Can't read SSH config" if f.content.empty? && f.size > 0
  @content = f.content
end

これは氷山の一角にすぎません。リソースの継承、リソース共有および再利用はこのモデルを含む OS を定義することと同様です。このためのインターフェイスをとても明確で明示的に作成することで、我々はリソースを作成し共有するための構造を提供します。

バックエンド実行

この部分は InSpec 内部に関することですが、我々にとっては非常に重要なことでした。

Serverspec のリソースを呼び出すと、実際の呼び出しは Serverspec と Specinfra という 2つのプロジェクトを通り抜けていきます。これにより、テストフレームワークを実際の転送レイヤーと実行レイヤーとに分離しています。Specinfra の中には、それぞれの OS がサポートするように実装された巨大な呼び出しの集合を見つけることができます。

我々は明快なモジュール化されたシステムを好んではいましたが、少しだけ変えたいと思っていました。リソースについて何も知らなくてよい、最小限の実行レイヤーおよび転送レイヤーを手にすることを考えていました。このレイヤーはコマンドを実行するためだけの機能のスモールセットや基本的な OS 情報を提供し、システムに関するファイルと相互通信するように設計されています。このプロジェクトは現在、train(transport interface)と呼ばれており、名前からも分かるように転送設定を取り扱います。ローカルで行ったコマンドを、SSH、WinRM、さらにはDocker上で行えます。

train の導入によって InSpec はリソースを管理し、実際にテストを行うことに注力できるようになりました。また、OS の差異はリソース自体、すなわち InSpec の内部で完結しました。このアプローチは抜本的にリソースの実行と通信の実行の間での相互通信を変えたため、もはや以前のモデルには適合しませんでした。

serverspec-and-inspec

CLI

最後のステップは便宜上のためのものであり、Ruby や RSpec、Rake に慣れていないユーザーに役立つ情報です。Serverspec はコードの適切な位置で RSpec を介して自らを設定する方法を備えており、rspec か Rake タスクを用いて自らを実行します。我々はかつて同様のアプローチをとった spec_helper.rb を作成しました。ユーザー、あるいは我々の場合はコンプライアンステストのバックエンドは、テストを呼び出して SSH/WinRM のための設定を環境変数に入れるでしょう。これらはヘルパースクリプトによって RSpec/Serverspec 設定に転送されるでしょう。基本的にはその他のコマンドラインユーティリティ無しで Serverspec テストを実行することができたのです。

我々が InSpec を作成した時、Rake や spec_helper.rb については何も知識がなくても、ユーザーにテストを実行する方法を提供したかったのです。快適で何でも設定できるコマンドラインユーティリティを持ちたかったのです。さらにユーザーは一般的な RSpec ユニットテストを模倣したような複雑なフォルダ構成を作成することなく、普通のファイルに書き込まれたテストが実行できなくてはなりませんでした。これは速いテストとデバッギングに向けられています。

InSpecでは、こんなに簡単な呼び出しだけでテストを実行できます:

inspec exec test.rb

SSH/WinRM を使用しているリモートマシンまたは Docker コンテナに対しても簡単にテストを実行できます。

inspec exec test.rb --target ssh://user@hostname
inspec exec test.rb --target winrm://Administrator@hostname
inspec exec test.rb --target docker://container_id

我々もまた RSpec ベースの設定を忘れられないので、Ruby ドリブンのワークフローに基づきテストを実行したいユーザーへの Rake タスクを追加する予定です。

まとめ

InSpec は Serverspec プロジェクトで使われた当初のバージョンから現在のインフラテストのためのスタンドアロンフレームワークまで、サーバーとコンプライアンステストにおける長旅の最終局面において登場しました。我々は、賢くて拡張可能なリソース、ローカル/リモート/コンテナテストおよび有用なユーティリティのための一般的な実行フレームワークを追加しました。

我々はプロジェクトへの協力者を手ぐすね引いてお待ちしています!

https://github.com/chef/inspec

Related post

mautic is open source marketing automation