コンテンツにスキップ

CT.SQS.PV.1

検証日: 2026-03-26 / リージョン: ap-northeast-1

コントロールの説明

組織外の IAM プリンシパル(IAM ユーザー、IAM ロール)による SQS リソースへのアクセスを拒否する。

本コントロールは RCP(リソースコントロールポリシー)により、組織外の IAM プリンシパルからの SQS API 操作をブロックする。Control Tower で対象の OU を指定して有効化すると、その OU 配下の全アカウントの全 SQS リソースに適用される。

RCP テンプレートの Condition は CT.S3.PV.4 と同じ構造で、以下の 2 つの条件を AND で評価し、両方を満たす場合に拒否する。

  • aws:PrincipalIsAWSServicefalse(AWS サービスプリンシパルでない)
  • aws:PrincipalOrgID が自組織の ID と一致しない(組織外のプリンシパルである)

aws:PrincipalIsAWSService 条件により、AWS サービスプリンシパル(sns.amazonaws.com 等)は拒否対象外となる。サービスプリンシパルの除外動作は CT.S3.PV.4 のステップ 7 で検証している。

パラメータは以下の 1 つ。

  • ExemptedPrincipalArns(任意): RCP の拒否対象外にする IAM プリンシパルの ARN。組織外のパートナーアカウントや外部サービスのロール等、特定のプリンシパルにアクセスを許可する場合に使用する

本コントロールはキューポリシーを変更するのではなく、独立したアクセス制御レイヤーとして機能する。キューポリシーで組織外アカウントにアクセスを許可していても、本コントロールが有効な場合は RCP により拒否される。

SQS の組織外アクセス制限には以下の方法がある。

方法内容スコープ
IAM Access Analyzer外部アクセスアナライザーで SQS の外部共有を検出検出のみ(ブロックしない)
キューポリシー(手動)キューごとにアクセス許可を管理キュー単位
CT.SQS.PV.1(本コントロール)OU 配下の全キューで組織外アクセスを自動的に拒否OU 単位
本コントロール(組織外アクセスの拒否)に直接対応する Security Hub CSPM コントロールは確認できませんでした。SQS.3 はキューポリシーのパブリックアクセスをチェックするものであり、組織外アクセスの制限は評価対象外です。

検証の流れ

    flowchart LR
    A[1. 事前準備] --> B[2. 組織内・組織外<br>両方成功]
    B --> C[3. CT.SQS.PV.1<br>有効化]
    C --> D[4. 既存設定<br>維持を確認]
    C --> E[5. 組織内アクセス<br>成功を確認]
    C --> F[6. 組織外アクセス<br>拒否を確認]
    D & E & F --> G[7. CT.SQS.PV.1<br>無効化]
    G --> H[8. 組織外アクセス<br>成功を確認]
  

結果

  • コントロールの有効化前、組織内・組織外の両方から SQS キューへのメッセージ送信・受信ができることを確認できた。
  • コントロールの有効化後、既存のキューポリシー(組織外アカウントへの許可)は維持されることを確認できた。
  • 組織内プリンシパルからのアクセスは引き続き成功することを確認できた。
  • 組織外プリンシパルからのアクセス(SendMessage、ReceiveMessage)が explicit deny in a resource control policy で拒否されることを確認できた。CT.S3.PV.4 では Access Denied のみが返されたが、SQS では CT.SECRETSMANAGER.PV.1CT.KMS.PV.7 と同様に詳細なエラーメッセージが返された。
  • RCP の拒否対象である SendMessage / ReceiveMessage は CloudTrail のデータイベントに分類されるため、デフォルトでは CloudTrail に記録されない。管理イベントとして記録される他コントロール(CT.SECRETSMANAGER.PV.1CT.KMS.PV.7 等)とは異なる(補足参照)。
  • コントロールの無効化後、再び組織外アカウントからメッセージを送信・受信できることを確認できた。

1. 事前準備

テスト用の SQS キューを作成する。

aws sqs create-queue \
  --queue-name ct-sqs-pv1-test
{
    "QueueUrl": "https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test"
}

