Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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: 28 days ago
JSON representation
Terraform module to update AWS securityhub findings such as suppressions
- Host: GitHub
- URL: https://github.com/infralicious/terraform-aws-securityhub-batchupdatefindings
- Owner: infralicious
- License: mit
- Created: 2024-06-03T06:28:51.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2024-09-22T23:47:24.000Z (about 2 months ago)
- Last Synced: 2024-09-27T18:43:12.522Z (about 1 month ago)
- Topics: aws, module, security, security-hub, securityhub, suppression, suppressions, terraform, terraform-module
- Language: HCL
- Homepage:
- Size: 49.8 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
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.yaml1 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"for_each = local.findings
findings = yamldecode(file(each.key)).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({| n/a | yes |
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)
}))
| [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