Skip to content

Commit 9b97d53

Browse files
authored
Merge branch 'main' into feature/eja-eli-414-spin-our-own-FHIR-operation-outcome
2 parents 24863f9 + 60d567d commit 9b97d53

File tree

4 files changed

+134
-9
lines changed

4 files changed

+134
-9
lines changed

infrastructure/stacks/api-layer/api_gateway.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ resource "aws_api_gateway_stage" "eligibility-signposting-api" {
5151
stage_name = "${local.workspace}-eligibility-signposting-api-live"
5252
xray_tracing_enabled = true
5353

54+
# Access log settings
55+
# A subscription filter (see csoc_log_forwarding.tf) forwards these logs to CSOC
5456
access_log_settings {
5557
destination_arn = module.eligibility_signposting_api_gateway.cloudwatch_destination_arn
56-
format = "{ \"requestId\":\"$context.requestId\", \"ip\": \"$context.identity.sourceIp\", \"caller\":\"$context.identity.caller\", \"user\":\"$context.identity.user\", \"requestTime\":\"$context.requestTime\", \"httpMethod\":\"$context.httpMethod\", \"resourcePath\":\"$context.resourcePath\", \"status\":\"$context.status\", \"protocol\":\"$context.protocol\", \"responseLength\":\"$context.responseLength\", \"accountId\":\"$context.accountId\", \"apiId\":\"$context.apiId\", \"stage\":\"$context.stage\", \"domainName\":\"$context.domainName\", \"error_message\":\"$context.error.message\", \"clientCertSerialNumber\":\"$context.identity.clientCert.serialNumber\", \"clientCertValidityNotBefore\":\"$context.identity.clientCert.validity.notBefore\", \"clientCertValidityNotAfter\":\"$context.identity.clientCert.validity.notAfter\" }"
58+
format = "{ \"requestId\":\"$context.requestId\", \"ip\": \"$context.identity.sourceIp\", \"caller\":\"$context.identity.caller\", \"user\":\"$context.identity.user\", \"requestTime\":\"$context.requestTime\", \"httpMethod\":\"$context.httpMethod\", \"resourcePath\":\"$context.resourcePath\", \"status\":\"$context.status\", \"protocol\":\"$context.protocol\", \"responseLength\":\"$context.responseLength\", \"accountId\":\"$context.accountId\", \"apiId\":\"$context.apiId\", \"stage\":\"$context.stage\", \"api_key\":\"$context.identity.apiKey\" }"
5759
}
5860

5961
depends_on = [
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# CSOC Log Forwarding Configuration
2+
# This file implements the requirements for forwarding API Gateway logs to CSOC
3+
# Based on https://nhsd-confluence.digital.nhs.uk/spaces/CCEP/pages/407374909/API+Gateway+Access+Logs
4+
#
5+
# This implementation uses the existing API Gateway log group and adds a subscription
6+
# filter to forward logs to CSOC, avoiding the need to create a duplicate log group.
7+
8+
# IAM Role for Cross Account Log Subscriptions
9+
# This role allows CloudWatch Logs service to assume a role for cross-account log delivery
10+
data "aws_iam_policy_document" "cwl_subscription_assume_role" {
11+
statement {
12+
effect = "Allow"
13+
actions = ["sts:AssumeRole"]
14+
15+
principals {
16+
type = "Service"
17+
identifiers = ["logs.amazonaws.com"]
18+
}
19+
20+
condition {
21+
test = "StringEquals"
22+
variable = "aws:SourceAccount"
23+
values = [data.aws_caller_identity.current.account_id]
24+
}
25+
}
26+
}
27+
28+
resource "aws_iam_role" "cwl_subscription_role" {
29+
name = "${var.environment}-${local.workspace}-CWLogsSubscriptionRole"
30+
description = "IAM role for CloudWatch Logs subscription filter to forward logs to CSOC"
31+
assume_role_policy = data.aws_iam_policy_document.cwl_subscription_assume_role.json
32+
33+
tags = merge(
34+
local.tags,
35+
{
36+
Name = "${var.environment}-${local.workspace}-CWLogsSubscriptionRole"
37+
Purpose = "CSOC log forwarding"
38+
}
39+
)
40+
}
41+
42+
# IAM policy to allow CloudWatch Logs to write to the CSOC destination
43+
# This is the permission policy for the role that CloudWatch Logs assumes
44+
data "aws_iam_policy_document" "cwl_to_csoc_destination" {
45+
statement {
46+
sid = "AllowPutLogEventsToDestination"
47+
effect = "Allow"
48+
actions = [
49+
"logs:PutLogEvents"
50+
]
51+
resources = [
52+
"arn:aws:logs:${var.default_aws_region}:693466633220:destination:api_gateway_log_destination"
53+
]
54+
}
55+
}
56+
57+
resource "aws_iam_policy" "cwl_to_csoc_destination" {
58+
name = "${var.environment}-${local.workspace}-CWLogsToCSOCDestinationPolicy"
59+
description = "Policy to allow CloudWatch Logs to write to CSOC destination"
60+
policy = data.aws_iam_policy_document.cwl_to_csoc_destination.json
61+
62+
tags = merge(
63+
local.tags,
64+
{
65+
Name = "${var.environment}-${local.workspace}-CWLogsToCSOCDestinationPolicy"
66+
Purpose = "CSOC log forwarding"
67+
}
68+
)
69+
}
70+
71+
# Attach the policy to the subscription role
72+
resource "aws_iam_role_policy_attachment" "cwl_to_csoc_destination" {
73+
role = aws_iam_role.cwl_subscription_role.name
74+
policy_arn = aws_iam_policy.cwl_to_csoc_destination.arn
75+
}
76+
77+
# Create the subscription filter to forward logs to CSOC
78+
# This forwards all logs from the existing API Gateway log group to the CSOC destination
79+
# Note: A log group can have up to 2 subscription filters
80+
resource "aws_cloudwatch_log_subscription_filter" "csoc_forwarding" {
81+
name = "${local.workspace}-csoc-subscription"
82+
log_group_name = split(":", module.eligibility_signposting_api_gateway.cloudwatch_destination_arn)[6]
83+
filter_pattern = "" # Empty filter pattern captures all log events
84+
destination_arn = "arn:aws:logs:${var.default_aws_region}:693466633220:destination:api_gateway_log_destination"
85+
role_arn = aws_iam_role.cwl_subscription_role.arn
86+
87+
depends_on = [
88+
module.eligibility_signposting_api_gateway,
89+
aws_iam_role.cwl_subscription_role,
90+
aws_iam_role_policy_attachment.cwl_to_csoc_destination
91+
]
92+
}

infrastructure/stacks/iams-developer-roles/github_actions_policies.tf

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ resource "aws_iam_policy" "dynamodb_management" {
115115
}
116116
],
117117
# to create test users in preprod
118-
var.environment == "preprod" ? [
118+
var.environment == "preprod" ? [
119119
{
120120
Effect = "Allow",
121121
Action = [
@@ -249,7 +249,11 @@ resource "aws_iam_policy" "api_infrastructure" {
249249
# CloudWatch Logs creation and management
250250
"logs:CreateLogGroup",
251251
"logs:CreateLogStream",
252-
"logs:PutLogEvents"
252+
"logs:PutLogEvents",
253+
# CloudWatch Logs subscription filters for CSOC forwarding
254+
"logs:PutSubscriptionFilter",
255+
"logs:DeleteSubscriptionFilter",
256+
"logs:DescribeSubscriptionFilters"
253257
],
254258
Resource = [
255259
# VPC Flow Logs
@@ -262,6 +266,17 @@ resource "aws_iam_policy" "api_infrastructure" {
262266
"arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/kinesisfirehose/*"
263267
]
264268
},
269+
{
270+
Effect = "Allow",
271+
Action = [
272+
# CloudWatch Logs subscription to CSOC cross-account destination
273+
"logs:PutSubscriptionFilter"
274+
],
275+
Resource = [
276+
# CSOC cross-account destination for API Gateway logs
277+
"arn:aws:logs:${var.default_aws_region}:693466633220:destination:api_gateway_log_destination"
278+
]
279+
},
265280
{
266281
Effect = "Allow",
267282
Action = [
@@ -279,7 +294,9 @@ resource "aws_iam_policy" "api_infrastructure" {
279294
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eventbridge-firehose-role*",
280295
# Kinesis Firehose S3 backup roles
281296
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*firehose*role*",
282-
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/splunk-firehose-assume-role*"
297+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/splunk-firehose-assume-role*",
298+
# CSOC CloudWatch Logs subscription role
299+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*-CWLogsSubscriptionRole"
283300
],
284301
Condition = {
285302
StringEquals = {
@@ -288,7 +305,8 @@ resource "aws_iam_policy" "api_infrastructure" {
288305
"apigateway.amazonaws.com",
289306
"vpc-flow-logs.amazonaws.com",
290307
"events.amazonaws.com",
291-
"firehose.amazonaws.com"
308+
"firehose.amazonaws.com",
309+
"logs.amazonaws.com"
292310
]
293311
}
294312
}
@@ -457,12 +475,15 @@ resource "aws_iam_policy" "iam_management" {
457475
"iam:CreateRole",
458476
"iam:DeleteRole",
459477
"iam:UpdateRole",
478+
"iam:UpdateAssumeRolePolicy",
460479
"iam:PutRolePolicy",
461480
"iam:PutRolePermissionsBoundary",
462481
"iam:AttachRolePolicy",
463482
"iam:DetachRolePolicy",
464483
"iam:CreatePolicy",
465484
"iam:CreatePolicyVersion",
485+
"iam:DeletePolicy",
486+
"iam:DeletePolicyVersion",
466487
"iam:TagRole",
467488
"iam:PassRole",
468489
"iam:TagPolicy",
@@ -477,9 +498,13 @@ resource "aws_iam_policy" "iam_management" {
477498
"arn:aws:iam::*:role/*-api-gateway-*-role",
478499
# External write role
479500
"arn:aws:iam::*:role/eligibility-signposting-api-*-external-write-role",
501+
# CSOC CloudWatch Logs subscription role
502+
"arn:aws:iam::*:role/*-CWLogsSubscriptionRole",
480503
# Project policies
481504
"arn:aws:iam::*:policy/*api-gateway-logging-policy",
482505
"arn:aws:iam::*:policy/*PermissionsBoundary",
506+
"arn:aws:iam::*:policy/*PutSubscriptionFilterPolicy",
507+
"arn:aws:iam::*:policy/*CWLogsToCSOCDestinationPolicy",
483508
# VPC flow logs role
484509
"arn:aws:iam::*:role/vpc-flow-logs-role",
485510
# API role
@@ -500,8 +525,8 @@ resource "aws_iam_policy" "iam_management" {
500525
# Assume role policy document for GitHub Actions
501526
data "aws_iam_policy_document" "github_actions_assume_role" {
502527
statement {
503-
sid = "OidcAssumeRoleWithWebIdentity"
504-
effect = "Allow"
528+
sid = "OidcAssumeRoleWithWebIdentity"
529+
effect = "Allow"
505530
actions = ["sts:AssumeRoleWithWebIdentity"]
506531

507532
principals {
@@ -514,13 +539,13 @@ data "aws_iam_policy_document" "github_actions_assume_role" {
514539
condition {
515540
test = "StringLike"
516541
variable = "token.actions.githubusercontent.com:sub"
517-
values = ["repo:${var.github_org}/${var.github_repo}:*"]
542+
values = ["repo:${var.github_org}/${var.github_repo}:*"]
518543
}
519544

520545
condition {
521546
test = "StringEquals"
522547
variable = "token.actions.githubusercontent.com:aud"
523-
values = ["sts.amazonaws.com"]
548+
values = ["sts.amazonaws.com"]
524549
}
525550
}
526551
}

infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,15 @@ data "aws_iam_policy_document" "permissions_boundary" {
101101
"iam:CreateRole",
102102
"iam:DeleteRole",
103103
"iam:UpdateRole",
104+
"iam:UpdateAssumeRolePolicy",
104105
"iam:PutRolePolicy",
105106
"iam:PutRolePermissionsBoundary",
106107
"iam:AttachRolePolicy",
107108
"iam:DetachRolePolicy",
108109
"iam:CreatePolicy",
109110
"iam:CreatePolicyVersion",
111+
"iam:DeletePolicy",
112+
"iam:DeletePolicyVersion",
110113
"iam:TagRole",
111114
"iam:UntagPolicy",
112115
"iam:PassRole",
@@ -177,6 +180,9 @@ data "aws_iam_policy_document" "permissions_boundary" {
177180
"logs:PutRetentionPolicy",
178181
"logs:AssociateKmsKey",
179182
"logs:PutMetricFilter",
183+
"logs:PutSubscriptionFilter",
184+
"logs:DeleteSubscriptionFilter",
185+
"logs:DescribeSubscriptionFilters",
180186

181187
# S3 - bucket and object management
182188
"s3:GetLifecycleConfiguration",

0 commit comments

Comments
 (0)