Lambda 代入具有细粒度权限的 AWS IAM 角色

Posted

技术标签:

【中文标题】Lambda 代入具有细粒度权限的 AWS IAM 角色【英文标题】:Lambda assuming AWS IAM role with fine-grained permissions 【发布时间】:2021-08-27 17:11:55 【问题描述】:

我正在尝试根据附加到 Amazon Cognito 用户的信息假设细粒度的 DynamoDB 访问。

目前的架构是这样的:

    React-Native 应用程序调用 http get,将 JWT 作为 Authorization 标头传递以通过 AWS Api 网关。 接收端的 lambda 函数对 JWT 进行解码并拉出附加到用户所属的 Cognito 组的 Role Arn。

在这组事件之后,我希望我对数据库调用的扫描会由于缺乏访问某些元素的权限而失败(稍后将使用查询而不是扫描,我只是想确保权限工作正常)。

但是扫描会继续从数据库中获取所有项目。

我一定是遗漏了一些简单的东西,这是我读到的用于执行此类操作的方法。

这是我们在 Lambda 函数中扮演的角色:

CognitoGroupRole:
 Type: AWS::IAM::Role
 Properties:
  Description: Cognito Assets Group Role
  AssumeRolePolicyDocument: 
    Version: 2012-10-17
    Statement:
      - Effect: Allow
        Principal:
          AWS:
            - !Sub 'arn:aws:iam::$AWS::AccountId:root'
        Action:
          - 'sts:AssumeRole'
  Policies:
    - PolicyName: Database1AccessPolicy
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "dynamodb:Scan"
              - "dynamodb:PutItem"
              - "dynamodb:UpdateItem"
              - "dynamodb:Query"
            Resource: !GetAtt Database1.Arn
    - PolicyName: Database2AccessPolicy
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - "dynamodb:Query"
            Resource: !GetAtt Database2.Arn

这是 Lambda 内部的函数,它承担角色并编写内联策略:

const assumeGroupRole = () => 
    /* Get JWT from event.headers.Authorization */
    const 
      headers:  Authorization ,
     = event;
    /* Get the function name from context.functionName */
    const  functionName  = context;
    /* Decode the JWT */
    const decodedToken = jwt_decode(Authorization);
    const Policy = JSON.stringify(
      Version: '2012-10-17',
      Statement: 
        Effect: 'Allow',
        Action: ['dynamodb:Query', 'dynamodb:Scan'] /* Scan only for testing */,
        Resource /* Device table ARN from environment variables */,
        Condition: 
          /* This condition *should* mean they can only access any row where the leading key is the AccountUniqueId */
          'ForAllValues:StringEquals': 
            'dynamodb:LeadingKeys': [decodedToken['custom:AccountUniqueId']],
          ,
        ,
      ,
    );
    console.info('Inline Policy', Policy);
    /* ARN of the role attached to the cognito group */
    const [RoleArn] = decodedToken['cognito:roles'];
    /* Create session name from the email and function name */
    const RoleSessionName = `$decodedToken.email+$
      functionName.split('-')[2]
    `;
    return new Promise((resolve, reject) => 
      sts.assumeRole( RoleArn, RoleSessionName, Policy , function (
        err,
        data,
      ) 
        if (err) reject(console.error('Assume role error', err, err.stack));
        else resolve(data);
      );
    );
  ;

我实际运行扫描的位置(在我得到这个工作后查询)以从 DynamoDB 中获取数据:

/* Create new DynamoDB Document client with Credentials from 
 assumeRole */
  const docClient = new AWS.DynamoDB.DocumentClient(
    accessKeyId: Credentials.AccessKeyId,
    secretAccessKey: Credentials.SecretAccessKey,
    sessionToken: Credentials.SessionToken,
  );

  const params = 
    TableName,
  ;

  try 
    /* Query the database with the above parameters to fetch only the 
    items that match the conditions */
    const  Items  = await docClient.scan(params).promise();
    console.info('Queried Items', Items);
    const statusCode = 200;
    const body = JSON.stringify(Items);
    logEvents(statusCode, body);
    return 
      statusCode,
      body,
    ;
   catch (e) 
    console.info('Error', e);
    const statusCode = 400;
    const body = JSON.stringify(e.message);
    logEvents(statusCode, body);
    return 
      statusCode,
      body,
    ;
  

【问题讨论】:

你实际在哪里运行查询,使用assumeRole返回的数据? 编辑了我的原始帖子以显示请求的信息。 如果你调用sts:GetCallerIdentity,它会返回什么?它是什么 ARN/用户? 可能是这样的,因为内联策略必须是主策略的子集。您的主要政策和内联政策的条件不同,因此内联政策可能会被忽略。我以前从未测试过它,但它可能值得检查。 【参考方案1】:

感谢您的回复。我最终通过与同事的随意交谈弄清楚了这一点。

事实证明,“扫描”调用忽略了这些类型的细粒度权限。我假设这是因为 scan 不是在寻找数据的子集,而是在寻找所有数据。

使用上面发布的相同代码和不匹配的AccountUniqueId,查询返回“未授权”错误。当我使用正确的AccountUniqueId 时,我得到了我期望的数据。

【讨论】:

以上是关于Lambda 代入具有细粒度权限的 AWS IAM 角色的主要内容,如果未能解决你的问题,请参考以下文章

IAM 策略条件中的 cognito 用户池自定义属性与 Dynamodb 细粒度访问

如何使用 Terraform 创建没有代入角色策略的 AWS IAM 角色?

Terraform - 具有编程访问权限的 AWS IAM 用户

AWS Lambda:即使在STS:AssumeRole成功之后,lambda函数仍然使用旧的IAM角色

如何使用 terraform 创建具有访问权限和密钥的 AWS IAM 服务账户

AWS ECS Docker 容器 Boto3 IAM 权限