マルチアカウントでAWS Configを使う時は「リトライ」機能に気を付けよう

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

CI2-1の松田です。こんにちは。

今日はあまり知られていないであろうAWS Configの「リトライ」の挙動についてお話します。
知らないとハマるかも?というところですので、特にマルチアカウントでAWS Configを使われている方は、最後までお付き合い頂ければ幸いです。

マルチアカウント環境でのAWS Configについて

マルチアカウント環境では、主に監査対応などを目的として、複数のAWSアカウントのログを一箇所に集約することが多くあります。以下のようなイメージです。

今回お話ししたいポイントは以下の2つです。

  • ログを集約するS3バケットのバケットポリシー
  • AWS Configが引き受けるIAM Role

S3のバケットポリシー

ログを集約するためには、S3バケットのバケットポリシーで、ログの送信元となる各AWSアカウントからの書き込みを許可する必要があります。
例えば、以下のようなバケットポリシーを設定する必要があります(公式ドキュメントより)。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSConfigBucketPermissionsCheck",
      "Effect": "Allow",
      "Principal": {
        "Service": "config.amazonaws.com"
      },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::targetBucketName",
      "Condition": { 
        "StringEquals": {
          "AWS:SourceAccount": "sourceAccountID"
        }
      }
    },
    {
      "Sid": "AWSConfigBucketExistenceCheck",
      "Effect": "Allow",
      "Principal": {
        "Service": "config.amazonaws.com"
      },
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::targetBucketName",
      "Condition": { 
        "StringEquals": {
          "AWS:SourceAccount": "sourceAccountID"
        }
      }
    },
    {
      "Sid": "AWSConfigBucketDelivery",
      "Effect": "Allow",
      "Principal": {
        "Service": "config.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::targetBucketName/[optional] prefix/AWSLogs/sourceAccountID/Config/*",
      "Condition": { 
        "StringEquals": { 
          "s3:x-amz-acl": "bucket-owner-full-control",
          "AWS:SourceAccount": "sourceAccountID"
        }
      }
    }
  ]
}

ただしこの書き方だと、ログの送信元となる全てのアカウントIDをCondition句に記載する必要があるため、アカウントが増えた際にポリシーをメンテナンスする必要があります。

"Condition": { 
    "StringEquals": { 
        "AWS:SourceAccount": [
            "アカウント1のアカウントID",
            "アカウント2のアカウントID",
            "アカウント3のアカウントID",
            ... ,
            "アカウントnのアカウントID"
        ]
    }
}

AWS Organizationsの利用が前提となりますが、Organizations環境下ではAWS:SourceAccountの代わりにAWS:PrincipalOrgIDを条件キーとして使用することが可能です。
以下の様に記述がシンプルになり、アカウント追加時のポリシーメンテナンスも不要なので、基本的にはこちらを使うのが望ましいでしょう。

"Condition": { 
    "StringEquals": { 
        "AWS:PrincipalOrgID": "AWS OrganizationsのID"
    }
}

"AWS:PrincipalOrgID"の注意事項

そんな便利なAWS:PrincipalOrgIDですが、サービスプリンシパルでは使用できないという制約が存在します。
ですのでAWS Configの場合には、所謂「サービスにリンクされたロール」ではなく、独自に作成したIAM Role(便宜上、カスタムIAM Roleと呼称します)を使用する必要があります。一応、公式ドキュメントの以下の文言がそれを意味する記述のようです。

可用性 – このキーは、プリンシパルが組織のメンバーである場合にのみリクエストコンテキストに含まれます。匿名リクエストには、このキーは含まれません。

docs.aws.amazon.com

