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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A Terraform module for creating and managing Amazon EKS (Elastic Kubernetes Serv
- **IRSA Support**: OIDC provider setup for IAM Roles for Service Accounts
- **EKS Addons**: Flexible addon configuration (CoreDNS, VPC CNI, Kube-proxy, Pod Identity Agent, EBS CSI Driver)
- **EKS Capabilities**: Support for ACK, KRO, and ArgoCD capabilities
- **AWS Load Balancer Controller**: Optional IAM role creation for AWS Load Balancer Controller (IRSA)
- **AWS Load Balancer Controller**: Optional IAM role creation for AWS Load Balancer Controller (IRSA). The IRSA role expects the controller's service account in the **`aws-load-balancer-controller`** namespace (e.g. when using the [official Helm chart](https://kubernetes-sigs.github.io/aws-load-balancer-controller/), deploy into that namespace or set the Helm chart namespace accordingly).
- **Security**: KMS encryption, IMDSv2 enforcement, security groups
- **CloudWatch Log Group**: Optional log group for EKS control plane logs; set `cloudwatch_log_group_force_destroy = true` to allow the log group to be deleted on `terraform destroy` (default is to protect it).

Expand Down Expand Up @@ -200,7 +200,7 @@ No modules.
| <a name="input_create_cloudwatch_log_group"></a> [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Whether to create a CloudWatch log group for EKS cluster logs | `bool` | `true` | no |
| <a name="input_create_kms_key"></a> [create\_kms\_key](#input\_create\_kms\_key) | Controls if a KMS key for cluster encryption should be created | `bool` | `true` | no |
| <a name="input_eks_managed_node_groups"></a> [eks\_managed\_node\_groups](#input\_eks\_managed\_node\_groups) | Map of EKS managed node group configurations | <pre>map(object({<br/> name = optional(string)<br/> ami_type = optional(string, "AL2023_x86_64_STANDARD")<br/> instance_types = optional(list(string), ["t3.medium"])<br/> min_size = optional(number, 1)<br/> max_size = optional(number, 3)<br/> desired_size = optional(number, 2)<br/> disk_size = optional(number, 20)<br/> subnet_ids = optional(list(string))<br/> enable_bootstrap_user_data = optional(bool, true)<br/> metadata_options = optional(object({<br/> http_endpoint = optional(string, "enabled")<br/> http_tokens = optional(string, "required")<br/> http_put_response_hop_limit = optional(number, 1)<br/> }))<br/> labels = optional(map(string), {})<br/> tags = optional(map(string), {})<br/> }))</pre> | `{}` | no |
| <a name="input_enable_aws_load_balancer_controller"></a> [enable\_aws\_load\_balancer\_controller](#input\_enable\_aws\_load\_balancer\_controller) | Whether to create IAM role for AWS Load Balancer Controller (IRSA) | `bool` | `false` | no |
| <a name="input_enable_aws_load_balancer_controller"></a> [enable\_aws\_load\_balancer\_controller](#input\_enable\_aws\_load\_balancer\_controller) | Whether to create IAM role for AWS Load Balancer Controller (IRSA). Role expects the controller's service account in the `aws-load-balancer-controller` namespace. | `bool` | `false` | no |
| <a name="input_enable_cluster_creator_admin_permissions"></a> [enable\_cluster\_creator\_admin\_permissions](#input\_enable\_cluster\_creator\_admin\_permissions) | Indicates whether or not to add the cluster creator (the identity used by Terraform) as an administrator via access entry | `bool` | `false` | no |
| <a name="input_enable_external_dns"></a> [enable\_external\_dns](#input\_enable\_external\_dns) | Whether to create IAM role for ExternalDNS (IRSA) | `bool` | `false` | no |
| <a name="input_enabled_cluster_log_types"></a> [enabled\_cluster\_log\_types](#input\_enabled\_cluster\_log\_types) | List of control plane logging types to enable | `list(string)` | <pre>[<br/> "api",<br/> "audit",<br/> "authenticator"<br/>]</pre> | no |
Expand Down
285 changes: 255 additions & 30 deletions aws-lb-controller-iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ data "aws_iam_policy_document" "aws_lb_controller_assume_role" {
condition {
test = "StringEquals"
variable = "${replace(aws_eks_cluster.this.identity[0].oidc[0].issuer, "https://", "")}:sub"
values = ["system:serviceaccount:kube-system:aws-load-balancer-controller"]
values = ["system:serviceaccount:aws-load-balancer-controller:aws-load-balancer-controller"]
}

condition {
Expand All @@ -46,64 +46,289 @@ resource "aws_iam_role" "aws_lb_controller" {
}

# Attach AWS managed policy for Elastic Load Balancing
resource "aws_iam_role_policy_attachment" "aws_lb_controller" {
for_each = var.enable_aws_load_balancer_controller ? {
elastic_load_balancing = "arn:${data.aws_partition.current.partition}:iam::aws:policy/ElasticLoadBalancingFullAccess"
ec2 = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEC2FullAccess"
} : {}
data "aws_iam_policy_document" "aws_lb_controller" {
count = var.enable_aws_load_balancer_controller ? 1 : 0

role = aws_iam_role.aws_lb_controller[0].name
policy_arn = each.value
}
statement {
sid = "CreateServiceLinkedRole"
effect = "Allow"
actions = ["iam:CreateServiceLinkedRole"]
resources = ["*"]
condition {
test = "StringEquals"
variable = "iam:AWSServiceName"
values = ["elasticloadbalancing.amazonaws.com"]
}
}

# IAM policy document for AWS Load Balancer Controller WAF, WAF Regional, and Shield permissions
data "aws_iam_policy_document" "aws_lb_controller_waf" {
count = var.enable_aws_load_balancer_controller ? 1 : 0
statement {
sid = "Describe"
effect = "Allow"
actions = [
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeVpcs",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeInstances",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeTags",
"ec2:GetCoipPoolUsage",
"ec2:DescribeCoipPools",
"ec2:GetSecurityGroupsForVpc",
"ec2:DescribeIpamPools",
"ec2:DescribeRouteTables",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeListenerCertificates",
"elasticloadbalancing:DescribeSSLPolicies",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DescribeTrustStores",
"elasticloadbalancing:DescribeListenerAttributes",
"elasticloadbalancing:DescribeCapacityReservation",
]
resources = ["*"]
}

statement {
sid = "WAFv2Permissions"
sid = "CognitoAcmIamWafShield"
effect = "Allow"
actions = [
"cognito-idp:DescribeUserPoolClient",
"acm:ListCertificates",
"acm:DescribeCertificate",
"iam:ListServerCertificates",
"iam:GetServerCertificate",
"waf-regional:GetWebACL",
"waf-regional:GetWebACLForResource",
"waf-regional:AssociateWebACL",
"waf-regional:DisassociateWebACL",
"wafv2:GetWebACL",
"wafv2:GetWebACLForResource",
"wafv2:AssociateWebACL",
"wafv2:DisassociateWebACL",
"wafv2:ListWebACLs"
"wafv2:ListWebACLs",
"shield:GetSubscriptionState",
"shield:DescribeProtection",
"shield:CreateProtection",
"shield:DeleteProtection",
]
resources = ["*"]
}

statement {
sid = "WAFRegionalPermissions"
sid = "SecurityGroupIngressEgress"
effect = "Allow"
actions = ["ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupIngress"]
resources = ["*"]
}

statement {
sid = "CreateSecurityGroup"
effect = "Allow"
actions = ["ec2:CreateSecurityGroup"]
resources = ["*"]
}

statement {
sid = "CreateTagsSecurityGroup"
effect = "Allow"
actions = ["ec2:CreateTags"]
resources = ["arn:${data.aws_partition.current.partition}:ec2:*:*:security-group/*"]
condition {
test = "StringEquals"
variable = "ec2:CreateAction"
values = ["CreateSecurityGroup"]
}
condition {
test = "Null"
variable = "aws:RequestTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "CreateDeleteTagsSecurityGroup"
effect = "Allow"
actions = ["ec2:CreateTags", "ec2:DeleteTags"]
resources = ["arn:${data.aws_partition.current.partition}:ec2:*:*:security-group/*"]
condition {
test = "Null"
variable = "aws:RequestTag/elbv2.k8s.aws/cluster"
values = ["true"]
}
condition {
test = "Null"
variable = "aws:ResourceTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "SecurityGroupManageTagged"
effect = "Allow"
actions = [
"waf-regional:GetWebACL",
"waf-regional:GetWebACLForResource",
"waf-regional:AssociateWebACL",
"waf-regional:DisassociateWebACL",
"waf-regional:ListWebACLs"
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress",
"ec2:DeleteSecurityGroup",
]
resources = ["*"]
condition {
test = "Null"
variable = "aws:ResourceTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "ShieldPermissions"
sid = "CreateLoadBalancerTargetGroup"
effect = "Allow"
actions = [
"shield:GetSubscriptionState",
"shield:DescribeProtection",
"shield:CreateProtection",
"shield:DeleteProtection"
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateTargetGroup",
]
resources = ["*"]
condition {
test = "Null"
variable = "aws:RequestTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "ListenerRule"
effect = "Allow"
actions = [
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:DeleteRule",
]
resources = ["*"]
}

statement {
sid = "AddRemoveTagsLbTargetGroup"
effect = "Allow"
actions = ["elasticloadbalancing:AddTags", "elasticloadbalancing:RemoveTags"]
resources = [
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:targetgroup/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/net/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/app/*/*",
]
condition {
test = "Null"
variable = "aws:RequestTag/elbv2.k8s.aws/cluster"
values = ["true"]
}
condition {
test = "Null"
variable = "aws:ResourceTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "AddRemoveTagsListener"
effect = "Allow"
actions = ["elasticloadbalancing:AddTags", "elasticloadbalancing:RemoveTags"]
resources = [
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener/net/*/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener/app/*/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener-rule/net/*/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:listener-rule/app/*/*/*",
]
}

statement {
sid = "ModifyDeleteLbTargetGroup"
effect = "Allow"
actions = [
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:SetIpAddressType",
"elasticloadbalancing:SetSecurityGroups",
"elasticloadbalancing:SetSubnets",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:DeleteTargetGroup",
"elasticloadbalancing:ModifyListenerAttributes",
"elasticloadbalancing:ModifyCapacityReservation",
"elasticloadbalancing:ModifyIpPools"
]
resources = ["*"]
condition {
test = "Null"
variable = "aws:ResourceTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "AddTagsOnCreate"
effect = "Allow"
actions = ["elasticloadbalancing:AddTags"]
resources = [
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:targetgroup/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/net/*/*",
"arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:loadbalancer/app/*/*",
]
condition {
test = "StringEquals"
variable = "elasticloadbalancing:CreateAction"
values = ["CreateTargetGroup", "CreateLoadBalancer"]
}
condition {
test = "Null"
variable = "aws:RequestTag/elbv2.k8s.aws/cluster"
values = ["false"]
}
}

statement {
sid = "RegisterTargets"
effect = "Allow"
actions = ["elasticloadbalancing:RegisterTargets", "elasticloadbalancing:DeregisterTargets"]
resources = ["arn:${data.aws_partition.current.partition}:elasticloadbalancing:*:*:targetgroup/*/*"]
}

statement {
sid = "SetWebAclModifyListenerRule"
effect = "Allow"
actions = [
"elasticloadbalancing:SetWebAcl",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:AddListenerCertificates",
"elasticloadbalancing:RemoveListenerCertificates",
"elasticloadbalancing:ModifyRule",
"elasticloadbalancing:SetRulePriorities"
]
resources = ["*"]
}
}

# IAM policy for AWS Load Balancer Controller WAF permissions
resource "aws_iam_role_policy" "aws_lb_controller_waf" {
resource "aws_iam_policy" "aws_lb_controller" {
count = var.enable_aws_load_balancer_controller ? 1 : 0

name = "${var.name}-aws-lb-controller-policy"
path = "/"
description = "Least-privilege permissions for AWS Load Balancer Controller"
policy = data.aws_iam_policy_document.aws_lb_controller[0].json

tags = var.tags
}

resource "aws_iam_role_policy_attachment" "aws_lb_controller" {
count = var.enable_aws_load_balancer_controller ? 1 : 0

name = "${var.name}-aws-lb-controller-waf-policy"
role = aws_iam_role.aws_lb_controller[0].id
policy = data.aws_iam_policy_document.aws_lb_controller_waf[0].json
role = aws_iam_role.aws_lb_controller[0].name
policy_arn = aws_iam_policy.aws_lb_controller[0].arn
}