bridgecrewio / checkov

Prevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.
https://www.checkov.io/
Apache License 2.0
7.17k stars 1.12k forks source link

Checkov failed to use custom policy to scan terraform code #5234

Closed configmaker closed 1 year ago

configmaker commented 1 year ago

Describe the issue I created a custom policy to scan if EC2 has been added required tags in Terraform codes. Terraform codes are stored in a folder, so I used -d to access it. Meanwhile, I used --external-checks-dir to indicate the location of the custom policy file. After running "checkov -d /path/to/ec2_instance.tf --external-checks-dir /path/to/cusomt_policy.yaml", it gave me tons of error message as follow:

[MainThread ] [ERROR] Cannot load external check 'payload' from /home/jenkins/.venvs/checkov/lib/python3.11/site-packages/aiohttp/payload.py Traceback (most recent call last): File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/checkov/common/checks/base_check_registry.py", line 207, in load_external_checks spec.loader.exec_module(module) # type: ignore[union-attr] # loader can't be None here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 940, in exec_module File "", line 241, in _call_with_frames_removed File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/aiohttp/payload.py", line 26, in from . import hdrs ImportError: attempted relative import with no known parent package ... ...

There is no error when I just used the built-in policies to scan the code.

Expected behavior Run my custom checks against the dictionary of terraform codes.

The custom policy is:

metadata:
  name: "Check EC2 has special Tags"
  id: CKV2_CUSTOM_1
  category: GENERAL_SECURITY
  guideline: "Please add required tags to EC2"

definition:
  cond_type: attribute
  resource_types:
    - "aws_instance"
  attribute: "tags"
  operator: "contains"
  value:
    - "project"
    - "owner"
    - "costcenter"

Examples The ec2_instance.tf file looks like:

resource "aws_instance" "nodes" {
  count           = var.ec2_instance_count
  ami             = nonsensitive(data.aws_ssm_parameter.ami.value)
  instance_type   = var.instance_type
  subnet_id       = var.subnets-id[count.index]
  security_groups = [var.security-group-id]
  key_name        = aws_key_pair.poc-ec2-keypair.key_name

  connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ec2-user"
    private_key = file("~/.ssh/poc-key.pem")

  }

  tags = {
    Name = "node-1"
  }
}

Version (please complete the following information):

gruebel commented 1 year ago

hey @configmaker thanks for reaching out.

It looks like your command is quite wrong. -d is for scanning a folder not file, use -f instead . --external-checks-dir is also for usage with a folder not file.

So, it should look like this

checkov -f /path/to/ec2_instance.tf --external-checks-dir /path/to

And your policy won't work anyway. Because to check against a list of values you need to use within and you are targeting tags, which is an object, you probably want to target tags.Name

configmaker commented 1 year ago

Hi @gruebel, thanks for your kind reply. I have terraform modules in a folder and stored the custom policy in a policy folder. checkov -d /path/to/terraform-folder --external-checks-dir /path/to/policy-folder I expect to check if EC2 instances contain required tag keys, such as "project", and "owner". I cannot understand the description of within - "Used with filter to focus the findings on a specific resource type or with attribute to provide a list of possible options", and its sample:

cond_type: filter
attribute: resource_type
value:
- google_logging_organization_sink
operator: within

Can you please explain how to make it work with within?

gruebel commented 1 year ago

ok, since you are looking for the tag name and not the actual value, it needs to be a nit different

definition:
  and:
    - cond_type: attribute
      resource_types:
        - "aws_instance"
      attribute: "tags.project"
      operator: "exists"
    - cond_type: attribute
      resource_types:
        - "aws_instance"
      attribute: "tags.owner"
      operator: "exists"

and if you actually want to check for the value of the owner tag for example, then you could use within like that

    - cond_type: attribute
      resource_types:
        - "aws_instance"
      attribute: "tags.owner"
      operator: "within"
      value:
        - me
        - other
configmaker commented 1 year ago

Thank you very much for the detailed explanation. When I used it to scan the directory of terraform script, checkov still gave me tons of errors as follows:

