定期的なコンプライアンスチェックを簡単に! Systems Manager + カスタムinspecを使ってみた

オペレーション部 江口です。 当社ではPCIDSSなどのコンプライアンス対応の一環として、対象資産で問題がないかの定期的な確認を行なっています。

この作業は定型化されていて、基本的にコマンドを実行してその結果を確認するだけなのですが、現在は監査に対応する人間が直接サーバにログインしオペレーションを行なっており、この辺もう少し工夫の余地はあるなーと思ってたりします。

で、色々調べてみていて、「コンプライアンス対応のチェックのためのテストツールとして"InSpec"というのがある」というのを知りました。 さらに調べると、どうもこのInSpecはAWSのSystems Managerと連携できるそうではないですか。

と言うか、このdevelopes.ioでも他の方が紹介した記事がすでにありました。

【アップデート】Systems Managerがインフラテストツール「InSpec」に対応しました!

上記の記事ではInSpecスクリプト(正確にはプロファイルという単位で管理します。後述)については事前に定義されたものを利用しての実行を紹介していますが、今回は自分で作成したInSpecスクリプトを実行〜Systems manager上で結果を確認してみました。というわけで、自前のInSpecスクリプトの作成方法も含めて手順を紹介してみたいと思います。

InSpecとは

前掲の記事でも紹介していますがあらためて。 InSpecはChef社がOSSで公開しているインフラストラクチャのテストツールです。(言語:Ruby)

https://www.inspec.io/

先行するインフラストラクチャのテストツールである「Serverspec」にインスパイアされて開発されたツールということですが、Topページで「Chef InSpec is compliance as code」と謳っているように、よりセキュリティ要件・コンプライアンスのチェックにフォーカスしています。チェックの内容をユーザがコードとして記述し、それに適合しているかどうかを確認・レポートしてくれます。

記述の仕方もServerSpecと似ています。細かい相違は本記事では省略しますが、開発者がどのように考えてInSpecを開発したかは、下記記事が参考になると思います。

https://www.creationline.com/lab/12102

