From 35ad999822c7bc5a043702e69f321bcbdb5cb3f2 Mon Sep 17 00:00:00 2001 From: Robert Fairburn <8029478+rfairburn@users.noreply.github.com> Date: Wed, 15 Jul 2026 07:45:23 -0500 Subject: [PATCH] S3 bucket tagging (#268) --- addons/logging-alb/README.md | 15 +++++++------ addons/logging-alb/main.tf | 4 ++++ addons/logging-alb/variables.tf | 6 +++++ addons/logging-destination-datadog/README.md | 22 ++++++------------- addons/logging-destination-datadog/main.tf | 1 + .../logging-destination-datadog/variables.tf | 6 +++++ addons/logging-destination-splunk/README.md | 11 +++++----- addons/logging-destination-splunk/main.tf | 1 + .../logging-destination-splunk/variables.tf | 6 +++++ addons/osquery-carve/README.md | 14 ++++++------ addons/osquery-carve/main.tf | 1 + addons/osquery-carve/variables.tf | 2 ++ 12 files changed, 55 insertions(+), 34 deletions(-) diff --git a/addons/logging-alb/README.md b/addons/logging-alb/README.md index 397a8b0..c383a54 100644 --- a/addons/logging-alb/README.md +++ b/addons/logging-alb/README.md @@ -89,22 +89,22 @@ No requirements. ## Providers | Name | Version | -|------|---------| -| [archive](#provider\_archive) | 2.7.1 | -| [aws](#provider\_aws) | 6.40.0 | +| ---- | ------- | +| [archive](#provider\_archive) | 2.8.0 | +| [aws](#provider\_aws) | 6.54.0 | | [terraform](#provider\_terraform) | n/a | ## Modules | Name | Source | Version | -|------|--------|---------| +| ---- | ------ | ------- | | [athena-s3-bucket](#module\_athena-s3-bucket) | terraform-aws-modules/s3-bucket/aws | 5.12.0 | | [s3\_bucket\_for\_logs](#module\_s3\_bucket\_for\_logs) | terraform-aws-modules/s3-bucket/aws | 5.12.0 | ## Resources | Name | Type | -|------|------| +| ---- | ---- | | [aws_athena_database.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_database) | resource | | [aws_athena_workgroup.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_workgroup) | resource | | [aws_cloudwatch_event_rule.sweep_reencrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | @@ -144,7 +144,7 @@ No requirements. ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| +| ---- | ----------- | ---- | ------- | :------: | | [alt\_path\_prefix](#input\_alt\_path\_prefix) | Used if the prefix inside of the s3 bucket doesn't match the name of the bucket prefix | `string` | `null` | no | | [enable\_athena](#input\_enable\_athena) | n/a | `bool` | `true` | no | | [enable\_reencrypt\_sweep](#input\_enable\_reencrypt\_sweep) | Enable the sweep and re-encrypt Lambda functions, EventBridge schedule, S3 bucket notification, and associated IAM roles. | `bool` | `false` | no | @@ -155,6 +155,7 @@ No requirements. | [kms\_base\_policy](#input\_kms\_base\_policy) | Optional base KMS key-policy statements to apply to module-created CMKs before module-required service access statements are merged in. If null, the module defaults to the historical root `kms:*` statement. |
list(object({
sid = string
effect = string
principals = object({
type = string
identifiers = list(string)
})
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
}))
| `null` | no | | [lambda\_log\_retention\_in\_days](#input\_lambda\_log\_retention\_in\_days) | CloudWatch log retention in days for the re-encrypt and sweep Lambda functions | `number` | `365` | no | | [prefix](#input\_prefix) | n/a | `string` | `"fleet"` | no | +| [s3\_bucket\_tags](#input\_s3\_bucket\_tags) | Additional tags to apply to all S3 buckets created by this module. | `map(string)` | `{}` | no | | [s3\_expiration\_days](#input\_s3\_expiration\_days) | n/a | `number` | `90` | no | | [s3\_newer\_noncurrent\_versions](#input\_s3\_newer\_noncurrent\_versions) | n/a | `number` | `5` | no | | [s3\_noncurrent\_version\_expiration\_days](#input\_s3\_noncurrent\_version\_expiration\_days) | n/a | `number` | `30` | no | @@ -163,5 +164,5 @@ No requirements. ## Outputs | Name | Description | -|------|-------------| +| ---- | ----------- | | [log\_s3\_bucket\_id](#output\_log\_s3\_bucket\_id) | S3 bucket used by ALB access logging (SSE-S3 on write, re-encrypted to SSE-KMS by Lambda) | diff --git a/addons/logging-alb/main.tf b/addons/logging-alb/main.tf index 308d202..70686d9 100644 --- a/addons/logging-alb/main.tf +++ b/addons/logging-alb/main.tf @@ -260,6 +260,8 @@ module "s3_bucket_for_logs" { bucket = local.landing_bucket_name + tags = var.s3_bucket_tags + # Allow deletion of non-empty bucket force_destroy = true @@ -646,6 +648,8 @@ module "athena-s3-bucket" { bucket = "${var.prefix}-alb-logs-athena" + tags = var.s3_bucket_tags + # Allow deletion of non-empty bucket force_destroy = true diff --git a/addons/logging-alb/variables.tf b/addons/logging-alb/variables.tf index c5983c0..8749b4b 100644 --- a/addons/logging-alb/variables.tf +++ b/addons/logging-alb/variables.tf @@ -86,3 +86,9 @@ variable "extra_s3_athena_policies" { type = list(any) default = [] } + +variable "s3_bucket_tags" { + description = "Additional tags to apply to all S3 buckets created by this module." + type = map(string) + default = {} +} diff --git a/addons/logging-destination-datadog/README.md b/addons/logging-destination-datadog/README.md index 5d130f4..785824e 100644 --- a/addons/logging-destination-datadog/README.md +++ b/addons/logging-destination-datadog/README.md @@ -9,15 +9,7 @@ This addon configures AWS Kinesis Firehose to send Fleet's osquery logs to Datad ## S3 Bucket Policy: Deny Non-HTTPS -Set `attach_deny_insecure_transport_policy = true` to attach a bucket policy to the failure S3 bucket that denies any requests made over plain HTTP: - -```hcl -module "datadog-logging" { - source = "github.com/fleetdm/fleet-terraform//addons/logging-destination-datadog?ref=" - attach_deny_insecure_transport_policy = true - # ... other configuration ... -} -``` +This module automatically attaches a bucket policy to the failure S3 bucket that denies any requests made over plain HTTP. No configuration is required. ## How to use @@ -132,8 +124,8 @@ No requirements. ## Providers | Name | Version | -|------|---------| -| [aws](#provider\_aws) | 6.41.0 | +| ---- | ------- | +| [aws](#provider\_aws) | n/a | ## Modules @@ -142,7 +134,7 @@ No modules. ## Resources | Name | Type | -|------|------| +| ---- | ---- | | [aws_iam_policy.firehose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.firehose-logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.firehose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | @@ -162,18 +154,18 @@ No modules. ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [attach\_deny\_insecure\_transport\_policy](#input\_attach\_deny\_insecure\_transport\_policy) | When true, attach a bucket policy to the S3 bucket that denies non-SSL requests. | `bool` | `false` | no | +| ---- | ----------- | ---- | ------- | :------: | | [compression\_format](#input\_compression\_format) | Compression format for the Firehose delivery stream | `string` | `"UNCOMPRESSED"` | no | | [datadog\_api\_key](#input\_datadog\_api\_key) | Datadog API key for authentication | `string` | n/a | yes | | [datadog\_url](#input\_datadog\_url) | Datadog HTTP API endpoint URL | `string` | n/a | yes | | [log\_destinations](#input\_log\_destinations) | A map of configurations for Datadog Firehose delivery streams. |
map(object({
name = string
buffering_size = number
buffering_interval = number
s3_buffering_size = number
s3_buffering_interval = number
content_encoding = string
common_attributes = optional(list(object({
name = string
value = string
})), [])
}))
|
{
"audit": {
"buffering_interval": 60,
"buffering_size": 2,
"common_attributes": [],
"content_encoding": "NONE",
"name": "fleet-audit-datadog",
"s3_buffering_interval": 400,
"s3_buffering_size": 10
},
"results": {
"buffering_interval": 60,
"buffering_size": 2,
"common_attributes": [],
"content_encoding": "NONE",
"name": "fleet-osquery-results-datadog",
"s3_buffering_interval": 400,
"s3_buffering_size": 10
},
"status": {
"buffering_interval": 60,
"buffering_size": 2,
"common_attributes": [],
"content_encoding": "NONE",
"name": "fleet-osquery-status-datadog",
"s3_buffering_interval": 400,
"s3_buffering_size": 10
}
}
| no | | [s3\_bucket\_config](#input\_s3\_bucket\_config) | Configuration for the S3 bucket used to store failed Datadog delivery attempts |
object({
name_prefix = optional(string, "fleet-datadog-failure")
expires_days = optional(number, 1)
})
|
{
"expires_days": 1,
"name_prefix": "fleet-datadog-failure"
}
| no | +| [s3\_bucket\_tags](#input\_s3\_bucket\_tags) | Additional tags to apply to the S3 failure bucket. | `map(string)` | `{}` | no | ## Outputs | Name | Description | -|------|-------------| +| ---- | ----------- | | [fleet\_extra\_environment\_variables](#output\_fleet\_extra\_environment\_variables) | Environment variables to configure Fleet to use Datadog logging via Firehose | | [fleet\_extra\_iam\_policies](#output\_fleet\_extra\_iam\_policies) | IAM policies required for Fleet to log to Datadog via Firehose | | [fleet\_s3\_datadog\_failure\_config](#output\_fleet\_s3\_datadog\_failure\_config) | S3 bucket details - datadog-failure | diff --git a/addons/logging-destination-datadog/main.tf b/addons/logging-destination-datadog/main.tf index 0202541..05fc2e4 100644 --- a/addons/logging-destination-datadog/main.tf +++ b/addons/logging-destination-datadog/main.tf @@ -2,6 +2,7 @@ data "aws_region" "current" {} resource "aws_s3_bucket" "datadog-failure" { #tfsec:ignore:aws-s3-encryption-customer-key:exp:2022-07-01 #tfsec:ignore:aws-s3-enable-versioning #tfsec:ignore:aws-s3-enable-bucket-logging:exp:2022-06-15 bucket_prefix = var.s3_bucket_config.name_prefix + tags = var.s3_bucket_tags } resource "aws_s3_bucket_lifecycle_configuration" "datadog-failure" { diff --git a/addons/logging-destination-datadog/variables.tf b/addons/logging-destination-datadog/variables.tf index 5ba41d6..6db709c 100644 --- a/addons/logging-destination-datadog/variables.tf +++ b/addons/logging-destination-datadog/variables.tf @@ -70,3 +70,9 @@ variable "datadog_api_key" { description = "Datadog API key for authentication" sensitive = true } + +variable "s3_bucket_tags" { + description = "Additional tags to apply to the S3 failure bucket." + type = map(string) + default = {} +} diff --git a/addons/logging-destination-splunk/README.md b/addons/logging-destination-splunk/README.md index 88f2b8c..852b66b 100644 --- a/addons/logging-destination-splunk/README.md +++ b/addons/logging-destination-splunk/README.md @@ -100,8 +100,8 @@ No requirements. ## Providers | Name | Version | -|------|---------| -| [aws](#provider\_aws) | 6.41.0 | +| ---- | ------- | +| [aws](#provider\_aws) | n/a | ## Modules @@ -110,7 +110,7 @@ No modules. ## Resources | Name | Type | -|------|------| +| ---- | ---- | | [aws_iam_policy.firehose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.firehose-logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.firehose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | @@ -130,16 +130,17 @@ No modules. ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| +| ---- | ----------- | ---- | ------- | :------: | | [compression\_format](#input\_compression\_format) | Compression format for the Firehose delivery stream | `string` | `"UNCOMPRESSED"` | no | | [iam\_policy\_name](#input\_iam\_policy\_name) | n/a | `string` | `"splunk-firehose-policy"` | no | | [log\_destinations](#input\_log\_destinations) | A map of configurations for Splunk Firehose delivery streams. |
map(object({
# hec endpoint/token are logically optional but validated to enforce
name = string
hec_endpoint = optional(string)
hec_token = optional(string)
hec_acknowledgement_timeout = optional(number, 600)
hec_endpoint_type = optional(string, "Raw")
s3_buffering_size = optional(number, 2)
s3_buffering_interval = optional(number, 400)
s3_error_output_prefix = optional(string, null)

}))
|
{
"audit": {
"hec_acknowledgement_timeout": 600,
"hec_endpoint_type": "Raw",
"name": "fleet-audit-splunk",
"s3_buffering_interval": 400,
"s3_buffering_size": 10,
"s3_error_output_prefix": "audit/"
},
"results": {
"hec_acknowledgement_timeout": 600,
"hec_endpoint_type": "Raw",
"name": "fleet-osquery-results-splunk",
"s3_buffering_interval": 400,
"s3_buffering_size": 10,
"s3_error_output_prefix": "results/"
},
"status": {
"hec_acknowledgement_timeout": 600,
"hec_endpoint_type": "Raw",
"name": "fleet-osquery-status-splunk",
"s3_buffering_interval": 400,
"s3_buffering_size": 10,
"s3_error_output_prefix": "status/"
}
}
| no | | [s3\_bucket\_config](#input\_s3\_bucket\_config) | Configuration for the S3 bucket used to store failed Splunk delivery attempts |
object({
name_prefix = optional(string, "fleet-splunk-failure")
expires_days = optional(number, 1)
})
|
{
"expires_days": 1,
"name_prefix": "fleet-splunk-failure"
}
| no | +| [s3\_bucket\_tags](#input\_s3\_bucket\_tags) | Additional tags to apply to the S3 failure bucket. | `map(string)` | `{}` | no | ## Outputs | Name | Description | -|------|-------------| +| ---- | ----------- | | [fleet\_extra\_environment\_variables](#output\_fleet\_extra\_environment\_variables) | Environment variables to configure Fleet to use Splunk logging via Firehose | | [fleet\_extra\_iam\_policies](#output\_fleet\_extra\_iam\_policies) | IAM policies required for Fleet to log to Splunk via Firehose | | [fleet\_s3\_splunk\_failure\_config](#output\_fleet\_s3\_splunk\_failure\_config) | S3 bucket details - splunk-failure | diff --git a/addons/logging-destination-splunk/main.tf b/addons/logging-destination-splunk/main.tf index 9e8fa97..8a355de 100644 --- a/addons/logging-destination-splunk/main.tf +++ b/addons/logging-destination-splunk/main.tf @@ -3,6 +3,7 @@ data "aws_region" "current" {} resource "aws_s3_bucket" "splunk-failure" { #tfsec:ignore:aws-s3-encryption-customer-key:exp:2022-07-01 #tfsec:ignore:aws-s3-enable-versioning #tfsec:ignore:aws-s3-enable-bucket-logging:exp:2022-06-15 bucket_prefix = var.s3_bucket_config.name_prefix force_destroy = true + tags = var.s3_bucket_tags } resource "aws_s3_bucket_lifecycle_configuration" "splunk-failure" { diff --git a/addons/logging-destination-splunk/variables.tf b/addons/logging-destination-splunk/variables.tf index 3ca3c3b..eeec99b 100644 --- a/addons/logging-destination-splunk/variables.tf +++ b/addons/logging-destination-splunk/variables.tf @@ -71,3 +71,9 @@ variable "compression_format" { default = "UNCOMPRESSED" description = "Compression format for the Firehose delivery stream" } + +variable "s3_bucket_tags" { + description = "Additional tags to apply to the S3 failure bucket." + type = map(string) + default = {} +} diff --git a/addons/osquery-carve/README.md b/addons/osquery-carve/README.md index 466c944..2ae6ca0 100644 --- a/addons/osquery-carve/README.md +++ b/addons/osquery-carve/README.md @@ -21,15 +21,15 @@ This module automatically attaches a bucket policy that denies any requests made ## Requirements | Name | Version | -|------|---------| +| ---- | ------- | | [terraform](#requirement\_terraform) | >= 1.12.0 | | [aws](#requirement\_aws) | >= 6.37.0 | ## Providers | Name | Version | -|------|---------| -| [aws](#provider\_aws) | 6.41.0 | +| ---- | ------- | +| [aws](#provider\_aws) | 6.39.0 | ## Modules @@ -38,7 +38,7 @@ No modules. ## Resources | Name | Type | -|------|------| +| ---- | ---- | | [aws_iam_policy.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_kms_alias.osquery_carve](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | | [aws_kms_key.osquery_carve](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | @@ -59,12 +59,12 @@ No modules. ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [osquery\_carve\_s3\_bucket](#input\_osquery\_carve\_s3\_bucket) | Configuration for the osquery carve S3 bucket, including optional customer-managed KMS settings. |
object({
name = optional(string, "fleet-osquery-results-archive")
expires_days = optional(number, 1)
kms = optional(object({
kms_key_arn = optional(string, null)
create_kms_key = optional(bool, false)
kms_alias = optional(string, "osquery-carve")
kms_base_policy = optional(list(object({
sid = string
effect = string
principals = object({
type = string
identifiers = list(string)
})
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
})), null)
extra_kms_policies = optional(list(any), [])
fleet_role_name = optional(string, null)
}), {
kms_key_arn = null
create_kms_key = false
kms_alias = "osquery-carve"
kms_base_policy = null
extra_kms_policies = []
fleet_role_name = null
})
})
|
{
"expires_days": 1,
"kms": {
"create_kms_key": false,
"extra_kms_policies": [],
"fleet_role_name": null,
"kms_alias": "osquery-carve",
"kms_base_policy": null,
"kms_key_arn": null
},
"name": "fleet-osquery-results-archive"
}
| no | +| ---- | ----------- | ---- | ------- | :------: | +| [osquery\_carve\_s3\_bucket](#input\_osquery\_carve\_s3\_bucket) | Configuration for the osquery carve S3 bucket, including optional customer-managed KMS settings. |
object({
name = optional(string, "fleet-osquery-results-archive")
expires_days = optional(number, 1)
tags = optional(map(string), {})
kms = optional(object({
kms_key_arn = optional(string, null)
create_kms_key = optional(bool, false)
kms_alias = optional(string, "osquery-carve")
kms_base_policy = optional(list(object({
sid = string
effect = string
principals = object({
type = string
identifiers = list(string)
})
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
})), null)
extra_kms_policies = optional(list(any), [])
fleet_role_name = optional(string, null)
}), {
kms_key_arn = null
create_kms_key = false
kms_alias = "osquery-carve"
kms_base_policy = null
extra_kms_policies = []
fleet_role_name = null
})
})
|
{
"expires_days": 1,
"kms": {
"create_kms_key": false,
"extra_kms_policies": [],
"fleet_role_name": null,
"kms_alias": "osquery-carve",
"kms_base_policy": null,
"kms_key_arn": null
},
"name": "fleet-osquery-results-archive",
"tags": {}
}
| no | ## Outputs | Name | Description | -|------|-------------| +| ---- | ----------- | | [fleet\_extra\_environment\_variables](#output\_fleet\_extra\_environment\_variables) | n/a | | [fleet\_extra\_iam\_policies](#output\_fleet\_extra\_iam\_policies) | IAM policies required for Fleet to access the osquery carve S3 bucket. | diff --git a/addons/osquery-carve/main.tf b/addons/osquery-carve/main.tf index 0e143cf..66e3e88 100644 --- a/addons/osquery-carve/main.tf +++ b/addons/osquery-carve/main.tf @@ -82,6 +82,7 @@ data "aws_iam_role" "osquery_carve_fleet" { resource "aws_s3_bucket" "main" { #tfsec:ignore:aws-s3-encryption-customer-key:exp:2028-07-01 #tfsec:ignore:aws-s3-enable-versioning #tfsec:ignore:aws-s3-enable-bucket-logging:exp:2028-07-01 bucket = var.osquery_carve_s3_bucket.name + tags = var.osquery_carve_s3_bucket.tags } resource "aws_s3_bucket_lifecycle_configuration" "main" { diff --git a/addons/osquery-carve/variables.tf b/addons/osquery-carve/variables.tf index 5e17bf6..52e9cf1 100644 --- a/addons/osquery-carve/variables.tf +++ b/addons/osquery-carve/variables.tf @@ -2,6 +2,7 @@ variable "osquery_carve_s3_bucket" { type = object({ name = optional(string, "fleet-osquery-results-archive") expires_days = optional(number, 1) + tags = optional(map(string), {}) kms = optional(object({ kms_key_arn = optional(string, null) create_kms_key = optional(bool, false) @@ -36,6 +37,7 @@ variable "osquery_carve_s3_bucket" { default = { name = "fleet-osquery-results-archive" expires_days = 1 + tags = {} kms = { kms_key_arn = null create_kms_key = false