AWS Systems Manager でOSユーザーの棚卸


はじめに

サーバーの運用としてやらねばならないことは多岐にわたりますが、セキュリティールールとしてよく定められている作業の一つに、ユーザーIDの棚卸というものがあります。非常に沢山の仮想サーバーを運用している環境では、これに結構な労力が必要になってしまいます。自動的にIDの情報を収集してレポートを生成してくれるID管理ソフトウェアなどもありますが、ここではSystems Managerを使ってEC2インスタンス(Linux)のOSユーザーIDを収集する方法を紹介します。

やりかた

1. Systems Managerのインベントリを有効にする

Systems Managerにはインベントリという便利な機能があります。標準でRPMなどの情報をインスタンスから収集でき、カスタマイズにより収集対象の情報を追加することも可能です。今回はこの収集対象にユーザーIDの情報を追加することにしますので、まずはインベントリ機能を利用可能な状態にします。
インベントリの有効化手順はAWSのユーザーガイドに記載されていますので、ここでは簡潔に説明します。前提として、対象のインスタンスのIAMロールに"AmazonEC2RoleforSSM"ポリシーがアタッチされているものとします(このポリシーは権限が強いので、自己判断でカスタマイズしてください)。また、Excelなどでの収集した情報の利用を想定して、CSVファイルへのエクスポート手順も紹介します。

1-1. インベントリ収集の設定をする

以下のガイドを参考に、インスタンスの収集を行うステートマネージャーの関連付けを作成します。関連付けの名前は自動的に"Inventory-Association"となります。関連付けの作成後、自動的にインスタンスからの情報収集が行われます。
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-inventory-configuring.html

1-2. リソースデータの同期を設定する

後でインベントリデータをCSVへエクスポート可能にするために、リソースデータの同期を設定します。
以下のガイドを参考に、S3バケットとリソースデータの同期を作成します。
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-inventory-datasync.html

1-3. 詳細ビューからCSVファイルにエクスポートする

同期されたデータを、AWS Athena、AWS Glueを使って集計します。以下のガイドを参考に設定を行い、CSVファイルへのエクスポートができることを確認します。経験上、リソースデータの同期を初めて選択した際にエラーになったことがありますが、2度目以降は問題ありませんでした。

2. カスタムインベントリにユーザーID情報を追加する

標準のインベントリ収集が可能になったところで、カスタムインベントリとしてユーザーIDの情報を収集できるようにします。カスタムインベントリについては以下のガイドに説明があります。

簡潔にまとめると、各インスタンス上の"/var/lib/amazon/ssm/<instance-id>/inventory/custom"以下にJSONファイルを作成することで、その内容をインベントリの情報収集対象に追加することができるようになります。

2-1. JSONファイル生成用スクリプトを作成する

まず、インスタンス上のユーザー情報を/etc/passwdから取得して、JSONファイルに書き出すスクリプトを作成することにします。JSONのスキーマは柔軟に定義できますが、ここでは簡単に以下の例のように定めて、ファイル名はUsers.jsonとすることにします。

{
  "SchemaVersion": "1.0",
  "TypeName": "Custom:User",
  "Content": [
    {
      "name" : "ユーザー名",
      "gecos" : "ユーザーの説明",
      "shell" : "ログインシェル",
      "wheel" : "wheelグループのメンバーか否か(0か1)",
      "hostname" : "ホスト名"
    },
    ...
  ]
}

JSONスキーマが決まったら、実際にファイルを生成するスクリプトを作成します。
以下は手っ取り早く雑に作った例ですので、実際にはよく考慮してコーディングしてください。なお、"$AWS_SSM_INSTANCE_ID"という変数は、後でSystems Managerでリモートから実行する際にインスタンスIDに置き換わります。

#!/bin/bash

readonly PASSWD_FILE="/etc/passwd"
readonly ADM_GROUP="wheel"
readonly INVENTORY_FILE="/var/lib/amazon/ssm/$AWS_SSM_INSTANCE_ID/inventory/custom/Users.json"
readonly TMP_FILE="/var/lib/amazon/ssm/$AWS_SSM_INSTANCE_ID/inventory/custom/Users.json.tmp"