(クリエーションライン様による日本語翻訳記事。原文はこちら

Systems ManagerとInSpecとの連携

AWSのSystems Managerは指定EC2インスタンス上で任意のコマンドを実行する機能を備えていて、その一環としてGithubリポジトリ、あるいはS3にアップロードされたInSpecスクリプトを実行することができます。各EC2インスタンスにログインする必要がなく、かつInSpecのスクリプトも各サーバに個別にデプロイする必要もない点が大きなメリットとなります。

さらに、コンプライアンスダッシュボードからInSpecの実行結果を確認することができるので、どのインスタンスでどのような違反が存在したのかの確認も簡単に行えます。

上記スクリーンショットは、この後に記したカスタムInSpecスクリプトを実際に実行した結果をコンプライアンスダッシュボードで表示しているところを撮ったものです。

では実際にスクリプトを作って実行させてみましょう!

InSpecスクリプトの作成

実施するテスト項目

まずどのようなテストを行うのか、を決める必要があります。 当社の定期チェックでは、例えば「対象サーバに意図したユーザアカウントのみが登録されているか」というのをチェックしています。 このチェックは

  • /etc/passwdでUID1000番以降のアカウントを抽出する
  • 抽出したリストを確認し、適正なアカウントのみが存在することを確認する

というものです。手頃なのでこれを確認するスクリプトを作ってみることにします。

下準備

開発環境

まずInspecをインストールした開発環境を用意します。Inspecは単体でもインストール可能ですが、ChefDKをインストールするとバンドルされてきますので、おすすめです。 ChefDKのインストールは下記を参照してください。

https://docs.chef.io/install_dk.html

プロファイルの作成

InSpecでは「プロファイル」という単位でテストを管理します。各テストは「コントロール」と呼ばれ、プロファイル内で管理されます。 InSpecがインストールされたCLIで以下のようにコマンドを実行すると、最低限必要なディレクトリ・ファイルが生成されます。

inspec init profile [プロファイル名]

実行すると、入力したプロファイル名のディレクトリの中は下記のようになっていると思います。

├── README.md
├── controls
│   └── example.rb
├── libraries
└── inspec.yml

inspec.ymlはプロファイルの概要を記したYAMLファイル、controlsは実際にテストを記載したスクリプトを配置するディレクトリとなります。 最初から例としてexample.rbというファイルが作成されています。

プロファイルの詳細は以下のページを参照してください。

https://www.inspec.io/docs/reference/profiles/

テストの作成

ではcontrolsの下にスクリプトのファイルを作成し、実際にテストを記述していきます。

title "User account check"

# 存在が期待されるユーザのリスト
allowed_users = ['ec2-user', 'ssm-user', 'nfsnobody']

control "Expected user existance check" do
  # テストに失敗した場合の影響度。0-1の間で記述
  impact 0.5
  title "存在すべきユーザのチェック"    
  # UID 1000-65534の範囲のユーザを取得   
  users.where { uid >= 1000 }.usernames.sort.each do |u|
    describe user(u) do
      # allowed_usersにエントリーのあるユーザであれば、存在するべき
      if allowed_users.include?(u)
        it { should exist }
      # エントリーにないユーザにであれば、存在するべきではない
      else
        it { should_not exist }
      end
    end
  end
end

・・・実際のところ、このスクリプトはInSpecのドキュメントにあったサンプルスクリプトほぼそのままです(ドンピシャのものがあったので・・・)

一応解説しておくと、存在するべきユーザのリストを定義(allowed_users)。 「users」というリソースタイプでユーザの情報を取得して、UIDが1000以降のエントリのみ抽出するようフィルタを適用。 その中で定義したユーザのリストにあるアカウントについては「存在するべき」、そうでないアカウントについては「存在しないべき」としてチェックを実施しています。

動作確認

Systems Managerと連携する前に動作確認をしてみましょう。

ローカルで実行する場合は、以下のようにコマンドを実行します。/path/to/profileはプロファイルのディレクトリのパスを記述します。

inspec exec /path/to/profile

結果は以下のような感じです。

$ inspec exec mytest

Profile: InSpec Profile (mytest)
Version: 0.1.0
Target:  local://

  ×  Expected user existance check: 存在すべきユーザのチェック (1 failed)
     ×  User blackuser should not exist
     expected User blackuser not to exist
     ✔  User ec2-user should exist
     ✔  User nfsnobody should exist
     ✔  User ssm-user should exist

上記では、期待するユーザのリストに入っていない'blackuser'というユーザが存在するため、これが違反として引っかかっています。

プロファイルのアップロード

Systems Managerとの連携のため、作成したプロファイルをアップロードします。 現状、Systems Managerと連携させるには、Githubリポジトリ(public/private)かS3バケットにアップロードする必要があります。

Systems Managerとの連携

InSpecのプロファイルをアップロードしたら、いよいよSystems Mangerとの連携です。

コマンドの(1回の)実行

コマンドを一度実行する場合の手順は、最初に紹介したInSpec紹介の記事にある通りです。

一応流れだけこちらにも書いておきます。パラメータの指定方法などはも前掲の記事に詳しいのでそちらを参照してください。

  • 対象のEC2にSSMエージェントをインストール(※Amazon Linuxなら標準で同梱)し、SSMによるアクセスを許可するIAMロールを付与
  • Systems Manager - 「コマンドの実行」で実行ドキュメントとして「AWS-RunInSpecChecks」を選択
  • コマンドのパラメータとしてSource Type(GithubかS3か), Source Info(選択したSource TypeでのInSpecプロファイルの配置場所情報)を指定
  • その他、ターゲットとするインスタンスや出力を保存するS3バケットなどの各種情報を指定

ステートマネージャーを利用した定期実行

一度のコマンド実行に加え、「ステートマネージャー」で関連付けを設定しておくと、対象ターゲットへの定期的なInSpec実行を行うこともできます。 これで都度コマンドを実行する手間から解放されますね!

この場合の手順は下記の通りです。

  • Systems Manager - 「ステートマネージャー」より「関連付けの作成」をクリックします。
  • 「コマンド実行」同様に設定を行います。
    • 実行ドキュメントとして「AWS-RunInSpecChecks」を選択
    • コマンドのパラメータとしてSource Type, Source Infoを設定
    • ターゲットを指定
  • 設定項目「スケジュールを指定」にて、任意のスケジュールを設定します。

結果の確認

「コマンドの実行」「ステートマネージャー」いずれを利用した場合でも、結果をコンプライアンスダッシュボードから確認することができます。 Systems Manager - 「コンプライアンス」で表示されるダッシュボード画面、「コンプライアンスリソースの概要」で「Custom:InSpec」と表示されているのが、InSpecの実行結果となります。 テストが失敗したリソースについては「非準拠」にカウントされています。

「準拠リソース」や「非準拠リソース」をクリックすると、下にある「リソースの詳細概要」で該当するリソースがフィルタされて表示されます。

ここで「ID」をクリックすると、結果の詳細にドリルダウンできます。各テストの結果が表示されるので、具体的にどのテストで問題があったのか、簡単に把握することができます。

終わりに

ということで、自前のInSpecスクリプトをSystems Managerを使って動かし、定期的なコンプライアンスチェックの自動的な実施、その結果の確認もコンソール上で行えることをご紹介しました。 便利な機能なので、当社での運用にも取り入れていければと思います。

Systems Managerでは、「コンプライアンスチェックに失敗した場合に自動的に修復するためのスクリプトを実行する」機能もあるので、そこまでできると最高ですね!次はそこまで検証してみたいと思います。