Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions infrastructure/aws/iam/ecr/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
resource "aws_iam_role" "nullplatform_application_role" {
name = "nullplatform-${var.cluster_name}-application-role"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [{
Effect = "Allow",
Principal = { AWS = var.application_manager_assume_role },
Action = "sts:AssumeRole",
}]
})
}

resource "aws_iam_policy" "nullplatform_ecr_manager_policy" {
name = "nullplatform-${var.cluster_name}-ecr-manager-policy"
description = "Policy for managing ECR repositories with restricted access"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:CompleteLayerUpload",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:CreateRepository",
"ecr:DeleteRepository",
"ecr:DescribeRepositories",
"ecr:TagResource",
"ecr:SetRepositoryPolicy"
]
Resource = ["arn:aws:ecr:*:*:repository/*"]
},
{
Effect = "Allow"
Action = ["sts:GetServiceBearerToken", "ecr:GetAuthorizationToken"]
Resource = "*"
}
]
})
}

resource "aws_iam_user" "nullplatform_build_workflow_user" {
name = "nullplatform-${var.cluster_name}-build-workflow-user"
}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

resource "aws_iam_access_key" "nullplatform_build_workflow_user_key" {
user = aws_iam_user.nullplatform_build_workflow_user.name
}

resource "aws_iam_role_policy_attachment" "ecr_manager_policy" {
role = aws_iam_role.nullplatform_application_role.name
policy_arn = aws_iam_policy.nullplatform_ecr_manager_policy.arn
}

resource "aws_iam_group" "nullplatform_ecr_managers" {
name = "nullplatform-${var.cluster_name}-ecr-managers"
}

Check warning

Code scanning / Trivy

IAM groups should have MFA enforcement activated. Medium

Artifact: infrastructure/aws/iam/ecr/main.tf
Type: terraform
Vulnerability AWS-0123
Severity: MEDIUM
Message: Multi-Factor authentication is not enforced for group
Link: AWS-0123
Comment on lines +60 to +62

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't fix / acknowledged. This group is exclusively for a CI/CD build workflow user that authenticates via programmatic access keys (no console login). MFA enforcement at the group level would require the access key to call sts:GetSessionToken with an MFA token on every CI run, which is not compatible with standard CI/CD pipelines. The attack surface is already minimized: the user has a scoped policy limited to ECR operations only.


resource "aws_iam_group_policy_attachment" "ecr_manager_policy_group" {
group = aws_iam_group.nullplatform_ecr_managers.name
policy_arn = aws_iam_policy.nullplatform_ecr_manager_policy.arn
}

resource "aws_iam_user_group_membership" "build_workflow_ecr_managers" {
user = aws_iam_user.nullplatform_build_workflow_user.name
groups = [aws_iam_group.nullplatform_ecr_managers.name]
}

resource "aws_iam_role" "ecr_cross_account_pull" {
count = var.enable_cross_account_pull ? 1 : 0
name = "nullplatform-${var.cluster_name}-ecr-cross-account-pull"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
AWS = [for id in var.pull_account_ids : "arn:aws:iam::${id}:root"]
}
Action = "sts:AssumeRole"
}]
})
}

resource "aws_iam_policy" "ecr_cross_account_pull" {
count = var.enable_cross_account_pull ? 1 : 0
name = "nullplatform-${var.cluster_name}-ecr-cross-account-pull-policy"
description = "Allows cross-account principals to pull images from ECR"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeImages",
"ecr:DescribeRepositories"
]
Resource = "arn:aws:ecr:*:*:repository/*"
},
{
Effect = "Allow"
Action = ["ecr:GetAuthorizationToken"]
Resource = "*"
}
]
})
}

resource "aws_iam_role_policy_attachment" "ecr_cross_account_pull" {
count = var.enable_cross_account_pull ? 1 : 0
role = aws_iam_role.ecr_cross_account_pull[0].name
policy_arn = aws_iam_policy.ecr_cross_account_pull[0].arn
}
20 changes: 20 additions & 0 deletions infrastructure/aws/iam/ecr/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
output "application_role_arn" {
description = "ARN of the IAM role used by applications to pull ECR images"
value = aws_iam_role.nullplatform_application_role.arn
}

output "build_workflow_access_key_id" {
description = "Access key ID for the CI/CD build workflow IAM user"
value = aws_iam_access_key.nullplatform_build_workflow_user_key.id
}

