From 3eec858006b01e87ebe753a143c394a0cdcda147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Taveira=20Ara=C3=BAjo?= Date: Wed, 1 May 2024 10:22:06 -0700 Subject: [PATCH] feat: allow KMS encryption of token environment variable This commit adds support for encrypting the `OBSERVE_TOKEN` environment variable in transit. Previously, this module accepted a `kms_key_arn` variable which affected all environment variables _at rest_. However, this still exposed the token in different contexts (e.g. AWS Config). We now allow reusing the KMS key to encrypt the variable, which gets decrypted by our lambda as of version `v1.0.20240501`. This commit also introduces a subtle API change to the module. We pass in an object, `kms_key`, rather than a string, `kms_key_arn`. This is more friendly to the `count` operator, which cannot determine the value of an attribute until apply time. --- README.md | 6 +++++- main.tf | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- variables.tf | 17 +++++++++++------ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6b0a860..7741261 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,14 @@ No modules. | Name | Type | |------|------| | [aws_cloudwatch_log_group.group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_iam_policy.kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.lambda_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.vpc_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.lambda_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.vpc_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_kms_ciphertext.token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_ciphertext) | resource | | [aws_lambda_function.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | @@ -69,7 +72,8 @@ No modules. | [dead\_letter\_queue\_destination](#input\_dead\_letter\_queue\_destination) | Send failed events/function executions to a dead letter queue arn sns or sqs | `string` | `null` | no | | [description](#input\_description) | Lambda description | `string` | `"Lambda function to forward events towards Observe"` | no | | [iam\_name\_prefix](#input\_iam\_name\_prefix) | Prefix used for all created IAM roles and policies | `string` | `"observe-lambda-"` | no | -| [kms\_key\_arn](#input\_kms\_key\_arn) | The ARN of the AWS Key Management Service (AWS KMS) key that's used to encrypt your function's environment variables.
If it's not provided, AWS Lambda uses a default service key. | `string` | `""` | no | +| [kms\_key](#input\_kms\_key) | The AWS Key Management Service (AWS KMS) key that's used to encrypt your
function's environment variables at rest. Additionally, the Observe Token
will be encrypted in transit. | `object({ arn = string })` | `null` | no | +| [kms\_key\_arn](#input\_kms\_key\_arn) | The ARN of the AWS Key Management Service (AWS KMS) key that's used to encrypt your function's environment variables.
If it's not provided, AWS Lambda uses a default service key. Deprecated, please use kms\_key instead" | `string` | `""` | no | | [lambda\_envvars](#input\_lambda\_envvars) | Environment variables | `map(any)` | `{}` | no | | [lambda\_iam\_role\_arn](#input\_lambda\_iam\_role\_arn) | ARN of IAM role to use for Lambda | `string` | `""` | no | | [lambda\_s3\_custom\_rules](#input\_lambda\_s3\_custom\_rules) | List of rules to evaluate how to upload a given S3 object to Observe |
list(object({
pattern = string
headers = map(string)
}))
| `[]` | no | diff --git a/main.tf b/main.tf index 56e675b..2b9f889 100644 --- a/main.tf +++ b/main.tf @@ -4,7 +4,7 @@ locals { lambda_iam_role_name = regex(".*role/(?P.*)$", local.lambda_iam_role_arn)["role_name"] s3_bucket = var.s3_bucket != "" ? var.s3_bucket : lookup(var.s3_regional_buckets, data.aws_region.current.name, local.default_lambda_bucket) s3_key = var.s3_key != "" ? var.s3_key : join("/", [var.s3_key_prefix, format("%s.zip", var.lambda_version)]) - + observe_token = var.kms_key != null ? aws_kms_ciphertext.token[0].ciphertext_blob : var.observe_token goarch = lookup( { "amd64" : { @@ -29,6 +29,15 @@ locals { data "aws_region" "current" {} +resource "aws_kms_ciphertext" "token" { + count = var.kms_key != null ? 1 : 0 + key_id = var.kms_key.arn + plaintext = var.observe_token + context = { + LambdaFunctionName = var.name + } +} + resource "aws_lambda_function" "this" { function_name = var.name s3_bucket = local.s3_bucket @@ -40,7 +49,7 @@ resource "aws_lambda_function" "this" { handler = local.goarch.handler runtime = local.goarch.runtime description = var.description - kms_key_arn = var.kms_key_arn + kms_key_arn = var.kms_key != null ? var.kms_key.arn : var.kms_key_arn tags = var.tags memory_size = var.memory_size @@ -55,7 +64,7 @@ resource "aws_lambda_function" "this" { environment { variables = merge({ OBSERVE_COLLECTION_ENDPOINT = var.observe_collection_endpoint != null ? var.observe_collection_endpoint : format("https://%s.collect.%s", var.observe_customer, var.observe_domain) - OBSERVE_TOKEN = var.observe_token + OBSERVE_TOKEN = local.observe_token }, length(var.lambda_s3_custom_rules) > 0 ? { S3_CUSTOM_RULES = base64encode(jsonencode(var.lambda_s3_custom_rules)) } : {} @@ -163,3 +172,37 @@ resource "aws_iam_role_policy_attachment" "vpc_access" { role = local.lambda_iam_role_name policy_arn = aws_iam_policy.vpc_access[0].arn } + +resource "aws_iam_policy" "kms_decrypt" { + count = var.kms_key != null ? 1 : 0 + name_prefix = var.iam_name_prefix + description = "IAM policy for decrypting ciphertext using KMS" + + policy = <