cloudformation-guard icon indicating copy to clipboard operation
cloudformation-guard copied to clipboard

[BUG] S3 check failing on unrelated IAM Role

Open Aaron-Garrett opened this issue 1 year ago • 1 comments

Describe the bug A clear and concise description of what the bug is.

The rule I wrote checking for S3 permissions on wildcard Resource is running and failing on IAM roles without any S3 permissions.

To Reproduce Please supply:

  1. Example rules and template that results in the error Template:
rImmutaRdsLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: immunment
      PermissionsBoundary: !Sub "arn:aws:iam::${AWS::AccountId}:policy/L-Boundary"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "rds.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "RdsInvokeLambdaPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "lambda:InvokeFunction"
                Resource: !GetAtt rIImmuta.Arn

CloudFormation Guard Rule:

# R5.0  Automatically inspect the code for * in S3 folder (prefix) access and present a warning statement to the developer if found

let aws_iam_policies_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_POLICY_NO_FULL_ACCESS_FOR_S3"
]

let aws_iam_roles_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Role/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_ROLE_NO_FULL_ACCESS_FOR_S3"
]

# Checks there is no wildcard in the bucket name in an IAM Role

rule IAM_ROLE_FULL_BUCKET_NAME_USED when %aws_iam_roles_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Role

rule IAM_ROLE_FULL_FOLDER_PATH_USED when %aws_iam_roles_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Role/]
    some Properties.Policies[*].PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks there is no wildcard in the bucket name in an IAM Policy

rule IAM_POLICY_FULL_BUCKET_NAME_USED when %aws_iam_policies_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

# Checks the full folder path is used with no wildcards before the second "/" in an IAM Policy

