【AWS CLI】全EC2のIMDSv1使用履歴を確認する方法

記事タイトルとURLをコピーする

こんにちは。AWS CLIが好きな福島です。

今回は、セキュリティ強化のため、EC2でIMDSv2を強制化するのに伴い、 AWS CLIを使い、全てのEC2でIMDSv1の使用履歴を確認したので、その方法をご紹介いたします。

IMDSv2については、以下のブログで紹介しているため、ご興味がある方は、ぜひご覧ください。

blog.serverworks.co.jp

実行環境

今回、コマンドを実行した環境は、以下の通りとなります。
(本記事でご紹介しているコマンドの中には、Linuxのコマンドを利用している箇所があります。)

# uname -a
Linux LAPTOP-CNM26HN6 4.4.0-18362-Microsoft #1049-Microsoft Thu Aug 14 12:01:00 PST 2020 x86_64 x86_64 x86_64 GNU/Linux
#

結論

以下のコマンドを実行しました。

◆ポイント

  • CloudWatchで取得できるメトリクス「MetadataNoToken」の値を利用
  • メトリクス「MetadataNoToken」の1時間ごとの最大値を2週間分、取得
  • MetadataNoToken$(date +"%Y%m%d_%H%M%S").csvファイルに結果を出力
  • B列を空けているため、1時間ごとの最大値をSUM関数により計算することでIMDSv1の使用履歴を確認
  • 値がNoneになる場合があります。(推測ですが、インスタンスを停止しているなどで値を取得できない場合にNoneになるかと存じます。)
CSV_FILE=MetadataNoToken_$(date +"%Y_%m%d_%H%M%S").csv;\
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId]" --output text | while read line
do
   cat << EOF > json
[
   {
      "Id":"q1",
      "MetricStat":
      {
         "Metric":
         {
            "Namespace":"AWS/EC2",
            "MetricName":"MetadataNoToken",
            "Dimensions":[
               {
                  "Name":"InstanceId",
                  "Value":"$line"
               }
            ]
         },
         "Period":3600,
         "Stat":"Maximum"
      }
   }
]
EOF
   echo $line,, > /tmp/awscli.tmp ;\
   aws cloudwatch get-metric-data --metric-data-queries file://json --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") --query "MetricDataResults[].[\
   Values[1],Values[2],Values[3],Values[4],Values[5],Values[6],Values[7],\
   Values[8],Values[9],Values[10],Values[11],Values[12],Values[13],Values[14],\
   Values[15],Values[16],Values[17],Values[18],Values[19],Values[20],Values[21],\
   Values[22],Values[23],Values[24],Values[25],Values[26],Values[27],Values[28],\
   Values[29],Values[30],Values[31],Values[32],Values[33],Values[34],Values[35],\
   Values[36],Values[37],Values[38],Values[39],Values[40],Values[41],Values[42],\
   Values[43],Values[44],Values[45],Values[46],Values[47],Values[48],Values[49],\
   Values[50],Values[51],Values[52],Values[53],Values[54],Values[55],Values[56],\
   Values[57],Values[58],Values[59],Values[60],Values[61],Values[62],Values[63],\
   Values[64],Values[65],Values[66],Values[67],Values[68],Values[69],Values[70],\
   Values[71],Values[72],Values[73],Values[74],Values[75],Values[76],Values[77],\
   Values[78],Values[79],Values[80],Values[81],Values[82],Values[83],Values[84],\
   Values[85],Values[86],Values[87],Values[88],Values[89],Values[90],Values[91],\
   Values[92],Values[93],Values[94],Values[95],Values[96],Values[97],Values[98],\
   Values[99],Values[100],Values[101],Values[102],Values[103],Values[104],Values[105],\
   Values[106],Values[107],Values[108],Values[109],Values[110],Values[111],Values[112],\
   Values[113],Values[114],Values[115],Values[116],Values[117],Values[118],Values[119],\
   Values[120],Values[121],Values[122],Values[123],Values[124],Values[125],Values[126],\
   Values[127],Values[128],Values[129],Values[130],Values[131],Values[132],Values[133],\
   Values[134],Values[135],Values[136],Values[137],Values[138],Values[139],Values[140],\
   Values[141],Values[142],Values[143],Values[144],Values[145],Values[146],Values[147],\
   Values[148],Values[149],Values[150],Values[151],Values[152],Values[153],Values[154],\
   Values[155],Values[156],Values[157],Values[158],Values[159],Values[160],Values[161],\
   Values[162],Values[163],Values[164],Values[165],Values[166],Values[167],Values[168],\
   StatusCode]" --output text | tr "\t" "," >> /tmp/awscli.tmp
   cat /tmp/awscli.tmp | tr "\n" " " | sed 's/ //g' >> ${CSV_FILE}
   echo "" >> ${CSV_FILE}
