Skip to content

Commit 5d209d5

Browse files
author
João Taveira Araújo
authored
feat: allow KMS encryption of token environment variable (#83)
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.
1 parent c660124 commit 5d209d5

File tree

3 files changed

+62
-10
lines changed

3 files changed

+62
-10
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@ No modules.
5454
| Name | Type |
5555
|------|------|
5656
| [aws_cloudwatch_log_group.group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
57+
| [aws_iam_policy.kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
5758
| [aws_iam_policy.lambda_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
5859
| [aws_iam_policy.vpc_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
5960
| [aws_iam_role.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
61+
| [aws_iam_role_policy_attachment.kms_decrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
6062
| [aws_iam_role_policy_attachment.lambda_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
6163
| [aws_iam_role_policy_attachment.vpc_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
64+
| [aws_kms_ciphertext.token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_ciphertext) | resource |
6265
| [aws_lambda_function.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
6366
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
6467

@@ -69,7 +72,8 @@ No modules.
6972
| <a name="input_dead_letter_queue_destination"></a> [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 |
7073
| <a name="input_description"></a> [description](#input\_description) | Lambda description | `string` | `"Lambda function to forward events towards Observe"` | no |
7174
| <a name="input_iam_name_prefix"></a> [iam\_name\_prefix](#input\_iam\_name\_prefix) | Prefix used for all created IAM roles and policies | `string` | `"observe-lambda-"` | no |
72-
| <a name="input_kms_key_arn"></a> [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.<br>If it's not provided, AWS Lambda uses a default service key. | `string` | `""` | no |
75+
| <a name="input_kms_key"></a> [kms\_key](#input\_kms\_key) | The AWS Key Management Service (AWS KMS) key that's used to encrypt your<br>function's environment variables at rest. Additionally, the Observe Token<br>will be encrypted in transit. | `object({ arn = string })` | `null` | no |
76+
| <a name="input_kms_key_arn"></a> [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.<br>If it's not provided, AWS Lambda uses a default service key. Deprecated, please use kms\_key instead" | `string` | `""` | no |
7377
| <a name="input_lambda_envvars"></a> [lambda\_envvars](#input\_lambda\_envvars) | Environment variables | `map(any)` | `{}` | no |
7478
| <a name="input_lambda_iam_role_arn"></a> [lambda\_iam\_role\_arn](#input\_lambda\_iam\_role\_arn) | ARN of IAM role to use for Lambda | `string` | `""` | no |
7579
| <a name="input_lambda_s3_custom_rules"></a> [lambda\_s3\_custom\_rules](#input\_lambda\_s3\_custom\_rules) | List of rules to evaluate how to upload a given S3 object to Observe | <pre>list(object({<br> pattern = string<br> headers = map(string)<br> }))</pre> | `[]` | no |

main.tf

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ locals {
44
lambda_iam_role_name = regex(".*role/(?P<role_name>.*)$", local.lambda_iam_role_arn)["role_name"]
55
s3_bucket = var.s3_bucket != "" ? var.s3_bucket : lookup(var.s3_regional_buckets, data.aws_region.current.name, local.default_lambda_bucket)
66
s3_key = var.s3_key != "" ? var.s3_key : join("/", [var.s3_key_prefix, format("%s.zip", var.lambda_version)])
7-
7+
observe_token = var.kms_key != null ? aws_kms_ciphertext.token[0].ciphertext_blob : var.observe_token
88
goarch = lookup(
99
{
1010
"amd64" : {
@@ -29,6 +29,15 @@ locals {
2929

3030
data "aws_region" "current" {}
3131

32+
resource "aws_kms_ciphertext" "token" {
33+
count = var.kms_key != null ? 1 : 0
34+
key_id = var.kms_key.arn
35+
plaintext = var.observe_token
36+
context = {
37+
LambdaFunctionName = var.name
38+
}
39+
}
40+
3241
resource "aws_lambda_function" "this" {
3342
function_name = var.name
3443
s3_bucket = local.s3_bucket
@@ -40,7 +49,7 @@ resource "aws_lambda_function" "this" {
4049
handler = local.goarch.handler
4150
runtime = local.goarch.runtime
4251
description = var.description
43-
kms_key_arn = var.kms_key_arn
52+
kms_key_arn = var.kms_key != null ? var.kms_key.arn : var.kms_key_arn
4453
tags = var.tags
4554

4655
memory_size = var.memory_size
@@ -55,7 +64,7 @@ resource "aws_lambda_function" "this" {
5564
environment {
5665
variables = merge({
5766
OBSERVE_COLLECTION_ENDPOINT = var.observe_collection_endpoint != null ? var.observe_collection_endpoint : format("https://%s.collect.%s", var.observe_customer, var.observe_domain)
58-
OBSERVE_TOKEN = var.observe_token
67+
OBSERVE_TOKEN = local.observe_token
5968
}, length(var.lambda_s3_custom_rules) > 0 ? {
6069
S3_CUSTOM_RULES = base64encode(jsonencode(var.lambda_s3_custom_rules))
6170
} : {}
@@ -163,3 +172,37 @@ resource "aws_iam_role_policy_attachment" "vpc_access" {
163172
role = local.lambda_iam_role_name
164173
policy_arn = aws_iam_policy.vpc_access[0].arn
165174
}
175+
176+
resource "aws_iam_policy" "kms_decrypt" {
177+
count = var.kms_key != null ? 1 : 0
178+
name_prefix = var.iam_name_prefix
179+
description = "IAM policy for decrypting ciphertext using KMS"
180+
181+
policy = <<EOF
182+
{
183+
"Version": "2012-10-17",
184+
"Statement": [
185+
{
186+
"Effect": "Allow",
187+
"Action": [
188+
"kms:Decrypt"
189+
],
190+
"Resource": [
191+
"${var.kms_key.arn}"
192+
],
193+
"Condition": {
194+
"StringEquals": {
195+
"kms:EncryptionContext:LambdaFunctionName": "${var.name}"
196+
}
197+
}
198+
}
199+
]
200+
}
201+
EOF
202+
}
203+
204+
resource "aws_iam_role_policy_attachment" "kms_decrypt" {
205+
count = length(aws_iam_policy.kms_decrypt)
206+
role = local.lambda_iam_role_name
207+
policy_arn = aws_iam_policy.kms_decrypt[count.index].arn
208+
}

variables.tf

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ variable "observe_customer" {
2222
variable "observe_token" {
2323
description = "Observe Token"
2424
type = string
25-
26-
validation {
27-
condition = contains(split("", var.observe_token), ":")
28-
error_message = "Token format does not follow {datastream_id}:{datastream_secret} format."
29-
}
3025
}
3126

3227
variable "observe_domain" {
@@ -136,10 +131,20 @@ variable "iam_name_prefix" {
136131
default = "observe-lambda-"
137132
}
138133

134+
variable "kms_key" {
135+
description = <<-EOF
136+
The AWS Key Management Service (AWS KMS) key that's used to encrypt your
137+
function's environment variables at rest. Additionally, the Observe Token
138+
will be encrypted in transit.
139+
EOF
140+
type = object({ arn = string })
141+
default = null
142+
}
143+
139144
variable "kms_key_arn" {
140145
description = <<-EOF
141146
The ARN of the AWS Key Management Service (AWS KMS) key that's used to encrypt your function's environment variables.
142-
If it's not provided, AWS Lambda uses a default service key.
147+
If it's not provided, AWS Lambda uses a default service key. Deprecated, please use kms_key instead"
143148
EOF
144149
type = string
145150
nullable = false

0 commit comments

Comments
 (0)