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
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

# define policy documents for backend services
# sample policies
data "aws_iam_policy_document" "resource_access_backendservice" {
statement {
effect = "Allow"
actions = [
"ec2:Describe*",
]
resources = ["arn:aws:ec2:::dev-*"]
}
# can be more statements here
}
10 changes: 10 additions & 0 deletions templates/kubernetes/terraform/environments/dev/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,14 @@ module "kubernetes" {

# Logging configuration
logging_type = "<% index .Params `loggingType` %>"

# Application policy list
application_policy_list = [
{
service_account = "backend-service"
namespace = "<% .Name %>"
policy = data.aws_iam_policy_document.resource_access_backendservice
}
# could be more policies defined here (if have)
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

# define policy documents for backend services
# sample policies
data "aws_iam_policy_document" "resource_access_backendservice" {
statement {
effect = "Allow"
actions = [
"ec2:Describe*",
]
resources = ["arn:aws:ec2:::prod-*"]
}
# can be more statements here
}
11 changes: 10 additions & 1 deletion templates/kubernetes/terraform/environments/prod/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,13 @@ module "kubernetes" {

# Logging configuration
logging_type = "<% index .Params `loggingType` %>"
}

# Application policy list
application_policy_list = [
{
service_account = "backend-service"
namespace = "<% .Name %>"
policy = data.aws_iam_policy_document.resource_access_backendservice
}
# could be more policies defined here (if have)
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

# define policy documents for backend services
# sample policies
data "aws_iam_policy_document" "resource_access_backendservice" {
statement {
effect = "Allow"
actions = [
"ec2:Describe*",
]
resources = ["arn:aws:ec2:::stage-*"]
}
# can be more statements here
}
10 changes: 10 additions & 0 deletions templates/kubernetes/terraform/environments/stage/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ module "kubernetes" {

# Logging configuration
logging_type = "<% index .Params `loggingType` %>"

# Application policy list
application_policy_list = [
{
service_account = "backend-service"
namespace = "<% .Name %>"
policy = data.aws_iam_policy_document.resource_access_backendservice
}
# could be more policies defined here (if have)
]
}
43 changes: 16 additions & 27 deletions templates/kubernetes/terraform/modules/kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,30 @@ A web-based GUI for viewing and modifying resources in a Kubernetes cluster. Usa

## AWS IAM / Kubernetes RBAC integration

Sometimes you may have an application running in your cluster that needs to access the AWS API (S3 is a common example.) In this case you want to be able to have fine-grained control over this, to allow an application only the very specific access it needs.
Sometimes you may have an application running in the Kubernetes cluster that needs to access the AWS API (S3 is a common example.) In this case you want to be able to have fine-grained control over this, to allow an application only the very specific access it needs.

Previously there were tools like `kube2iam` or `kiam` that would enable this functionality, but now there is a new official method that AWS introduced that they call [IRSA (IAM Roles for Service Accounts)](https://aws.amazon.com/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/)
There is an official method for EKS called [IRSA (IAM Roles for Service Accounts)](https://aws.amazon.com/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/). This uses AWS IAM OIDC support to be able to mount tokens into pods automatically that can then be used to auth with the AWS API using a specific role. Any pods that come up in that deployment will automatically have env vars injected called `AWS_ROLE_ARN` and `AWS_WEB_IDENTITY_TOKEN_FILE` that will let them use the AWS API.

This uses their OIDC IAM support to be able to mount tokens into pods automatically that can then be used to auth with the AWS API using a specific role.
*Note that you may need to use a minimum specific version of the AWS API to take advantage of this automatically. You can see a list of the version numbers in the link above.*

The `cert_manager.tf` config has a good example of using this in practice. To allow a pod to have a specific level of access you need to:
The `irsa` module makes it easy to grant a pod to have a specific level of access. You need to:

- Create a policy in `environments/<env>/application_iam_policy.tf`, there should already be examples there. These will be the AWS policies that grant a specific level of access to AWS resources.
- Add your policy, namespace and service account name to `application_policy_list` in `environments/<env>/application_iam_policy.tf`. This is a mapping of a policy to a specific application that will run in the cluster.

- Create a role that allows being assumed by a web identity:
```
module "iam_assumable_role_my_role_name" {
source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
version = "~> v2.14.0"
create_role = true
role_name = "my-role-name"
provider_url = replace(data.aws_eks_cluster.cluster.identity.0.oidc.0.issuer, "https://", "")
role_policy_arns = [aws_iam_policy.external_dns.arn]
oidc_fully_qualified_subjects = ["system:serviceaccount:kube-system:my-service-account-name"]
}
{
service_account = "backendservice" # The name of your app. Unique per namespace
namespace = "my-app" # The namespace your app is in
policy = data.aws_iam_policy_document.resource_access_backendservice
},
```
- Create a service account for your kubernetes service to use, with an annotation specifying which IAM role is associated:

- This will create a Kubernetes "service account" in your cluster. You would refrence this in your application deployment manifest inside the pod template:
```
resource "kubernetes_service_account" "my_service_account" {
metadata {
name = "my-service-account-name"
namespace = "kube-system"
annotations = {
"eks.amazonaws.com/role-arn" = module.iam_assumable_role.my_role_name.this_iam_role_arn
}
}
}
spec:
serviceAccountName: backendservice
```
- Use this service account in your deployment spec.

Any pods that come up in that deployment will automatically have env vars injected called `AWS_ROLE_ARN` and `AWS_WEB_IDENTITY_TOKEN_FILE` that will let them use the AWS API.



Expand Down
43 changes: 43 additions & 0 deletions templates/kubernetes/terraform/modules/kubernetes/irsa.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# IRSA support: allow backend service to have a specific policy via service-account and role

# application_policy_list is passed from main.tf as below:
# application_policy_list = [
# {
# service_account = "backend-service"
# namespace = "my-app"
# policy = data.aws_iam_policy_document.resource_access_app1
# }
# # could be more here
# ]

# Create a role using oidc to map service accounts
module "iam_assumable_role_irsa" {
count = length(var.application_policy_list)
source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
version = "~> v2.14.0"
create_role = true
role_name = "${var.project}-k8s-${var.environment}-${var.application_policy_list[count.index].service_account}"
provider_url = replace(data.aws_eks_cluster.cluster.identity.0.oidc.0.issuer, "https://", "")
role_policy_arns = [aws_iam_policy.irsa[count.index].arn]
oidc_fully_qualified_subjects = ["system:serviceaccount:${var.application_policy_list[count.index].namespace}:${var.application_policy_list[count.index].service_account}"]
}

# Create policies
resource "aws_iam_policy" "irsa" {
count = length(var.application_policy_list)
name_prefix = "${var.project}-k8s-${var.environment}"
description = "policy for service account ${var.application_policy_list[count.index].namespace}:${var.application_policy_list[count.index].service_account}"
policy = var.application_policy_list[count.index].policy.json
}

# Create kubernetes service account
resource "kubernetes_service_account" "irsa" {
count = length(var.application_policy_list)
metadata {
name = var.application_policy_list[count.index].service_account
namespace = var.application_policy_list[count.index].namespace
annotations = {
"eks.amazonaws.com/role-arn" = module.iam_assumable_role_irsa[count.index].this_iam_role_arn
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ variable "logging_type" {
error_message = "Invalid value. Valid values are cloudwatch or kibana."
}
}

variable "application_policy_list" {
description = "Application policies"
type = list
default = []
}