rule IAM_POLICY_FULL_FOLDER_PATH_USED when %aws_iam_policies_no_full_access_for_s3 !empty {
  let violationsString = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

let s3_no_full_permission = Resources.*[ Type == 'AWS::S3::BucketPolicy'
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "S3_NO_FULL_PERMISSION"
]

# Checks there is no wildcard in the bucket name in an IAM Policy

rule BUCKET_POLICY_FULL_BUCKET_NAME_USED when %s3_no_full_permission !empty {
  let violationsString = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  let violationsSub = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
    some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::(.*?)(.*?)(\*)(.*?)\/$/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}


# Checks the full folder path is used with no wildcards before the second "/" in an IAM Policy

rule BUCKET_POLICY_FULL_FOLDER_PATH_USED when %s3_no_full_permission !empty {
  let violationsString = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  let violationsSub = Resources.*[
    Type == 'AWS::S3::BucketPolicy'
    some Properties.PolicyDocument.Statement[*] {
      some Resource[*][keys == "Fn::Sub"] in [/^arn:aws:s3:::llp(.*?)(\*)(.*?)\/(.*?)\/(.*?)/, /^arn:aws:s3:::llp(.*?)\/(.*?)(\*)(.*?)\/(.*)/]
    }
  ]
  %violationsString empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
  %violationsSub empty
  <<
    Violation: * in S3 folder (prefix) access.
    Fix: Avoid wildcard matching * in S3 folder (prefix) access
  >>
}

  1. The commands you used to invoke the tool

OUTPUT=RUST_BACKTRACE=1 ./cfn-guard-v3-ubuntu-latest/cfn-guard validate --show-summary pass,fail --data "$TEMPLATE_FILE" --rules .github/folderlib/cfn-guard/rules/ 2>&1

  1. The output of a -v log level if it's not related to cfn-guard-lambda, or the relevant CloudWatch log messages if it is related to the cfn-guard-lambda
s3_no_full_access.guard/IAM_ROLE_NO_FULL_ACCESS_FOR_S3      FAIL
---
Evaluating data template.yml against rules s3_no_full_access.guard
Number of non-compliant resources 1
Resource = rImmutaRdsLambdaRole {
  Type      = AWS::IAM::Role
  Rule = IAM_ROLE_NO_FULL_ACCESS_FOR_S3 {
    ALL {
      Check =  %violationsSub EMPTY   {
        ComparisonError {
          Message          {
            Violation: * in S3 folder (prefix) access.
            Fix: Avoid wildcard matching * in S3 folder (prefix) access
          }
          Error            = Check was not compliant as property [/Resources/rImmutaRdsLambdaRole[L:1549,C:4]] was not empty.
          PropertyPath    = /Resources/rImmutaRdsLambdaRole[L:1549,C:4]
          Operator        = EMPTY
          Code:
             1547.      Principal: secretsmanager.amazonaws.com
             1548.
             1549.  rImmutaRdsLambdaRole:
             1550.    Type: "AWS::IAM::Role"
             1551.    Properties:
             1552.      RoleName: !Sub immuta-rds-invoke-lambda-role-

        }
      }
    }
  }
}

Expected behavior A clear and concise description of what you expected to happen.

The test should skip

Operating System: [eg, MacOS, Windows, Ubuntu, etc]

Linux

OS Version [eg Catalina, 10, 18.04, etc]

Ubuntu latest

Aaron-Garrett avatar Aug 22 '24 19:08 Aaron-Garrett

Hi @Aaron-Garrett it seems like the rule you are referring to isn't included here. Can you please update your example so we can either look further into this, or help you debug?

Thanks,

joshfried-aws avatar Aug 26 '24 13:08 joshfried-aws

Hi, since we are unable to assist you given the information provided, I am going to close this issue out. Feel free to reopen it and provide us with what was asked above.

joshfried-aws avatar Sep 03 '24 11:09 joshfried-aws

Sorry, been busy. The rule is there. That is the problem. It seems totally unrelated. All of those IAM s3 policy rules are the ones that are erroring out.

Aaron-Garrett avatar Sep 16 '24 15:09 Aaron-Garrett

Sorry, been busy. The rule is there. That is the problem. It seems totally unrelated. All of those IAM s3 policy rules are the ones that are erroring out.

Hi @Aaron-Garrett, I think there's some confusion here. The output you provided here

s3_no_full_access.guard/IAM_ROLE_NO_FULL_ACCESS_FOR_S3      FAIL
---
Evaluating data template.yml against rules s3_no_full_access.guard
Number of non-compliant resources 1
Resource = rImmutaRdsLambdaRole {
  Type      = AWS::IAM::Role
  Rule = IAM_ROLE_NO_FULL_ACCESS_FOR_S3 {
    ALL {
      Check =  %violationsSub EMPTY   {
        ComparisonError {
          Message          {
            Violation: * in S3 folder (prefix) access.
            Fix: Avoid wildcard matching * in S3 folder (prefix) access
          }
          Error            = Check was not compliant as property [/Resources/rImmutaRdsLambdaRole[L:1549,C:4]] was not empty.
          PropertyPath    = /Resources/rImmutaRdsLambdaRole[L:1549,C:4]
          Operator        = EMPTY
          Code:
             1547.      Principal: secretsmanager.amazonaws.com
             1548.
             1549.  rImmutaRdsLambdaRole:
             1550.    Type: "AWS::IAM::Role"
             1551.    Properties:
             1552.      RoleName: !Sub immuta-rds-invoke-lambda-role-

        }
      }
    }
  }
}

tells me a rule called IAM_ROLE_NO_FULL_ACCESS_FOR_S3 is the cause for the failure. The issue here is the snippet you provided of your guard rules does not define this rule anywhere. The only place it is mentioned is in this block

let aws_iam_policies_no_full_access_for_s3 = Resources.*[
  Type in [/AWS::IAM::Policy/, /AWS::IAM::ManagedPolicy/]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_POLICY_NO_FULL_ACCESS_FOR_S3"
]

but that still doesnt show me the definition, or where it's being evaluated. This leads me to believe you are possibly passing a list of rules, or a directory that contains multiple rules? This rule has to be coming from somewhere.

joshfried-aws avatar Sep 16 '24 15:09 joshfried-aws