From 73324b625daadb9bf9847063cd8b940dd89c82ab Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:30:46 +0100 Subject: [PATCH 1/4] eli-385 finessing github permissions --- .../github_actions_policies.tf | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf index 6b69b5025..598c2d111 100644 --- a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf +++ b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf @@ -163,6 +163,8 @@ resource "aws_iam_policy" "s3_management" { "arn:aws:s3:::*eligibility-signposting-api-${var.environment}-truststore/*", "arn:aws:s3:::*eligibility-signposting-api-${var.environment}-truststore-access-logs", "arn:aws:s3:::*eligibility-signposting-api-${var.environment}-truststore-access-logs/*", + "arn:aws:s3:::*eligibility-signposting-api-${var.environment}-eli-splunk-backup", + "arn:aws:s3:::*eligibility-signposting-api-${var.environment}-eli-splunk-backup/*" ] } ] @@ -196,20 +198,62 @@ resource "aws_iam_policy" "api_infrastructure" { # ACM for certs "acm:DescribeCertificate", "acm:GetCertificate", - "acm:ListCertificates", - # S3 for mTLS truststore - "s3:GetObject", - # CloudWatch Logs for logging - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", - # IAM PassRole for logging role association (if needed) - "iam:PassRole" + "acm:ListCertificates" ], Resource = "*" #checkov:skip=CKV_AWS_289: Actions require wildcard resource }, + { + Effect = "Allow", + Action = [ + # CloudWatch Logs creation and management + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + Resource = [ + # VPC Flow Logs + "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/vpc/*", + # Lambda function logs + "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/*", + # API Gateway logs + "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/*", + # Kinesis Firehose logs + "arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/kinesisfirehose/*" + ] + }, + { + Effect = "Allow", + Action = [ + # IAM PassRole for specific service roles only + "iam:PassRole" + ], + Resource = [ + # Lambda execution roles + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eligibility_lambda-role*", + # API Gateway CloudWatch logging role + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*-api-gateway-*-role", + # VPC Flow Logs role + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/vpc-flow-logs-role*", + # EventBridge to Firehose role + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eventbridge-firehose-role*", + # Kinesis Firehose S3 backup roles + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*firehose*role*", + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/splunk-firehose-assume-role*" + ], + Condition = { + StringEquals = { + "iam:PassedToService" = [ + "lambda.amazonaws.com", + "apigateway.amazonaws.com", + "vpc-flow-logs.amazonaws.com", + "events.amazonaws.com", + "firehose.amazonaws.com" + ] + } + } + }, { Effect = "Allow", Action = [ From a7cb93232cd775c739519ae0904a14329a38cd2f Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:55:03 +0100 Subject: [PATCH 2/4] eli-385 updating permissions boundary to only include exact permissions we use --- .../iams_permissions_boundary.tf | 239 ++++++++++++++++-- 1 file changed, 211 insertions(+), 28 deletions(-) diff --git a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf index 8d7940668..4b37cde68 100644 --- a/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf +++ b/infrastructure/stacks/iams-developer-roles/iams_permissions_boundary.tf @@ -6,35 +6,218 @@ data "aws_iam_policy_document" "permissions_boundary" { effect = "Allow" actions = [ - "acm:*", - "application-autoscaling:*", + # ACM - only specific actions needed for certificate management + "acm:DescribeCertificate", + "acm:GetCertificate", + "acm:ListCertificates", + "acm:ListTagsForCertificate", + "acm:RequestCertificate", + "acm:AddTagsToCertificate", + "acm:ImportCertificate", + + # API Gateway - specific actions for deployment "apigateway:*", - "cloudtrail:*", - "cloudwatch:*", - "config:*", - "dynamodb:*", - "ec2:*", - "events:*", - "firehose:*", - "glue:*", - "health:*", - "iam:*", - "kms:*", - "lambda:*", - "logs:*", - "network-firewall:*", - "pipes:*", - "s3:*", - "schemas:*", - "sns:*", - "servicequotas:*", - "ssm:*", - "states:*", - "support:*", - "sqs:*", - "tag:*", - "trustedadvisor:*", - "xray:*" + + # CloudWatch - monitoring and alarms + "cloudwatch:PutMetricAlarm", + "cloudwatch:DeleteAlarms", + "cloudwatch:DescribeAlarms", + "cloudwatch:DescribeAlarmsForMetric", + "cloudwatch:ListTagsForResource", + "cloudwatch:TagResource", + "cloudwatch:UntagResource", + + # DynamoDB - table management + "dynamodb:DescribeTimeToLive", + "dynamodb:DescribeTable", + "dynamodb:DescribeContinuousBackups", + "dynamodb:ListTables", + "dynamodb:DeleteTable", + "dynamodb:CreateTable", + "dynamodb:TagResource", + "dynamodb:ListTagsOfResource", + + # EC2 - networking infrastructure + "ec2:Describe*", + "ec2:ModifyVpcBlockPublicAccessOptions", + "ec2:CreateTags", + "ec2:CreateNetworkAclEntry", + "ec2:CreateNetworkAcl", + "ec2:AssociateRouteTable", + "ec2:CreateVpc", + "ec2:ModifyVpcAttribute", + "ec2:DeleteVpc", + "ec2:CreateRouteTable", + "ec2:CreateSubnet", + "ec2:RevokeSecurityGroupIngress", + "ec2:CreateSecurityGroup", + "ec2:RevokeSecurityGroupEgress", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:CreateVpcEndpoint", + "ec2:CreateFlowLogs", + "ec2:ReplaceNetworkAclAssociation", + "ec2:DeleteSecurityGroup", + "ec2:DeleteNetworkAcl", + + # EventBridge - alarm forwarding to Splunk + "events:PutRule", + "events:PutTargets", + "events:DeleteRule", + "events:RemoveTargets", + "events:DescribeRule", + "events:ListTargetsByRule", + "events:TagResource", + "events:UntagResource", + + # Kinesis Firehose - log streaming + "firehose:CreateDeliveryStream", + "firehose:DeleteDeliveryStream", + "firehose:DescribeDeliveryStream", + "firehose:UpdateDestination", + "firehose:PutRecord", + "firehose:PutRecordBatch", + "firehose:TagDeliveryStream", + "firehose:ListTagsForDeliveryStream", + "firehose:UntagDeliveryStream", + "firehose:StartDeliveryStreamEncryption", + "firehose:StopDeliveryStreamEncryption", + + # IAM - specific role and policy management + "iam:GetRole", + "iam:GetRolePolicy", + "iam:GetPolicy", + "iam:GetPolicyVersion", + "iam:ListRoles", + "iam:ListPolicies", + "iam:ListRolePolicies", + "iam:ListAttachedRolePolicies", + "iam:ListPolicyVersions", + "iam:CreateRole", + "iam:DeleteRole", + "iam:UpdateRole", + "iam:PutRolePolicy", + "iam:PutRolePermissionsBoundary", + "iam:AttachRolePolicy", + "iam:DetachRolePolicy", + "iam:CreatePolicy", + "iam:CreatePolicyVersion", + "iam:TagRole", + "iam:PassRole", + "iam:TagPolicy", + + # KMS - encryption key management + "kms:CreateKey", + "kms:DescribeKey", + "kms:Describe*", + "kms:CreateAlias", + "kms:ListKeys", + "kms:List*", + "kms:ListAliases", + "kms:GetKeyPolicy", + "kms:GetKeyPolicy*", + "kms:GetKeyRotationStatus", + "kms:DeleteAlias", + "kms:UpdateKeyDescription", + "kms:CreateGrant", + "kms:TagResource", + "kms:EnableKeyRotation", + "kms:ScheduleKeyDeletion", + "kms:PutKeyPolicy", + "kms:Encrypt", + "kms:Decrypt", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey", + + # Lambda - function management + "lambda:CreateFunction", + "lambda:UpdateFunctionCode", + "lambda:UpdateFunctionConfiguration", + "lambda:DeleteFunction", + "lambda:GetFunction", + "lambda:GetFunctionConfiguration", + "lambda:GetFunctionCodeSigningConfig", + "lambda:ListVersionsByFunction", + "lambda:TagResource", + "lambda:UntagResource", + "lambda:ListTags", + "lambda:PublishVersion", + "lambda:CreateAlias", + "lambda:UpdateAlias", + "lambda:DeleteAlias", + "lambda:ListAliases", + "lambda:AddPermission", + "lambda:RemovePermission", + "lambda:GetPolicy", + + # CloudWatch Logs - log management + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:Describe*", + "logs:ListTagsForResource", + "logs:PutRetentionPolicy", + "logs:AssociateKmsKey", + "logs:PutMetricFilter", + + # S3 - bucket and object management + "s3:GetLifecycleConfiguration", + "s3:PutLifecycleConfiguration", + "s3:GetBucketVersioning", + "s3:GetEncryptionConfiguration", + "s3:PutEncryptionConfiguration", + "s3:GetBucketPolicy", + "s3:GetBucketObjectLockConfiguration", + "s3:GetBucketLogging", + "s3:GetReplicationConfiguration", + "s3:GetBucketWebsite", + "s3:GetBucketRequestPayment", + "s3:GetBucketCORS", + "s3:GetBucketAcl", + "s3:PutBucketAcl", + "s3:GetAccelerateConfiguration", + "s3:ListBucket", + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:GetBucketLocation", + "s3:GetBucketPublicAccessBlock", + "s3:PutBucketCORS", + "s3:CreateBucket", + "s3:DeleteBucket", + "s3:GetBucketTagging", + "s3:PutBucketPolicy", + "s3:PutBucketVersioning", + "s3:PutBucketPublicAccessBlock", + "s3:PutBucketLogging", + "s3:GetObjectTagging", + "s3:PutObjectTagging", + "s3:GetObjectVersion", + + # SNS - notification management + "sns:CreateTopic", + "sns:DeleteTopic", + "sns:GetTopicAttributes", + "sns:SetTopicAttributes", + "sns:ListTopics", + "sns:ListTagsForResource", + "sns:TagResource", + "sns:UntagResource", + "sns:Subscribe", + "sns:Unsubscribe", + "sns:ListSubscriptions", + "sns:ListSubscriptionsByTopic", + + # SSM - parameter management + "ssm:DescribeParameters", + "ssm:GetParameter", + "ssm:GetParameters", + "ssm:ListTagsForResource", + "ssm:PutParameter", + "ssm:AddTagsToResource" ] resources = ["*"] From 99f557273af576cc9091c10b2545d79ae01045b9 Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Thu, 7 Aug 2025 17:00:34 +0100 Subject: [PATCH 3/4] eli-385 changing assumed role permissions boundary --- .../assumed_role_permissions_boundary.tf | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf b/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf index 980bf8e61..efc3168b2 100644 --- a/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf +++ b/infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf @@ -6,35 +6,53 @@ data "aws_iam_policy_document" "assumed_role_permissions_boundary" { effect = "Allow" actions = [ - "acm:*", - "application-autoscaling:*", - "apigateway:*", - "cloudtrail:*", - "cloudwatch:*", - "config:*", - "dynamodb:*", - "ec2:*", - "events:*", - "firehose:*", - "glue:*", - "health:*", - "iam:*", - "kms:*", - "lambda:*", - "logs:*", - "network-firewall:*", - "pipes:*", - "s3:*", - "schemas:*", - "sns:*", - "servicequotas:*", - "ssm:*", - "states:*", - "support:*", - "sqs:*", - "tag:*", - "trustedadvisor:*", - "xray:*" + # DynamoDB - table operations for Lambda and external write roles + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + + # S3 - bucket and object operations for Lambda and Firehose + "s3:GetObject", + "s3:ListBucket", + "s3:PutObject", + "s3:PutObjectAcl", + "s3:AbortMultipartUpload", + "s3:GetBucketLocation", + "s3:ListBucketMultipartUploads", + + # KMS - encryption/decryption for DynamoDB and S3 + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey", + "kms:GenerateDataKey*", + "kms:DescribeKey", + + # CloudWatch Logs - Lambda execution and Firehose logging + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + + # EC2 - VPC access for Lambda (from AWSLambdaVPCAccessExecutionRole) + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AttachNetworkInterface", + "ec2:DetachNetworkInterface", + + # Kinesis Firehose - Lambda writing audit data + "firehose:PutRecord", + "firehose:PutRecordBatch", + + # X-Ray - Lambda tracing + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" ] resources = ["*"] From b2d9a0b852c49541fe00c18c151bfa4ec8dd885a Mon Sep 17 00:00:00 2001 From: Edd Almond <102675624+eddalmond1@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:14:03 +0100 Subject: [PATCH 4/4] eli-385 restricting kms key decryption to only those keys created in the account --- .../github_actions_policies.tf | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf index 598c2d111..cb72a7966 100644 --- a/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf +++ b/infrastructure/stacks/iams-developer-roles/github_actions_policies.tf @@ -343,24 +343,22 @@ resource "aws_iam_policy" "kms_creation" { { Effect = "Allow", Action = [ + # Key creation and listing actions require wildcard resource "kms:CreateKey", - "kms:DescribeKey", "kms:CreateAlias", "kms:List*", - "kms:ListAliases", - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", + "kms:ListAliases" ], Resource = "*" }, { Effect = "Allow", Action = [ + # Key management actions on account-specific keys only + "kms:DescribeKey", "kms:Describe*", "kms:GetKeyPolicy*", "kms:GetKeyRotationStatus", - "kms:Decrypt*", "kms:DeleteAlias", "kms:UpdateKeyDescription", "kms:CreateGrant", @@ -369,8 +367,9 @@ resource "aws_iam_policy" "kms_creation" { "kms:ScheduleKeyDeletion", "kms:PutKeyPolicy", "kms:Encrypt", - "kms:TagResource", - "kms:GenerateDataKey", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey" ], Resource = [ "arn:aws:kms:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:key/*",