2023-06-23 01:58:24,849 [MainThread  ] [ERROR]  Cannot load external check 'payload' from ./.venvs/checkov/lib/python3.>
Traceback (most recent call last):
  File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/checkov/common/checks/base_check_registry.py", line 2>
    spec.loader.exec_module(module)  # type: ignore[union-attr] # loader can't be None here
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/jenkins/./.venvs/checkov/lib/python3.11/site-packages/aiohttp/payload.py", line 26, in <module>
    from . import hdrs
ImportError: attempted relative import with no known parent package
2023-06-23 01:58:24,851 [MainThread  ] [ERROR]  Cannot load external check 'web_runner' from ./.venvs/checkov/lib/pytho>
Traceback (most recent call last):
  File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/checkov/common/checks/base_check_registry.py", line 2>
    spec.loader.exec_module(module)  # type: ignore[union-attr] # loader can't be None here
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/jenkins/./.venvs/checkov/lib/python3.11/site-packages/aiohttp/web_runner.py", line 9, in <module>
    from .web_app import Application
ImportError: attempted relative import with no known parent package
2023-06-23 01:58:24,853 [MainThread  ] [ERROR]  Cannot load external check 'base_protocol' from ./.venvs/checkov/lib/py>
Traceback (most recent call last):
  File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/checkov/common/checks/base_check_registry.py", line 2>
    spec.loader.exec_module(module)  # type: ignore[union-attr] # loader can't be None here
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/jenkins/./.venvs/checkov/lib/python3.11/site-packages/aiohttp/base_protocol.py", line 4, in <module>
    from .tcp_helpers import tcp_nodelay
ImportError: attempted relative import with no known parent package
2023-06-23 01:58:24,853 [MainThread  ] [ERROR]  Cannot load external check 'web_routedef' from ./.venvs/checkov/lib/pyt>
Traceback (most recent call last):
  File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/checkov/common/checks/base_check_registry.py", line 2>
    spec.loader.exec_module(module)  # type: ignore[union-attr] # loader can't be None here
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/jenkins/./.venvs/checkov/lib/python3.11/site-packages/aiohttp/web_routedef.py", line 19, in <module>
    from . import hdrs
ImportError: attempted relative import with no known parent package

Can you please tell me what is the root cause and how to solve it?

gruebel commented 1 year ago

is there something else in the folder, which you reference for the custom policies/checks?

configmaker commented 1 year ago

The content in the folder is only for TF code, looks as:

/var/lib/jenkins/workspace/poc-tag
├── aws
│   ├── network
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── variables.tf
│   └── vms
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── main.tf
├── outputs.tf
├── terraform.tfvars
└── variables.tf

And the policy is:

metadata:
  name: "Check EC2 has special Tags"
  id: "CKV2_AWS_999"
  category: "GENERAL_SECURITY"
  guideline: "Please add required tags to EC2"
scope:
  provider: aws

definition:
  and:
    - cond_type: attribute
      resource_types:
        - "aws_instance"
      attribute: "tags.project"
      operator: "exists"
    - cond_type: attribute
      resource_types:
        - "aws_instance"
      attribute: "tags.owner"
      operator: "exists"
gruebel commented 1 year ago

it looks like you run on Jenkins and I can't tell what else is interfering there. Did you try to run it locally with

checkov -f ec2_instance.tf --external-checks-dir custom_checks

make sure, nothing else is in the folder, where the custom checks are stored.

configmaker commented 1 year ago

Yes, I would like to use it locally before I integrate it into Jenkins. I cloned it from GitHub to the Jenkins folder and put my custom checks in a separate folder. This time, before I run the custom checks against a TF file, I deleted the .gitfolder to ensure there were only TF files in the folder. It still got tons of problems which as the same as the former. I wonder why there are error message as:

[ERROR]  Cannot load external check 'payload' from ./.venvs/checkov/lib/python3.>
Traceback (most recent call last):
  File "/home/jenkins/.venvs/checkov/lib/python3.11/site-packages/checkov/common/checks/base_check_registry.py", line 2>
    spec.loader.exec_module(module)  # type: ignore[union-attr] # loader can't be None here
configmaker commented 1 year ago

I found the problem - I forgot that I put a hidden folder in the custom policy folder. After I remove it, checkov works fine. I appreciate your time and your help. Happy weekend!

gruebel commented 1 year ago

Glad to hear it works now 🙂