Skip to content

Commit 4d7b6cc

Browse files
eli-540 attach secret access policy to lambda and external role (#493)
* eli-540 attach roles to lambda and external role * eli-540 pass hashing secret name in env variable * eli-540 set no initial secret * eli-540 refactored get person * eli-540 log corrected * eli-540 log corrected * eli-540 log corrected
1 parent cd01ca8 commit 4d7b6cc

File tree

10 files changed

+69
-31
lines changed

10 files changed

+69
-31
lines changed

infrastructure/modules/lambda/lambda.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ resource "aws_lambda_function" "eligibility_signposting_lambda" {
2424
LOG_LEVEL = var.log_level
2525
ENABLE_XRAY_PATCHING = var.enable_xray_patching
2626
API_DOMAIN_NAME = var.api_domain_name
27+
HASHING_SECRET_NAME = var.hashing_secret_name
2728
}
2829
}
2930

infrastructure/modules/lambda/variables.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,8 @@ variable "api_domain_name" {
7878
description = "api domain name - env variable for status endpoint response"
7979
type = string
8080
}
81+
82+
variable "hashing_secret_name" {
83+
description = "hashing secret name"
84+
type = string
85+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
output "aws_hashing_secret_arn" {
2+
value = aws_secretsmanager_secret.hashing_secret.arn
3+
}
4+
5+
output "aws_hashing_secret_name" {
6+
value = aws_secretsmanager_secret.hashing_secret.name
7+
}
8+

infrastructure/modules/secrets_manager/secrets_manager.tf

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,6 @@ resource "aws_secretsmanager_secret" "hashing_secret" {
1010
}
1111
}
1212

