KINTO Tech Blog
AWS

AWS CloudTrailに大量のNotFound系エラーが出てるんですけど!?

Shintaro Kurihara
Shintaro Kurihara
Cover Image for AWS CloudTrailに大量のNotFound系エラーが出てるんですけど!?

AWS CloudTrailに大量のNotFoundエラーイベントが出てるんですけど!?

こんにちは。(今更)酒癖50を観てもお酒を嫌いになれなかったKINTO テクノロジーズCCoEチーム所属の栗原です。以前に同じチームの多田からKINTOテクノロジーズにおけるCCoEの活動内容を紹介しましたが、クラウド環境をセキュアに保てるよう日々活動しています。AWSアカウントの健全性を確認するためAWS CloudTrailのログを分析していたところ、大量のNotFound系のエラーが定期的に発生していたことに気がつきました。地味な話になりますが、AWSを利用しているユーザーであれば同じ事象に遭遇しているはずなのにググってもヒットしなかったので調査内容をブログにしてみました。

結論

結論から言いますと、AWS CloudTrailの分析時には、AWS Configレコーダーのサービスリンクロール経由のNot Found系エラーは除外して分析するべき。になります。AWS Configの挙動上、どうしても発生してしまうエラーイベントが存在するので、適切にフィルタリングして分析ノイズを減らすことが可能です。

調査内容

KINTO テクノロジーズでは、AWS マルチアカウント管理を実現するベストプラクティスに則り、AWS Control TowerでLanding Zoneを管理するマルチアカウント構成をとっています。そのため AWS Configで構成情報を、AWS CloudTrailで監査ログを管理しています。

AWSアカウントの健全性を確認するためAWS CloudTrailのログを分析していたところ、NotFound系のエラーイベントが大量かつ定期的に発生していることがわかりました。

とあるAWSアカウントの1ヶ月程度のCloudTrailログのAWS Athenaでの分析結果がこちらです。このアカウントは発行して最低限のセキュリティ設定を施したのみで、ワークロードは構築していないアカウントとなります。

-- errorCodeの上位を分析
WITH filterd AS (
  SELECT
    *
  FROM
    cloudtrail_logs
  WHERE
    errorCode IS NOT NULL
)
SELECT
  errorCode,
  count(errorcode) as eventCount,
  count(errorCode) * 100 / (select count(*) from filterd) as errorRate
FROM
  filterd
GROUP BY
errorCode eventCount errorRate
ResourceNotFoundException 1,515 18
ReplicationConfigurationNotFoundError 1,112 13
ObjectLockConfigurationNotFoundError 958 11
NoSuchWebsiteConfiguration 954 11
NoSuchCORSConfiguration 952 11
InvalidRequestException 627 7
Client.RequestLimitExceeded 609 7
-- 特定のerroCodeの発生頻度を確認
SELECT
  date(from_iso8601_timestamp(eventtime)) as "date"
  count(*) as count
FROM
  cloudtrail_logs
WHERE
  errorcode = 'ResourceNotFoundException'
GROUP BY
  date(from_iso8601_timestamp(eventtime))
ORDER BY
  "date" ASC
LIMIT 5
date count
2023-10-19 52
2023-10-20 80
2023-10-21 80
2023-10-22 80
2023-10-23 80

いくつかのerrorCodeをピックアップして、AWS CloudTrailのレコードを眺めると(実際のAWS CloudTrailログは記事の最後に記載します。)、アクセス元であるuserIdentityのarnフィールドに記録されているのは全てarn:aws:sts::${AWS_ACCOUNT_ID}:assumed-role/AWSServiceRoleForConfig/${SESSION_NAME}となっていました。これはAWS Configにアタッチされるサービスリンクロールです。対象リソースは存在するのにNotFoundになる理由がわからなかったのですが、eventNameの箇所を確認すると、リソース本体の構成情報を取得するAPIではなく、それぞれの従属するリソースの情報を取得するAPIであることがわかりました。

リソース errorCode 呼ばれていたAPI(eventName)
Lambda ResourceNotFoundException GetPolicy20150331v2
S3 ReplicationConfigurationNotFoundError GetBucketReplication
S3 NoSuchCORSConfiguration GetBucketCors

