SNS主题未发布到SQS

58

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

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

和这个队列:

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

我使用JS Scratchpad创建了队列,使用控制台添加了订阅,使用Scratchpad为队列添加了AddPermission。现在,队列策略如下:

{  
   "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"
      }
   ]
}

我在同一个主题上订阅了邮件,邮件可以正常到达,但消息从未到达队列。 我尝试直接向队列发送消息(而不是通过SNS),并使用Scratchpad测试也可以正常工作。 有什么想法为什么它不能发送到队列?

10个回答

39

这篇帖子是一段时间前发布在AWS论坛上的:https://forums.aws.amazon.com/thread.jspa?messageID=202798

然后我向SNS主题授予了向SQS队列发送消息的权限。这里的诀窍是允许所有原则。SNS不会使用您的账户 ID 发送信息--它有自己的账户 ID 来发送。


谢谢,谢谢。我在 AWS 支持中心有一个未解决的工单,并恳求他们将此答案添加到文档中的策略示例中。 - user427875
3
请限制对sourceArn的访问:https://docs.aws.amazon.com/sns/latest/dg/sns-access-policy-use-cases.html#sns-publish-messages-to-sqs-queue - Mmm Donuts
1
这是非常糟糕的答案。通过这样做,每个人都可以从这个主题发送消息。 用户应该寻求允许他们做自己想做的事情的政策,同时是最严格的政策 - 这样更安全。我认为在使用*作为主体后,您的SQS将变成公共的。 - Shoter
请返回翻译后的文本:例子:https://docs.amazonaws.cn/en_us/sns/latest/dg/subscribe-sqs-queue-to-sns-topic.html#SendMessageToSQS.sqs.permissions - borjab
1
AWS论坛的链接已被归档,最好的选择是在链接中附上您提出的答案的上下文。 - Shadman R

27

补充Skyler的答案,如果像我一样,你不喜欢允许任何负责人(Principal: '*'),那么你可以将负责人限制为SNS:

Principal:
  Service: sns.amazonaws.com

虽然这种行为没有文档说明,但它确实有效。


1
这是问题的正确答案。通过这种方式做事,您不会将SQS暴露给整个世界。 - Shoter
哇,这是我在尝试其他方法数小时后找到的可行解决方案,非常感谢 :) - Sandeep K Nair
1
我在这个指南的第2步中找到了这个文档:https://docs.aws.amazon.com/sns/latest/dg/subscribe-sqs-queue-to-sns-topic.html - dnc253

23

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

来自 AWS文档

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

因此,强烈不建议使用这个Principal。
相反,您需要将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订阅。


你知道SNS是否使用SendMessageBatch来降低成本吗? - Harshit
8
这是一个不错的回答。当我在AWS控制台中将SNS连接到SQS时,我很惊讶它没有自动处理权限部分。 - piggybox
我喜欢额外安全性的想法,但是我在将此策略添加到SNS主题访问策略时遇到了问题。报告的问题是:“策略声明动作超出服务范围!”在阅读主题访问政策信息后-这是有道理的。主题访问策略控制谁可以访问主题-而不是队列。尝试在队列中做同样的事情... - tishma

17

这是 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 SNS消息发送到Amazon SQS队列文档中提供了更多选项。


3
据我所知,该语句需要包含一个资源属性:"Resource": {"Fn::GetAtt": ["MyQueue", "Arn"] }。 - Digitalkapitaen
嘿!仍然很有帮助!非常感谢你! - Alejandro Molina

14

我刚刚遇到了这个问题,花费了一些时间才找出原因:

如果我在SNS控制台中创建SQS订阅,它不会向SQS访问策略中添加必要的权限。

如果我在SQS控制台中创建到同一个SNS的订阅,则会添加这些权限。


如果你只想通过控制台完成操作而不必手动修改策略,这个回答非常好。唯一奇怪的是它允许任何主体,但是通过条件过滤主题ARN。 - undefined
尽管这样做还有一个需要注意的地方,就是你无法获得其他订阅选项,与使用SNS设置相比,尤其是在我这种情况下的“原始消息传递”。不过你可以转到SNS并编辑订阅以启用此功能。 - undefined

5

和其他答案提到的一样,您必须选择并授权此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
}

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"]}
                   }
              }
            ]
      }
 }

你可能想要使用 topicARN 而不是 queueARN。 - k.liakos
1
感谢您更新答案以便正确地处理Principal! :) - Shoter

2
如果您已经在队列上启用了加密,这也可能是导致SNS无法将消息放到订阅者队列的原因。您需要授予SNS对该KMS密钥的访问权限。 此文章说明了如何解决此问题:

1

只需在队列控制台订阅主题,无需其他操作。

  • 步骤一:选择队列
  • 步骤二:队列操作
  • 步骤三:将队列订阅到SNS主题
  • 步骤四:选择主题
  • 完成。

0

这是通过 AWS::SQS::QueuePolicy 实现的。

您需要定义此类策略,以允许特定 SNS 对特定 SQS 执行操作。

QueuePolicy:
  Type: AWS::SQS::QueuePolicy
  Properties:
    Queues:
    - Ref: MyQueue1
    - Fn::Sub: arn:aws:sqs:us-east-1:${AWS::AccountId}:my-queue-*
    PolicyDocument:
      Version: '2012-10-17'
      Statement:
      - Sid: allow-sns-messages
        Effect: Allow
        Principal:
          Service: sns.amazonaws.com
        Action: sqs:SendMessage
        Resource:
          - Fn::Sub: arn:aws:sqs:us-east-1:${AWS::AccountId}:my-queue-*
        Condition:
          ArnEquals:
            aws:SourceArn:
              - Fn::Sub: arn:aws:sns:us-east-1:${AWS::AccountId}:source-sns-*

使用Lambda,这种策略不适用。如果有人知道原因,请分享一下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接