38

我正在尝试使用 SNS 和 SQS 对分布式应用程序进行原型设计。我有这个主题:

arn:aws:sns:us-east-1:574008783416:us-east-1-live-auction

这个队列:

arn:aws:sqs:us-east-1:574008783416:queue4

我使用 JS Scratchpad 创建了队列。我使用控制台添加了订阅。我使用暂存器将权限添加到队列。队列策略现在是:

{  
   "Version":"2008-10-17",
   "Id":"arn:aws:sqs:us-east-1:574008783416:queue4/SQSDefaultPolicy",
   "Statement":[  
      {  
         "Sid":"RootPerms",
         "Effect":"Allow",
         "Principal":{  
            "AWS":"574008783416"
         },
         "Action":"SQS:*",
         "Resource":"arn:aws:sqs:us-east-1:574008783416:queue4"
      }
   ]
}

我有关于同一主题的电子邮件订阅,并且电子邮件可以正常到达,但消息永远不会到达队列。我已经尝试使用 Scratchpad 将 SendMessage 直接发送到队列 - 而不是通过 SNS - 它工作正常。任何想法为什么它不会发送到队列?

4

8 回答 8

32

这是不久前在 AWS 论坛上发布的:https ://forums.aws.amazon.com/thread.jspa?messageID=202798

然后我授予 SNS 主题向 SQS 队列发送消息的权限。这里的诀窍是允许所有主体。SNS 不会从您的帐户 ID 发送 - 它有自己的帐户 ID,它是从该帐户 ID 发送的。

于 2011-05-09T02:39:44.053 回答
13

这是 Skyler 回答的完整 CloudFormation 示例

{
  "Resources": {
    "MyTopic": {
      "Type": "AWS::SNS::Topic"
    },
    "MyQueue": {
      "Type": "AWS::SQS::Queue"
    },
    "Subscription": {
      "Type" : "AWS::SNS::Subscription",
      "Properties" : {
          "Protocol" : "sqs",
          "TopicArn" : {"Ref": "MyTopic"},
          "Endpoint": {"Fn::GetAtt": ["MyQueue", "Arn"]}
        }
    },
    "QueuePolicy": {
      "Type": "AWS::SQS::QueuePolicy",
      "Properties": {
        "Queues": [
          {"Ref": "MyQueue"}
        ],
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "allow-sns-messages",
              "Effect": "Allow",
              "Principal": {"Service": "sns.amazonaws.com"},
              "Action": "sqs:SendMessage",
              "Resource": {"Fn::GetAtt": ["MyQueue", "Arn"]},
              "Condition": {
                "ArnEquals": {
                  "aws:SourceArn": {"Ref": "MyTopic"}
                }
              }
            }
          ]
        }
      }
    }
  }
}

Amazon 在将Amazon SNS 消息发送到 Amazon SQS 队列文档中有更多选项。

于 2016-05-06T23:45:16.853 回答
13

除了 Skyler 的回答之外,如果您像我一样对允许任何主体 ( Principal: '*') 的想法感到畏缩,您可以将主体限制为 SNS:

Principal:
  Service: sns.amazonaws.com

尽管此行为未记录在案,但它确实有效。

于 2019-02-08T18:39:58.047 回答
11

大多数答案(除了@spg 答案)都建议使用principal: *- 这是非常危险的做法,它会将您的 SQS 暴露给全世界。

来自AWS 文档

对于基于资源的策略,例如 Amazon S3 存储桶策略,主体元素中的通配符 (*) 指定所有用户或公共访问。
我们强烈建议您不要在角色信任策略的 Principal 元素中使用通配符,除非您通过策略中的 Condition 元素限制访问。否则,您分区中任何账户中的任何 IAM 用户都可以访问该角色。

因此,强烈不建议使用此主体。

相反,您需要指定 sns 服务作为您的主体:

"Principal": {
        "Service": "sns.amazonaws.com"
},

示例策略:

{
  "Version": "2012-10-17",
  "Id": "Policy1596186813341",
  "Statement": [
    {
      "Sid": "Stmt1596186812579",
      "Effect": "Allow",
      "Principal": {
        "Service": "sns.amazonaws.com"
      },
      "Action": [
        "sqs:SendMessage",
        "sqs:SendMessageBatch"
      ],
      "Resource": "Your-SQS-Arn"
    }
  ]
}

使用此策略,sns 将能够向您的 SQS 发送消息。

SQS有更多权限,但据我所知SendMessageSendMessageBatch对于 SNS->SQS 订阅来说应该足够了。

于 2020-07-31T14:06:43.307 回答
4

老问题,但使用 AWS SDK 版本 > 1.10

查看文档SQS-SNS sendMessage 权限

private static void updateQueuePolicy(AmazonSQS sqs, String queueURL, String topicARN) {
   Map<String, String> attributes = new HashMap<String, String>(1);
   Action actions = new Action() {
       @Override
       public String getActionName() {
           return "sqs:SendMessage"; // Action name
       }
   };
   Statement mainQueueStatements = new Statement(Statement.Effect.Allow)
           .withActions(actions)
           .withPrincipals(new Principal("Service", "sns.amazonaws.com"))
           .withConditions(
               new Condition()
               .withType("ArnEquals")
               .withConditionKey("aws:SourceArn")
               .withValues(topicARN)
           );

   final Policy mainQueuePolicy = new Policy()
        .withId("MainQueuePolicy")
        .withStatements(mainQueueStatements);

   attributes.put("Policy", mainQueuePolicy.toJson());

   updateQueueAttributes(sqs, queueURL, attributes);
}

输出类似于的策略

{
   Policy={
      "Version":"2012-10-17",
       "Id":"MainQueuePolicy",
       "Statement":
            [
              {
                 "Sid":"1",
                 "Effect":"Allow",
                 "Principal": {
                   "Service": "sns.amazonaws.com"
                 },
                 "Action":["sqs:SendMessage"],
                 "Condition":
                   {"ArnEquals":
                     {"aws:SourceArn":["arn:aws:sns:us-east-1:3232:testSubscription"]}
                   }
              }
            ]
      }
 }
于 2018-10-29T17:52:32.647 回答
2

与提到的其他答案一样,您必须选择加入并授予此 SNS 主题的权限才能发布到您的 SQS 队列。

如果您使用 terraform,则可以使用sqs_queue_policy资源。

这是一个例子

resource "aws_sqs_queue_policy" "your_queue_policy" {
    queue_url = "${aws_sqs_queue.your_queue.id}"

    policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "sqspolicy",
  "Statement": [
    {
      "Sid": "First",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "sqs:SendMessage",
      "Resource": "${aws_sqs_queue.your_queue.arn}",
      "Condition": {
        "ArnEquals": {
          "aws:SourceArn": "${aws_sns_topic.your_topic.arn}"
        }
      }
    }
  ]
}
POLICY
}
于 2021-05-24T14:26:32.097 回答
1

只有您需要从队列控制台为队列订阅主题。

  • 第一步:选择队列
  • 第二步:排队动作
  • 第三步:订阅队列到 SNS 主题
  • 步骤选择:主题
  • 结尾。
于 2020-05-25T00:30:14.567 回答
0

如果您在队列上启用了加密,这也可能是 SNS 无法将消息放入订阅者队列的原因。您需要向该 KMS 密钥授予对 SNS 的访问权限。

这篇文章解释了如何解决这个问题:

于 2021-06-17T13:17:00.970 回答