diff --git a/terraform/README.md b/terraform/README.md index 05c7c11..ba4f0fa 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -54,6 +54,7 @@ Resources created by this code repository. | [aws_s3_bucket_public_access_block.tf_backend_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_server_side_encryption_configuration.management_events](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_server_side_encryption_configuration.tf_backend_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_ssm_parameter.user_bot_slack_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | | [archive_file.user_bot_placeholder](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | ## Inputs diff --git a/terraform/user-bot.tf b/terraform/user-bot.tf index 66e2da8..923f0c1 100644 --- a/terraform/user-bot.tf +++ b/terraform/user-bot.tf @@ -110,6 +110,13 @@ resource "aws_lambda_function" "user_bot" { filename = data.archive_file.user_bot_placeholder.output_path source_code_hash = data.archive_file.user_bot_placeholder.output_base64sha256 + // The name, not the token. The function reads and decrypts the value at cold start. + environment { + variables = { + SLACK_TOKEN_PARAMETER = aws_ssm_parameter.user_bot_slack_token.name + } + } + depends_on = [aws_cloudwatch_log_group.user_bot] lifecycle { @@ -117,6 +124,40 @@ resource "aws_lambda_function" "user_bot" { } } +// --------------------------------------------------------------------------- +// Slack bot token +// --------------------------------------------------------------------------- + +// The Slack app's bot token (xoxb-...). Terraform creates the parameter with a +// placeholder; the real token is set by hand with `aws ssm put-parameter --overwrite` +// (see lambda/user-bot/README.md), so it never appears in git or in Terraform state. +// See hackforla/devops-security#212. +// +// value_wo rather than value is what keeps the token out of state. With `value`, the +// provider reads the decrypted parameter back into state on every refresh. With the +// write-only value_wo, its read sets `value` to null, and the value is only ever +// written when value_wo_version changes. +// +// Two traps, both because the real value lives outside Terraform: +// - Never change value_wo_version. That writes the placeholder over the real token, +// and the bot refuses to send until the token is set again. +// - Do not change other arguments, such as description, in place. The provider then +// sends an empty value and the apply fails. +resource "aws_ssm_parameter" "user_bot_slack_token" { + region = local.user_bot_region + + name = "/user-bot/slack-bot-token" + description = "Slack bot token for the user-bot Lambda. Set by hand; see lambda/user-bot/README.md in hackforla/devops-security." + type = "SecureString" + + value_wo = "placeholder-set-by-hand" + value_wo_version = 1 + + lifecycle { + prevent_destroy = true + } +} + // --------------------------------------------------------------------------- // Execution role // --------------------------------------------------------------------------- @@ -165,6 +206,14 @@ resource "aws_iam_role_policy" "user_bot" { } } }, + // No kms:Decrypt statement: the parameter uses the AWS-managed aws/ssm key, whose + // key policy already allows decryption through SSM for principals in the account. + { + Sid = "ReadSlackToken" + Effect = "Allow" + Action = "ssm:GetParameter" + Resource = aws_ssm_parameter.user_bot_slack_token.arn + }, { Sid = "WriteOwnLogs" Effect = "Allow"