Skip to content

Lambda Star Permissions

Level: Warning

Initial version: 0.1.3

cfn-lint: WS1003

tflint: aws_iam_role_lambda_no_star

With Lambda functions, you should follow least-privileged access and only allow the access needed to perform a given operation. Attaching a role with more permissions than necessary can open up your systems for abuse.

Limitations on policies

This rule only works with inline policies defined as part of the IAM role resource. It will not check managed policies or policies defined as separate resources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Resources:
  MyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      # The rule will check this policy
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: dynamodb:Query
                Resource: "*"

  # It will not check this policy
  MyPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: dynamodb:*
                Resource: "*"
      Roles:
        - !GetAtt MyRole.Arn
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
resource "aws_iam_role" "this" {
  name = "my-function-role"
  assume_role_policy = data.aws_iam_policy_document.assume.json

  # The rule will check this policy
  inline_policy {
    name = "FunctionPolicy"
    policy = data.aws_iam_policy_document.valid.json
  }
}

# It will not check this policy
resource "aws_iam_policy" "this" {
  policy = data.aws_iam_policy_document.invalid.json
}

resource "aws_iam_policy_attachment" "this" {
  roles = [aws_iam_role.this.name]
  policy_arn = aws_iam_policy.this.arn
}

data "aws_iam_policy_document" "assume" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type       = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

data "aws_iam_policy_document" "valid" {
  statement {
    actions = ["dynamodb:Query"]
    resources = ["arn:aws:dynamodb:eu-west-1:111122223333:table/my-table"]
  }
}

data "aws_iam_policy_document" "invalid" {
  statement {
    actions = ["dynamodb:*"]
    resources = ["*"]
  }
}

Why is this a warning?

If your Lambda function needs a broad range of permissions, you do not know ahead of time which permissions you will need, and you have evaluated the risks of using broad permissions for this function, you might ignore this rule.

Implementations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { AttributeType, Table } from '@aws-cdk/aws-dynamodb';
import { Code, Function, Runtime } from '@aws-cdk/aws-lambda';

export class MyStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const myTable = new Table(
      scope, 'MyTable',
      {
        partitionKey: {
          name: 'id',
          type: AttributeType.STRING,
        }
      },
    );

    const myFunction = new Function(
      scope, 'MyFunction',
      {
        code: Code.fromAsset('src/hello/'),
        handler: 'main.handler',
        runtime: Runtime.PYTHON_3_8,
      }
    );

    // Grant read access to the DynamoDB table
    table.grantReadData(myFunction);
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "Resources": {
    "MyFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "CodeUri": ".",
        "Runtime": "python3.8",
        "Handler": "main.handler",

        "Policies": [{
          "Version": "2012-10-17",
          "Statement": [{
            "Effect": "Allow",
            // Tightly scoped permissions to just 's3:GetObject'
            // instead of 's3:*' or '*'
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-bucket/*"
          }]
        }]
      }
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Runtime: python3.8
      Handler: main.handler

      Policies:
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              # Tightly scoped permissions to just 's3:GetObject'
              # instead of 's3:*' or '*'
              Action: s3:GetObject
              Resource: "arn:aws:s3:::my-bucket/*"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
provider:
  name: aws
  iam:
    role:
      name: my-function-role
      statements:
        - Effect: Allow
          # Tightly scoped permissions to just 's3:GetObject'
          # instead of 's3:*' or '*'
          Action: s3:GetObject
          Resource: "arn:aws:s3:::my-bucket/*"

functions:
  hello:
    handler: handler.hello
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
resource "aws_iam_role" "this" {
  name = "my-function-role"
  assume_role_policy = data.aws_iam_policy_document.assume.json

  inline_policy {
    name = "FunctionPolicy"
    policy = data.aws_iam_policy_document.this.json
  }
}

data "aws_iam_policy_document" "assume" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type       = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

data "aws_iam_policy_document" "this" {
  statement {
    # Tightly scoped permissions to just 'dynamodb:Query'
    # instead of 'dynamodb:*' or '*'
    actions = ["dynamodb:Query"]
    resources = ["arn:aws:dynamodb:eu-west-1:111122223333:table/my-table"]
  }
}

resource "aws_lambda_function" "this" {
  function_name = "my-function"
  handler       = "main.handler"
  runtime       = "python3.8"
  filename      = "function.zip"
  role          = aws_iam_role.this.arn
}

See also