キューポリシーで組織外アカウントに sqs:SendMessagesqs:ReceiveMessage を許可する。

aws sqs set-queue-attributes \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --attributes '{
    "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowExternalAccess\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名>\"},\"Action\":[\"sqs:SendMessage\",\"sqs:ReceiveMessage\"],\"Resource\":\"arn:aws:sqs:ap-northeast-1:<Workload のアカウント ID>:ct-sqs-pv1-test\"}]}"
  }'
(出力なし)

2. 有効化前の確認

組織内アクセス(成功)

メッセージを送信する。

aws sqs send-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --message-body "test message from internal"
{
    "MD5OfMessageBody": "<メッセージボディの MD5>",
    "MessageId": "<メッセージ ID>"
}

メッセージを受信する。

aws sqs receive-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --query 'Messages[0].{MessageId:MessageId,Body:Body}'
{
    "MessageId": "<メッセージ ID>",
    "Body": "test message from internal"
}

受信確認済みのメッセージをキューから全削除する。

aws sqs purge-queue \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test
(出力なし)

組織外アクセス(成功)

組織外アカウントの認証情報で、キューポリシーにより許可されたアクセスが成功することを確認する。

aws sqs send-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --message-body "test message from external" \
  --region ap-northeast-1 \
  --profile <組織外プロファイル名>
{
    "MD5OfMessageBody": "<メッセージボディの MD5>",
    "MessageId": "<メッセージ ID>"
}
aws sqs receive-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --query 'Messages[0].{MessageId:MessageId,Body:Body}' \
  --region ap-northeast-1 \
  --profile <組織外プロファイル名>
{
    "MessageId": "<メッセージ ID>",
    "Body": "test message from external"
}

キューポリシーにより、組織外アカウントからもメッセージの送信・受信ができた。

受信確認済みのメッセージをキューから全削除する。

aws sqs purge-queue \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test
(出力なし)

3. CT.SQS.PV.1 の有効化

Control Tower の管理アカウントで、対象の OU にコントロールが有効になっていないことを確認する。

aws controltower list-enabled-controls \
  --target-identifier <OU の ARN> \
  --profile Master
{
    "enabledControls": []
}

コントロールを有効化する。

aws controltower enable-control \
  --control-identifier arn:aws:controlcatalog:::control/54ih5t4iifupgyf22zo3kw45m \
  --target-identifier <OU の ARN> \
  --profile Master
{
    "arn": "arn:aws:controltower:ap-northeast-1:<管理アカウント ID>:enabledcontrol/<enabledcontrol ID>",
    "operationIdentifier": "<オペレーション ID>"
}

有効化が完了するまで待機する。

aws controltower get-control-operation \
  --operation-identifier <オペレーション ID> \
  --query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
  --profile Master
{
    "operationType": "ENABLE_CONTROL",
    "status": "SUCCEEDED",
    "statusMessage": "Operation was successful."
}

4. 既存設定の維持確認

RCP はキューポリシーを変更するのではなく、アクセス制御のレイヤーとして機能する。キューポリシーが自動的に変更されていないことを確認する。

aws sqs get-queue-attributes \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --attribute-names Policy \
  --query 'Attributes.Policy' --output text | jq .
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowExternalAccess",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名>"
            },
            "Action": [
                "sqs:SendMessage",
                "sqs:ReceiveMessage"
            ],
            "Resource": "arn:aws:sqs:ap-northeast-1:<Workload のアカウント ID>:ct-sqs-pv1-test"
        }
    ]
}

キューポリシーは変更されていない。組織外アカウントへの許可が残っているが、RCP により実際のアクセスは拒否される。

5. 組織内アクセスの確認

組織内プリンシパルからのアクセスは引き続き成功する。

aws sqs send-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --message-body "test message after enable"
{
    "MD5OfMessageBody": "<メッセージボディの MD5>",
    "MessageId": "<メッセージ ID>"
}
aws sqs receive-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --query 'Messages[0].{MessageId:MessageId,Body:Body}'
{
    "MessageId": "<メッセージ ID>",
    "Body": "test message after enable"
}