cp /dev/null $TMP_FILE
cat <<HEADER >$TMP_FILE
{
  "SchemaVersion": "1.0",
  "TypeName": "Custom:User",
  "Content": [
HEADER

hostname=`hostname`
last=`cat $PASSWD_FILE | wc -l`
counter=0
while read -r user
do
  ((counter++))
  name=`echo $user | cut -d":" -f1`
  gecos=`echo $user | cut -d":" -f5`
  shell=`echo $user | cut -d":" -f7`
  groups $name | grep " $ADM_GROUP" > /dev/null
  [ 0 -eq $? ] && admgrp=1 || admgrp=0

  cat <<USER >>$TMP_FILE
    {
      "name" : "$name",
      "gecos" : "$gecos",
      "shell" : "$shell",
      "wheel" : "$admgrp",
      "hostname" : "$hostname"
USER
  if [ $counter -eq $last ]; then
    echo "    }" >>$TMP_FILE
  else
    echo "    }," >>$TMP_FILE
  fi
done < $PASSWD_FILE

cat <<FOOTER >>$TMP_FILE
  ]
}
FOOTER

cp $TMP_FILE $INVENTORY_FILE
rm -f $TMP_FILE

2-2. AWS-RunShellScriptを使ってスクリプトを実行しJSONファイルを生成する

2-1.で作成したスクリプトを、ステートマネージャーの関連付けにより定期的にインスタンス上で実行し、ユーザーID情報をJSONファイルに保管します。スクリプトの実行には、AWSから提供されているAWS-RunShellScriptドキュメントを利用します。

Systems Managerコンソールのステートマネージャーページを開き、[関連付けの作成]を選択します。

関連付けでは、次の3つの項目を設定します。他の項目はデフォルトのままで構いません。

項目名 設定値
ドキュメント AWS-RunShellScript
パラメーター > Commands 2-1.のスクリプト
ターゲット このアカウント、このリージョンのすべてのマネージドインスタンスの選択


設定が済んだら、[関連付けの作成]を選択します。

関連付けが作成されると、自動的に各インスタンス上でスクリプトが実行され、JSONファイルの作成が行われます。ステートマネージャーのページで関連付けのステータスが"成功"になったら、試しにどこかのインスタンスにログインして、Users.jsonが生成されていることを確認してください。

# cat  /var/lib/amazon/ssm/<instance-id>/inventory/custom/Users.json
{
  "SchemaVersion": "1.0",
  "TypeName": "Custom:User",
  "Content": [
    {
      "name" : "root",
      "gecos" : "root",
      "shell" : "/bin/bash",
      "wheel" : "0",
      "hostname" : "ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal"
    },
    {
      "name" : "bin",
      "gecos" : "bin",
      "shell" : "/sbin/nologin",
      "wheel" : "0",
      "hostname" : "ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal"
    },
    ...
    {
      "name" : "ec2-user",
      "gecos" : "EC2 Default User",
      "shell" : "/bin/bash",
      "wheel" : "1",
      "hostname" : "ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal"
    },
    {
      "name" : "ssm-user",
      "gecos" : "",
      "shell" : "/bin/bash",
      "wheel" : "0",
      "hostname" : "ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal"
    }
  ]
}

2-3. インベントリの再収集

そのまま30分待つか、ステートマネージャーのページで関連付け"Inventory-Association"を手動で適用(実行)すると、インベントリの再収集が行われ、Users.jsonの内容がSystems Managerのインベントリに登録されます。

マネージドインスタンスのページでどれかのインスタンスを選択し、インベントリタブから収集した情報を確認しましょう。インベントリタイプから"Custom:User"を選択すれば、収集したユーザーIDの情報を参照することが可能です。

さらにそのまま半日待つか、Glueのコンソールでクローラを手動で実行すると、すべてのインスタンスの情報を集計してCSVファイルにエクスポートできるようになります。エクスポートの手順は、1-3.と同様です。

おわりに

今回は、Systems Managerの機能を使ってEC2インスタンスのOSユーザー情報を収集する方法を紹介しました。実際の運用プロセスでは、収集した情報を元にサーバー管理者や運用担当チームなどが各ユーザーIDの要不要を判断し、不要なIDの削除を行うといった作業が続くことになります。そういった作業を支援するためのID管理ソフトウェアもありますが、ここでは紹介の範囲外とします。
また、スクリプトを工夫して様々なJSONファイルを生成することで、カスタムインベントリはユーザー以外にも様々な情報を収集するためのツールとして利用できます。サーバーの運用を自動化するために覚えておきたい機能ですね。
この記事が皆様のお役に立てば幸いです。