diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc21bc9c25..b73ed98715 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.92.1 + rev: v1.92.2 hooks: - id: terraform_fmt - id: terraform_docs diff --git a/examples/karpenter/main.tf b/examples/karpenter/main.tf index 14435b6354..72ceff275a 100644 --- a/examples/karpenter/main.tf +++ b/examples/karpenter/main.tf @@ -126,6 +126,8 @@ module "karpenter" { cluster_name = module.eks.cluster_name + enable_v1_permissions = true + enable_pod_identity = true create_pod_identity_association = true @@ -155,7 +157,7 @@ resource "helm_release" "karpenter" { repository_username = data.aws_ecrpublic_authorization_token.token.user_name repository_password = data.aws_ecrpublic_authorization_token.token.password chart = "karpenter" - version = "0.37.0" + version = "1.0.0" wait = false values = [ diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md index 1bcf514f47..6810d0fa0c 100644 --- a/modules/karpenter/README.md +++ b/modules/karpenter/README.md @@ -121,6 +121,8 @@ No modules. | [aws_iam_policy_document.controller_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.node_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.queue](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.v033](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.v1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | @@ -141,6 +143,7 @@ No modules. | [enable\_irsa](#input\_enable\_irsa) | Determines whether to enable support for IAM role for service accounts | `bool` | `false` | no | | [enable\_pod\_identity](#input\_enable\_pod\_identity) | Determines whether to enable support for EKS pod identity | `bool` | `true` | no | | [enable\_spot\_termination](#input\_enable\_spot\_termination) | Determines whether to enable native spot termination handling | `bool` | `true` | no | +| [enable\_v1\_permissions](#input\_enable\_v1\_permissions) | Determines whether to enable permissions suitable for v1+ (`true`) or for v0.33.x-v0.37.x (`false`) | `bool` | `false` | no | | [iam\_policy\_description](#input\_iam\_policy\_description) | IAM policy description | `string` | `"Karpenter controller IAM policy"` | no | | [iam\_policy\_name](#input\_iam\_policy\_name) | Name of the IAM policy | `string` | `"KarpenterController"` | no | | [iam\_policy\_path](#input\_iam\_policy\_path) | Path of the IAM policy | `string` | `"/"` | no | diff --git a/modules/karpenter/main.tf b/modules/karpenter/main.tf index fee75db43a..fc4a5dca57 100644 --- a/modules/karpenter/main.tf +++ b/modules/karpenter/main.tf @@ -84,346 +84,7 @@ resource "aws_iam_role" "controller" { data "aws_iam_policy_document" "controller" { count = local.create_iam_role ? 1 : 0 - statement { - sid = "AllowScopedEC2InstanceActions" - resources = [ - "arn:${local.partition}:ec2:*::image/*", - "arn:${local.partition}:ec2:*::snapshot/*", - "arn:${local.partition}:ec2:*:*:spot-instances-request/*", - "arn:${local.partition}:ec2:*:*:security-group/*", - "arn:${local.partition}:ec2:*:*:subnet/*", - "arn:${local.partition}:ec2:*:*:launch-template/*", - ] - - actions = [ - "ec2:RunInstances", - "ec2:CreateFleet" - ] - } - - statement { - sid = "AllowScopedEC2InstanceActionsWithTags" - resources = [ - "arn:${local.partition}:ec2:*:*:fleet/*", - "arn:${local.partition}:ec2:*:*:instance/*", - "arn:${local.partition}:ec2:*:*:volume/*", - "arn:${local.partition}:ec2:*:*:network-interface/*", - "arn:${local.partition}:ec2:*:*:launch-template/*", - "arn:${local.partition}:ec2:*:*:spot-instances-request/*", - ] - actions = [ - "ec2:RunInstances", - "ec2:CreateFleet", - "ec2:CreateLaunchTemplate" - ] - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringLike" - variable = "aws:RequestTag/karpenter.sh/nodepool" - values = ["*"] - } - } - - statement { - sid = "AllowScopedResourceCreationTagging" - resources = [ - "arn:${local.partition}:ec2:*:*:fleet/*", - "arn:${local.partition}:ec2:*:*:instance/*", - "arn:${local.partition}:ec2:*:*:volume/*", - "arn:${local.partition}:ec2:*:*:network-interface/*", - "arn:${local.partition}:ec2:*:*:launch-template/*", - "arn:${local.partition}:ec2:*:*:spot-instances-request/*", - ] - actions = ["ec2:CreateTags"] - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringEquals" - variable = "ec2:CreateAction" - values = [ - "RunInstances", - "CreateFleet", - "CreateLaunchTemplate", - ] - } - - condition { - test = "StringLike" - variable = "aws:RequestTag/karpenter.sh/nodepool" - values = ["*"] - } - } - - statement { - sid = "AllowScopedResourceTagging" - resources = ["arn:${local.partition}:ec2:*:*:instance/*"] - actions = ["ec2:CreateTags"] - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringLike" - variable = "aws:ResourceTag/karpenter.sh/nodepool" - values = ["*"] - } - - condition { - test = "ForAllValues:StringEquals" - variable = "aws:TagKeys" - values = [ - "karpenter.sh/nodeclaim", - "Name", - ] - } - } - - statement { - sid = "AllowScopedDeletion" - resources = [ - "arn:${local.partition}:ec2:*:*:instance/*", - "arn:${local.partition}:ec2:*:*:launch-template/*" - ] - - actions = [ - "ec2:TerminateInstances", - "ec2:DeleteLaunchTemplate" - ] - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringLike" - variable = "aws:ResourceTag/karpenter.sh/nodepool" - values = ["*"] - } - } - - statement { - sid = "AllowRegionalReadActions" - resources = ["*"] - actions = [ - "ec2:DescribeAvailabilityZones", - "ec2:DescribeImages", - "ec2:DescribeInstances", - "ec2:DescribeInstanceTypeOfferings", - "ec2:DescribeInstanceTypes", - "ec2:DescribeLaunchTemplates", - "ec2:DescribeSecurityGroups", - "ec2:DescribeSpotPriceHistory", - "ec2:DescribeSubnets" - ] - - condition { - test = "StringEquals" - variable = "aws:RequestedRegion" - values = [local.region] - } - } - - statement { - sid = "AllowSSMReadActions" - resources = coalescelist(var.ami_id_ssm_parameter_arns, ["arn:${local.partition}:ssm:${local.region}::parameter/aws/service/*"]) - actions = ["ssm:GetParameter"] - } - - statement { - sid = "AllowPricingReadActions" - resources = ["*"] - actions = ["pricing:GetProducts"] - } - - dynamic "statement" { - for_each = local.enable_spot_termination ? [1] : [] - - content { - sid = "AllowInterruptionQueueActions" - resources = [try(aws_sqs_queue.this[0].arn, null)] - actions = [ - "sqs:DeleteMessage", - "sqs:GetQueueAttributes", - "sqs:GetQueueUrl", - "sqs:ReceiveMessage" - ] - } - } - - statement { - sid = "AllowPassingInstanceRole" - resources = var.create_node_iam_role ? [aws_iam_role.node[0].arn] : [var.node_iam_role_arn] - actions = ["iam:PassRole"] - - condition { - test = "StringEquals" - variable = "iam:PassedToService" - values = ["ec2.amazonaws.com"] - } - } - - statement { - sid = "AllowScopedInstanceProfileCreationActions" - resources = ["*"] - actions = ["iam:CreateInstanceProfile"] - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringEquals" - variable = "aws:RequestTag/topology.kubernetes.io/region" - values = [local.region] - } - - condition { - test = "StringLike" - variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" - values = ["*"] - } - } - - statement { - sid = "AllowScopedInstanceProfileTagActions" - resources = ["*"] - actions = ["iam:TagInstanceProfile"] - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/topology.kubernetes.io/region" - values = [local.region] - } - - condition { - test = "StringEquals" - variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/topology.kubernetes.io/region" - values = [local.region] - } - - condition { - test = "StringLike" - variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" - values = ["*"] - } - - condition { - test = "StringLike" - variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" - values = ["*"] - } - } - - statement { - sid = "AllowScopedInstanceProfileActions" - resources = ["*"] - actions = [ - "iam:AddRoleToInstanceProfile", - "iam:RemoveRoleFromInstanceProfile", - "iam:DeleteInstanceProfile" - ] - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" - values = ["owned"] - } - - condition { - test = "StringEquals" - variable = "aws:ResourceTag/topology.kubernetes.io/region" - values = [local.region] - } - - condition { - test = "StringLike" - variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" - values = ["*"] - } - } - - statement { - sid = "AllowInstanceProfileReadActions" - resources = ["*"] - actions = ["iam:GetInstanceProfile"] - } - - statement { - sid = "AllowAPIServerEndpointDiscovery" - resources = ["arn:${local.partition}:eks:${local.region}:${local.account_id}:cluster/${var.cluster_name}"] - actions = ["eks:DescribeCluster"] - } - - dynamic "statement" { - for_each = var.iam_policy_statements - - content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) - - dynamic "principals" { - for_each = try(statement.value.principals, []) - - content { - type = principals.value.type - identifiers = principals.value.identifiers - } - } - - dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) - - content { - type = not_principals.value.type - identifiers = not_principals.value.identifiers - } - } - - dynamic "condition" { - for_each = try(statement.value.conditions, []) - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } + source_policy_documents = var.enable_v1_permissions ? [data.aws_iam_policy_document.v1[0].json] : [data.aws_iam_policy_document.v033[0].json] } resource "aws_iam_policy" "controller" { diff --git a/modules/karpenter/policy.tf b/modules/karpenter/policy.tf new file mode 100644 index 0000000000..456a27f417 --- /dev/null +++ b/modules/karpenter/policy.tf @@ -0,0 +1,749 @@ +################################################################################ +# v0.33.x - v0.37.x Controller IAM Policy +################################################################################ + +data "aws_iam_policy_document" "v033" { + count = local.create_iam_role ? 1 : 0 + + statement { + sid = "AllowScopedEC2InstanceActions" + resources = [ + "arn:${local.partition}:ec2:*::image/*", + "arn:${local.partition}:ec2:*::snapshot/*", + "arn:${local.partition}:ec2:*:*:spot-instances-request/*", + "arn:${local.partition}:ec2:*:*:security-group/*", + "arn:${local.partition}:ec2:*:*:subnet/*", + "arn:${local.partition}:ec2:*:*:launch-template/*", + ] + + actions = [ + "ec2:RunInstances", + "ec2:CreateFleet" + ] + } + + statement { + sid = "AllowScopedEC2InstanceActionsWithTags" + resources = [ + "arn:${local.partition}:ec2:*:*:fleet/*", + "arn:${local.partition}:ec2:*:*:instance/*", + "arn:${local.partition}:ec2:*:*:volume/*", + "arn:${local.partition}:ec2:*:*:network-interface/*", + "arn:${local.partition}:ec2:*:*:launch-template/*", + "arn:${local.partition}:ec2:*:*:spot-instances-request/*", + ] + actions = [ + "ec2:RunInstances", + "ec2:CreateFleet", + "ec2:CreateLaunchTemplate" + ] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowScopedResourceCreationTagging" + resources = [ + "arn:${local.partition}:ec2:*:*:fleet/*", + "arn:${local.partition}:ec2:*:*:instance/*", + "arn:${local.partition}:ec2:*:*:volume/*", + "arn:${local.partition}:ec2:*:*:network-interface/*", + "arn:${local.partition}:ec2:*:*:launch-template/*", + "arn:${local.partition}:ec2:*:*:spot-instances-request/*", + ] + actions = ["ec2:CreateTags"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "ec2:CreateAction" + values = [ + "RunInstances", + "CreateFleet", + "CreateLaunchTemplate", + ] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowScopedResourceTagging" + resources = ["arn:${local.partition}:ec2:*:*:instance/*"] + actions = ["ec2:CreateTags"] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.sh/nodepool" + values = ["*"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "aws:TagKeys" + values = [ + "karpenter.sh/nodeclaim", + "Name", + ] + } + } + + statement { + sid = "AllowScopedDeletion" + resources = [ + "arn:${local.partition}:ec2:*:*:instance/*", + "arn:${local.partition}:ec2:*:*:launch-template/*" + ] + + actions = [ + "ec2:TerminateInstances", + "ec2:DeleteLaunchTemplate" + ] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowRegionalReadActions" + resources = ["*"] + actions = [ + "ec2:DescribeAvailabilityZones", + "ec2:DescribeImages", + "ec2:DescribeInstances", + "ec2:DescribeInstanceTypeOfferings", + "ec2:DescribeInstanceTypes", + "ec2:DescribeLaunchTemplates", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSpotPriceHistory", + "ec2:DescribeSubnets" + ] + + condition { + test = "StringEquals" + variable = "aws:RequestedRegion" + values = [local.region] + } + } + + statement { + sid = "AllowSSMReadActions" + resources = coalescelist(var.ami_id_ssm_parameter_arns, ["arn:${local.partition}:ssm:${local.region}::parameter/aws/service/*"]) + actions = ["ssm:GetParameter"] + } + + statement { + sid = "AllowPricingReadActions" + resources = ["*"] + actions = ["pricing:GetProducts"] + } + + dynamic "statement" { + for_each = local.enable_spot_termination ? [1] : [] + + content { + sid = "AllowInterruptionQueueActions" + resources = [try(aws_sqs_queue.this[0].arn, null)] + actions = [ + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + "sqs:ReceiveMessage" + ] + } + } + + statement { + sid = "AllowPassingInstanceRole" + resources = var.create_node_iam_role ? [aws_iam_role.node[0].arn] : [var.node_iam_role_arn] + actions = ["iam:PassRole"] + + condition { + test = "StringEquals" + variable = "iam:PassedToService" + values = ["ec2.amazonaws.com"] + } + } + + statement { + sid = "AllowScopedInstanceProfileCreationActions" + resources = ["*"] + actions = ["iam:CreateInstanceProfile"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + } + + statement { + sid = "AllowScopedInstanceProfileTagActions" + resources = ["*"] + actions = ["iam:TagInstanceProfile"] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + } + + statement { + sid = "AllowScopedInstanceProfileActions" + resources = ["*"] + actions = [ + "iam:AddRoleToInstanceProfile", + "iam:RemoveRoleFromInstanceProfile", + "iam:DeleteInstanceProfile" + ] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + } + + statement { + sid = "AllowInstanceProfileReadActions" + resources = ["*"] + actions = ["iam:GetInstanceProfile"] + } + + statement { + sid = "AllowAPIServerEndpointDiscovery" + resources = ["arn:${local.partition}:eks:${local.region}:${local.account_id}:cluster/${var.cluster_name}"] + actions = ["eks:DescribeCluster"] + } + + dynamic "statement" { + for_each = var.iam_policy_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, null) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, null) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +################################################################################ +# v1.0.x Controller IAM Policy +################################################################################ + +data "aws_iam_policy_document" "v1" { + count = local.create_iam_role ? 1 : 0 + + statement { + sid = "AllowScopedEC2InstanceAccessActions" + resources = [ + "arn:${local.partition}:ec2:${local.region}::image/*", + "arn:${local.partition}:ec2:${local.region}::snapshot/*", + "arn:${local.partition}:ec2:${local.region}:*:security-group/*", + "arn:${local.partition}:ec2:${local.region}:*:subnet/*", + ] + + actions = [ + "ec2:RunInstances", + "ec2:CreateFleet" + ] + } + + statement { + sid = "AllowScopedEC2LaunchTemplateAccessActions" + resources = [ + "arn:${local.partition}:ec2:${local.region}:*:launch-template/*" + ] + + actions = [ + "ec2:RunInstances", + "ec2:CreateFleet" + ] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowScopedEC2InstanceActionsWithTags" + resources = [ + "arn:${local.partition}:ec2:${local.region}:*:fleet/*", + "arn:${local.partition}:ec2:${local.region}:*:instance/*", + "arn:${local.partition}:ec2:${local.region}:*:volume/*", + "arn:${local.partition}:ec2:${local.region}:*:network-interface/*", + "arn:${local.partition}:ec2:${local.region}:*:launch-template/*", + "arn:${local.partition}:ec2:${local.region}:*:spot-instances-request/*", + ] + actions = [ + "ec2:RunInstances", + "ec2:CreateFleet", + "ec2:CreateLaunchTemplate" + ] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = [var.cluster_name] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowScopedResourceCreationTagging" + resources = [ + "arn:${local.partition}:ec2:${local.region}:*:fleet/*", + "arn:${local.partition}:ec2:${local.region}:*:instance/*", + "arn:${local.partition}:ec2:${local.region}:*:volume/*", + "arn:${local.partition}:ec2:${local.region}:*:network-interface/*", + "arn:${local.partition}:ec2:${local.region}:*:launch-template/*", + "arn:${local.partition}:ec2:${local.region}:*:spot-instances-request/*", + ] + actions = ["ec2:CreateTags"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = [var.cluster_name] + } + + condition { + test = "StringEquals" + variable = "ec2:CreateAction" + values = [ + "RunInstances", + "CreateFleet", + "CreateLaunchTemplate", + ] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowScopedResourceTagging" + resources = ["arn:${local.partition}:ec2:${local.region}:*:instance/*"] + actions = ["ec2:CreateTags"] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.sh/nodepool" + values = ["*"] + } + + condition { + test = "StringEqualsIfExists" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = [var.cluster_name] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "aws:TagKeys" + values = [ + "eks:eks-cluster-name", + "karpenter.sh/nodeclaim", + "Name", + ] + } + } + + statement { + sid = "AllowScopedDeletion" + resources = [ + "arn:${local.partition}:ec2:${local.region}:*:instance/*", + "arn:${local.partition}:ec2:${local.region}:*:launch-template/*" + ] + + actions = [ + "ec2:TerminateInstances", + "ec2:DeleteLaunchTemplate" + ] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.sh/nodepool" + values = ["*"] + } + } + + statement { + sid = "AllowRegionalReadActions" + resources = ["*"] + actions = [ + "ec2:DescribeAvailabilityZones", + "ec2:DescribeImages", + "ec2:DescribeInstances", + "ec2:DescribeInstanceTypeOfferings", + "ec2:DescribeInstanceTypes", + "ec2:DescribeLaunchTemplates", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSpotPriceHistory", + "ec2:DescribeSubnets" + ] + + condition { + test = "StringEquals" + variable = "aws:RequestedRegion" + values = [local.region] + } + } + + statement { + sid = "AllowSSMReadActions" + resources = coalescelist(var.ami_id_ssm_parameter_arns, ["arn:${local.partition}:ssm:${local.region}::parameter/aws/service/*"]) + actions = ["ssm:GetParameter"] + } + + statement { + sid = "AllowPricingReadActions" + resources = ["*"] + actions = ["pricing:GetProducts"] + } + + dynamic "statement" { + for_each = local.enable_spot_termination ? [1] : [] + + content { + sid = "AllowInterruptionQueueActions" + resources = [try(aws_sqs_queue.this[0].arn, null)] + actions = [ + "sqs:DeleteMessage", + "sqs:GetQueueUrl", + "sqs:ReceiveMessage" + ] + } + } + + statement { + sid = "AllowPassingInstanceRole" + resources = var.create_node_iam_role ? [aws_iam_role.node[0].arn] : [var.node_iam_role_arn] + actions = ["iam:PassRole"] + + condition { + test = "StringEquals" + variable = "iam:PassedToService" + values = ["ec2.amazonaws.com"] + } + } + + statement { + sid = "AllowScopedInstanceProfileCreationActions" + resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"] + actions = ["iam:CreateInstanceProfile"] + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = [var.cluster_name] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + } + + statement { + sid = "AllowScopedInstanceProfileTagActions" + resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"] + actions = ["iam:TagInstanceProfile"] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/eks:eks-cluster-name" + values = [var.cluster_name] + } + + condition { + test = "StringEquals" + variable = "aws:RequestTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + + condition { + test = "StringLike" + variable = "aws:RequestTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + } + + statement { + sid = "AllowScopedInstanceProfileActions" + resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"] + actions = [ + "iam:AddRoleToInstanceProfile", + "iam:RemoveRoleFromInstanceProfile", + "iam:DeleteInstanceProfile" + ] + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}" + values = ["owned"] + } + + condition { + test = "StringEquals" + variable = "aws:ResourceTag/topology.kubernetes.io/region" + values = [local.region] + } + + condition { + test = "StringLike" + variable = "aws:ResourceTag/karpenter.k8s.aws/ec2nodeclass" + values = ["*"] + } + } + + statement { + sid = "AllowInstanceProfileReadActions" + resources = ["arn:${local.partition}:iam::${local.account_id}:instance-profile/*"] + actions = ["iam:GetInstanceProfile"] + } + + statement { + sid = "AllowAPIServerEndpointDiscovery" + resources = ["arn:${local.partition}:eks:${local.region}:${local.account_id}:cluster/${var.cluster_name}"] + actions = ["eks:DescribeCluster"] + } + + dynamic "statement" { + for_each = var.iam_policy_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, null) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, null) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} diff --git a/modules/karpenter/variables.tf b/modules/karpenter/variables.tf index 96d898b245..a2c307fc05 100644 --- a/modules/karpenter/variables.tf +++ b/modules/karpenter/variables.tf @@ -116,6 +116,13 @@ variable "enable_pod_identity" { default = true } +# TODO - make v1 permssions the default policy at next breaking change +variable "enable_v1_permissions" { + description = "Determines whether to enable permissions suitable for v1+ (`true`) or for v0.33.x-v0.37.x (`false`)" + type = bool + default = false +} + ################################################################################ # IAM Role for Service Account (IRSA) ################################################################################