ワークロードに影響があるエラーではないですが、通常の監視やトラブルシューティングのノイズになるため解消していきたいところですが、そのためには"関連リソースになにかしらの設定をする"(例えばLambdaのリソースベースポリシーに、自身のアカウントからのみInvokeFunctionのActionを許可する)といった、本質的ではない対応をする必要があります。

結果として、我々CCoEチームではAWS CloudTrailの分析時にAWS Configのサービスリンクロールからのアクセスは除外する。という対応する結論にいたりました。AWS Athenaで分析するのであれば以下の様なクエリを実行するイメージです。

SELECT
  *
FROM
  cloudtrail_logs
WHERE
  userIdentity.arn not like '%AWSServiceRoleForConfig%'

少しだけDeep Dive

本調査の過程でわかったAWS Configの構成情報の記録の挙動を少しDeep Diveします。公式ドキュメントにも明文化されていないが、本調査でわかったことが2点あります。

  • 従属(補足)リソース(勝手に命名しました。)の記録の挙動
  • 従属(補足)リソースの記録頻度

従属(補足)リソースの記録の挙動

AWS Configはリソース本体の構成情報を記録するだけでなく、関連リソース(relationship)も合わせて記録してくれる挙動があります。これらには、「直接的な」関係「関節的な」関係と名前がつけられています。

AWS Config は、設定フィールドからほとんどのリソースタイプの関係を導き出します。これを「直接的な」関係と呼びます。直接的な関係は、リソース (A) と別のリソース (B) との間の一方向関係 (A→B) であり、通常、リソース (A) の Describe API レスポンスから取得されます。以前は、AWS Config が当初サポートしていた一部のリソースタイプについて、他のリソースの設定から関係もキャプチャし、双方向 (B→A) の「間接的な」関係を作成していました。例えば、Amazon EC2 インスタンスとそのセキュリティグループの関係は直接的です。セキュリティグループは Amazon EC2 インスタンスの Describe API レスポンスに含まれるためです。一方、セキュリティグループと Amazon EC2 インスタンスの関係は間接的です。セキュリティグループを記述しても、関連付けられているインスタンスに関する情報は返されないためです。その結果、リソース設定の変更が検出されると、AWS Configはそのリソースの CI を作成するだけでなく、間接的な関係を持つリソースを含む関連リソースの CI も生成します。例えば、Amazon EC2AWS Config インスタンスの変更を検出すると、そのインスタンスの CI と、そのインスタンスに関連付けられているセキュリティグループの CI が作成されます。

-- https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/faq.html#faq-1

従属(補足)リソースと勝手に命名していますが、関連リソースとはまた別に、リソース本体の設定であるように見えるものの、取得APIも分かれているようなリソースがあります。Lambdaのケースでいうと、Lambda自体はGetFunctionで取得できるリソースですが、リソースベースポリシーはまた別のリソースで、GetPolicyで取得できるリソースです。CI(Configuration Item)をみてみると、従属(補足)リソースであるリソースベースポリシーは以下の様に、supplementaryConfigurationフィールドに記録されます。

{
  "version": "1.3",
  "accountId": "<$AWS_ACCOUNT_ID>",
  "configurationItemCaptureTime": "2023-12-15T09:52:19.238Z",
  "configurationItemStatus": "OK",
  "configurationStateId": "************",
  "configurationItemMD5Hash": "",
  "arn": "arn:aws:lambda:ap-northeast-1:<$AWS_ACCOUNT_ID>:function:check-config-behavior",
  "resourceType": "AWS::Lambda::Function",
  "resourceId": "check-config-behavior",
  "resourceName": "check-config-behavior",
  "awsRegion": "ap-northeast-1",
  "availabilityZone": "Not Applicable",
  "tags": {
    "Purpose": "investigate"
  },
  "relatedEvents": [],

  # 関連リソース
  "relationships": [
    {
      "resourceType": "AWS::IAM::Role",
      "resourceName": "check-config-behavior-role-nkmqq3sh",
      "relationshipName": "Is associated with "
    }
  ],

  ... 中略

  # 従属(補足)リソース
  "supplementaryConfiguration": {
    "Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"default\",\"Statement\":[{\"Sid\":\"test-poilcy\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::<$AWS_ACCOUNT_ID>:root\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:<$AWS_ACCOUNT_ID>:function:check-config-behavior\"}]}",
    "Tags": {
      "Purpose": "investigate"
    }
  }
}