なお先ほど紹介したS3のバケットポリシーでは、アクセスを許可するプリンシパルでconfig.amazonaws.comを指定しているため、ここも修正する必要があります。最終的には以下のようなポリシーになります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSConfigBucketPermissionsCheck",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::targetBucketName",
      "Condition": { 
        "StringEquals": {
          "AWS:PrincipalOrgID": "AWS OrganizationsのID"
        }
      }
    },
    {
      "Sid": "AWSConfigBucketExistenceCheck",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::targetBucketName",
      "Condition": { 
        "StringEquals": {
          "AWS:PrincipalOrgID": "AWS OrganizationsのID"
        }
      }
    },
    {
      "Sid": "AWSConfigBucketDelivery",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::targetBucketName/[optional] prefix/AWSLogs/*/Config/*",
      "Condition": { 
        "StringEquals": { 
          "s3:x-amz-acl": "bucket-owner-full-control",
          "AWS:PrincipalOrgID": "AWS OrganizationsのID"
        }
      }
    }
  ]
}

AWS ConfigにカスタムIAM Roleを渡すには

ご参考までに、具体的な設定方法を書いておきます。
設定画面で以下の様に「Choose a role from your account」を選ぶことで、AWS Configは独自で作成したIAM Roleの権限を使用して処理を実行するようになります。

カスタムIAM Roleに付与するIAM Policyは、AWS管理ポリシーであるAWS_ConfigRoleをアタッチするのが簡単です。

AWS Configの「リトライ」について

本題です。
AWS Configに渡すカスタムIAM Roleには、AWS_ConfigRoleさえアタッチすればOKというわけではありません。AWS_ConfigRoleにはS3バケットへの書き込み権限が含まれないため、S3への書き込み権限を定義したポリシーを追加でアタッチする必要があります。クロスアカウントの場合、バケットポリシーでの許可だけでは権限が足りないため注意が必要なポイントです(出典)。

しかし、S3への書き込み権限を定義したポリシーをアタッチしなくとも、S3への書き込みが成功してしまう場合があります。私が遭遇した事象では、これはAWS Configの持つ「リトライ」とでも呼ぶべき機能に由来するものでした。

その「リトライ」機能は以下のようなものです(公式ドキュメントからの引用)。

AWS Config がアカウントの Amazon S3 バケットに設定情報 (履歴ファイルやスナップショット) を送信する場合、AWS Config の設定時に割り当てた IAM ロールを引き受けます。AWS Config が別のアカウントの Amazon S3 バケットに設定情報を送信する場合、最初に IAM ロールの使用を試行します。ただし、バケットのアクセスポリシーが IAM ロールへのWRITEアクセス許可を付与しない場合、この試行は失敗します。この場合、AWS Config は再度情報を送信しますが、今回は AWS Config サービスプリンシパルとして送信します。

図解するとこういうことになります。

図中③(AWS Configサービスプリンシパルによるリトライ)では、S3のバケットポリシーさえ適切に設定されていれば、ログの出力に成功します。一方①の場合は、バケットポリシーだけでなくIAM Roleも適切に設定していなければエラーとなってしまいます。

このように①と③で、利用者側で必要な設定が微妙に異なるため、カスタムIAM RoleにS3バケットへの書き込み権限を明示的に付与していないにもかかわらず、S3にログが出力できるという事象が起こり得ます。

この場合、見かけ上は正しく動作しているのですが、実はカスタムIAM Roleによる処理はすべて失敗しています。それでもS3にログが出力されているのは、実はサービスプリンシパルによるリトライ処理があるおかげ...ということになります。

よしなにやってくれて便利な反面、ちょっと怖い気もしますね。

まとめ

結構ややこしい話なので、簡単にまとめます。
AWS Configのログ集約を行う際は、以下のポイントに注意しましょう。

  • S3のバケットポリシーにはAWS:PrincipalOrgIDを利用してメンテの手間を減らそう
  • AWS Configにはサービスにリンクされたロールではなく、カスタムIAM Roleを使おう
    • AWS:PrincipalOrgID利用の必須条件
  • カスタムIAM RoleにはS3への書き込み許可を明示的に付与しよう

余談

このブログを書くきっかけになった事象を簡単に記載しておきます。

事象

「バケットポリシーでAWS:PrincipalOrgIDを使うとログ出力が失敗、AWS:SourceAccountを使うと成功する」というものでした。

前提

事象発生時の情報を簡単に記載しておきます。

  • AWS ConfigのIAM Role:カスタムIAM Roleを使用
  • IAM Roleの持つ権限:AWS_ConfigRoleのみ(S3の書き込み権限はなし)
  • ログ送信元のアカウントと送信先のアカウントは別

挙動の解説

以下のようなロジックです。

AWS:PrincipalOrgID の場合

AWS:SourceAccount の場合

リトライの挙動を踏まえると、そんなに難しい話ではないですね。
しかしリトライのことを知らないともはや心霊現象ですので、今後私のような者が出ないことを切に願います。

ちなみに対処自体は簡単で、IAM RoleにS3への書き込み許可を与えてあげればよいです。公式ドキュメントにサンプルもありますので、こちらを参考にポリシーを作成しましょう。

松田 渓(記事一覧)

2021年10月入社。散歩が得意です。