aws-cloudformation / aws-guard-rules-registry

Rules Registry for Compliance Frameworks
Apache License 2.0
107 stars 24 forks source link

(IAM): IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS Suppression Ignored with Multiple Managed Policies #246

Open Abhiek187 opened 1 year ago

Abhiek187 commented 1 year ago

What is the problem?

I'm using iam_policy_no_statements_with_full_access.guard to validate my CloudFormation template. I'm trying to suppress the rule for one of my managed policies, but it seems to get ignored and still fails the test.

Reproduction Steps

The following is a reproducible snippet of my CloudFormation template (named repro.yaml):

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  GitHubRemainingPolicy:
    Type: AWS::IAM::ManagedPolicy
    Metadata:
      guard:
        SuppressedRules:
          - IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: AWSCloudFormationFullAccess
            Effect: Allow
            Action:
              - "cloudformation:*"
            Resource: "*"
          - Sid: AmazonSNSFullAccess
            Effect: Allow
            Action:
              - "sns:*"
            Resource: "*"
          - Sid: AmazonS3FullAccess
            Effect: Allow
            Action:
              - "s3:*"
              - "s3-object-lambda:*"
            Resource: "*"
          - Sid: AmazonAPIGatewayAdministrator
            Effect: Allow
            Action:
              - "apigateway:*"
            Resource: "arn:aws:apigateway:*::/*"
  CodeBuildPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodeBuildAccess
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: CloudWatchLogsPolicy
            Effect: Allow
            Action:
              - "logs:CreateLogGroup"
              - "logs:CreateLogStream"
              - "logs:PutLogEvents"
            Resource: "*"
          - Sid: S3ObjectPolicy
            Effect: Allow
            Action:
              - "s3:DeleteObject"
              - "s3:GetObject"
              - "s3:ListBucket"
              - "s3:PutObject"
            Resource:
              - !GetAtt ReactAppBucket.Arn
              - !Sub "${ReactAppBucket.Arn}/*"

I ran cfn-guard validate --show-summary fail --output-format single-line-summary --data repro.yaml --rules iam_policy_no_statements_with_full_access.guard to validate this template.

What did you expect to happen?

Since I added the Metadata block to suppress IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS, the cfn-guard validate command shouldn't show any failures.

What actually happened?

The validate command failed with the following output:

repro.yaml Status = FAIL
FAILED rules
iam_policy_no_statements_with_full_access.guard/IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS    FAIL
---
Evaluating data repro.yaml against rules iam_policy_no_statements_with_full_access.guard
Number of non-compliant resources 1
Resource = GitHubRemainingPolicy {
  Type      = AWS::IAM::ManagedPolicy
  Rule = IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS {
    ALL {
      Check =  %violations EMPTY   {
        ComparisonError {
          Message          {
            Violation: One or more IAM Managed Policies allow full access to at least 1 AWS service
            Fix: Remove policy statements that match {"Effect": "Allow", "Action": "<service-name>:*" ... } or {"Effect": "Allow", "Action": "*" ... }
          }
          Error            = Check was not compliant as property [/Resources/GitHubRemainingPolicy[L:3,C:4]] was not empty.
          PropertyPath    = /Resources/GitHubRemainingPolicy[L:3,C:4]
          Operator        = EMPTY
          Code:
                1.AWSTemplateFormatVersion: "2010-09-09"
                2.Resources:
                3.  GitHubRemainingPolicy:
                4.    Type: AWS::IAM::ManagedPolicy
                5.    Metadata:
                6.      guard:

        }
      }
    }
  }
}

CloudFormation Guard Version

cfn-guard 3.0.0

OS

Ubuntu (running Windows Subsystem for Linux on Windows 11)

OS Version

Ubuntu 20.04.6 LTS

Other information

After debugging for hours, I realized the issue may be due to the guard rule when multiple managed policies are defined in the CloudFormation template. Looking at iam_policy_no_statements_with_full_access.guard:

let aws_iam_managed_policies_no_statements_with_full_access = Resources.*[ Type == 'AWS::IAM::ManagedPolicy'
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS" 
]

rule IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS when %aws_iam_managed_policies_no_statements_with_full_access !empty {
  let violations = Resources.*[
    Type == 'AWS::IAM::ManagedPolicy' 
    some Properties.PolicyDocument.Statement[*] {
      some Action[*] in ["*", /^[a-zA-Z0-9]*:\*$/]
      Effect == "Allow"
      some Resource in ["*"]
    }
  ]
  %violations empty
  <<
    Violation: One or more IAM Managed Policies allow full access to at least 1 AWS service
    Fix: Remove policy statements that match {"Effect": "Allow", "Action": "<service-name>:*" ... } or {"Effect": "Allow", "Action": "*" ... }
  >>
} 

From my understanding:

  1. aws_iam_managed_policies_no_statements_with_full_access is looking at all the managed policies without any suppression metadata. This will get the CodeBuildPolicy since it's a managed policy and no rules are suppressed there.
  2. The IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS rule will kick in since aws_iam_managed_policies_no_statements_with_full_access is not empty, based on step 1.
  3. violations will check the Action, Effect, and Resource values in all the managed policy documents.
  4. Since GitHubRemainingPolicy contains policies allowing all actions for all resources (such as "cloudformation:*"), violations is non-empty, causing the rule to fail.

I think the bug is due to step 3. Even though the rule was originally executed for CodeBuildPolicy, it's checking GitHubRemainingPolicy as well, effectively ignoring the suppression earlier. I've noticed most of the other guard rules, such as IAM_NO_INLINE_POLICY_CHECK, reference the let variable inside the rule block:

let aws_iam_entities_no_inline_policy = Resources.*[
  Type in [ /AWS::IAM::User/,
            /AWS::IAM::Role/,
            /AWS::IAM::Group/ ]
  Metadata.guard.SuppressedRules not exists or
  Metadata.guard.SuppressedRules.* != "IAM_NO_INLINE_POLICY_CHECK"
]

rule IAM_NO_INLINE_POLICY_CHECK when %aws_iam_entities_no_inline_policy !empty {
  %aws_iam_entities_no_inline_policy.Properties.Policies empty
  <<
    Violation: Inline policies are not allowed on IAM Users, Roles, or Groups.
    Fix: Remove the Policies list property from any IAM Users, Roles, or Groups.
  >>
}

which only applies the check to all the resources that satisfy the filters defined in the let variable.

Workaround: If I remove CodeBuildPolicy or add the suppression metadata in that policy as well, the cfn-guard validate command succeeds, because aws_iam_managed_policies_no_statements_with_full_access is now empty, causing the rule to be skipped. However, I would have to apply this fix to any other managed policies I add to the CloudFormation template, so it's not an ideal solution.

I've also noticed that IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS and RESTRICTED_INCOMING_TRAFFIC follow a similar violations pattern without referencing the let variable, so this bug might extend to those rules as well.