受信確認済みのメッセージをキューから全削除する。

aws sqs purge-queue \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test
(出力なし)

6. 組織外アクセスの拒否確認

組織外 SendMessage(拒否)

aws sqs send-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --message-body "test message from external" \
  --region ap-northeast-1 \
  --profile <組織外プロファイル名>
An error occurred (AccessDenied) when calling the SendMessage operation:
User: arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名>
is not authorized to perform: sqs:sendmessage on resource:
arn:aws:sqs:ap-northeast-1:<Workload のアカウント ID>:ct-sqs-pv1-test
with an explicit deny in a resource control policy

組織外 ReceiveMessage(拒否)

aws sqs receive-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --region ap-northeast-1 \
  --profile <組織外プロファイル名>
An error occurred (AccessDenied) when calling the ReceiveMessage operation:
User: arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名>
is not authorized to perform: sqs:receivemessage on resource:
arn:aws:sqs:ap-northeast-1:<Workload のアカウント ID>:ct-sqs-pv1-test
with an explicit deny in a resource control policy

キューポリシーで許可されているにもかかわらず、組織外プリンシパルからのアクセスが拒否された。エラーメッセージに explicit deny in a resource control policy と記載されており、RCP による拒否であることがわかる。CT.S3.PV.4 では組織外プリンシパルへの拒否時に Access Denied のみが返されたが、SQS では CT.SECRETSMANAGER.PV.1CT.KMS.PV.7 と同様に詳細なエラーメッセージが返された。

7. CT.SQS.PV.1 の無効化

aws controltower disable-control \
  --control-identifier arn:aws:controlcatalog:::control/54ih5t4iifupgyf22zo3kw45m \
  --target-identifier <OU の ARN> \
  --profile Master
{
    "operationIdentifier": "<オペレーション ID>"
}
aws controltower get-control-operation \
  --operation-identifier <オペレーション ID> \
  --query 'controlOperation.{operationType:operationType,status:status,statusMessage:statusMessage}' \
  --profile Master
{
    "operationType": "DISABLE_CONTROL",
    "status": "SUCCEEDED",
    "statusMessage": "Operation was successful."
}

8. 制限解除の確認

コントロール無効化後、組織外プリンシパルからのアクセスが再び成功することを確認する。

aws sqs send-message \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test \
  --message-body "test message after disable" \
  --region ap-northeast-1 \
  --profile <組織外プロファイル名>
{
    "MD5OfMessageBody": "<メッセージボディの MD5>",
    "MessageId": "<メッセージ ID>"
}

組織外プリンシパルからのアクセスが再び成功した。

9. クリーンアップ

aws sqs delete-queue \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/<Workload のアカウント ID>/ct-sqs-pv1-test
(出力なし)

補足: CloudTrail での確認について

RCP による拒否であっても、対象の API が管理イベントに分類される場合は CloudTrail に記録される。例えば、同じ RCP 型の CT.SECRETSMANAGER.PV.1 では GetSecretValue の拒否が管理イベントとして記録され、CloudTrail のイベント履歴や CloudWatch Logs から確認できた。

一方、本コントロールの拒否対象である SendMessage / ReceiveMessage 等は CloudTrail のデータイベントに分類される。データイベントはデフォルトでは記録されず、確認するには証跡でデータイベントの記録を明示的に有効化する必要がある。

以下の手順で、RCP による拒否がデータイベントとして記録されることを確認できる。

データイベント記録用の証跡を作成

ログ配信先バケットを作成し、CloudTrail サービスプリンシパルに書き込みを許可する。

aws s3api create-bucket \
  --bucket <ログ配信先バケット名> \
  --create-bucket-configuration LocationConstraint=ap-northeast-1
