Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/infralicious/terraform-aws-securityhub-batchupdatefindings

Terraform module to update AWS securityhub findings such as suppressions
https://github.com/infralicious/terraform-aws-securityhub-batchupdatefindings

aws module security security-hub securityhub suppression suppressions terraform terraform-module

Last synced: about 3 hours ago
JSON representation

Terraform module to update AWS securityhub findings such as suppressions

Awesome Lists containing this project

README

        

# terraform-aws-securityhub-batch-update-findings

This is handy when codifying suppressions using terraform and a map structure such as YAML.

## Usage

Example of module usage

```hcl
module "securityhub_batch_update_findings" {
source = "infralicious/securityhub-batchupdatefindings/aws"
# It's recommended to pin every module to a specific version
# version = "x.x.x"

findings = yamldecode(file("${path.module}/findings.yaml")).findings
default_product_arn = "arn:aws:securityhub:us-east-1:ACCOUNTID:product/ACCOUNTID/default"
default_workflow = "SUPPRESSED"
note_suffix = "\n\nAdded using terraform"
}
```

Example of `findings.yaml` file

```yaml
# findings.yaml
findings:
# Every finding should have an adequate note for the suppression.
# A single resource can have multiple findings.
# We can codify the resource either in the note or in an inline comment.
- id: "arn:aws:securityhub:us-east-1:ACCOUNTID:subscription/aws-foundational-security-best-practices/v/1.0.0/S3.11/finding/e4c171dc-12e6-433b-8a51-a382e8d24e37"
product_arn: "arn:aws:securityhub:us-east-1:ACCOUNTID:product/ACCOUNTID/default"
note:
text: "INFOSEC-1234: Suppressed since public IP ingress is for data partner"
workflow:
status: "SUPPRESSED"
```

## Misc

### Generate the YAML file

The yaml file can be autogenerated from existing suppressions using this `awscli` command with `yq`.
Remember to add the `findings` parent key.
The `title` and `resource_id` are for inline comments on what the control the finding was for.

```bash
aws securityhub get-findings \
--filters '{"WorkflowStatus": [{"Value": "SUPPRESSED", "Comparison": "EQUALS"}] }' \
--query 'Findings[].{
id: Id,
product_arn: ProductArn,
note: { text: Note.Text },
workflow: { status: `"SUPPRESSED"` }
title: Title,
resource_id: Resources[0].Id,
}' | yq -P . > findings.yaml
```

This can be done for specific controls and with more information.
Additional information in the YAML is helpful and is ignored by the terraform.

Here is an example with `RDS.13`, sorting by engine version, and populating the yaml with the desired fields

```bash
aws securityhub get-findings \
--filters '{"SeverityLabel": [{"Value": "HIGH", "Comparison": "EQUALS"}], "WorkflowStatus": [{"Value": "NEW", "Comparison": "EQUALS"}], "ComplianceSecurityControlId": [{"Value": "RDS.13", "Comparison": "EQUALS"}] }' \
--query 'sort_by(
Findings,
&Resources[0].Details.AwsRdsDbInstance.EngineVersion
)[
?contains(GeneratorId, `"security-control"`)
].{
id: Id,
product_arn: ProductArn,
workflow: { status: `"SUPPRESSED"` },
title: Title,
engine_version: Resources[0].Details.AwsRdsDbInstance.EngineVersion,
resource_id: Resources[0].Id
}' | yq -P . > findings.yaml
```

### Test

1. Run a plan
1. Retrieve the existing suppression for a specific finding
1. Use `terraform apply -target` to suppress and add a note to the same finding
1. Repeat the previous retrieval to see the new result
1. Compare with the old result and see if there are differences

### Compare the counts between suppressions and codified suppressions

This will give the count of suppressions in aws.

```bash
aws securityhub get-findings \
--filters '{"WorkflowStatus": [{"Value": "SUPPRESSED", "Comparison": "EQUALS"}] }' \
--query 'Findings[] | length(@)
```

This will give the codified suppression count.

```bash
yq '.findings | length' findings.yaml
```

If the counts differ, then the clickops'ed suppression(s) can be moved to the yaml file.

### Instead of a single file, use multiple files

If the `findings.yaml` file is too long, consider breaking it up by each control.

```bash
~ tree findings/
findings
├── EC2.1.yaml
├── EC2.2.yaml
├── EC2.3.yaml
└── S3.1.yaml

1 directory, 4 files
```

The terraform can then be modified

```hcl
locals {
findings = flatten(concat([
for file in fileset(path.module, "findings/*.yaml"):
yamldecode(file("${path.module}/${file}")).findings
]))
}

module "securityhub_batch_update_findings" {
source = "infralicious/securityhub-batchupdatefindings/aws"
# It's recommended to pin every module to a specific version
# version = "x.x.x"

findings = local.findings
# ...
}
```

---

## Requirements

| Name | Version |
|------|---------|
| [terraform](#requirement\_terraform) | >= 1.1.0 |
| [null](#requirement\_null) | > 1 |

## Providers

| Name | Version |
|------|---------|
| [null](#provider\_null) | > 1 |

## Resources

| Name | Type |
|------|------|
| [null_resource.default](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| [default\_product\_arn](#input\_default\_product\_arn) | The default product ARN for each finding. This can be overridden using the key `product_arn`. | `string` | n/a | yes |
| [findings](#input\_findings) | The list of findings to run the awscli command on. |

list(object({
id = string
note = object({
text = string
updated_by = optional(string)
})
workflow = object({
status = string
})
product_arn = optional(string)
verification_state = optional(string)
confidence = optional(number)
criticality = optional(number)
}))
| n/a | yes |
| [awscli\_additional\_arguments](#input\_awscli\_additional\_arguments) | n/a | `string` | `""` | no |
| [awscli\_command](#input\_awscli\_command) | n/a | `string` | `"aws"` | no |
| [default\_note\_updated\_by](#input\_default\_note\_updated\_by) | The default UpdatedBy for each finding for its note if a note is provided. This can be overridden using the key `note_updatedby`. | `string` | `"terraform"` | no |
| [default\_workflow](#input\_default\_workflow) | The default workflow for each finding. This can be overridden using the key `workflow`. | `string` | `"SUPPRESSED"` | no |
| [dryrun\_enabled](#input\_dryrun\_enabled) | Whether or not to add an echo before the command to verify the commands prior to applying. | `bool` | `false` | no |
| [note\_suffix](#input\_note\_suffix) | Add a suffix to each note. | `string` | `""` | no |

## References

- https://ekantmate.medium.com/how-to-suppress-particular-findings-in-aws-security-hub-using-terraform-558bd3819b31
- https://github.com/hashicorp/terraform-provider-aws/issues/29164
- https://registry.terraform.io/modules/infralicious/securityhub-batchupdatefindings/aws
- https://library.tf/modules/infralicious/securityhub-batchupdatefindings/aws/latest