-
Notifications
You must be signed in to change notification settings - Fork 3
terraform: add aws-eks-operator #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
59b3f84
terraform: add aws-eks
clstokes 9bb7067
rename aws-eks to aws-eks-operator
clstokes dc27e4b
Merge branch 'main' into clstokes/eks-operator
clstokes 1b49a5b
cleanup and tflint fixes
clstokes 386d6c6
terraform fmt
clstokes 849beaa
address most copilot feedback
clstokes b4741ac
rename terraform-tflint to terraform-check-tflint
clstokes 42dded2
tflint
clstokes 110e277
address more copilot feedback
clstokes 657078d
move ha proxy to external kubectl manifest, and various cleanup
clstokes 431d846
add 'terraform-fmt' to Makefile
clstokes 9c9b5c9
add operator_name output
clstokes e51384a
provision ha proxy with null_resource local-exec provisioner
clstokes 6ac0452
cleanup
clstokes 0fadf5b
Update Makefile
clstokes ea8dc02
use a random suffix for ha proxy name
clstokes d0b1672
fix tflint issue
clstokes d0fdb41
use aws_eks_cluster_versions data source for latest version
clstokes e3a880c
update prerequisites
clstokes 835bf8f
add enable_ha_proxy_service local variable
clstokes 2982944
remove beta from api_version for aws auth call
clstokes 3ac53c2
use a data source instead of aws cli
clstokes 473ecfc
Update terraform/aws/aws-eks-operator/README.md
clstokes c0dbf1c
Update README.md
clstokes 2c00fca
Merge branch 'clstokes/eks-operator' of github.com:tailscale-dev/exam…
clstokes dcc6e08
Apply suggestions from code review
clstokes 31e21c7
remove unused helm chart config
clstokes d456cb3
terraform fmt
clstokes 6022c67
set more cluster properties, remove unused outputs
clstokes 9bd1ea3
Change kubectl logs example
clstokes 7b0b9e6
Merge branch 'main' into clstokes/eks-operator
clstokes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # aws-eks-operator | ||
|
|
||
| This example creates the following: | ||
|
|
||
| - a VPC and related resources including a NAT Gateway | ||
| - an EKS cluster with a managed node group | ||
| - a Kubernetes namespace for the [Tailscale operator](https://tailscale.com/kb/1236/kubernetes-operator) | ||
| - the Tailscale Kubernetes Operator deployed via [Helm](https://tailscale.com/kb/1236/kubernetes-operator#helm) | ||
| - a [high availability API server proxy](https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy) | ||
|
|
||
| ## Considerations | ||
|
|
||
| - The EKS cluster is configured with both public and private API server access for flexibility | ||
| - The Tailscale operator is deployed in a dedicated `tailscale` namespace | ||
| - The operator will create a Tailscale device for API server proxy access | ||
| - Any additional Tailscale resources (like ingress controllers) created by the operator will appear in your Tailnet | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Follow the [Kubernetes Operator prerequisites](https://tailscale.com/kb/1236/kubernetes-operator#prerequisites). | ||
| - For the [high availability API server proxy](https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy): | ||
| - The configuration as-is currently only works on macOS or Linux clients. Remove or comment out the `null_resource` provisioners that deploy `tailscale-api-server-ha-proxy.yaml` to run from other platforms. | ||
| - Requires the [kubectl CLI](https://kubernetes.io/docs/reference/kubectl/) and [AWS CLI](https://aws.amazon.com/cli/). | ||
|
|
||
|
|
||
| ## To use | ||
|
|
||
| Follow the documentation to configure the Terraform providers: | ||
|
|
||
| - [AWS](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) | ||
|
|
||
| ### Configure variables | ||
|
|
||
| Create a `terraform.tfvars` file with your Tailscale OAuth credentials: | ||
|
|
||
| ```hcl | ||
| tailscale_oauth_client_id = "your-oauth-client-id" | ||
| tailscale_oauth_client_secret = "your-oauth-client-secret" | ||
| ``` | ||
|
|
||
| ### Deploy | ||
|
|
||
| ```shell | ||
| terraform init | ||
| terraform apply | ||
| ``` | ||
|
|
||
| #### Verify deployment | ||
|
|
||
| After deployment, configure kubectl to access your cluster: | ||
|
|
||
| ```shell | ||
| aws eks update-kubeconfig --region $AWS_REGION --name $(terraform output -raw cluster_name) | ||
| ``` | ||
|
|
||
| Check that the Tailscale operator is running: | ||
|
|
||
| ```shell | ||
| kubectl get pods -n tailscale | ||
| kubectl logs -n tailscale -l app=operator | ||
| ``` | ||
|
|
||
| #### Verify connectivity via the [API server proxy](https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy) | ||
|
|
||
| After deployment, configure kubectl to access your cluster using Tailscale: | ||
|
|
||
| ```shell | ||
| tailscale configure kubeconfig $(terraform output -raw ha_proxy_service_name) | ||
| ``` | ||
|
|
||
| ```shell | ||
| kubectl get pods -n tailscale | ||
| ``` | ||
|
|
||
| ## To destroy | ||
|
|
||
| ```shell | ||
| terraform destroy | ||
|
|
||
| # remove leftover Tailscale devices at https://login.tailscale.com/admin/machines and services at https://login.tailscale.com/admin/services | ||
| ``` | ||
|
|
||
| ## Limitations | ||
|
|
||
| - The [HA API server proxy](https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy) is deployed using a [terraform null_resource](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) instead of [kubernetes_manifest](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest.html) due to a Terraform limitation that results in `cannot create REST client: no client config` errors on first run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| data "aws_region" "current" {} | ||
|
|
||
| data "aws_eks_cluster_versions" "latest" { | ||
| default_only = true | ||
| } | ||
|
|
||
| data "aws_eks_cluster_auth" "this" { | ||
| name = module.eks.cluster_name | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| locals { | ||
| name = "example-${basename(path.cwd)}" | ||
| name_with_random_suffix = "${local.name}-${random_integer.operator_name_suffix.result}" | ||
|
|
||
| aws_tags = { | ||
| Name = local.name | ||
| } | ||
|
|
||
| # Modify these to use your own VPC | ||
| vpc_id = module.vpc.vpc_id | ||
| subnet_ids = module.vpc.private_subnets | ||
|
|
||
| # EKS cluster configuration | ||
| cluster_name = local.name_with_random_suffix | ||
| cluster_version = data.aws_eks_cluster_versions.latest.cluster_versions[0].cluster_version | ||
| node_instance_type = "t3.medium" | ||
| desired_size = 2 | ||
| max_size = 2 | ||
| min_size = 1 | ||
|
|
||
| # Tailscale Operator configuration | ||
| namespace_name = "tailscale" | ||
| operator_name = local.name_with_random_suffix | ||
| operator_version = "1.92.4" | ||
| tailscale_oauth_client_id = var.tailscale_oauth_client_id | ||
| tailscale_oauth_client_secret = var.tailscale_oauth_client_secret | ||
|
|
||
| enable_ha_proxy_service = true | ||
| ha_proxy_service_name = "${helm_release.tailscale_operator.name}-ha" | ||
| } | ||
|
|
||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # This isn't required but helps avoid conflicts and Let's Encrypt throttling to make testing and iterating easier. | ||
| resource "random_integer" "operator_name_suffix" { | ||
| min = 100 | ||
| max = 999 | ||
| } | ||
|
|
||
| # Remove this to use your own VPC. | ||
| module "vpc" { | ||
| source = "../internal-modules/aws-vpc" | ||
|
|
||
| name = local.name | ||
| tags = local.aws_tags | ||
| } | ||
|
|
||
| module "eks" { | ||
| source = "terraform-aws-modules/eks/aws" | ||
| version = ">= 21.0, < 22.0" | ||
|
|
||
| name = local.cluster_name | ||
| kubernetes_version = local.cluster_version | ||
|
|
||
| tags = local.aws_tags | ||
|
|
||
| addons = { | ||
| coredns = {} | ||
| eks-pod-identity-agent = { | ||
| before_compute = true | ||
| } | ||
| kube-proxy = {} | ||
| vpc-cni = { | ||
| before_compute = true | ||
| } | ||
| } | ||
|
|
||
| # Once the Tailscale operator is installed, `endpoint_public_access` can be disabled. | ||
| # This is left enabled for the sake of easy adoption. | ||
| endpoint_public_access = true | ||
|
|
||
| # Optional: Adds the current caller identity as an administrator via cluster access entry | ||
| enable_cluster_creator_admin_permissions = true | ||
|
|
||
| vpc_id = local.vpc_id | ||
| subnet_ids = local.subnet_ids | ||
|
|
||
| eks_managed_node_groups = { | ||
| main = { | ||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # Truncate the node group name to 20 characters to comply with AWS/EKS | ||
| # node group naming length constraints. | ||
| name = substr(local.name, 0, 20) | ||
| instance_types = [local.node_instance_type] | ||
|
|
||
| labels = {} | ||
|
|
||
| launch_template_name = local.name | ||
| launch_template_tags = local.aws_tags | ||
|
|
||
| desired_size = local.desired_size | ||
| max_size = local.max_size | ||
| min_size = local.min_size | ||
| } | ||
| } | ||
| } | ||
|
|
||
| resource "kubernetes_namespace_v1" "tailscale_operator" { | ||
| provider = kubernetes.this | ||
|
|
||
| metadata { | ||
| name = local.namespace_name | ||
| labels = { | ||
| "pod-security.kubernetes.io/enforce" = "privileged" | ||
| } | ||
| } | ||
|
|
||
| depends_on = [ | ||
| module.eks, | ||
| ] | ||
| } | ||
|
|
||
| # | ||
| # https://tailscale.com/kb/1236/kubernetes-operator#helm | ||
| # | ||
| resource "helm_release" "tailscale_operator" { | ||
| provider = helm.this | ||
|
|
||
| name = local.operator_name | ||
| namespace = kubernetes_namespace_v1.tailscale_operator.metadata[0].name | ||
|
|
||
| repository = "https://pkgs.tailscale.com/helmcharts" | ||
| chart = "tailscale-operator" | ||
| version = local.operator_version | ||
|
|
||
| values = [ | ||
| yamlencode({ | ||
| operatorConfig = { | ||
| image = { | ||
| tag = "v${local.operator_version}" | ||
| } | ||
| hostname = local.operator_name | ||
| } | ||
| apiServerProxyConfig = { | ||
| mode = "true" | ||
| allowImpersonation = "true" | ||
| } | ||
| }) | ||
| ] | ||
|
|
||
| set_sensitive = [ | ||
| { | ||
| name = "oauth.clientId" | ||
| value = local.tailscale_oauth_client_id | ||
| }, | ||
| { | ||
| name = "oauth.clientSecret" | ||
| value = local.tailscale_oauth_client_secret | ||
| }, | ||
| ] | ||
|
|
||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| depends_on = [ | ||
| module.eks, | ||
| ] | ||
| } | ||
|
|
||
| # | ||
| # https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy | ||
| # | ||
| # Remove or comment out the `null_resource` provisioners that deploy `tailscale-api-server-ha-proxy.yaml` for the | ||
| # high availability API server proxy to run from other platforms. | ||
| # | ||
| resource "null_resource" "kubectl_ha_proxy" { | ||
| count = local.enable_ha_proxy_service ? 1 : 0 | ||
|
|
||
| triggers = { | ||
| region = data.aws_region.current.region | ||
| cluster_arn = module.eks.cluster_arn | ||
| cluster_name = module.eks.cluster_name | ||
| ha_proxy_service_name = local.ha_proxy_service_name | ||
| } | ||
|
|
||
| # | ||
| # Create provisioners | ||
| # | ||
| provisioner "local-exec" { | ||
| command = "aws eks update-kubeconfig --region ${self.triggers.region} --name ${self.triggers.cluster_name}" | ||
| } | ||
| provisioner "local-exec" { | ||
| command = "HA_PROXY_SERVICE_NAME=${self.triggers.ha_proxy_service_name} envsubst < ${path.module}/tailscale-api-server-ha-proxy.yaml | kubectl apply --context=${self.triggers.cluster_arn} -f -" | ||
| } | ||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # | ||
| # Destroy provisioners | ||
| # | ||
| provisioner "local-exec" { | ||
| when = destroy | ||
| command = "aws eks update-kubeconfig --region ${self.triggers.region} --name ${self.triggers.cluster_name}" | ||
| } | ||
| provisioner "local-exec" { | ||
| when = destroy | ||
| command = "HA_PROXY_SERVICE_NAME=${self.triggers.ha_proxy_service_name} envsubst < ${path.module}/tailscale-api-server-ha-proxy.yaml | kubectl delete --context=${self.triggers.cluster_arn} -f -" | ||
| } | ||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| depends_on = [ | ||
| module.vpc, # prevent network changes before this finishes during a destroy | ||
| helm_release.tailscale_operator, | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| output "vpc_id" { | ||
| description = "VPC ID where the EKS cluster is deployed" | ||
| value = module.vpc.vpc_id | ||
| } | ||
|
|
||
| output "cluster_name" { | ||
| description = "EKS cluster name" | ||
| value = module.eks.cluster_name | ||
| } | ||
|
|
||
| output "operator_namespace" { | ||
| description = "Kubernetes namespace where Tailscale operator is deployed" | ||
| value = kubernetes_namespace_v1.tailscale_operator.metadata[0].name | ||
| } | ||
|
|
||
| output "operator_name" { | ||
| description = "Configured name of the Tailscale operator" | ||
| value = helm_release.tailscale_operator.name | ||
| } | ||
|
|
||
| output "ha_proxy_service_name" { | ||
| description = "Configured name of the Tailscale operator HA Service Proxy" | ||
| value = local.ha_proxy_service_name | ||
| } | ||
|
|
||
| output "cmd_kubeconfig_aws" { | ||
| description = "Command to configure kubeconfig for public access to the EKS cluster" | ||
| value = "aws eks update-kubeconfig --region ${data.aws_region.current.region} --name ${module.eks.cluster_name}" | ||
| } | ||
|
|
||
| output "cmd_kubeconfig_tailscale" { | ||
| description = "Command to configure kubeconfig for Tailscale access to the EKS cluster" | ||
| value = "tailscale configure kubeconfig ${helm_release.tailscale_operator.name}" | ||
| } | ||
|
|
||
| output "cmd_kubeconfig_tailscale_ha" { | ||
| description = "Command to configure kubeconfig for Tailscale access to the EKS cluster using the HA Service Proxy" | ||
| value = "tailscale configure kubeconfig ${local.ha_proxy_service_name}" | ||
| } |
11 changes: 11 additions & 0 deletions
11
terraform/aws/aws-eks-operator/tailscale-api-server-ha-proxy.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy | ||
| apiVersion: tailscale.com/v1alpha1 | ||
| kind: ProxyGroup | ||
| metadata: | ||
| name: ${HA_PROXY_SERVICE_NAME} | ||
| spec: | ||
| type: kube-apiserver | ||
| replicas: 2 | ||
| tags: ["tag:k8s"] | ||
| kubeAPIServer: | ||
| mode: auth |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| variable "tailscale_oauth_client_id" { | ||
| description = "Tailscale OAuth client ID" | ||
| type = string | ||
| sensitive = true | ||
|
|
||
| validation { | ||
| condition = length(var.tailscale_oauth_client_id) > 0 | ||
| error_message = "Tailscale OAuth client ID must not be empty." | ||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| variable "tailscale_oauth_client_secret" { | ||
| description = "Tailscale OAuth client secret" | ||
| type = string | ||
| sensitive = true | ||
|
|
||
| validation { | ||
| condition = length(var.tailscale_oauth_client_secret) > 0 | ||
| error_message = "Tailscale OAuth client secret must not be empty." | ||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.