output "build_workflow_access_key_secret" {
description = "Secret access key for the CI/CD build workflow IAM user"
value = aws_iam_access_key.nullplatform_build_workflow_user_key.secret
sensitive = true
}

output "cross_account_pull_role_arn" {
description = "ARN of the IAM role that cross-account principals can assume to pull ECR images. Empty string when enable_cross_account_pull is false."
value = var.enable_cross_account_pull ? aws_iam_role.ecr_cross_account_pull[0].arn : ""
}
8 changes: 8 additions & 0 deletions infrastructure/aws/iam/ecr/validations.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "terraform_data" "validations" {
lifecycle {
precondition {
condition = !var.enable_cross_account_pull || length(var.pull_account_ids) > 0
error_message = "pull_account_ids must have at least one account when enable_cross_account_pull is true."
}
}
}
22 changes: 22 additions & 0 deletions infrastructure/aws/iam/ecr/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
variable "cluster_name" {
description = "Name of the cluster, used to namespace IAM resource names"
type = string
}

variable "application_manager_assume_role" {
description = "ARN of the IAM role assumed by the application manager"
type = string
default = "arn:aws:iam::283477532906:role/application_manager"
}

variable "enable_cross_account_pull" {
description = "Enable cross-account ECR pull access by creating an IAM role that external accounts can assume"
type = bool
default = false
}

variable "pull_account_ids" {
description = "AWS account IDs allowed to assume the cross-account ECR pull role. Required when enable_cross_account_pull is true."
type = list(string)
default = []
}
3 changes: 0 additions & 3 deletions nullplatform/asset/ecr/data.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
data "aws_caller_identity" "current" {
}

data "aws_region" "current" {
}
90 changes: 0 additions & 90 deletions nullplatform/asset/ecr/iam.tf

This file was deleted.

26 changes: 0 additions & 26 deletions nullplatform/asset/ecr/locals.tf

This file was deleted.

15 changes: 6 additions & 9 deletions nullplatform/asset/ecr/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ resource "nullplatform_provider_config" "ecr" {
attributes = jsonencode({
"ci" : {
"region" : data.aws_region.current.region,
"access_key" : aws_iam_access_key.nullplatform_build_workflow_user_key.id,
"secret_key" : aws_iam_access_key.nullplatform_build_workflow_user_key.secret
"access_key" : var.build_workflow_access_key_id,
"secret_key" : var.build_workflow_access_key_secret
},
"setup" : merge(
{
"region" : data.aws_region.current.region,
"role_arn" : aws_iam_role.nullplatform_application_role.arn,
},
local.setup_policy
)
"setup" : {
"region" : data.aws_region.current.region,
"role_arn" : var.application_role_arn,
}
})
lifecycle {
ignore_changes = [attributes]
Expand Down
6 changes: 0 additions & 6 deletions nullplatform/asset/ecr/validations.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
resource "terraform_data" "validations" {
lifecycle {
precondition {
condition = !var.enable_cross_account_pull || length(var.repository_policy_pull_accounts) > 0
error_message = "repository_policy_pull_accounts must have at least one account when enable_cross_account_pull is true."
}
}
}
31 changes: 12 additions & 19 deletions nullplatform/asset/ecr/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,24 @@ variable "nrn" {
type = string
}

variable "application_manager_assume_role" {
description = "ARN of the IAM role assumed by the application manager"
type = string
default = "arn:aws:iam::283477532906:role/application_manager"
}

variable "cluster_name" {
description = "Name of the cluster where the policy runs"
type = string
}

variable "dimensions" {
description = "Dimensions to segment the nullplatform provider config (e.g. by region, environment)"
type = map(string)
default = {}
}

variable "enable_cross_account_pull" {
description = "Enable cross-account ECR pull access via a repository policy"
type = bool
default = false
variable "application_role_arn" {
description = "ARN of the IAM role used by applications to pull ECR images"
type = string
}

variable "repository_policy_pull_accounts" {
description = "AWS account IDs allowed to pull images from ECR. The account where this module is deployed is always included."
type = list(string)
default = []
variable "build_workflow_access_key_id" {
description = "Access key ID for the CI/CD build workflow IAM user"
type = string
}

variable "build_workflow_access_key_secret" {
description = "Secret access key for the CI/CD build workflow IAM user"
type = string
sensitive = true
}