aws s3api put-bucket-policy \
  --bucket <ログ配信先バケット名> \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AWSCloudTrailAclCheck",
        "Effect": "Allow",
        "Principal": {"Service": "cloudtrail.amazonaws.com"},
        "Action": "s3:GetBucketAcl",
        "Resource": "arn:aws:s3:::<ログ配信先バケット名>"
      },
      {
        "Sid": "AWSCloudTrailWrite",
        "Effect": "Allow",
        "Principal": {"Service": "cloudtrail.amazonaws.com"},
        "Action": "s3:PutObject",
        "Resource": "arn:aws:s3:::<ログ配信先バケット名>/AWSLogs/<Workload のアカウント ID>/*",
        "Condition": {"StringEquals": {"s3:x-amz-acl": "bucket-owner-full-control"}}
      }
    ]
  }'
(出力なし)

証跡を作成し、対象キューの SQS データイベントのみを記録するよう設定する。SQS データイベントはアドバンスドイベントセレクタでのみサポートされるため、CT.S3.PV.5 の補足2(基本イベントセレクタ)とはコマンドが異なる。

aws cloudtrail create-trail \
  --name <証跡名> \
  --s3-bucket-name <ログ配信先バケット名>
aws cloudtrail put-event-selectors \
  --trail-name <証跡名> \
  --advanced-event-selectors '[{
    "Name": "SQS data events",
    "FieldSelectors": [
      {"Field": "eventCategory", "Equals": ["Data"]},
      {"Field": "resources.type", "Equals": ["AWS::SQS::Queue"]},
      {"Field": "resources.ARN", "Equals": ["arn:aws:sqs:ap-northeast-1:<Workload のアカウント ID>:<キュー名>"]}
    ]
  }]'
aws cloudtrail start-logging \
  --name <証跡名>
(出力なし)

拒否イベントの確認

CT.SQS.PV.1 有効化中に組織外プリンシパルから SendMessage を実行して拒否された後、数分〜数十分程度待つとログ配信先バケットにログファイルが配信される。

aws s3 ls s3://<ログ配信先バケット名>/ --recursive
<ログ更新日時>        938 AWSLogs/<Workload のアカウント ID>/CloudTrail/ap-northeast-1/2026/03/26/<ログファイル名>.json.gz

ログファイルを取得して中身を確認する。

aws s3 cp s3://<ログ配信先バケット名>/<ログファイルパス> /tmp/sqs-trail-log.json.gz
gunzip /tmp/sqs-trail-log.json.gz
cat /tmp/sqs-trail-log.json | jq '.Records[] | {eventName, errorCode, errorMessage, eventCategory, managementEvent}'
{
  "eventName": "SendMessage",
  "errorCode": "AccessDenied",
  "errorMessage": "User: arn:aws:iam::<組織外アカウント ID>:user/<IAM ユーザー名> is not authorized to perform: sqs:sendmessage on resource: arn:aws:sqs:ap-northeast-1:<Workload のアカウント ID>:<キュー名> with an explicit deny in a resource control policy: arn:aws:organizations::<管理アカウント ID>:policy/<組織 ID>/resource_control_policy/<ポリシー ID>",
  "eventCategory": "Data",
  "managementEvent": false
}

eventCategoryDatamanagementEventfalse であり、データイベントとして記録されていることがわかる。errorMessageexplicit deny in a resource control policy が記録されており、RCP による拒否であることも確認できる。

注意事項

  • cloudtrail lookup-events は管理イベントと Insights イベントのみを検索でき、データイベントは検索対象外である。データイベントの拒否を確認するには、ログ配信先の S3 バケットからログファイルを直接取得する必要がある。
  • Control Tower が自動作成する Organization Trail(aws-controltower-BaselineCloudTrail)は管理イベントのみを記録する設定になっている。そのため、Organization Trail の S3 バケット(LogArchive アカウント)や CloudWatch Logs にもデータイベントは配信されない。データイベントを記録するには、上記のように別途証跡を作成する。Control Tower のランディングゾーン設定では Organization Trail をオプトアウトして自前の CloudTrail を管理することも可能であり、その場合は自前の Organization Trail でデータイベントを含めた設定を自由に行える。

Amazonアソシエイトリンク