従属(補足)リソースの記録頻度

AWS ConfigのCIの記録頻度は、RecordingModeの設定に従いますが、従属(補足)リソースについてはその限りではないようです。NotFound系だった場合リトライしている可能性もありそうですが、12時間や24時間に1回記録を試みているような動作になっていました。これも従属(補足)リソースの種類によって規則性があるわけではないようです。なかなかにブラックボックスな挙動ですがこの様な調査結果となりました。

まとめ

以上、AWS CloudTrailに出力されている謎のNotFound系エラーイベントの正体と、対策について紹介しました。今後詳細を調査予定ですが、Macieのサービスリンクロールからも同じ様なエラーイベントが発生していることが確認できています。AWS CloudTrailの分析は退屈な作業ではありますが、AWSサービスの挙動を深く理解できる機会にもなるので、積極的に実施していきましょう!AWSを使い倒したいエンジニアの方、小出恵介さんってやっぱいい俳優だよね!という方、プラットフォームGで絶賛採用募集中です!

最後にそれぞれのAWS CloudTrailエラーイベントを記載して終わりにします。ご拝読ありがとうございました。

Lambda: ResourceNotFoundException

{
  "eventVersion": "1.08",
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "************:LambdaDescribeHandlerSession",
    "arn": "arn:aws:sts::<$AWS_ACCOUNT_ID>:assumed-role/AWSServiceRoleForConfig/LambdaDescribeHandlerSession",
    "accountId": "<$AWS_ACCOUNT_ID>",
    "accessKeyId": "*********",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "principalId": "*********",
        "arn": "arn:aws:iam::<$AWS_ACCOUNT_ID>:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
        "accountId": "<$AWS_ACCOUNT_ID>",
        "userName": "AWSServiceRoleForConfig"
      },
      "webIdFederationData": {},
      "attributes": {
        "creationDate": "2023-12-03T09:09:17Z",
        "mfaAuthenticated": "false"
      }
    },
    "invokedBy": "config.amazonaws.com"
  },
  "eventTime": "2023-12-03T09:09:19Z",
  "eventSource": "lambda.amazonaws.com",
  "eventName": "GetPolicy20150331v2",
  "awsRegion": "ap-northeast-1",
  "sourceIPAddress": "config.amazonaws.com",
  "userAgent": "config.amazonaws.com",
  "errorCode": "ResourceNotFoundException",
  "errorMessage": "The resource you requested does not exist.",
  "requestParameters": {
    "functionName": "**************"
  },
  "responseElements": null,
  "requestID": "******************",
  "eventID": "******************",
  "readOnly": true,
  "eventType": "AwsApiCall",
  "managementEvent": true,
  "recipientAccountId": "<$AWS_ACCOUNT_ID>",
  "eventCategory": "Management"
}

S3: ReplicationConfigurationNotFoundError

