Skip to content

Commit d9295a2

Browse files
authored
Merge pull request #448 from NHSDigital/feature/eja-eli-510-add-CSOC-api-log-forwarding
eli-510 adding CSOC log forwarding of our API Gateway logs
2 parents 17d7212 + 7858399 commit d9295a2

File tree

4 files changed

+110
-9
lines changed

4 files changed

+110
-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: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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.${var.default_aws_region}.amazonaws.com"]
18+
}
19+
}
20+
}
21+
22+
resource "aws_iam_role" "cwl_subscription_role" {
23+
name = "${var.environment}-${local.workspace}-CWLogsSubscriptionRole"
24+
description = "IAM role for CloudWatch Logs subscription filter to forward logs to CSOC"
25+
assume_role_policy = data.aws_iam_policy_document.cwl_subscription_assume_role.json
26+
27+
tags = merge(
28+
local.tags,
29+
{
30+
Name = "${var.environment}-${local.workspace}-CWLogsSubscriptionRole"
31+
Purpose = "CSOC log forwarding"
32+
}
33+
)
34+
}
35+
36+
# IAM policy to allow PutSubscriptionFilter on the existing API Gateway log group and CSOC destination
37+
data "aws_iam_policy_document" "put_subscription_filter" {
38+
statement {
39+
sid = "AllowPutAPIGSubFilter"
40+
effect = "Allow"
41+
actions = [
42+
"logs:PutSubscriptionFilter"
43+
]
44+
resources = [
45+
"${module.eligibility_signposting_api_gateway.cloudwatch_destination_arn}:*",
46+
"arn:aws:logs:${var.default_aws_region}:693466633220:destination:api_gateway_log_destination"
47+
]
48+
}
49+
}
50+
51+
resource "aws_iam_policy" "put_subscription_filter" {
52+
name = "${var.environment}-${local.workspace}-PutSubscriptionFilterPolicy"
53+
description = "Policy to allow creating subscription filters for CSOC log forwarding"
54+
policy = data.aws_iam_policy_document.put_subscription_filter.json
55+
56+
tags = merge(
57+
local.tags,
58+
{
59+
Name = "${var.environment}-${local.workspace}-PutSubscriptionFilterPolicy"
60+
Purpose = "CSOC log forwarding"
61+
}
62+
)
63+
}
64+
65+
# Attach the policy to the subscription role
66+
resource "aws_iam_role_policy_attachment" "put_subscription_filter" {
67+
role = aws_iam_role.cwl_subscription_role.name
68+
policy_arn = aws_iam_policy.put_subscription_filter.arn
69+
}
70+
71+
# Create the subscription filter to forward logs to CSOC
72+
# This forwards all logs from the existing API Gateway log group to the CSOC destination
73+
# Note: A log group can have up to 2 subscription filters
74+
resource "aws_cloudwatch_log_subscription_filter" "csoc_forwarding" {
75+
name = "${local.workspace}-csoc-subscription"
76+
log_group_name = split(":", module.eligibility_signposting_api_gateway.cloudwatch_destination_arn)[6]
77+
filter_pattern = "" # Empty filter pattern captures all log events
78+
destination_arn = "arn:aws:logs:${var.default_aws_region}:693466633220:destination:api_gateway_log_destination"
79+
role_arn = aws_iam_role.cwl_subscription_role.arn
80+
81+
depends_on = [
82+
module.eligibility_signposting_api_gateway,
83+
aws_iam_role.cwl_subscription_role,
84+
aws_iam_role_policy_attachment.put_subscription_filter
85+
]
86+
}

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

Lines changed: 18 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
@@ -279,7 +283,9 @@ resource "aws_iam_policy" "api_infrastructure" {
279283
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eventbridge-firehose-role*",
280284
# Kinesis Firehose S3 backup roles
281285
"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*"
286+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/splunk-firehose-assume-role*",
287+
# CSOC CloudWatch Logs subscription role
288+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*-CWLogsSubscriptionRole"
283289
],
284290
Condition = {
285291
StringEquals = {
@@ -288,7 +294,8 @@ resource "aws_iam_policy" "api_infrastructure" {
288294
"apigateway.amazonaws.com",
289295
"vpc-flow-logs.amazonaws.com",
290296
"events.amazonaws.com",
291-
"firehose.amazonaws.com"
297+
"firehose.amazonaws.com",
298+
"logs.amazonaws.com"
292299
]
293300
}
294301
}
@@ -477,9 +484,12 @@ resource "aws_iam_policy" "iam_management" {
477484
"arn:aws:iam::*:role/*-api-gateway-*-role",
478485
# External write role
479486
"arn:aws:iam::*:role/eligibility-signposting-api-*-external-write-role",
487+
# CSOC CloudWatch Logs subscription role
488+
"arn:aws:iam::*:role/*-CWLogsSubscriptionRole",
480489
# Project policies
481490
"arn:aws:iam::*:policy/*api-gateway-logging-policy",
482491
"arn:aws:iam::*:policy/*PermissionsBoundary",
492+
"arn:aws:iam::*:policy/*PutSubscriptionFilterPolicy",
483493
# VPC flow logs role
484494
"arn:aws:iam::*:role/vpc-flow-logs-role",
485495
# API role
@@ -500,8 +510,8 @@ resource "aws_iam_policy" "iam_management" {
500510
# Assume role policy document for GitHub Actions
501511
data "aws_iam_policy_document" "github_actions_assume_role" {
502512
statement {
503-
sid = "OidcAssumeRoleWithWebIdentity"
504-
effect = "Allow"
513+
sid = "OidcAssumeRoleWithWebIdentity"
514+
effect = "Allow"
505515
actions = ["sts:AssumeRoleWithWebIdentity"]
506516

507517
principals {
@@ -514,13 +524,13 @@ data "aws_iam_policy_document" "github_actions_assume_role" {
514524
condition {
515525
test = "StringLike"
516526
variable = "token.actions.githubusercontent.com:sub"
517-
values = ["repo:${var.github_org}/${var.github_repo}:*"]
527+
values = ["repo:${var.github_org}/${var.github_repo}:*"]
518528
}
519529

520530
condition {
521531
test = "StringEquals"
522532
variable = "token.actions.githubusercontent.com:aud"
523-
values = ["sts.amazonaws.com"]
533+
values = ["sts.amazonaws.com"]
524534
}
525535
}
526536
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ data "aws_iam_policy_document" "permissions_boundary" {
177177
"logs:PutRetentionPolicy",
178178
"logs:AssociateKmsKey",
179179
"logs:PutMetricFilter",
180+
"logs:PutSubscriptionFilter",
181+
"logs:DeleteSubscriptionFilter",
182+
"logs:DescribeSubscriptionFilters",
180183

181184
# S3 - bucket and object management
182185
"s3:GetLifecycleConfiguration",

0 commit comments

Comments
 (0)