-
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
base: main
Are you sure you want to change the base?
Changes from all commits
59b3f84
9bb7067
dc27e4b
1b49a5b
386d6c6
849beaa
b4741ac
42dded2
110e277
657078d
431d846
9c9b5c9
e51384a
6ac0452
0fadf5b
ea8dc02
d0b1672
d0fdb41
e3a880c
835bf8f
2982944
3ac53c2
473ecfc
c0dbf1c
2c00fca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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.kubernetes.io/name=$(terraform output -raw operator_name) | ||||||
| ``` | ||||||
|
|
||||||
| #### 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 operator_name} | ||||||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
command substitution is ( not { |
||||||
| ``` | ||||||
|
|
||||||
| ```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. | ||||||
| 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 | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,188 @@ | ||||||||||
| locals { | ||||||||||
| name = "example-${basename(path.cwd)}" | ||||||||||
|
|
||||||||||
| 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_version = data.aws_eks_cluster_versions.latest.cluster_versions[0].cluster_version | ||||||||||
|
||||||||||
| cluster_version = data.aws_eks_cluster_versions.latest.cluster_versions[0].cluster_version | |
| cluster_version = length(data.aws_eks_cluster_versions.latest.cluster_versions) > 0 ? data.aws_eks_cluster_versions.latest.cluster_versions[0].cluster_version : "1.30" |
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The substr function truncates the name to 20 characters, but there's no comment explaining why this limit is necessary. This appears to be working around an AWS EKS naming constraint. Adding a comment would improve code maintainability and help other developers understand the limitation.
| main = { | |
| main = { | |
| # Truncate the node group name to 20 characters to comply with AWS/EKS | |
| # node group naming length constraints. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| repo = "tailscale/k8s-operator" | |
| repository = "tailscale/k8s-operator" |
https://github.com/tailscale/tailscale/blob/main/cmd/k8s-operator/deploy/chart/values.yaml#L52
In helm u can fill the values with stuff that doesnt exist and things will silently still work. So you likely weren't running into issues for these reasons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| mode = true | |
| mode = "true" | |
| allowImpersonation = "true" |
https://github.com/tailscale/tailscale/blob/main/cmd/k8s-operator/deploy/chart/values.yaml#L132 defualts to false but because your using proxygroup in auth mode you will need cluster roles created and allow impersonation.
spec:
type: kube-apiserver
kubeAPIServer:
mode: auth
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you setting tag here? this field doesn't even exist please see https://github.com/tailscale/tailscale/blob/main/cmd/k8s-operator/deploy/chart/values.yaml#L131
in addition tag:k8s-api-server is outside of the scope of what our documented used tags are.
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
| 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 "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_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_kubectl_ha_proxy_apply" { | ||||||||||||||||||||||||||||||||||
| description = "Command to deploy the Tailscale high availability API server proxy - https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy" | ||||||||||||||||||||||||||||||||||
| value = "OPERATOR_NAME=${helm_release.tailscale_operator.name} envsubst < tailscale-api-server-ha-proxy.yaml | kubectl apply -f -" | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| output "cmd_kubectl_ha_proxy_delete" { | ||||||||||||||||||||||||||||||||||
| description = "Command to delete the Tailscale high availability API server proxy - https://tailscale.com/kb/1437/kubernetes-operator-api-server-proxy#configuring-a-high-availability-api-server-proxy" | ||||||||||||||||||||||||||||||||||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
| value = "OPERATOR_NAME=${helm_release.tailscale_operator.name} envsubst < tailscale-api-server-ha-proxy.yaml | kubectl delete -f -" | ||||||||||||||||||||||||||||||||||
clstokes marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+31
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Untested but I think this is correct |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| 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 |
| 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
|
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is probably easier