done
  • 実行結果

ExcelのSUM関数を利用して計算

B列が0のインスタンスは、IMDSv1を使用していない(2週間)ことが分かる。

処理の説明

CSV_FILE=MetadataNoToken_$(date +"%Y_%m%d_%H%M%S").csv;\

 ⇒変数名の定義

aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId]" --output text | while read line

 ⇒全EC2のインスタンスIDをwhileにより1つずつline変数に代入

   cat << EOF > json
[
   {
      "Id":"q1",
      "MetricStat":
      {
         "Metric":
         {
            "Namespace":"AWS/EC2",
            "MetricName":"MetadataNoToken",
            "Dimensions":[
               {
                  "Name":"InstanceId",
                  "Value":"$line" ★
               }
            ]
         },
         "Period":3600,
         "Stat":"Maximum"
      }
   }
]
EOF

 ⇒CloudWatchコマンドで使うJSONファイルを生成。
  Value(★)にインスタンスIDを記載。

   aws cloudwatch get-metric-data --metric-data-queries file://json --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") --query "MetricDataResults[].[\
   Values[1],Values[2],Values[3],Values[4],Values[5],Values[6],Values[7],\
   Values[8],Values[9],Values[10],Values[11],Values[12],Values[13],Values[14],\
   Values[15],Values[16],Values[17],Values[18],Values[19],Values[20],Values[21],\
   Values[22],Values[23],Values[24],Values[25],Values[26],Values[27],Values[28],\
   Values[29],Values[30],Values[31],Values[32],Values[33],Values[34],Values[35],\
   Values[36],Values[37],Values[38],Values[39],Values[40],Values[41],Values[42],\
   Values[43],Values[44],Values[45],Values[46],Values[47],Values[48],Values[49],\
   Values[50],Values[51],Values[52],Values[53],Values[54],Values[55],Values[56],\
   Values[57],Values[58],Values[59],Values[60],Values[61],Values[62],Values[63],\
   Values[64],Values[65],Values[66],Values[67],Values[68],Values[69],Values[70],\
   Values[71],Values[72],Values[73],Values[74],Values[75],Values[76],Values[77],\
   Values[78],Values[79],Values[80],Values[81],Values[82],Values[83],Values[84],\
   Values[85],Values[86],Values[87],Values[88],Values[89],Values[90],Values[91],\
   Values[92],Values[93],Values[94],Values[95],Values[96],Values[97],Values[98],\
   Values[99],Values[100],Values[101],Values[102],Values[103],Values[104],Values[105],\
   Values[106],Values[107],Values[108],Values[109],Values[110],Values[111],Values[112],\
   Values[113],Values[114],Values[115],Values[116],Values[117],Values[118],Values[119],\
   Values[120],Values[121],Values[122],Values[123],Values[124],Values[125],Values[126],\
   Values[127],Values[128],Values[129],Values[130],Values[131],Values[132],Values[133],\
   Values[134],Values[135],Values[136],Values[137],Values[138],Values[139],Values[140],\
   Values[141],Values[142],Values[143],Values[144],Values[145],Values[146],Values[147],\
   Values[148],Values[149],Values[150],Values[151],Values[152],Values[153],Values[154],\
   Values[155],Values[156],Values[157],Values[158],Values[159],Values[160],Values[161],\
   Values[162],Values[163],Values[164],Values[165],Values[166],Values[167],Values[168],\
   StatusCode]" --output text | tr "\t" "," >> /tmp/awscli.tmp

 ⇒CloudWatchコマンドによりメトリクス「MetadataNoToken」の1時間ごとの最大値を2週間分、取得。

   cat /tmp/awscli.tmp | tr "\n" " " | sed 's/ //g' >> ${CSV_FILE}
   echo "" >> ${CSV_FILE}

   ⇒CSVファイルへ出力

おまけ①

上記スクリプトでは、IMDSv1が使用履歴を確認することはできますが、
詳細な時間まで確認することはできません。

詳細な時間を確認したい場合は、cloudwatchコマンドのqueryで「Timestamps[0]」、「Timestamps[1]」と指定します。

  • 実行例
aws cloudwatch get-metric-data --metric-data-queries file://json \
--start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") \
--end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--query "MetricDataResults[].[Timestamps[0],Values[0],Timestamps[1],Values[1],StatusCode]" --output text | tr "\t" ","
  • 実行結果
2021-02-09T09:00:00+00:00,3.0,2021-02-09T08:00:00+00:00,3.0,Complete

