Cognito 身份池 - 具有 "dynamic" 个属性的基于属性的访问控制

Cognito Identity Pools - Attribute-based access control with "dynamic" attributes

我在 Cognito 用户池中有数百个 S3 存储桶和数十个用户。我希望能够 select 哪个用户可以访问哪个 S3 存储桶,例如:

等等。

我希望能够在不创建专用 API 创建动态策略的情况下做到这一点。我考虑过使用 Cognito 身份池和基于属性的访问控制。

那里 is a cool example 用户获得属性 "department": "legal" 然后被分配一个角色,允许只查询带有 -legal 后缀的桶,感谢 ${aws:PrincipalTag/department} 魔法.

如果我的用户只能访问一个存储桶,那将是一种解决方案。 但是,在我的例子中,一个用户可能会被分配到数十个或数百个存储桶(想想 AWS 文档示例中的“多个部门”)。

我想到了对每个用户使用多个自定义属性:

并创建允许您访问给定 bucket_n 当且仅当您具有属性 bucket_n: true.

的策略

如果我最多有 50 个存储桶(Cognito 中自定义属性的硬限制),这将有效。

在我的例子中,这个值略高(几百)。我可以让用户访问 200 多个存储桶,也可以让用户只允许访问一个存储桶。

有什么方法可以通过 Cognito 身份池和 IAM 策略实现我的目标吗?

好吧,我找到了一个可能对你们中的某些人有用的“解决方法”。

首先,转到 Cognito 用户池并创建一个您选择的自定义属性来保存您的所有值。请记住,Cognito 的属性值限制为 2048 个字符。

我们称该属性为 attribute_groups。将所有值作为一个字符串放在那里,并带有您选择的分隔符。在我的例子中 user1 属性 custom:attribute_groups = "123o789" 意味着 user1 在逻辑上属于组 123789.

映射这些属性 in Cognito Identity Pools。按照示例 主体 的标签键:groups 将从 属性名称 中选择:custom:attribute_groups.

最后,设置您在用户池中经过身份验证的用户采用的 IAM 策略,如下所示。就我而言,我想允许组 123 列出 s3://my_bucket/123456 列出 s3://my_bucket/456 等等。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my_bucket"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "123/*"
                    ],
                    "aws:PrincipalTag/groups": [
                        "*123*"
                    ]
                }
            }
        },
        {
            "Sid": "2",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my_bucket"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "456/*"
                    ],
                    "aws:PrincipalTag/groups": [
                        "*456*"
                    ]
                }
            }
        },
        {
            "Sid": "3",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my_bucket"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "789/*"
                    ],
                    "aws:PrincipalTag/groups": [
                        "*789*"
                    ]
                }
            }
        }
    ]
}

完成我的示例 - 当 user1 获得他们的凭据时(您可以通过创建用户、设置其属性然后执行 aws cognito-idp initiate-auth 获取 ID token 来测试,然后 aws cognito-identity get-id 获得 identity 最后 aws cognito-identity get-credentials-for-identity 获得访问密钥 id/secret 访问 key/session 令牌),他们可以执行 aws s3 ls s3://my_bucket/123aws s3 ls s3://my_bucket/789.

不幸的是,这仍然受到严重限制:

  • IAM 策略字符限制 (10240)
  • Cognito 自定义属性长度限制 (2048)

使用如此精细的 IAM 策略,您最多可以拥有 15-20 个“组”。

您可以通过创建多个策略和多个(“假的”)Cognito 用户池组来以某种方式反击,让每个用户附加到每个组,然后使用 cognito:roles 属性遍历这些组 ID token 并使用允许您设置 CustomRoleArnGetCredentialForIdentity 承担这些角色。它有效(我已经检查过),但真的很笨拙。

但是,有一个技巧可以将 Cognito 功能扩展到你们中的某些人并摆脱该限制。要求是目录结构是可预测的,您不需要访问子目录。我们将使用 ${s3:prefix} 也是一个变量这一事实。

如果是这样,您的保单可能如下所示:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my_bucket"
            ],
            "Condition": {
                "StringLike": {
                    "aws:PrincipalTag/groups": [
                        "*${s3:prefix}*"
                    ]
                }
            }
        },
        {
            "Sid": "2",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::my_bucket/${s3:prefix}/*"
            ],
            "Condition": {
                "StringLike": {
                    "aws:PrincipalTag/groups": [
                        "*${s3:prefix}*"
                    ]
                }
            }
        }
    ]
}

然后在 custom:attribute_groups 中,您必须指定给定用户有权访问的所有前缀(即所有目录)。请注意,不允许他们访问这些目录的子目录,因为 ${s3:prefix} 会有所不同。

例如,用户:

custom:attribute_groups = directoryA####directoryA/subdirectoryAA####directoryC/subdirectoryCA/subsubdirectoryCAA

将被允许访问以下位置的文件:

  • my_bucket/directoryA
  • my_bucket/directoryA/subdirectoryAA
  • my_bucket/directoryC/subdirectoryCA/subsubdirectoryCAA

并且没有其他(子)目录directoryA/subdirectoryAB 无法访问,即使 directoryA 可以访问。

当然,每个属性最多可以容纳2048个字符。但是您可以在 Cognito 中创建最多 50 个自定义属性并相应地扩展您的策略。我认为这对于大多数常规用例来说应该足够了。