13-
# Initial secrets
14-
resource "aws_secretsmanager_secret_version" "hashing_secrets_test" {
15-
secret_id = aws_secretsmanager_secret.hashing_secret.id
16-
secret_string = "initial_secret"
17-
lifecycle {
18-
ignore_changes = [secret_string]
19-
}
20-
}
21-
2213
# Resource-based policy attached to the secret
2314
resource "aws_secretsmanager_secret_policy" "hashing_secret_policy" {
2415
secret_arn = aws_secretsmanager_secret.hashing_secret.arn

infrastructure/modules/secrets_manager/variables.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
variable "external_write_access_role_arn" {
2-
description = "Arn of the external write access role to provide secret manager access"
3-
type = string
2+
description = "List of ARNs for external write access roles"
3+
type = list(string)
44
}
55

66
variable "eligibility_lambda_role_arn" {

infrastructure/stacks/api-layer/iam_policies.tf

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,34 @@ resource "aws_iam_role_policy" "external_audit_kms_access_policy" {
530530
role = aws_iam_role.write_access_role[count.index].id
531531
policy = data.aws_iam_policy_document.external_role_s3_audit_kms_access_policy.json
532532
}
533+
534+
# IAM policy document for Lambda secret access
535+
data "aws_iam_policy_document" "secrets_access_policy" {
536+
statement {
537+
effect = "Allow"
538+
539+
actions = [
540+
"secretsmanager:GetSecretValue",
541+
"secretsmanager:DescribeSecret",
542+
]
543+
544+
resources = [
545+
module.secrets_manager.aws_hashing_secret_arn
546+
]
547+
}
548+
}
549+
550+
# Attach secret read policy to Lambda role
551+
resource "aws_iam_role_policy" "lambda_secret_read_policy_attachment" {
552+
name = "LambdaSecretReadAccess"
553+
role = aws_iam_role.eligibility_lambda_role.id
554+
policy = data.aws_iam_policy_document.secrets_access_policy.json
555+
}
556+
557+
# Attach secret read policy to external write role
558+
resource "aws_iam_role_policy" "external_secret_read_policy_attachment" {
559+
count = length(aws_iam_role.write_access_role)
560+
name = "ExternalSecretReadAccess"
561+
role = aws_iam_role.write_access_role[count.index].id
562+
policy = data.aws_iam_policy_document.secrets_access_policy.json
563+
}

infrastructure/stacks/api-layer/lambda.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module "eligibility_signposting_lambda_function" {
2525
eligibility_rules_bucket_name = module.s3_rules_bucket.storage_bucket_name
2626
eligibility_status_table_name = module.eligibility_status_table.table_name
2727
kinesis_audit_stream_to_s3_name = module.eligibility_audit_firehose_delivery_stream.firehose_stream_name
28+
hashing_secret_name = module.secrets_manager.aws_hashing_secret_name
2829
lambda_insights_extension_version = 38
2930
log_level = "INFO"
3031
enable_xray_patching = "true"
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
module "secrets_manager" {
22
source = "../../modules/secrets_manager"
3-
count = length(aws_iam_role.write_access_role)
4-
external_write_access_role_arn = aws_iam_role.write_access_role[count.index].arn
5-
environment = var.environment
6-
stack_name = local.stack_name
7-
workspace = terraform.workspace
8-
eligibility_lambda_role_arn = aws_iam_role.eligibility_lambda_role.arn
3+
external_write_access_role_arn = aws_iam_role.write_access_role[*].arn
4+
environment = var.environment
5+
stack_name = local.stack_name
6+
workspace = terraform.workspace
7+
eligibility_lambda_role_arn = aws_iam_role.eligibility_lambda_role.arn
98
}

src/eligibility_signposting_api/repos/person_repo.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,30 @@ def get_person_record(self, nhs_hash: str | None) -> Any:
5555
return None
5656

5757
def get_eligibility_data(self, nhs_number: NHSNumber) -> Person:
58-
# AWSCURRENT secret
59-
nhs_hash = self._hashing_service.hash_with_current_secret(nhs_number)
60-
items = self.get_person_record(nhs_hash)
58+
# Hash using AWSCURRENT secret and fetch items
59+
items = None
60+
nhs_hashed_with_current = self._hashing_service.hash_with_current_secret(nhs_number)
61+
if nhs_hashed_with_current:
62+
items = self.get_person_record(nhs_hashed_with_current)
63+
if not items:
64+
logger.warning("The AWSCURRENT secret was tried, but no person record was found")
6165

6266
if not items:
63-
logger.error("No person record found for hashed nhs_number using secret AWSCURRENT")
64-
65-
# AWSPREVIOUS secret
66-
nhs_hash = self._hashing_service.hash_with_previous_secret(nhs_number)
67-
68-
if nhs_hash is not None:
69-
items = self.get_person_record(nhs_hash)
67+
# Hash using AWSPREVIOUS secret and fetch items
68+
nhs_hashed_with_previous = self._hashing_service.hash_with_previous_secret(nhs_number)
69+
if nhs_hashed_with_previous:
70+
items = self.get_person_record(nhs_hashed_with_previous)
7071
if not items:
71-
logger.error("No person record found for hashed nhs_number using secret AWSPREVIOUS")
72+
logger.error("The AWSPREVIOUS secret was also tried, but no person record was found")
7273
message = "Person not found after checking AWSCURRENT and AWSPREVIOUS."
7374
raise NotFoundError(message)
7475
else:
75-
# fallback not hashed NHS number
76+
# fallback : Fetch using Raw NHS number
7677
items = self.get_person_record(nhs_number)
7778
if not items:
78-
logger.error("No person record found for not hashed nhs_number")
79+
logger.error("The not hashed nhs number was also tried, but no person record was found")
7980
message = "Person not found after checking AWSCURRENT, AWSPREVIOUS, and not hashed NHS numbers."
8081
raise NotFoundError(message)
8182

83+
logger.info("Person record found")
8284
return Person(data=items)

src/eligibility_signposting_api/repos/secret_repo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def _get_secret_by_stage(self, secret_name: str, stage: str) -> dict[str, str]:
2626
return {stage: response["SecretString"]}
2727

2828
except ClientError:
29-
logger.exception("Failed to get secret %s at stage %s", secret_name, stage)
29+
logger.warning("Failed to get secret %s at stage %s", secret_name, stage)
3030
return {}
3131

3232
def get_secret_current(self, secret_name: str) -> dict[str, str]:

0 commit comments

Comments
 (0)