おまけ②(IMDSv2設定確認)

IMDSv2を強制化できているか確認するコマンドは、以下の通りです。 ※メタデータのHttpTokensおよびHttpEndpointの設定値を確認します。

  • 実行例
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens,MetadataOptions.HttpEndpoint]" --output text
  • 実行結果
i-0f6126b7aeedfabd6     required        enabled
i-050536efdd9dc1126     optional        enabled
i-0b66442bd16bf7f21     optional        enabled
i-0808672558492fde8     optional        enabled

 ⇒ 2列目が「required」、3列目が「enabled」になっていれば、IMDSv2のみ使う設定になっています。

おまけ③(IMDSv2設定変更)

  • 実行例
aws ec2 modify-instance-metadata-options --instance-id i-0f61b7aedddabd6 --http-tokens required --http-endpoint enabled

おまけ④(Nameタグ付き)

CSV_FILE=MetadataNoToken_$(date +"%Y_%m%d_%H%M%S").csv;\
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId,Tags[?Key=='Name'] | [0].Value]" --output text | tr "\t" "," | while read line
do
   instance_id=$(echo $line | awk -F, '{print $1}')
   name_tag=$(echo $line | awk -F, '{print $2}')
   cat << EOF > json
[
   {
      "Id":"q1",
      "MetricStat":
      {
         "Metric":
         {
            "Namespace":"AWS/EC2",
            "MetricName":"MetadataNoToken",
            "Dimensions":[
               {
                  "Name":"InstanceId",
                  "Value":"$instance_id"
               }
            ]
         },
         "Period":3600,
         "Stat":"Maximum"
      }
   }
]
EOF
   echo $instance_id,$name_tag,, > /tmp/awscli.tmp ;\
   aws cloudwatch get-metric-data --metric-data-queries file://json --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") --query "MetricDataResults[].[\
   Values[1],Values[2],Values[3],Values[4],Values[5],Values[6],Values[7],\
   Values[8],Values[9],Values[10],Values[11],Values[12],Values[13],Values[14],\
   Values[15],Values[16],Values[17],Values[18],Values[19],Values[20],Values[21],\
   Values[22],Values[23],Values[24],Values[25],Values[26],Values[27],Values[28],\
   Values[29],Values[30],Values[31],Values[32],Values[33],Values[34],Values[35],\
   Values[36],Values[37],Values[38],Values[39],Values[40],Values[41],Values[42],\
   Values[43],Values[44],Values[45],Values[46],Values[47],Values[48],Values[49],\
   Values[50],Values[51],Values[52],Values[53],Values[54],Values[55],Values[56],\
   Values[57],Values[58],Values[59],Values[60],Values[61],Values[62],Values[63],\
   Values[64],Values[65],Values[66],Values[67],Values[68],Values[69],Values[70],\
   Values[71],Values[72],Values[73],Values[74],Values[75],Values[76],Values[77],\
   Values[78],Values[79],Values[80],Values[81],Values[82],Values[83],Values[84],\
   Values[85],Values[86],Values[87],Values[88],Values[89],Values[90],Values[91],\
   Values[92],Values[93],Values[94],Values[95],Values[96],Values[97],Values[98],\
   Values[99],Values[100],Values[101],Values[102],Values[103],Values[104],Values[105],\
   Values[106],Values[107],Values[108],Values[109],Values[110],Values[111],Values[112],\
   Values[113],Values[114],Values[115],Values[116],Values[117],Values[118],Values[119],\
   Values[120],Values[121],Values[122],Values[123],Values[124],Values[125],Values[126],\
   Values[127],Values[128],Values[129],Values[130],Values[131],Values[132],Values[133],\
   Values[134],Values[135],Values[136],Values[137],Values[138],Values[139],Values[140],\
   Values[141],Values[142],Values[143],Values[144],Values[145],Values[146],Values[147],\
   Values[148],Values[149],Values[150],Values[151],Values[152],Values[153],Values[154],\
   Values[155],Values[156],Values[157],Values[158],Values[159],Values[160],Values[161],\
   Values[162],Values[163],Values[164],Values[165],Values[166],Values[167],Values[168],\
   StatusCode]" --output text | tr "\t" "," >> /tmp/awscli.tmp
   cat /tmp/awscli.tmp | tr "\n" " " | sed 's/ //g' >> ${CSV_FILE}
   echo "" >> ${CSV_FILE}
done

おわりに

今回は、IMDSv1の使用履歴を確認するスクリプトをご紹介しました。
どなたかのお役に立てれば幸いです。

福島 和弥 (記事一覧)

2019/10 入社

AWS CLIが好きです。