Skip to content

Commit

Permalink
UML-3738: eventbridge & lambda permissions (#3014)
Browse files Browse the repository at this point in the history
* change file name

* add sqs and variables

* update variable names

* instance key and && operator

* add event target bus name

* allow lambda messages from sqs

* add vars and outputs

* use arn instead of id

* just use name

* propagate outputs

* rewmove lambda var

* add lambda permission and change to arn

* ensure queue visibility timeout aligns

* correct ecr

* stop pipeline pass on failed preprod plan

* update lambda to allow execute from sqs

* explicitly declare resource

* add sqs queue policy resource

* allow lambda decrypt permissions

* remove condition

* rename kms

* allow permissions on kms key

* temporarily comment kms

* enable kms cmk

* update kms vars

* remove duplicated key

* add cmk

* conditionally create resources in upper environments

* conditionally create resources

* remove old kms

* use correct kms key

* allow describe key on lambda role

* allow decrypt on *

* try specific target key arn

* update account ids
  • Loading branch information
jay-whitwell authored Jan 10, 2025
1 parent d394448 commit 965911e
Show file tree
Hide file tree
Showing 18 changed files with 289 additions and 148 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/pull-request-path.yml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ jobs:
- update_documentation
- docker_build_scan_push
- run_behat_suite
- terraform_preproduction_plan_environment
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -332,7 +333,7 @@ jobs:
- name: workflow has ended without issue
run: |
if ${{ contains(needs.run_behat_suite.result, 'success') && contains(needs.ecr_scan_results.result, 'success') }}; then
if ${{ contains(needs.run_behat_suite.result, 'success') && contains(needs.ecr_scan_results.result, 'success') && contains(needs.terraform_preproduction_plan_environment.result, 'success') }}; then
echo "${{ needs.workflow_variables.outputs.safe_branch_name }} PR environment tested, built and deployed"
echo "Tag Used: ${{ needs.workflow_variables.outputs.safe_branch_name }}-${{ needs.workflow_variables.outputs.short_sha }}"
echo "URL: https://${{ needs.workflow_variables.outputs.workspace_name }}.use-lasting-power-of-attorney.service.gov.uk"
Expand Down
23 changes: 18 additions & 5 deletions lambda-functions/event-receiver/app/main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
package main

import (
"context"
"fmt"

"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)

func Handler(ctx context.Context) (string, error) {
fmt.Println("Hello World")
return "Hello World!", nil
func handler(event events.SQSEvent) error {
for _, record := range event.Records {
err := processMessage(record)
if err != nil {
return err
}
}
fmt.Println("done")
return nil
}

func processMessage(record events.SQSMessage) error {
fmt.Printf("Processed message %s\n", record.Body)
fmt.Printf("Hello, world!\n")
return nil
}

func main() {
lambda.Start(Handler)
lambda.Start(handler)
fmt.Printf("Hello, world!\n")
}
35 changes: 4 additions & 31 deletions terraform/account/kms.tf
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ module "sessions_actor_mrk" {
}
}

module "sqs_mrk" {
module "event_receiver_mrk" {
source = "./modules/multi_region_kms"

key_description = "KMS key for sqs"
key_alias = "sqs-mrk"
key_description = "KMS key for received events"
key_alias = "${local.environment}-event-receiver-mrk"
key_policy = data.aws_iam_policy_document.event_receiver_kms.json
deletion_window_in_days = 7

providers = {
Expand Down Expand Up @@ -147,20 +148,6 @@ data "aws_iam_policy_document" "cloudwatch_kms" {
}
}

module "event_receiver_mrk" {
source = "./modules/multi_region_kms"

key_description = "KMS key for received events"
key_alias = "event-receiver-mrk"
key_policy = data.aws_iam_policy_document.event_receiver_kms.json
deletion_window_in_days = 7

providers = {
aws.primary = aws.eu_west_1
aws.secondary = aws.eu_west_2
}
}

data "aws_iam_policy_document" "event_receiver_kms" {
statement {
sid = "Allow Encryption by Service"
Expand Down Expand Up @@ -199,18 +186,4 @@ data "aws_iam_policy_document" "event_receiver_kms" {
]
}
}

statement {
sid = "Enable Root account permissions on Key"
effect = "Allow"
actions = ["kms:*"]
resources = ["*"]

principals {
type = "AWS"
identifiers = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root",
]
}
}
}
52 changes: 51 additions & 1 deletion terraform/environment/lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module "lambda_update_statistics" {
memory = 1024
}


# Additional IAM permissions
resource "aws_iam_role_policy" "lambda_update_statistics" {
name = "lambda-update-statistics-${local.environment_name}"
Expand Down Expand Up @@ -89,16 +90,65 @@ resource "aws_lambda_permission" "cloudwatch_to_update_statistics_lambda" {
}

module "event_receiver" {
count = local.environment.event_bus_enabled ? 1 : 0
source = "./modules/lambda"
lambda_name = "event-receiver"
environment_variables = {
ENVIRONMENT = local.environment_name
REGION = data.aws_region.current.name
}
image_uri = "${data.aws_ecr_repository.use_an_lpa_event_receiver.repository_url}:${var.container_version}"
ecr_arn = data.aws_ecr_repository.use_an_lpa_upload_statistics.arn
ecr_arn = data.aws_ecr_repository.use_an_lpa_event_receiver.arn
environment = local.environment_name
kms_key = data.aws_kms_alias.cloudwatch_encryption.target_key_arn
timeout = 900
memory = 128
}

resource "aws_iam_role_policy" "lambda_event_receiver" {
count = local.environment.event_bus_enabled ? 1 : 0
name = "${local.environment_name}-lambda-event-receiver"
role = module.event_receiver[0].lambda_role.name
policy = data.aws_iam_policy_document.lambda_event_receiver[0].json
}


data "aws_iam_policy_document" "lambda_event_receiver" {
count = local.environment.event_bus_enabled ? 1 : 0
statement {
sid = "${local.environment_name}EventReceiverSQS"
effect = "Allow"
actions = [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes",
]
resources = [module.eu_west_1[0].receive_events_sqs_queue_arn[0]]
}

statement {
sid = "${local.environment_name}KMSDecrypt"
effect = "Allow"
actions = [
"kms:Decrypt",
"kms:DescribeKey"
]
resources = [data.aws_kms_alias.event_receiver.target_key_arn]
}
}

resource "aws_lambda_event_source_mapping" "receive_events_mapping" {
count = local.environment.event_bus_enabled ? 1 : 0
event_source_arn = module.eu_west_1[0].receive_events_sqs_queue_arn[0]
function_name = module.event_receiver[0].lambda_name
enabled = true
}

resource "aws_lambda_permission" "receive_events_permission" {
count = local.environment.event_bus_enabled ? 1 : 0
statement_id = "AllowExecutionFromSQS"
action = "lambda:InvokeFunction"
function_name = module.event_receiver[0].lambda_name
principal = "sqs.amazonaws.com"
source_arn = module.eu_west_1[0].receive_events_sqs_queue_arn[0]
}
5 changes: 5 additions & 0 deletions terraform/environment/modules/lambda/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ output "lambda_role" {
description = "The lambda role"
value = aws_iam_role.lambda_role
}

output "lambda_name" {
description = "The lambda name"
value = aws_lambda_function.lambda_function.function_name
}
4 changes: 4 additions & 0 deletions terraform/environment/region.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module "eu_west_1" {
ecs_task_roles = module.iam.ecs_task_roles
environment_name = local.environment_name
event_bus_enabled = local.environment.event_bus_enabled
event_reciever_kms_key_arn = data.aws_kms_alias.event_receiver.target_key_arn
google_analytics_id_use = local.environment.google_analytics_id_use
google_analytics_id_view = local.environment.google_analytics_id_view
gov_uk_onelogin_client_id_secret_name = local.environment.gov_uk_onelogin_client_id_secret_name
Expand All @@ -33,6 +34,7 @@ module "eu_west_1" {
lpa_codes_endpoint = local.environment.lpa_codes_endpoint
lpas_collection_endpoint = local.environment.lpas_collection_endpoint
lpa_data_store_endpoint = local.environment.lpa_data_store_endpoint
receive_account_ids = local.environment.receive_account_ids
mock_onelogin_enabled = local.environment.mock_onelogin_enabled
mock_onelogin_service_container_version = local.mock_onelogin_version
mock_onelogin_service_repository_url = data.aws_ecr_repository.mock_onelogin.repository_url
Expand Down Expand Up @@ -108,6 +110,7 @@ module "eu_west_2" {
ecs_task_roles = module.iam.ecs_task_roles
environment_name = local.environment_name
event_bus_enabled = local.environment.event_bus_enabled
event_reciever_kms_key_arn = data.aws_kms_alias.event_receiver.target_key_arn
google_analytics_id_use = local.environment.google_analytics_id_use
google_analytics_id_view = local.environment.google_analytics_id_view
gov_uk_onelogin_client_id_secret_name = local.environment.gov_uk_onelogin_client_id_secret_name
Expand All @@ -121,6 +124,7 @@ module "eu_west_2" {
lpa_codes_endpoint = local.environment.lpa_codes_endpoint
lpas_collection_endpoint = local.environment.lpas_collection_endpoint
lpa_data_store_endpoint = local.environment.lpa_data_store_endpoint
receive_account_ids = local.environment.receive_account_ids
mock_onelogin_enabled = local.environment.mock_onelogin_enabled
mock_onelogin_service_container_version = local.mock_onelogin_version
mock_onelogin_service_repository_url = data.aws_ecr_repository.mock_onelogin.repository_url
Expand Down
11 changes: 7 additions & 4 deletions terraform/environment/region/event_bus.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
module "event_bus" {
source = "./modules/event_bus"
environment_name = var.environment_name
event_bus_enabled = var.event_bus_enabled
current_region = data.aws_region.current.name
source = "./modules/event_bus"
environment_name = var.environment_name
event_bus_enabled = var.event_bus_enabled
current_region = data.aws_region.current.name
receive_account_ids = var.receive_account_ids
queue_visibility_timeout = local.queue_visibility_timeout
event_reciever_kms_key_arn = var.event_reciever_kms_key_arn
providers = {
aws.region = aws.region
}
Expand Down
2 changes: 2 additions & 0 deletions terraform/environment/region/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ locals {
admin_desired_count = local.is_active_region ? 1 : 0
mock_onelogin_desired_count = var.environment_name != "production" && var.mock_onelogin_enabled && local.is_active_region ? 1 : 0

queue_visibility_timeout = 900

# Replace the region in the ARN of the DynamoDB tables with the region of the current stack as the tables are created in the primary region
# and replicated to the secondary region. This allows use to grant access to the tables in the secondary region for applications running in the secondary region.
dynamodb_tables_arns = {
Expand Down
65 changes: 65 additions & 0 deletions terraform/environment/region/modules/event_bus/bus.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
resource "aws_cloudwatch_event_bus" "main" {
count = var.event_bus_enabled ? 1 : 0
name = var.environment_name
provider = aws.region
}

resource "aws_cloudwatch_event_archive" "main" {
count = var.event_bus_enabled ? 1 : 0
name = var.environment_name
event_source_arn = aws_cloudwatch_event_bus.main[0].arn
provider = aws.region
}

resource "aws_cloudwatch_event_rule" "receive_events_from_mlpa" {
count = var.event_bus_enabled ? 1 : 0
name = "${var.environment_name}-mlpa-events-to-use"
description = "Receive events from mlpa"
event_bus_name = aws_cloudwatch_event_bus.main[0].name

event_pattern = jsonencode({
source = ["opg.poas.makeregister"],
detail-type = ["lpa-access-granted"]
})

provider = aws.region
}

resource "aws_cloudwatch_event_bus_policy" "cross_account_receive" {
count = length(var.receive_account_ids) > 0 && var.event_bus_enabled ? 1 : 0
event_bus_name = aws_cloudwatch_event_bus.main[0].name
policy = data.aws_iam_policy_document.cross_account_receive[0].json
provider = aws.region
}

# Allow MLPA account to send messages
data "aws_iam_policy_document" "cross_account_receive" {
count = var.event_bus_enabled ? 1 : 0
statement {
sid = "CrossAccountAccess"
effect = "Allow"
actions = [
"events:PutEvents",
]
resources = [
aws_cloudwatch_event_bus.main[0].arn
]

principals {
type = "AWS"
identifiers = var.receive_account_ids
}
}
}

resource "aws_cloudwatch_event_target" "receive_events" {
count = var.event_bus_enabled ? 1 : 0
rule = aws_cloudwatch_event_rule.receive_events_from_mlpa[0].name
arn = aws_sqs_queue.receive_events_queue[0].arn
event_bus_name = aws_cloudwatch_event_bus.main[0].name
dead_letter_config {
arn = aws_sqs_queue.receive_events_deadletter[0].arn
}

provider = aws.region
}
Loading

0 comments on commit 965911e

Please sign in to comment.