{
  "eventVersion": "1.09",
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "**********:AWSConfig-Describe",
    "arn": "arn:aws:sts::<$AWS_ACCOUNT_ID>:assumed-role/AWSServiceRoleForConfig/AWSConfig-Describe",
    "accountId": "<$AWS_ACCOUNT_ID>",
    "accessKeyId": "*************",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "principalId": "*************",
        "arn": "arn:aws:iam::<$AWS_ACCOUNT_ID>:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
        "accountId": "<$AWS_ACCOUNT_ID>",
        "userName": "AWSServiceRoleForConfig"
      },
      "attributes": {
        "creationDate": "2023-12-03T13:09:16Z",
        "mfaAuthenticated": "false"
      }
    },
    "invokedBy": "config.amazonaws.com"
  },
  "eventTime": "2023-12-03T13:09:55Z",
  "eventSource": "s3.amazonaws.com",
  "eventName": "GetBucketReplication",
  "awsRegion": "ap-northeast-1",
  "sourceIPAddress": "config.amazonaws.com",
  "userAgent": "config.amazonaws.com",
  "errorCode": "ReplicationConfigurationNotFoundError",
  "errorMessage": "The replication configuration was not found",
  "requestParameters": {
    "replication": "",
    "bucketName": "*********",
    "Host": "*************"
  },
  "responseElements": null,
  "additionalEventData": {
    "SignatureVersion": "SigV4",
    "CipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
    "bytesTransferredIn": 0,
    "AuthenticationMethod": "AuthHeader",
    "x-amz-id-2": "**************",
    "bytesTransferredOut": 338
  },
  "requestID": "**********",
  "eventID": "*************",
  "readOnly": true,
  "resources": [
    {
      "accountId": "<$AWS_ACCOUNT_ID>",
      "type": "AWS::S3::Bucket",
      "ARN": "arn:aws:s3:::***********"
    }
  ],
  "eventType": "AwsApiCall",
  "managementEvent": true,
  "recipientAccountId": "<$AWS_ACCOUNT_ID>",
  "vpcEndpointId": "vpce-***********",
  "eventCategory": "Management"
}

S3: NoSuchCORSConfiguration

{
  "eventVersion": "1.09",
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "***********:AWSConfig-Describe",
    "arn": "arn:aws:sts::<$AWS_ACCOUNT_ID>:assumed-role/AWSServiceRoleForConfig/AWSConfig-Describe",
    "accountId": "<$AWS_ACCOUNT_ID>",
    "accessKeyId": "***************",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "principalId": "*************",
        "arn": "arn:aws:iam::<$AWS_ACCOUNT_ID>:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
        "accountId": "<$AWS_ACCOUNT_ID>",
        "userName": "AWSServiceRoleForConfig"
      },
      "attributes": {
        "creationDate": "2023-12-03T13:09:16Z",
        "mfaAuthenticated": "false"
      }
    },
    "invokedBy": "config.amazonaws.com"
  },
  "eventTime": "2023-12-03T13:09:55Z",
  "eventSource": "s3.amazonaws.com",
  "eventName": "GetBucketCors",
  "awsRegion": "ap-northeast-1",
  "sourceIPAddress": "config.amazonaws.com",
  "userAgent": "config.amazonaws.com",
  "errorCode": "NoSuchCORSConfiguration",
  "errorMessage": "The CORS configuration does not exist",
  "requestParameters": {
    "bucketName": "********",
    "Host": "*************************8",
    "cors": ""
  },
  "responseElements": null,
  "additionalEventData": {
    "SignatureVersion": "SigV4",
    "CipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
    "bytesTransferredIn": 0,
    "AuthenticationMethod": "AuthHeader",
    "x-amz-id-2": "*********************",
    "bytesTransferredOut": 339
  },
  "requestID": "***********",
  "eventID": "*****************",
  "readOnly": true,
  "resources": [
    {
      "accountId": "<$AWS_ACCOUNT_ID>",
      "type": "AWS::S3::Bucket",
      "ARN": "arn:aws:s3:::*************"
    }
  ],
  "eventType": "AwsApiCall",
  "managementEvent": true,
  "recipientAccountId": "<$AWS_ACCOUNT_ID>",
  "vpcEndpointId": "vpce-********",
  "eventCategory": "Management"
}
Facebook

関連記事 | Related Posts

We are hiring!

【データエンジニア】分析G/東京・名古屋・大阪

分析グループについてKINTOにおいて開発系部門発足時から設置されているチームであり、それほど経営としても注力しているポジションです。決まっていること、分かっていることの方が少ないぐらいですので、常に「なぜ」を考えながら、未知を楽しめるメンバーが集まっております。

【プラットフォームエンジニア】プラットフォームG/東京・大阪

プラットフォームグループについてAWS を中心とするインフラ設計、構築、運用などを担当しています。