Lambda 承担具有细粒度权限的 AWS IAM 角色

时间:2021-06-10 16:53:34

标签: node.js aws-lambda amazon-cognito amazon-iam aws-sts

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

目前的架构是这样的:

  1. React-Native 应用调用 http get,将 JWT 作为授权标头传递以通过 AWS Api 网关。
  2. 接收端的 lambda 函数对 JWT 进行解码并拉出附加到用户所属 Cognito 组的角色 Arn。
  3. Lambda 函数然后使用上述 RoleArn 调用 sts.assumeRole、包含各种用户信息的 RoleSessionName 以及基于附加到所述 Cognito 用户的自定义属性限制数据库访问的内嵌策略。

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

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

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

这是我们在 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,
    };
  }

1 个答案:

答案 0 :(得分:0)

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

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

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