diff --git a/.changelog/2287.txt b/.changelog/2287.txt new file mode 100644 index 0000000000..fa0cc77502 --- /dev/null +++ b/.changelog/2287.txt @@ -0,0 +1,3 @@ +```release-note:doc +Add example module for configuring OIDC authentication on EKS +```release-note:doc diff --git a/_examples/eks/eks-cluster/cluster.tf b/_examples/eks/eks-cluster/cluster.tf index 4dfd37b7d4..632e321e19 100644 --- a/_examples/eks/eks-cluster/cluster.tf +++ b/_examples/eks/eks-cluster/cluster.tf @@ -26,7 +26,7 @@ resource "aws_eks_node_group" "k8s-acc" { scaling_config { desired_size = 1 - max_size = 1 + max_size = 3 min_size = 1 } @@ -38,3 +38,11 @@ resource "aws_eks_node_group" "k8s-acc" { aws_iam_role_policy_attachment.k8s-acc-AmazonEC2ContainerRegistryReadOnly, ] } + +output "cluster_url" { + value = aws_eks_cluster.k8s-acc.endpoint +} + +output "cluster_ca" { + value = aws_eks_cluster.k8s-acc.certificate_authority[0].data +} \ No newline at end of file diff --git a/_examples/eks/eks-cluster/variables.tf b/_examples/eks/eks-cluster/variables.tf index 3d4a0584ab..2296874895 100644 --- a/_examples/eks/eks-cluster/variables.tf +++ b/_examples/eks/eks-cluster/variables.tf @@ -7,5 +7,5 @@ variable "cluster_name" { variable "kubernetes_version" { type = string - default = "1.19" + default = "1.27" } diff --git a/_examples/eks/eks-cluster/version.tf b/_examples/eks/eks-cluster/version.tf index 9ef0103eab..acd18c8af2 100644 --- a/_examples/eks/eks-cluster/version.tf +++ b/_examples/eks/eks-cluster/version.tf @@ -5,7 +5,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "3.38.0" + version = "~> 5.0" } } } diff --git a/_examples/eks/eks-oidc/README.md b/_examples/eks/eks-oidc/README.md new file mode 100644 index 0000000000..7681502cfe --- /dev/null +++ b/_examples/eks/eks-oidc/README.md @@ -0,0 +1,23 @@ +# Configuring EKS for OIDC identity providers + +Kubernetes, and by extension EKS, natively supports OIDC as an indentity provider to which it will delegate user authentication. The result of a successful authentication through OIDC is a base64 encoded token of data describing the user identity. The format of this token is called JWT (JSON Web Token) and is described by [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519). + +The Kubernetes and Helm providers for Terraform are already designed to accept JWTs as indentity carriers. A token is passed to the provider by setting the `token` attribute on the provider block (or the `KUBE_TOKEN` environment variable). + +Terraform Cloud can act as an OIDC identity provider to kubernetes, issuing JWT tokens that it designates as ["workload identity"](https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/workload-identity-tokens). This module is designed around using TFC as an indentity provider, but will likely work with any OIDC compliant IDp, such as Okta. + +# OIDC on EKS + +EKS can be configured with an external IDp through the [`aws_eks_identity_provider_config`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_identity_provider_config) Terraform resource. This module is a thin wrapper around it, adding some meaningful defaults in the context of TFC (which can, of course be overridden) as well as the necessary RBAC role binding to grant permissions to the indentity obtained from OIDC. + +To make use of the module, roughly follow the following steps (adapt for you actual needs): + +1. Create an EKS cluster + + Use the method of your choice to spin up an EKS cluster. One simple example is provided right here, in the sibling folder `eks-cluster`. Another way is making use of ["terraform-aws-modules/eks/aws"](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest). Take note of the cluster's API endpoint URL as well as the cluster's API CA certificate. These will be needed later to configure the Kuberentes provider. + +2. Apply this module + + Make sure the same AWS credentials used for the above EKS cluster are avialable in the environment. Provide values for input variables as needed for your use case. For Terraform Cloud, reasonable defaults are baked into the module and all that's required is the name of the TFC Organization that will be used a the "admin group". Identities for all workloads in this org will be granted `cluster-admin` permission on the EKS cluster, via the group name extracted from the configured JWT claim (see input variables). To that end, this module creates a ClusterRoleBinding resource to bind the `cluster-admin` role with the user's group. + +You are now ready to access your EKS cluster with indentity tokens provided by Terraform Cloud or your IDp of choice. The Kubernetes provider now only needs to be configured for [host endpoint](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#host) and [cluster CA](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#cluster_ca_certificate). In case you are running in Terraform Cloud, the token will automatically be injected into every run's environment (feature not rolled out yet). With other identity providers, you have to collect the token and supply it to the provider using the `KUBE_TOKEN` or the `token` provider attribute. \ No newline at end of file diff --git a/_examples/eks/eks-oidc/main.tf b/_examples/eks/eks-oidc/main.tf new file mode 100644 index 0000000000..ca8ad826bb --- /dev/null +++ b/_examples/eks/eks-oidc/main.tf @@ -0,0 +1,74 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +variable "cluster_name" { + type = string +} + +variable "oidc_issuer_url" { + default = "https://app.terraform.io" +} + +variable "oidc_audience" { + default = "kubernetes" +} + +variable "oidc_idp_name" { + default = "terraform-cloud" +} + +variable "rbac_group_oidc_claim" { + default = "terraform_organization_name" +} + +variable "rbac_admin_group_name" { + type = string +} + +variable "rbac_group_cluster_role" { + default = "cluster-admin" +} + +resource "aws_eks_identity_provider_config" "oidc_config" { + cluster_name = var.cluster_name + + oidc { + identity_provider_config_name = var.oidc_idp_name + client_id = var.oidc_audience + issuer_url = var.oidc_issuer_url + username_claim = "sub" + groups_claim = var.rbac_group_oidc_claim + } +} + +data "aws_eks_cluster" "target_eks" { + name = var.cluster_name +} + +data "aws_eks_cluster_auth" "target_eks_auth" { + name = var.cluster_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.target_eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.target_eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.target_eks_auth.token +} + +resource "kubernetes_cluster_role_binding_v1" "oidc_role" { + metadata { + name = "odic-identity" + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = var.rbac_group_cluster_role + } + + subject { + api_group = "rbac.authorization.k8s.io" + kind = "Group" + name = var.rbac_admin_group_name + } +}