From f4c4f75a2c9cd3c8eb2ea2a2340ef2e71a7723f8 Mon Sep 17 00:00:00 2001 From: Riccardo Schirone <562321+ret2libc@users.noreply.github.com> Date: Wed, 26 Mar 2025 08:49:25 +0100 Subject: [PATCH] deployment: support Tailscale (#235) --- .gitignore | 6 +- deployment/.terraform.lock.hcl | 91 +- deployment/CHANGELOG.md | 46 + deployment/Makefile | 39 + deployment/README.md | 206 +- deployment/crs-architecture.sh | 125 + deployment/env.template | 23 + deployment/k8s/README.md | 6 +- .../tailscale-connections/kustomization.yaml | 4 + .../base/tailscale-connections/proxies.yaml | 22 + .../tailscale-coredns/coredns-custom.template | 11 + .../base/tailscale-coredns/kustomization.yaml | 4 + .../k8s/base/tailscale-dns/dnsconfig.yaml | 10 + .../k8s/base/tailscale-dns/kustomization.yaml | 4 + .../tailscale-operator/kustomization.yaml | 6 + .../base/tailscale-operator/operator.template | 5040 +++++++++++++++++ deployment/k8s/charts/build-bot/values.yaml | 2 +- .../competition-api/templates/configmap.yaml | 2 + .../competition-api/templates/deployment.yaml | 4 +- .../competition-api/templates/service.yaml | 2 + .../k8s/charts/competition-api/values.yaml | 1 + .../k8s/charts/coverage-bot/values.yaml | 2 +- deployment/k8s/charts/fuzzer-bot/values.yaml | 2 +- .../k8s/charts/program-model/values.yaml | 15 +- deployment/k8s/charts/scheduler/values.yaml | 16 +- deployment/k8s/charts/seed-gen/values.yaml | 2 +- .../k8s/charts/task-downloader/values.yaml | 15 +- .../charts/task-server/templates/ingress.yaml | 25 + .../charts/task-server/templates/service.yaml | 10 +- deployment/k8s/charts/task-server/values.yaml | 8 +- deployment/k8s/charts/tracer-bot/values.yaml | 6 - deployment/k8s/templates/common-env.yaml | 19 +- deployment/k8s/templates/secrets.yaml | 3 +- deployment/k8s/values-aks.template | 36 + deployment/k8s/values.yaml | 96 +- deployment/main.tf | 132 +- deployment/outputs.tf | 10 +- deployment/providers.tf | 34 + deployment/variables.tf | 34 +- 39 files changed, 5683 insertions(+), 436 deletions(-) create mode 100644 deployment/CHANGELOG.md create mode 100644 deployment/Makefile create mode 100755 deployment/crs-architecture.sh create mode 100644 deployment/env.template create mode 100644 deployment/k8s/base/tailscale-connections/kustomization.yaml create mode 100644 deployment/k8s/base/tailscale-connections/proxies.yaml create mode 100644 deployment/k8s/base/tailscale-coredns/coredns-custom.template create mode 100644 deployment/k8s/base/tailscale-coredns/kustomization.yaml create mode 100644 deployment/k8s/base/tailscale-dns/dnsconfig.yaml create mode 100644 deployment/k8s/base/tailscale-dns/kustomization.yaml create mode 100644 deployment/k8s/base/tailscale-operator/kustomization.yaml create mode 100644 deployment/k8s/base/tailscale-operator/operator.template create mode 100644 deployment/k8s/charts/task-server/templates/ingress.yaml create mode 100644 deployment/k8s/values-aks.template create mode 100644 deployment/providers.tf diff --git a/.gitignore b/.gitignore index a88acc52..3dbfc22c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,9 @@ errored.tfstate ## Generated Kubernetes Resources .k8s -deployment/k8s/values-override.yaml +deployment/k8s/values-*.yaml +deployment/k8s/base/tailscale-coredns/coredns-custom.yaml +deployment/k8s/base/tailscale-operator/operator.yaml ## Python __pycache__/ @@ -45,4 +47,4 @@ pdm.lock common/common/datastructures/fuzzer_msg* crs_scratch/* -tasks_storage/ +tasks_storage/* diff --git a/deployment/.terraform.lock.hcl b/deployment/.terraform.lock.hcl index add923a6..a5a8d2d0 100644 --- a/deployment/.terraform.lock.hcl +++ b/deployment/.terraform.lock.hcl @@ -2,61 +2,62 @@ # Manual edits may be lost in future updates. provider "registry.terraform.io/azure/azapi" { - version = "2.0.1" - constraints = "2.0.1" + version = "2.2.0" + constraints = "2.2.0" hashes = [ - "h1:NybiPNJ4MsKsTtRUnxV5vrhkbPkK+puqgfa0j0Q5nlU=", - "zh:3df16ed604be5f4ccd5d52a02c2681d8eb2f5a4462625c983cb17c20cdf0bfb2", - "zh:4efd9961ea52990e21385086f0b3324edfb534ea6a8f0f6ba146a74bfb56aa63", - "zh:5561418efc9744c9873855a146226608778e29b4c0c3b3872634ef2da2d86593", - "zh:7ebcb4c6ca71c87850df67d4e5f79ce4a036d4131b8c11ae0b9b8787353843b8", - "zh:81a9259cb1e45507e9431794fbd354dd4d8b78c6a9508b0bfa108b00e6ad23cb", - "zh:8c1836fa186272347f97c7a3884556979618d1b93721e8a24203d90ff4efbd40", - "zh:a72bdd43a11a383525764720d24cb78ec5d9f1167f129d05448108fef1ba7af3", - "zh:ade9d17c6b8717e7b04af5a9d1a948d047ac4dcf6affb2485afa3ad0a2eaee15", - "zh:b3c5bfcab98251cb0c157dbe78dc6d0864c9bf364d316003c84c1e624a3c3524", - "zh:c33b872a2473a9b052add89e4557d361b0ebaa42865e99b95465050d2c858d43", - "zh:efe425f8ecd4d79448214c93ef10881b3b74cf2d9b5211d76f05aced22621eb4", - "zh:ff704c5e73e832507367d9d962b6b53c0ca3c724689f0974feffd5339c3db18a", + "h1:yckm1jqUMUGSeS57a3uR0gG/V7scwvjpkRVXnQIUAo4=", + "zh:062be5d8272cac297a88c2057449f449ea6906c4121ba3dfdeb5cecb3ff91178", + "zh:1fd9abec3ffcbf8d0244408334e9bfc8f49ada50978cd73ee0ed5f8560987267", + "zh:48e84b0302af99d7e7f4248a724088fb1c34aeee78c9ca63ec5a9464ec5054a0", + "zh:4e7302883fd9dd83bfbbcd72ebd55f83d8b16ccc6d12d1573d578058e604d5cf", + "zh:5b6e181e32cbf62f5d2ce34f9d6d9ffe17192e24943450bbe335e1baf0494e66", + "zh:62d525d426c6d5f10109ab04a9abc231b204ea413238f5690f69b420a8b8583a", + "zh:90aab23497ec9c7af44ad9ea1a1d6063dc3331334915e1c549527a73c2c6948d", + "zh:91ecf30a01df5e832191e0c55c87f8403a1f584796fd70f9c9c913d35c2e2a37", + "zh:bc3a5db5e4b9695a69dff47cf1e7184eaf5564d3dc50f231cbcbf535dd140d19", + "zh:cb566bec2676511bf4722e24d0dfc9bf58aff78af38b8e0864970f20d263118f", + "zh:d4fa0c1462b389cee313e1c152e00f5dfc175a1be3615d3b23b526a8581e39a5", + "zh:f8136b0f41045a1e5a6dedc6b6fb055faee3d825f84a3192312e3ac5d057ff72", ] } provider "registry.terraform.io/hashicorp/azurerm" { - version = "4.7.0" - constraints = "4.7.0" + version = "4.17.0" + constraints = "4.17.0" hashes = [ - "h1:gCTgXiVJIPfPmeMtWHEkzcuzmJzBBjCo5FF0tIShiPc=", - "zh:220994020593f3bc3e8d73bfaa98d7f0abccea2f51f76029bb9f6dc2a5bb3436", - "zh:2cdfb75f3b375410da63deb17b7dadec1e3ddfac7e2f5168624a4fc51dd3e177", - "zh:37ee931a839228c4a74fa96cd053b8e0c3de4637cc85fba963fccaf3d67120a4", - "zh:4b12d06e4b782af96df2cfd3445c330c8553f5ac9cf73b2ab045004cf02edbe5", - "zh:7c80ee772836ddf66087a688d50ce404eeaa4a6e72a5f8f5f3f3232068a0c0ca", - "zh:826acb1bf9b335ca6992796bfd3898b15d0f99a888f02d9c97b7771c31765456", - "zh:83dea535e067547215595141ffd954aab36dd67f646a53a12dc811557fb38acd", - "zh:8d5d08e4367f679b877eec33a9e0062d66de0aedd557412ab88b696bdf08c7ef", - "zh:c2c4efbc861248d89620db3e36370ce3053d886930db38d87c6e24a73ca5017e", - "zh:d7e568f956c6e2a764b0d8f465e5c971cc6edab58bb8232f63ea423c459ffc85", + "h1:gpFgaBSkRTxhavgPAuqQcElHJqmRJ1RpQGr1K0dvVW8=", + "zh:163b81a3bf29c8f161a1c100a48164b1bd1af434cd564b44596cb71a6c33f03d", + "zh:2996b107d3c05a9db14458b32b6f22f8cde0adb96263196d82d3dc302907a257", + "zh:361abd84b6e73016ebebb9ef9cd14c237d8b1e4500ea75f73243ff0534e5e4fb", + "zh:4872445dcb109fe8bbaba439d3dffaaef849a92645df3f8a854d3a40ac962f68", + "zh:61974eb7379acadbceb47b001ae1de2cdefe8cf078a15fff3a6fcc753cd24273", + "zh:75c60ca6e7851fe1d52fe9f5a0ae3d219e300ee5aa63bc8f807e3e0cab569ff0", + "zh:7c79305cff7849e6c5d9d60fe570510f95fb2e2bd5ae801da0281702f21dd779", + "zh:964b7da03f2dc55583cda3c277fef3511824b183a3a88344ae4ff9823af79109", + "zh:cad1593d364eb22b68578a1da4fd4d84749dc81f20e6591b27c6cb1eed9d2072", + "zh:db1a2ca17aae78813e8e0676bb9ef941e1a1e32d9fc6e1b239c24661605a8425", + "zh:e3a65d2f6f5a63cd1beeeb60a23e7e6b7328ebbd46ffe994792aaac6738186c3", "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - "zh:f866502c6a996e5c2cf42b262a5e5c35e13f3bcb40f364f90c9f4e8e64db733d", ] } -provider "registry.terraform.io/hashicorp/kubernetes" { - version = "2.35.1" +provider "registry.terraform.io/hashicorp/helm" { + version = "2.17.0" + constraints = "2.17.0" hashes = [ - "h1:zgXeWvp4//Ry+4glwNrLMpPFOU8QBQlARNmR9WCNe9o=", - "zh:12212ca5ae47823ce14bfafb909eeb6861faf1e2435fb2fc4a8b334b3544b5f5", - "zh:3f49b3d77182df06b225ab266667de69681c2e75d296867eb2cf06a8f8db768c", - "zh:40832494d19f8a2b3cd0c18b80294d0b23ef6b82f6f6897b5fe00248a9997460", - "zh:739a5ddea61a77925ee7006a29c8717377a2e9d0a79a0bbd98738d92eec12c0d", - "zh:a02b472021753627c5c39447a56d125a32214c29ff9108fc499f2dcdf4f1cc4f", - "zh:b78865b3867065aa266d6758c9601a2756741478f5735a838c20d633d65e085b", - "zh:d362e87464683f5632790e66920ea803adb54c2bc0cb24b6fd9a314d2b1efffd", - "zh:d98206fe88c2c9a52b8d2d0cb2c877c812a4a51d19f9d8428e63cbd5fd8a304d", - "zh:dfa320946b1ce3f3615c42b3447a28dc9f604c06d8b9a6fe289855ab2ade4d11", + "h1:K5FEjxvDnxb1JF1kG1xr8J3pNGxoaR3Z0IBG9Csm/Is=", + "zh:06fb4e9932f0afc1904d2279e6e99353c2ddac0d765305ce90519af410706bd4", + "zh:104eccfc781fc868da3c7fec4385ad14ed183eb985c96331a1a937ac79c2d1a7", + "zh:129345c82359837bb3f0070ce4891ec232697052f7d5ccf61d43d818912cf5f3", + "zh:3956187ec239f4045975b35e8c30741f701aa494c386aaa04ebabffe7749f81c", + "zh:66a9686d92a6b3ec43de3ca3fde60ef3d89fb76259ed3313ca4eb9bb8c13b7dd", + "zh:88644260090aa621e7e8083585c468c8dd5e09a3c01a432fb05da5c4623af940", + "zh:a248f650d174a883b32c5b94f9e725f4057e623b00f171936dcdcc840fad0b3e", + "zh:aa498c1f1ab93be5c8fbf6d48af51dc6ef0f10b2ea88d67bcb9f02d1d80d3930", + "zh:bf01e0f2ec2468c53596e027d376532a2d30feb72b0b5b810334d043109ae32f", + "zh:c46fa84cc8388e5ca87eb575a534ebcf68819c5a5724142998b487cb11246654", + "zh:d0c0f15ffc115c0965cbfe5c81f18c2e114113e7a1e6829f6bfd879ce5744fbb", "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - "zh:fc1debd2e695b5222d2ccc8b24dab65baba4ee2418ecce944e64d42e79474cb5", - "zh:fdaf960443720a238c09e519aeb30faf74f027ac5d1e0a309c3b326888e031d7", ] } @@ -64,7 +65,7 @@ provider "registry.terraform.io/hashicorp/random" { version = "3.6.3" constraints = "3.6.3" hashes = [ - "h1:zG9uFP8l9u+yGZZvi5Te7PV62j50azpgwPunq2vTm1E=", + "h1:Fnaec9vA8sZ8BXVlN3Xn9Jz3zghSETIKg7ch8oXhxno=", "zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda9785396807c00451", "zh:448f56199f3e99ff75d5c0afacae867ee795e4dfda6cb5f8e3b2a72ec3583dd8", "zh:4b4c11ccfba7319e901df2dac836b1ae8f12185e37249e8d870ee10bb87a13fe", @@ -84,7 +85,7 @@ provider "registry.terraform.io/hashicorp/time" { version = "0.12.1" constraints = "0.12.1" hashes = [ - "h1:JzYsPugN8Fb7C4NlfLoFu7BBPuRVT2/fCOdCaxshveI=", + "h1:6BhxSYBJdBBKyuqatOGkuPKVenfx6UmLdiI13Pb3his=", "zh:090023137df8effe8804e81c65f636dadf8f9d35b79c3afff282d39367ba44b2", "zh:26f1e458358ba55f6558613f1427dcfa6ae2be5119b722d0b3adb27cd001efea", "zh:272ccc73a03384b72b964918c7afeb22c2e6be22460d92b150aaf28f29a7d511", diff --git a/deployment/CHANGELOG.md b/deployment/CHANGELOG.md new file mode 100644 index 00000000..0fad609f --- /dev/null +++ b/deployment/CHANGELOG.md @@ -0,0 +1,46 @@ +# CHANGELOG.md + +## 2025-03-07 + +- Adds tailscale configuration for ingress +- Removes nginx-ingress component +- Removes cert-manager / letsencrypt component +- Removes cluster issuer component +- Updates to env variables +- Updates to README.md + +## 2025-03-01 + +- Modified variable names +- Uppdated example deployment to use echo server from GHCR authenticated registry +- Implmented `Makefile` to drive deployment and teardown of architecture +- DNS records created during deploy (up) are removed during destroy (down) +- Updates to README.md + +## 2025-02-20 + +- Moved kuberenetes resources out of terraform HCL due to inconsistencies and reliability concerns +- Terraform now only creates the initial AKS infrastructure +- Introduced kustomize manifests to deploy kubernetes resources into the AKS cluster +- Iintroduced wrapper script, crs-architecture, to manage the proper deployment of all environments and variables within +- Leveraging generic template files for kustomize usage of environment variables to simplify end user burden + +## 2025-01-19 + +- Updates to outputs.tf +- Updates to variables.tf +- Changes hardcoded credentials environment variables +- Updates to README.md + +## 2025-01-15 + +- Adds example CRS webservice docker image via deployment.tf +- Adds private load balancer for CRS webservice via deployment.tf +- Adds docker registry auth via secrets.tf +- Updates to providers.tf to supprt `kubernetes` provider +- Updates to README.md + +## 2024-12-03 + +- Initial commit of example-crs-architecture +- Added linter rules to root of `example-crs-architecture` repository diff --git a/deployment/Makefile b/deployment/Makefile new file mode 100644 index 00000000..d9004545 --- /dev/null +++ b/deployment/Makefile @@ -0,0 +1,39 @@ +# Makefile + +# Variables +SCRIPT := crs-architecture.sh +GENERATED_FILES := k8s/base/tailscale-coredns/coredns-custom.yaml \ + k8s/base/tailscale-operator/operator.yaml \ + k8s/values-aks.yaml + +# Default target when just running `make` +.PHONY: help +help: + @echo "Usage:" + @echo " make up # Deploys the AKS cluster and resources" + @echo " make down # Destroys the AKS cluster and resources" + @echo " make clean # Removes generated files from templates (optional)" + +#check script exists and is executable +.PHONY: check +check: + @test -f $(SCRIPT) || (echo "Error: $(SCRIPT) not found" && exit 1) + @test -x $(SCRIPT) || (echo "Error: $(SCRIPT) not executable" && exit 1) + +# up target +.PHONY: up +up: check clean + @bash $(SCRIPT) up + +# down target +.PHONY: down +down: check + @bash $(SCRIPT) down + +down-k8s: + @bash $(SCRIPT) down-k8s + +# Clean up generated files (optional) +.PHONY: clean +clean: + @rm -f $(GENERATED_FILES) diff --git a/deployment/README.md b/deployment/README.md index 68a84bd4..a278f65f 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -1,12 +1,29 @@ -# Deployment of the Trail of Bits AIxCC Finals CRS +# Example AKS Cluster -## Pre-requisites +## Overview + +The following is an example on how to deploy an Azure Kubernetes Service cluster within an Azure subscription. + +This configuration deploys an AKS cluster (`primary`) with two node pools: + +- The default node pool (`sys`) - contains 2 system mode nodes +- A User node pool (`usr`) - contains 3 user mode nodes + +The resource group name is generated with the `"random_pet"` resource from the `hashicorp/random` provider; and are prefixed with `example` + +The VM Size for both pools are using `Standard_D5_v2` in this example. You can change this to suit your needs by editing the `vm_size` values in `main.tf`. + +The standard `azure` networking profile is used. + +## Prerequisites - Azure CLI installed: [az cli install instructions](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) - Terraform installed: [terraform install instructions](https://developer.hashicorp.com/terraform/tutorials/azure-get-started/install-cli) - Kubernetes CLI installed: [kubectl install instructions](https://kubernetes.io/docs/tasks/tools/#kubectl) +- `gettext` package - An active Azure subscription. - An account in Azure Entra ID. +- Access credentials to the competition Tailscale tailnet. ### Azure @@ -30,14 +47,15 @@ SubscriptionID Tenant A service principal account (SPA) is required to automate the creation of resources and objects within your subscription. -You can create a SPA several ways, the following describes using azure cli. +- You can create a SPA several ways, the following describes using Azure cli. ```bash az ad sp create-for-rbac --name "ExampleSPA" --role Contributor --scopes /subscriptions/ ``` -> Replace "ExampleSPA" with the name of the SPA you wish to create. Replace `` with your azure subscription ID. -> If using resource group locks, additional configuration may be neccessary which is out of scope of this example; e.g. adding the role `Microsoft.Authorization/locks/` for write, read and delete to the SPA. +> Replace "ExampleSPA" with the name of the SPA you wish to create. +> Replace `` with your Azure subscription ID. +> If using resource group locks, additional configuration may be necessary which is out of scope of this example; e.g. adding the role `Microsoft.Authorization/locks/` for write, read and delete to the SPA. On successful SPA creation, you will receive output similar to the following: @@ -50,39 +68,84 @@ On successful SPA creation, you will receive output similar to the following: } ``` -Make note of these values, they will be used in the AKS deployment as the following environment variables: +Make note of these values, they will be needed in the AKS deployment as the following environment variables: ```bash -ARM_TENANT_ID="" -ARM_CLIENT_ID="" -ARM_CLIENT_SECRET="" -ARM_SUBSCRIPTION_ID="" +TF_ARM_TENANT_ID="" +TF_ARM_CLIENT_ID="" +TF_ARM_CLIENT_SECRET="" +TF_ARM_SUBSCRIPTION_ID="" ``` -You can export these as environment variables from the host you're deploying from. +### Environment Variables + +The following environment variables are required to be passed into the terraform and kubernetes configurations: + +| Variable Name | Description | +| ---------------------------- | ----------------------------------------------------------------------------- | +| `TF_VAR_ARM_SUBSCRIPTION_ID` | Azure subscription ID | +| `TF_VAR_ARM_TENANT_ID` | Azure tenant ID | +| `TF_VAR_ARM_CLIENT_ID` | Azure client ID (service principal account) | +| `TF_VAR_ARM_CLIENT_SECRET` | Azure client ID secret | +| `CRS_API_HOSTNAME` | The hostname you want to assign to your API. Exmaple: `teamX-api` | +| `TS_CLIENT_ID` | Tailscale oauth client ID (provided by the Organizers) | +| `TS_CLIENT_SECRET` | Tailscale oauth client secret (provided by the Organizers) | +| `TS_OP_TAG` | Tailscale operator tag (provided by the Organizers) | +| `COMPETITION_API_KEY_ID` | HTTP basic auth username for the competition API (provided by the Organizers) | +| `COMPETITION_API_KEY_TOKEN` | HTTP basic auth password for the competition API (provided by the Organizers) | +| `CRS_KEY_ID` | HTTP basic auth username for the CRS API | +| `CRS_KEY_TOKEN` | HTTP basic auth password for the CRS API | +| `GHCR_AUTH` | Base64 encoded credentials for GHCR | + +**These variables are stored in `./env` , and must be updated with accurate values.** + +### CRS HTTP basic auth + +_WIP (Current example is using the `jmalloc/echo-server` image as a PoC)_ +The crs-webapp image expects the following environment variables to be passed to it for HTTP basic authentication: + +- `CRS_KEY_ID` - The CRS's username/ID +- `CRS_KEY_TOKEN` - The CRS's password +- `COMPETITION_API_KEY_ID` - The competition APIs username/ID +- `COMPETITION_API_KEY_TOKEN` - The competition APIs password + +These values can be generated with the following python calls: ```bash -export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000" -export ARM_CLIENT_SECRET="12345678-0000-0000-0000-000000000000" -export ARM_TENANT_ID="10000000-0000-0000-0000-000000000000" -export ARM_SUBSCRIPTION_ID="20000000-0000-0000-0000-000000000000" +key_id: +python3 -c 'import uuid; print(str(uuid.uuid4()))' + +key_token: +python3 -c 'import secrets, string; print("".join(secrets.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(32)))' ``` +### GitHub personal access token + +_WIP (Current example is using the `jmalloc/echo-server` image as a PoC)_ +You will need to have a GitHub personal access token (PAT) scoped to at least `read:packages`. + +To create the PAT, go to your account, `Settings` > `Developer settings` > `Personal access tokens`, and generate a Token (classic) with the scopes needed for your use case. + +For this example, the `read:packages` scope is required. + +Once you have your PAT, you will need to base64 encode it for use within `secrets.tf`: + +```bash +echo -n "ghcr_username:ghcr_token" | base64 +``` + +> replace `ghcr_username` and `ghcr_token` with your GitHub username and your PAT respectively. + +Add your base64 encoded credentials to `GHCR_AUTH` + ## Remote Terraform State Storage -By default the terraform state for the CRS is saved on Azure in the following resource: -``` - resource_group_name = "tfstate-rg" - storage_account_name = "tfstateserviceact" - container_name = "tfstate" - key = "terraform.tfstate" -``` - -If you want to change the resources used to save the Terraform state, you may need to create other resources. +By default, terraform stores its state locally. It is best practice to store terraform state in a remote location. +This can help with collaboration, security, recovery and scalability. To do this within Azure, you need to create resources to do so. ### Azure CLI -The following is an example of how to create the resources needed for remote state configuration. +The following is an example of how to create the resources needed for remote state configuration. These resources will be used in the `backend.tf` configuration file. - Create remote state resource group. @@ -94,13 +157,13 @@ az group create --name example-tfstate-rg --location eastus - Create storage account for remote state. ```bash -az storage account create --resource-group example-tfstate-rg --name exampleserviceaccountname --sku Standard_LRS --encryption-services blob +az storage account create --resource-group example-tfstate-rg --name examplestorageaccountname --sku Standard_LRS --encryption-services blob ``` - Create storage container for remote state ```bash -az storage container create --name tfstate --account-name exampleserviceaccountname --auth-mode login +az storage container create --name tfstate --account-name examplestorageaccountname --auth-mode login ``` ### backend.tf @@ -111,25 +174,39 @@ Replace the values for `resource_group_name`, `storage_account_name`, `container terraform { backend "azurerm" { resource_group_name = "example-tfstate-rg" - storage_account_name = "exampleserviceaccountname" + storage_account_name = "examplestorageaccountname" container_name = "tfstate" key = "terraform.tfstate" } } ``` +## Makefile + +The deployment of the AKS cluster and its resources are performed by the `Makefile`, which leverages `crs-architecture.sh`. This wrapper utilizes several mechanisms to properly configure both the teraform and kubernetes environments. + ## Deploy - Log into your Azure tenant with `az login --tenant aixcc.tech` -- Export the environment variables for your [SPA Configuration](#service-principal-account) if needed. -- Initialize terraform: `terraform init` -- Run plan: `terraform plan` - review output -- Deploy: `terraform apply` - - type `yes` when prompted to apply +- Clone this repository if needed: `git clone git@github.com:aixcc-finals/example-crs-architecture.git /` +- Make required changes to `backend.tf` +- Make any wanted changes to `main.tf`, `outputs.tf`, `providers.tf`, and `variables.tf` +- Update `./env` with accurate values for each variable +- Run `make up` from within the `example-crs-architecture` directory + This will execute a deployment of your cluster via a combination of terraform and kubectl based on your unique values in `./env` -A handful of outputs will be provided based on `outputs.tf` when the apply completes. +## Useful Cluster Commands -You can see the outputs values with `terraform output` or `terraform output `. +- `az aks get-credentials --name --resource-group ` - retrieves access credentials and updates kubeconfig for the current cluster +- `kubectl get namespaces` - lists all namespaces +- `kubectl get -n crs-webservice all` - lists all resources within the crs-webservice namespace +- `kubectl get -n tailscale all` - lists all resources within the tailscale namespace +- `kubectl get pods -A` - lists all pods in all namespaces +- `kubectl config get-contexts` - lists the current contexts in your kubeconfig +- `kubectl describe deployment -n crs-webservice crs-webapp` - print detailed information about the deployment, crs-webapp +- `kubectl logs ` - retrieves the stdout and stderr streams from the containers within the specified pod +- `kubectl get svc -A` - lists status of all services +- `kubectl get -n crs-webservice ingress` - lists the tailscale ingress address of your API ## State @@ -138,61 +215,16 @@ You can see the outputs values with `terraform output` or `terraform output ! kube.config -export KUBECONFIG=$(pwd)/kube.config -``` +To reset or redeploy a running CRS cluster: -```shell -kubectl get namespaces -``` +- Run `make down && make up` from within the `example-crs-architecture` directory -## Access kubernetes cluster nodes / services -```shell -kubectl port-forward service/orchestrator -n crs 18000:8000 -``` +## Clean -Then access `127.0.0.1:18000`. - - -## Troubleshooting - -### Error acquiring the state lock -``` -Error: Error acquiring the state lock -│ -│ Error message: state blob is already locked -│ Lock Info: -│ ID: 7a304f3a-6b83-34a1-6773-b70ec456e6cc -│ Path: tfstate/terraform.tfstate -│ Operation: OperationTypeApply -│ Who: ret2libc@macbookpro.lan -│ Version: 1.10.4 -│ Created: 2025-01-14 12:56:49.352199 +0000 UTC -│ Info: -│ -│ -│ Terraform acquires a state lock to protect the state from being written -│ by multiple users at the same time. Please resolve the issue above and try -│ again. For most commands, you can disable locking with the "-lock=false" -│ flag, but this is not recommended. -╵ -``` - -First, ensure that someone else is not really running some `terraform` command -concurrently. If that's not the case, the state file might have not been -unlocked (e.g. you CTRL-C terraform at some point and it did not unlocked the -file before exiting), you can try adding the `-lock=false` as specified, or, if -that does not work, go to the [Azure portal](https://portal.azure.com), open the -`tfstate-rg` Resource Group, `tfstateserviceact`, then in Data Storage > -Containers select `tfstate`. Select the `terraform.tfstate` blob and click -`Break lease. +Optionally, you can run `make clean` to remove any generated files create from the included templates at runtime. This action is executed during `make up`. diff --git a/deployment/crs-architecture.sh b/deployment/crs-architecture.sh new file mode 100755 index 00000000..f42bfcc6 --- /dev/null +++ b/deployment/crs-architecture.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +set -e + +##set ANSI escaape codes +NC='\033[0m' +RED='\033[1;31m' +GRN='\033[1;32m' +BLU='\033[1;36m' + +#executes a series of terraform, az cli, and kubernetes commands to deploy or destroy an example crs architecture + +echo -e "${BLU}Applying environment variables from ./env${NC}" +# shellcheck disable=SC1094 +source ./env +echo -e "${GRN}Current azure account status:${NC}" +az account show --query "{SubscriptionID:id, Tenant:tenantId}" --output table + +#deploy the AKS cluster and kubernetes resources function +up() { + + echo -e "${BLU}Applying environment variables to yaml from templates${NC}" + CLIENT_BASE64=$(echo -n "$TF_VAR_ARM_CLIENT_SECRET" | base64) + CRS_KEY_BASE64=$(echo -n "$CRS_KEY_TOKEN" | base64) + COMPETITION_API_KEY_BASE64=$(echo -n "$COMPETITION_API_KEY_TOKEN" | base64) + export CLIENT_BASE64 + export CRS_KEY_BASE64 + export COMPETITION_API_KEY_BASE64 + export TS_DNS_IP + envsubst k8s/base/tailscale-operator/operator.yaml + + #deploy AKS resources in Azure + echo -e "${BLU}Deploying AKS cluster Resources${NC}" + terraform init + terraform apply -auto-approve + + #set resource group name and kubernetes cluster name variables from terraform outputs + + KUBERNETES_CLUSTER_NAME=$(terraform output -raw kubernetes_cluster_name) + RESOURCE_GROUP_NAME=$(terraform output -raw resource_group_name) + + echo -e "${GRN}KUBERNETES_CLUSTER_NAME is $KUBERNETES_CLUSTER_NAME" + echo "RESOURCE_GROUP_NAME is $RESOURCE_GROUP_NAME${NC}" + echo -e "${BLU}Retrieving credentials to access AKS cluster${NC}" + #retrieve credentials to access AKS cluster + + az aks get-credentials --resource-group "$RESOURCE_GROUP_NAME" --name "$KUBERNETES_CLUSTER_NAME" + + # Create namespace if it doesn't exist + kubectl create namespace crs || true + + # Set secrets + GHCR_PAT=$(echo -n "$GHCR_AUTH" | base64 -d | cut -d: -f2) + GHCR_USERNAME=$(echo -n "$GHCR_AUTH" | base64 -d | cut -d: -f1) + echo -e "${BLU}Creating ghcr secret${NC}" + kubectl delete secret ghcr --namespace crs || true + kubectl create secret generic ghcr \ + --namespace crs \ + --from-literal=pat="$GHCR_PAT" \ + --from-literal=username="$GHCR_USERNAME" \ + --from-literal=scantron_github_pat="$SCANTRON_GITHUB_PAT" || echo -e "${GRN}ghcr secret already exists${NC}" + + #deploy kubernetes resources in AKS cluster + kubectl apply -k k8s/base/tailscale-operator/ + kubectl apply -k k8s/base/tailscale-dns/ + + echo -e "${BLU}Waiting for the service nameserver to exist${NC}" + timeout 5m bash -c "until kubectl get svc -n tailscale nameserver > /dev/null 2>&1; do sleep 1; done" || echo -e "${RED}Error: nameserver failed to exist within 5 minutes${NC}" + echo -e "${BLU}Waiting for nameserver to have a valid ClusterIP${NC}" + timeout 5m bash -c "until kubectl get svc -n tailscale nameserver -o jsonpath='{.spec.clusterIP}' | grep -v '' > /dev/null 2>&1; do sleep 1; done" || echo -e "${RED}Error: nameserver failed to obtain a valid CLusterIP within 5 minutes${NC}" + TS_DNS_IP=$(kubectl get svc -n tailscale nameserver -o jsonpath='{.spec.clusterIP}') + envsubst k8s/base/tailscale-coredns/coredns-custom.yaml + + kubectl apply -k k8s/base/tailscale-coredns/ + # Install buttercup CRS + # TODO: allow for minikube environment as well + echo -e "${BLU}Installing buttercup CRS${NC}" + umask 077 # Ensure new files are created with permissions only for current user + envsubst k8s/values-aks.yaml + helm install buttercup --namespace crs ./k8s -f ./k8s/values-aks.yaml || helm upgrade buttercup --namespace crs ./k8s -f ./k8s/values-aks.yaml + umask 022 # Reset umask to default value + + kubectl apply -k k8s/base/tailscale-connections/ + + echo -e "${BLU}Waiting for ingress hostname DNS registration${NC}" + timeout 5m bash -c "until kubectl get ingress -n crs buttercup-task-server -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' | grep -q '.'; do sleep 1; done" || echo -e "${BLU}Error: Ingress hostname failed to be to set within 5 minutes${NC}" + INGRESS_HOSTNAME=$(kubectl get ingress -n crs buttercup-task-server -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') + echo -e "${GRN}Your ingress DNS hostname is $INGRESS_HOSTNAME${NC}" + +} + +#destroy the AKS cluster and kubernetes resources function +down() { + down-k8s + echo -e "${BLU}Destroying AKS cluster${NC}" + terraform apply -destroy -auto-approve +} + +# destroys the kubernetes resources only +down-k8s() { + echo -e "${BLU}Deleting Kubernetes resource${NC}" + set +e + kubectl delete -k k8s/base/tailscale-connections/ + helm uninstall --wait --namespace crs buttercup + kubectl delete -k k8s/base/tailscale-coredns/ + kubectl delete -k k8s/base/tailscale-dns/ + kubectl delete -k k8s/base/tailscale-operator/ + kubectl delete secret ghcr --namespace crs + kubectl delete namespace crs + set -e +} + +case $1 in +up) + up + ;; +down) + down + ;; +down-k8s) + down-k8s + ;; +*) + echo -e "${RED}The only acceptable arguments are up and down${NC}" + ;; +esac diff --git a/deployment/env.template b/deployment/env.template new file mode 100644 index 00000000..2967228b --- /dev/null +++ b/deployment/env.template @@ -0,0 +1,23 @@ +export TF_VAR_ARM_CLIENT_ID="" +export TF_VAR_ARM_CLIENT_SECRET="" +export TF_VAR_ARM_TENANT_ID="" +export TF_VAR_ARM_SUBSCRIPTION_ID="" +export CRS_API_HOSTNAME="" +export TS_CLIENT_ID="" +export TS_CLIENT_SECRET="" +export TS_OP_TAG=" +export COMPETITION_API_KEY_ID="" +export COMPETITION_API_KEY_TOKEN="" +export COMPETITION_API_URL="" +export CRS_KEY_ID="" +export CRS_KEY_TOKEN="" +export CRS_KEY_TOKEN_HASH='' +export CRS_URL="" +export GHCR_AUTH="" +export AZURE_API_BASE="" +export AZURE_API_KEY="" +export OPENAI_API_KEY="" +export ANTHROPIC_API_KEY="" +export LANGFUSE_HOST="" +export LANGFUSE_PUBLIC_KEY="" +export LANGFUSE_SECRET_KEY="" diff --git a/deployment/k8s/README.md b/deployment/k8s/README.md index e1b47470..49b07cd9 100644 --- a/deployment/k8s/README.md +++ b/deployment/k8s/README.md @@ -54,7 +54,7 @@ This is controlled via the `global.environment` setting in the values.yaml or va api_key_id: 515cc8a0-3019-4c9f-8c1c-72d0b54ae561 api_key_token: VGuAC8axfOnFXKBB7irpNDOKcDjOlnyB api_key_token_hash: "$argon2id$v=19$m=65536,t=3,p=4$Dg1v6NPGTyXPoOPF4ozD5A$wa/85ttk17bBsIASSwdR/uGz5UKN/bZuu4wu+JIy1iA" - # api_url: "https://api.tail7e9b4c.ts.net" + # api_url: "https://ethereal-logic.tail7e9b4c.ts.net" competition_api_key_id: 11111111-1111-1111-1111-111111111111 competition_api_key_token: secret # competition_api_url: "https://api.tail7e9b4c.ts.net" @@ -101,8 +101,8 @@ For pulling private container images, create a Kubernetes secret named `ghcr-aut ```bash kubectl create secret generic ghcr \ --from-literal=pat=YOUR_GITHUB_PAT \ - --from-literal=username=YOUR_GITHUB_USERNAME \ - --from-literal=email=YOUR_EMAIL + --from-literal=username=USERNAME \ + --from-literal=scantron_github_pat=GITHUB_PAT_WITH_REPO_AND_GHCR_PERMISSIONS ``` ## System Architecture diff --git a/deployment/k8s/base/tailscale-connections/kustomization.yaml b/deployment/k8s/base/tailscale-connections/kustomization.yaml new file mode 100644 index 00000000..556f22c4 --- /dev/null +++ b/deployment/k8s/base/tailscale-connections/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - proxies.yaml diff --git a/deployment/k8s/base/tailscale-connections/proxies.yaml b/deployment/k8s/base/tailscale-connections/proxies.yaml new file mode 100644 index 00000000..59e8eef2 --- /dev/null +++ b/deployment/k8s/base/tailscale-connections/proxies.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: competition-echo-egress + namespace: tailscale + annotations: + tailscale.com/tailnet-fqdn: "echo.tail7e9b4c.ts.net" #Basic http echo test server +spec: + externalName: placeholder + type: ExternalName +--- +apiVersion: v1 +kind: Service +metadata: + name: competition-api-1-egress + namespace: tailscale + annotations: + tailscale.com/tailnet-fqdn: "api.tail7e9b4c.ts.net" #Competition API FQDN +spec: + externalName: placeholder + type: ExternalName diff --git a/deployment/k8s/base/tailscale-coredns/coredns-custom.template b/deployment/k8s/base/tailscale-coredns/coredns-custom.template new file mode 100644 index 00000000..1dc790c3 --- /dev/null +++ b/deployment/k8s/base/tailscale-coredns/coredns-custom.template @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: coredns-custom + namespace: kube-system +data: + tailscale.server: | + tail7e9b4c.ts.net:53 { + forward . ${TS_DNS_IP} + cache 30 + } diff --git a/deployment/k8s/base/tailscale-coredns/kustomization.yaml b/deployment/k8s/base/tailscale-coredns/kustomization.yaml new file mode 100644 index 00000000..ce2ba2d8 --- /dev/null +++ b/deployment/k8s/base/tailscale-coredns/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - coredns-custom.yaml diff --git a/deployment/k8s/base/tailscale-dns/dnsconfig.yaml b/deployment/k8s/base/tailscale-dns/dnsconfig.yaml new file mode 100644 index 00000000..2d3af486 --- /dev/null +++ b/deployment/k8s/base/tailscale-dns/dnsconfig.yaml @@ -0,0 +1,10 @@ +apiVersion: tailscale.com/v1alpha1 +kind: DNSConfig +metadata: + namespace: tailscale + name: tailscale-dns +spec: + nameserver: + image: + repo: tailscale/k8s-nameserver + tag: unstable diff --git a/deployment/k8s/base/tailscale-dns/kustomization.yaml b/deployment/k8s/base/tailscale-dns/kustomization.yaml new file mode 100644 index 00000000..4d1f0563 --- /dev/null +++ b/deployment/k8s/base/tailscale-dns/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - dnsconfig.yaml diff --git a/deployment/k8s/base/tailscale-operator/kustomization.yaml b/deployment/k8s/base/tailscale-operator/kustomization.yaml new file mode 100644 index 00000000..3127a9d1 --- /dev/null +++ b/deployment/k8s/base/tailscale-operator/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - operator.yaml + #- dnsconfig.yaml + #- coredns-custom.yaml diff --git a/deployment/k8s/base/tailscale-operator/operator.template b/deployment/k8s/base/tailscale-operator/operator.template new file mode 100644 index 00000000..1a0df659 --- /dev/null +++ b/deployment/k8s/base/tailscale-operator/operator.template @@ -0,0 +1,5040 @@ +# Copyright (c) Tailscale Inc & AUTHORS +# SPDX-License-Identifier: BSD-3-Clause + +apiVersion: v1 +kind: Namespace +metadata: + name: tailscale +--- +apiVersion: v1 +kind: Secret +metadata: + name: operator-oauth + namespace: tailscale +stringData: + client_id: "${TS_CLIENT_ID}" + client_secret: "${TS_CLIENT_SECRET}" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: operator + namespace: tailscale +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: proxies + namespace: tailscale +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: connectors.tailscale.com +spec: + group: tailscale.com + names: + kind: Connector + listKind: ConnectorList + plural: connectors + shortNames: + - cn + singular: connector + scope: Cluster + versions: + - additionalPrinterColumns: + - description: CIDR ranges exposed to tailnet by a subnet router defined via this Connector instance. + jsonPath: .status.subnetRoutes + name: SubnetRoutes + type: string + - description: Whether this Connector instance defines an exit node. + jsonPath: .status.isExitNode + name: IsExitNode + type: string + - description: Whether this Connector instance is an app connector. + jsonPath: .status.isAppConnector + name: IsAppConnector + type: string + - description: Status of the deployed Connector resources. + jsonPath: .status.conditions[?(@.type == "ConnectorReady")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Connector defines a Tailscale node that will be deployed in the cluster. The + node can be configured to act as a Tailscale subnet router and/or a Tailscale + exit node. + Connector is a cluster-scoped resource. + More info: + https://tailscale.com/kb/1441/kubernetes-operator-connector + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ConnectorSpec describes the desired Tailscale component. + More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + appConnector: + description: |- + AppConnector defines whether the Connector device should act as a Tailscale app connector. A Connector that is + configured as an app connector cannot be a subnet router or an exit node. If this field is unset, the + Connector does not act as an app connector. + Note that you will need to manually configure the permissions and the domains for the app connector via the + Admin panel. + Note also that the main tested and supported use case of this config option is to deploy an app connector on + Kubernetes to access SaaS applications available on the public internet. Using the app connector to expose + cluster workloads or other internal workloads to tailnet might work, but this is not a use case that we have + tested or optimised for. + If you are using the app connector to access SaaS applications because you need a predictable egress IP that + can be whitelisted, it is also your responsibility to ensure that cluster traffic from the connector flows + via that predictable IP, for example by enforcing that cluster egress traffic is routed via an egress NAT + device with a static IP address. + https://tailscale.com/kb/1281/app-connectors + properties: + routes: + description: |- + Routes are optional preconfigured routes for the domains routed via the app connector. + If not set, routes for the domains will be discovered dynamically. + If set, the app connector will immediately be able to route traffic using the preconfigured routes, but may + also dynamically discover other routes. + https://tailscale.com/kb/1332/apps-best-practices#preconfiguration + items: + format: cidr + type: string + minItems: 1 + type: array + type: object + exitNode: + description: |- + ExitNode defines whether the Connector device should act as a Tailscale exit node. Defaults to false. + This field is mutually exclusive with the appConnector field. + https://tailscale.com/kb/1103/exit-nodes + type: boolean + hostname: + description: |- + Hostname is the tailnet hostname that should be assigned to the + Connector node. If unset, hostname defaults to -connector. Hostname can contain lower case letters, numbers and + dashes, it must not start or end with a dash and must be between 2 + and 63 characters long. + pattern: ^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$ + type: string + proxyClass: + description: |- + ProxyClass is the name of the ProxyClass custom resource that + contains configuration options that should be applied to the + resources created for this Connector. If unset, the operator will + create resources with the default configuration. + type: string + subnetRouter: + description: |- + SubnetRouter defines subnet routes that the Connector device should + expose to tailnet as a Tailscale subnet router. + https://tailscale.com/kb/1019/subnets/ + If this field is unset, the device does not get configured as a Tailscale subnet router. + This field is mutually exclusive with the appConnector field. + properties: + advertiseRoutes: + description: |- + AdvertiseRoutes refer to CIDRs that the subnet router should make + available. Route values must be strings that represent a valid IPv4 + or IPv6 CIDR range. Values can be Tailscale 4via6 subnet routes. + https://tailscale.com/kb/1201/4via6-subnets/ + items: + format: cidr + type: string + minItems: 1 + type: array + required: + - advertiseRoutes + type: object + tags: + description: |- + Tags that the Tailscale node will be tagged with. + Defaults to [tag:k8s]. + To autoapprove the subnet routes or exit node defined by a Connector, + you can configure Tailscale ACLs to give these tags the necessary + permissions. + See https://tailscale.com/kb/1337/acl-syntax#autoapprovers. + If you specify custom tags here, you must also make the operator an owner of these tags. + See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator. + Tags cannot be changed once a Connector node has been created. + Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$. + items: + pattern: ^tag:[a-zA-Z][a-zA-Z0-9-]*$ + type: string + type: array + type: object + x-kubernetes-validations: + - message: A Connector needs to have at least one of exit node, subnet router or app connector configured. + rule: has(self.subnetRouter) || (has(self.exitNode) && self.exitNode == true) || has(self.appConnector) + - message: The appConnector field is mutually exclusive with exitNode and subnetRouter fields. + rule: "!((has(self.subnetRouter) || (has(self.exitNode) && self.exitNode == true)) && has(self.appConnector))" + status: + description: |- + ConnectorStatus describes the status of the Connector. This is set + and managed by the Tailscale operator. + properties: + conditions: + description: |- + List of status conditions to indicate the status of the Connector. + Known condition types are `ConnectorReady`. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + hostname: + description: |- + Hostname is the fully qualified domain name of the Connector node. + If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the + node. + type: string + isAppConnector: + description: IsAppConnector is set to true if the Connector acts as an app connector. + type: boolean + isExitNode: + description: IsExitNode is set to true if the Connector acts as an exit node. + type: boolean + subnetRoutes: + description: |- + SubnetRoutes are the routes currently exposed to tailnet via this + Connector instance. + type: string + tailnetIPs: + description: |- + TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6) + assigned to the Connector node. + items: + type: string + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: dnsconfigs.tailscale.com +spec: + group: tailscale.com + names: + kind: DNSConfig + listKind: DNSConfigList + plural: dnsconfigs + shortNames: + - dc + singular: dnsconfig + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Service IP address of the nameserver + jsonPath: .status.nameserver.ip + name: NameserverIP + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + DNSConfig can be deployed to cluster to make a subset of Tailscale MagicDNS + names resolvable by cluster workloads. Use this if: A) you need to refer to + tailnet services, exposed to cluster via Tailscale Kubernetes operator egress + proxies by the MagicDNS names of those tailnet services (usually because the + services run over HTTPS) + B) you have exposed a cluster workload to the tailnet using Tailscale Ingress + and you also want to refer to the workload from within the cluster over the + Ingress's MagicDNS name (usually because you have some callback component + that needs to use the same URL as that used by a non-cluster client on + tailnet). + When a DNSConfig is applied to a cluster, Tailscale Kubernetes operator will + deploy a nameserver for ts.net DNS names and automatically populate it with records + for any Tailscale egress or Ingress proxies deployed to that cluster. + Currently you must manually update your cluster DNS configuration to add the + IP address of the deployed nameserver as a ts.net stub nameserver. + Instructions for how to do it: + https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#configuration-of-stub-domain-and-upstream-nameserver-using-coredns (for CoreDNS), + https://cloud.google.com/kubernetes-engine/docs/how-to/kube-dns (for kube-dns). + Tailscale Kubernetes operator will write the address of a Service fronting + the nameserver to dsnconfig.status.nameserver.ip. + DNSConfig is a singleton - you must not create more than one. + NB: if you want cluster workloads to be able to refer to Tailscale Ingress + using its MagicDNS name, you must also annotate the Ingress resource with + tailscale.com/experimental-forward-cluster-traffic-via-ingress annotation to + ensure that the proxy created for the Ingress listens on its Pod IP address. + NB: Clusters where Pods get assigned IPv6 addresses only are currently not supported. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec describes the desired DNS configuration. + More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + nameserver: + description: |- + Configuration for a nameserver that can resolve ts.net DNS names + associated with in-cluster proxies for Tailscale egress Services and + Tailscale Ingresses. The operator will always deploy this nameserver + when a DNSConfig is applied. + properties: + image: + description: Nameserver image. Defaults to tailscale/k8s-nameserver:unstable. + properties: + repo: + description: Repo defaults to tailscale/k8s-nameserver. + type: string + tag: + description: Tag defaults to unstable. + type: string + type: object + type: object + required: + - nameserver + type: object + status: + description: |- + Status describes the status of the DNSConfig. This is set + and managed by the Tailscale operator. + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + nameserver: + description: Nameserver describes the status of nameserver cluster resources. + properties: + ip: + description: |- + IP is the ClusterIP of the Service fronting the deployed ts.net nameserver. + Currently you must manually update your cluster DNS config to add + this address as a stub nameserver for ts.net for cluster workloads to be + able to resolve MagicDNS names associated with egress or Ingress + proxies. + The IP address will change if you delete and recreate the DNSConfig. + type: string + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: proxyclasses.tailscale.com +spec: + group: tailscale.com + names: + kind: ProxyClass + listKind: ProxyClassList + plural: proxyclasses + singular: proxyclass + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Status of the ProxyClass. + jsonPath: .status.conditions[?(@.type == "ProxyClassReady")].reason + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + ProxyClass describes a set of configuration parameters that can be applied to + proxy resources created by the Tailscale Kubernetes operator. + To apply a given ProxyClass to resources created for a tailscale Ingress or + Service, use tailscale.com/proxy-class= label. To apply a + given ProxyClass to resources created for a Connector, use + connector.spec.proxyClass field. + ProxyClass is a cluster scoped resource. + More info: + https://tailscale.com/kb/1445/kubernetes-operator-customization#cluster-resource-customization-using-proxyclass-custom-resource + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired state of the ProxyClass resource. + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + metrics: + description: |- + Configuration for proxy metrics. Metrics are currently not supported + for egress proxies and for Ingress proxies that have been configured + with tailscale.com/experimental-forward-cluster-traffic-via-ingress + annotation. Note that the metrics are currently considered unstable + and will likely change in breaking ways in the future - we only + recommend that you use those for debugging purposes. + properties: + enable: + description: |- + Setting enable to true will make the proxy serve Tailscale metrics + at :9002/metrics. + A metrics Service named -metrics will also be created in the operator's namespace and will + serve the metrics at :9002/metrics. + + In 1.78.x and 1.80.x, this field also serves as the default value for + .spec.statefulSet.pod.tailscaleContainer.debug.enable. From 1.82.0, both + fields will independently default to false. + + Defaults to false. + type: boolean + serviceMonitor: + description: |- + Enable to create a Prometheus ServiceMonitor for scraping the proxy's Tailscale metrics. + The ServiceMonitor will select the metrics Service that gets created when metrics are enabled. + The ingested metrics for each Service monitor will have labels to identify the proxy: + ts_proxy_type: ingress_service|ingress_resource|connector|proxygroup + ts_proxy_parent_name: name of the parent resource (i.e name of the Connector, Tailscale Ingress, Tailscale Service or ProxyGroup) + ts_proxy_parent_namespace: namespace of the parent resource (if the parent resource is not cluster scoped) + job: ts__[]_ + properties: + enable: + description: If Enable is set to true, a Prometheus ServiceMonitor will be created. Enable can only be set to true if metrics are enabled. + type: boolean + labels: + additionalProperties: + maxLength: 63 + pattern: ^(([a-zA-Z0-9][-._a-zA-Z0-9]*)?[a-zA-Z0-9])?$ + type: string + description: |- + Labels to add to the ServiceMonitor. + Labels must be valid Kubernetes labels. + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + type: object + required: + - enable + type: object + required: + - enable + type: object + x-kubernetes-validations: + - message: ServiceMonitor can only be enabled if metrics are enabled + rule: "!(has(self.serviceMonitor) && self.serviceMonitor.enable && !self.enable)" + statefulSet: + description: |- + Configuration parameters for the proxy's StatefulSet. Tailscale + Kubernetes operator deploys a StatefulSet for each of the user + configured proxies (Tailscale Ingress, Tailscale Service, Connector). + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations that will be added to the StatefulSet created for the proxy. + Any Annotations specified here will be merged with the default annotations + applied to the StatefulSet by the Tailscale Kubernetes operator as + well as any other annotations that might have been applied by other + actors. + Annotations must be valid Kubernetes annotations. + https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set + type: object + labels: + additionalProperties: + maxLength: 63 + pattern: ^(([a-zA-Z0-9][-._a-zA-Z0-9]*)?[a-zA-Z0-9])?$ + type: string + description: |- + Labels that will be added to the StatefulSet created for the proxy. + Any labels specified here will be merged with the default labels + applied to the StatefulSet by the Tailscale Kubernetes operator as + well as any other labels that might have been applied by other + actors. + Label keys and values must be valid Kubernetes label keys and values. + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + type: object + pod: + description: Configuration for the proxy Pod. + properties: + affinity: + description: |- + Proxy Pod's affinity rules. + By default, the Tailscale Kubernetes operator does not apply any affinity rules. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#affinity + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: |- + Annotations that will be added to the proxy Pod. + Any annotations specified here will be merged with the default + annotations applied to the Pod by the Tailscale Kubernetes operator. + Annotations must be valid Kubernetes annotations. + https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set + type: object + imagePullSecrets: + description: |- + Proxy Pod's image pull Secrets. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + maxLength: 63 + pattern: ^(([a-zA-Z0-9][-._a-zA-Z0-9]*)?[a-zA-Z0-9])?$ + type: string + description: |- + Labels that will be added to the proxy Pod. + Any labels specified here will be merged with the default labels + applied to the Pod by the Tailscale Kubernetes operator. + Label keys and values must be valid Kubernetes label keys and values. + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + type: object + nodeName: + description: |- + Proxy Pod's node name. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + type: string + nodeSelector: + additionalProperties: + type: string + description: |- + Proxy Pod's node selector. + By default Tailscale Kubernetes operator does not apply any node + selector. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + type: object + securityContext: + description: |- + Proxy Pod's security context. + By default Tailscale Kubernetes operator does not apply any Pod + security context. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-2 + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxChangePolicy: + description: |- + seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod. + It has no effect on nodes that do not support SELinux or to volumes does not support SELinux. + Valid values are "MountOption" and "Recursive". + + "Recursive" means relabeling of all files on all Pod volumes by the container runtime. + This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node. + + "MountOption" mounts all eligible Pod volumes with `-o context` mount option. + This requires all Pods that share the same volume to use the same SELinux label. + It is not possible to share the same volume among privileged and unprivileged Pods. + Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes + whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their + CSIDriver instance. Other volumes are always re-labelled recursively. + "MountOption" value is allowed only when SELinuxMount feature gate is enabled. + + If not specified and SELinuxMount feature gate is enabled, "MountOption" is used. + If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes + and "Recursive" for all other volumes. + + This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers. + + All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state. + Note that this field cannot be set when spec.os.name is windows. + type: string + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + tailscaleContainer: + description: Configuration for the proxy container running tailscale. + properties: + debug: + description: |- + Configuration for enabling extra debug information in the container. + Not recommended for production use. + properties: + enable: + description: |- + Enable tailscaled's HTTP pprof endpoints at :9001/debug/pprof/ + and internal debug metrics endpoint at :9001/debug/metrics, where + 9001 is a container port named "debug". The endpoints and their responses + may change in backwards incompatible ways in the future, and should not + be considered stable. + + In 1.78.x and 1.80.x, this setting will default to the value of + .spec.metrics.enable, and requests to the "metrics" port matching the + mux pattern /debug/ will be forwarded to the "debug" port. In 1.82.x, + this setting will default to false, and no requests will be proxied. + type: boolean + type: object + env: + description: |- + List of environment variables to set in the container. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables + Note that environment variables provided here will take precedence + over Tailscale-specific environment variables set by the operator, + however running proxies with custom values for Tailscale environment + variables (i.e TS_USERSPACE) is not recommended and might break in + the future. + items: + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + pattern: ^[-._a-zA-Z][-._a-zA-Z0-9]*$ + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded using the previously defined + environment variables in the container and any service environment + variables. If a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single $, which + allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or not. Defaults + to "". + type: string + required: + - name + type: object + type: array + image: + description: |- + Container image name. By default images are pulled from + docker.io/tailscale/tailscale, but the official images are also + available at ghcr.io/tailscale/tailscale. Specifying image name here + will override any proxy image values specified via the Kubernetes + operator's Helm chart values or PROXY_IMAGE env var in the operator + Deployment. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: |- + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + enum: + - Always + - Never + - IfNotPresent + type: string + resources: + description: |- + Container resource requirements. + By default Tailscale Kubernetes operator does not apply any resource + requirements. The amount of resources required wil depend on the + amount of resources the operator needs to parse, usage patterns and + cluster size. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: |- + Container security context. + Security context specified here will override the security context set by the operator. + By default the operator sets the Tailscale container and the Tailscale init container to privileged + for proxies created for Tailscale ingress and egress Service, Connector and ProxyGroup. + You can reduce the permissions of the Tailscale container to cap NET_ADMIN by + installing device plugin in your cluster and configuring the proxies tun device to be created + by the device plugin, see https://github.com/tailscale/tailscale/issues/10814#issuecomment-2479977752 + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + type: object + tailscaleInitContainer: + description: Configuration for the proxy init container that enables forwarding. + properties: + debug: + description: |- + Configuration for enabling extra debug information in the container. + Not recommended for production use. + properties: + enable: + description: |- + Enable tailscaled's HTTP pprof endpoints at :9001/debug/pprof/ + and internal debug metrics endpoint at :9001/debug/metrics, where + 9001 is a container port named "debug". The endpoints and their responses + may change in backwards incompatible ways in the future, and should not + be considered stable. + + In 1.78.x and 1.80.x, this setting will default to the value of + .spec.metrics.enable, and requests to the "metrics" port matching the + mux pattern /debug/ will be forwarded to the "debug" port. In 1.82.x, + this setting will default to false, and no requests will be proxied. + type: boolean + type: object + env: + description: |- + List of environment variables to set in the container. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables + Note that environment variables provided here will take precedence + over Tailscale-specific environment variables set by the operator, + however running proxies with custom values for Tailscale environment + variables (i.e TS_USERSPACE) is not recommended and might break in + the future. + items: + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + pattern: ^[-._a-zA-Z][-._a-zA-Z0-9]*$ + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded using the previously defined + environment variables in the container and any service environment + variables. If a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single $, which + allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or not. Defaults + to "". + type: string + required: + - name + type: object + type: array + image: + description: |- + Container image name. By default images are pulled from + docker.io/tailscale/tailscale, but the official images are also + available at ghcr.io/tailscale/tailscale. Specifying image name here + will override any proxy image values specified via the Kubernetes + operator's Helm chart values or PROXY_IMAGE env var in the operator + Deployment. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: |- + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + enum: + - Always + - Never + - IfNotPresent + type: string + resources: + description: |- + Container resource requirements. + By default Tailscale Kubernetes operator does not apply any resource + requirements. The amount of resources required wil depend on the + amount of resources the operator needs to parse, usage patterns and + cluster size. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: |- + Container security context. + Security context specified here will override the security context set by the operator. + By default the operator sets the Tailscale container and the Tailscale init container to privileged + for proxies created for Tailscale ingress and egress Service, Connector and ProxyGroup. + You can reduce the permissions of the Tailscale container to cap NET_ADMIN by + installing device plugin in your cluster and configuring the proxies tun device to be created + by the device plugin, see https://github.com/tailscale/tailscale/issues/10814#issuecomment-2479977752 + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + type: object + tolerations: + description: |- + Proxy Pod's tolerations. + By default Tailscale Kubernetes operator does not apply any + tolerations. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: |- + Proxy Pod's topology spread constraints. + By default Tailscale Kubernetes operator does not apply any topology spread constraints. + https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/ + items: + description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. + properties: + labelSelector: + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. + format: int32 + type: integer + minDomains: + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + format: int32 + type: integer + nodeAffinityPolicy: + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + If this value is nil, the behavior is equivalent to the Honor policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. + type: string + nodeTaintsPolicy: + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + If this value is nil, the behavior is equivalent to the Ignore policy. + This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag. + type: string + topologyKey: + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. + type: string + whenUnsatisfiable: + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + type: object + tailscale: + description: |- + TailscaleConfig contains options to configure the tailscale-specific + parameters of proxies. + properties: + acceptRoutes: + description: |- + AcceptRoutes can be set to true to make the proxy instance accept + routes advertized by other nodes on the tailnet, such as subnet + routes. + This is equivalent of passing --accept-routes flag to a tailscale Linux client. + https://tailscale.com/kb/1019/subnets#use-your-subnet-routes-from-other-devices + Defaults to false. + type: boolean + type: object + type: object + status: + description: |- + Status of the ProxyClass. This is set and managed automatically. + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + conditions: + description: |- + List of status conditions to indicate the status of the ProxyClass. + Known condition types are `ProxyClassReady`. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: proxygroups.tailscale.com +spec: + group: tailscale.com + names: + kind: ProxyGroup + listKind: ProxyGroupList + plural: proxygroups + shortNames: + - pg + singular: proxygroup + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Status of the deployed ProxyGroup resources. + jsonPath: .status.conditions[?(@.type == "ProxyGroupReady")].reason + name: Status + type: string + - description: ProxyGroup type. + jsonPath: .spec.type + name: Type + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + ProxyGroup defines a set of Tailscale devices that will act as proxies. + Currently only egress ProxyGroups are supported. + + Use the tailscale.com/proxy-group annotation on a Service to specify that + the egress proxy should be implemented by a ProxyGroup instead of a single + dedicated proxy. In addition to running a highly available set of proxies, + ProxyGroup also allows for serving many annotated Services from a single + set of proxies to minimise resource consumption. + + More info: https://tailscale.com/kb/1438/kubernetes-operator-cluster-egress + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec describes the desired ProxyGroup instances. + properties: + hostnamePrefix: + description: |- + HostnamePrefix is the hostname prefix to use for tailnet devices created + by the ProxyGroup. Each device will have the integer number from its + StatefulSet pod appended to this prefix to form the full hostname. + HostnamePrefix can contain lower case letters, numbers and dashes, it + must not start with a dash and must be between 1 and 62 characters long. + pattern: ^[a-z0-9][a-z0-9-]{0,61}$ + type: string + proxyClass: + description: |- + ProxyClass is the name of the ProxyClass custom resource that contains + configuration options that should be applied to the resources created + for this ProxyGroup. If unset, and there is no default ProxyClass + configured, the operator will create resources with the default + configuration. + type: string + replicas: + description: |- + Replicas specifies how many replicas to create the StatefulSet with. + Defaults to 2. + format: int32 + minimum: 0 + type: integer + tags: + description: |- + Tags that the Tailscale devices will be tagged with. Defaults to [tag:k8s]. + If you specify custom tags here, make sure you also make the operator + an owner of these tags. + See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator. + Tags cannot be changed once a ProxyGroup device has been created. + Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$. + items: + pattern: ^tag:[a-zA-Z][a-zA-Z0-9-]*$ + type: string + type: array + type: + description: |- + Type of the ProxyGroup proxies. Supported types are egress and ingress. + Type is immutable once a ProxyGroup is created. + enum: + - egress + - ingress + type: string + x-kubernetes-validations: + - message: ProxyGroup type is immutable + rule: self == oldSelf + required: + - type + type: object + status: + description: |- + ProxyGroupStatus describes the status of the ProxyGroup resources. This is + set and managed by the Tailscale operator. + properties: + conditions: + description: |- + List of status conditions to indicate the status of the ProxyGroup + resources. Known condition types are `ProxyGroupReady`. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + devices: + description: List of tailnet devices associated with the ProxyGroup StatefulSet. + items: + properties: + hostname: + description: |- + Hostname is the fully qualified domain name of the device. + If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the + node. + type: string + tailnetIPs: + description: |- + TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6) + assigned to the device. + items: + type: string + type: array + required: + - hostname + type: object + type: array + x-kubernetes-list-map-keys: + - hostname + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: recorders.tailscale.com +spec: + group: tailscale.com + names: + kind: Recorder + listKind: RecorderList + plural: recorders + shortNames: + - rec + singular: recorder + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Status of the deployed Recorder resources. + jsonPath: .status.conditions[?(@.type == "RecorderReady")].reason + name: Status + type: string + - description: URL on which the UI is exposed if enabled. + jsonPath: .status.devices[?(@.url != "")].url + name: URL + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Recorder defines a tsrecorder device for recording SSH sessions. By default, + it will store recordings in a local ephemeral volume. If you want to persist + recordings, you can configure an S3-compatible API for storage. + + More info: https://tailscale.com/kb/1484/kubernetes-operator-deploying-tsrecorder + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec describes the desired recorder instance. + properties: + enableUI: + description: |- + Set to true to enable the Recorder UI. The UI lists and plays recorded sessions. + The UI will be served at :443. Defaults to false. + Corresponds to --ui tsrecorder flag https://tailscale.com/kb/1246/tailscale-ssh-session-recording#deploy-a-recorder-node. + Required if S3 storage is not set up, to ensure that recordings are accessible. + type: boolean + statefulSet: + description: |- + Configuration parameters for the Recorder's StatefulSet. The operator + deploys a StatefulSet for each Recorder resource. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations that will be added to the StatefulSet created for the Recorder. + Any Annotations specified here will be merged with the default annotations + applied to the StatefulSet by the operator. + https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set + type: object + labels: + additionalProperties: + type: string + description: |- + Labels that will be added to the StatefulSet created for the Recorder. + Any labels specified here will be merged with the default labels applied + to the StatefulSet by the operator. + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + type: object + pod: + description: Configuration for pods created by the Recorder's StatefulSet. + properties: + affinity: + description: |- + Affinity rules for Recorder Pods. By default, the operator does not + apply any affinity rules. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#affinity + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + description: |- + Annotations that will be added to Recorder Pods. Any annotations + specified here will be merged with the default annotations applied to + the Pod by the operator. + https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set + type: object + container: + description: Configuration for the Recorder container running tailscale. + properties: + env: + description: |- + List of environment variables to set in the container. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#environment-variables + Note that environment variables provided here will take precedence + over Tailscale-specific environment variables set by the operator, + however running proxies with custom values for Tailscale environment + variables (i.e TS_USERSPACE) is not recommended and might break in + the future. + items: + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + pattern: ^[-._a-zA-Z][-._a-zA-Z0-9]*$ + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded using the previously defined + environment variables in the container and any service environment + variables. If a variable cannot be resolved, the reference in the input + string will be unchanged. Double $$ are reduced to a single $, which + allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or not. Defaults + to "". + type: string + required: + - name + type: object + type: array + image: + description: |- + Container image name including tag. Defaults to docker.io/tailscale/tsrecorder + with the same tag as the operator, but the official images are also + available at ghcr.io/tailscale/tsrecorder. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + type: string + imagePullPolicy: + description: |- + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#image + enum: + - Always + - Never + - IfNotPresent + type: string + resources: + description: |- + Container resource requirements. + By default, the operator does not apply any resource requirements. The + amount of resources required wil depend on the volume of recordings sent. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#resources + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: |- + Container security context. By default, the operator does not apply any + container security context. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + type: object + imagePullSecrets: + description: |- + Image pull Secrets for Recorder Pods. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + description: |- + Labels that will be added to Recorder Pods. Any labels specified here + will be merged with the default labels applied to the Pod by the operator. + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set + type: object + nodeSelector: + additionalProperties: + type: string + description: |- + Node selector rules for Recorder Pods. By default, the operator does + not apply any node selector rules. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + type: object + securityContext: + description: |- + Security context for Recorder Pods. By default, the operator does not + apply any Pod security context. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-2 + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxChangePolicy: + description: |- + seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod. + It has no effect on nodes that do not support SELinux or to volumes does not support SELinux. + Valid values are "MountOption" and "Recursive". + + "Recursive" means relabeling of all files on all Pod volumes by the container runtime. + This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node. + + "MountOption" mounts all eligible Pod volumes with `-o context` mount option. + This requires all Pods that share the same volume to use the same SELinux label. + It is not possible to share the same volume among privileged and unprivileged Pods. + Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes + whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their + CSIDriver instance. Other volumes are always re-labelled recursively. + "MountOption" value is allowed only when SELinuxMount feature gate is enabled. + + If not specified and SELinuxMount feature gate is enabled, "MountOption" is used. + If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes + and "Recursive" for all other volumes. + + This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers. + + All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state. + Note that this field cannot be set when spec.os.name is windows. + type: string + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + tolerations: + description: |- + Tolerations for Recorder Pods. By default, the operator does not apply + any tolerations. + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array + type: object + type: object + storage: + description: |- + Configure where to store session recordings. By default, recordings will + be stored in a local ephemeral volume, and will not be persisted past the + lifetime of a specific pod. + properties: + s3: + description: |- + Configure an S3-compatible API for storage. Required if the UI is not + enabled, to ensure that recordings are accessible. + properties: + bucket: + description: |- + Bucket name to write to. The bucket is expected to be used solely for + recordings, as there is no stable prefix for written object names. + type: string + credentials: + description: |- + Configure environment variable credentials for managing objects in the + configured bucket. If not set, tsrecorder will try to acquire credentials + first from the file system and then the STS API. + properties: + secret: + description: |- + Use a Kubernetes Secret from the operator's namespace as the source of + credentials. + properties: + name: + description: |- + The name of a Kubernetes Secret in the operator's namespace that contains + credentials for writing to the configured bucket. Each key-value pair + from the secret's data will be mounted as an environment variable. It + should include keys for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY if + using a static access key. + type: string + type: object + type: object + endpoint: + description: S3-compatible endpoint, e.g. s3.us-east-1.amazonaws.com. + type: string + type: object + type: object + tags: + description: |- + Tags that the Tailscale device will be tagged with. Defaults to [tag:k8s]. + If you specify custom tags here, make sure you also make the operator + an owner of these tags. + See https://tailscale.com/kb/1236/kubernetes-operator/#setting-up-the-kubernetes-operator. + Tags cannot be changed once a Recorder node has been created. + Tag values must be in form ^tag:[a-zA-Z][a-zA-Z0-9-]*$. + items: + pattern: ^tag:[a-zA-Z][a-zA-Z0-9-]*$ + type: string + type: array + type: object + status: + description: |- + RecorderStatus describes the status of the recorder. This is set + and managed by the Tailscale operator. + properties: + conditions: + description: |- + List of status conditions to indicate the status of the Recorder. + Known condition types are `RecorderReady`. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + devices: + description: List of tailnet devices associated with the Recorder StatefulSet. + items: + properties: + hostname: + description: |- + Hostname is the fully qualified domain name of the device. + If MagicDNS is enabled in your tailnet, it is the MagicDNS name of the + node. + type: string + tailnetIPs: + description: |- + TailnetIPs is the set of tailnet IP addresses (both IPv4 and IPv6) + assigned to the device. + items: + type: string + type: array + url: + description: |- + URL where the UI is available if enabled for replaying recordings. This + will be an HTTPS MagicDNS URL. You must be connected to the same tailnet + as the recorder to access it. + type: string + required: + - hostname + type: object + type: array + x-kubernetes-list-map-keys: + - hostname + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: tailscale-operator +rules: + - apiGroups: + - "" + resources: + - events + - services + - services/status + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + - ingresses/status + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch + - apiGroups: + - tailscale.com + resources: + - connectors + - connectors/status + - proxyclasses + - proxyclasses/status + - proxygroups + - proxygroups/status + verbs: + - get + - list + - watch + - update + - apiGroups: + - tailscale.com + resources: + - dnsconfigs + - dnsconfigs/status + verbs: + - get + - list + - watch + - update + - apiGroups: + - tailscale.com + resources: + - recorders + - recorders/status + verbs: + - get + - list + - watch + - update + - apiGroups: + - apiextensions.k8s.io + resourceNames: + - servicemonitors.monitoring.coreos.com + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: tailscale-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: tailscale-operator +subjects: + - kind: ServiceAccount + name: operator + namespace: tailscale +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: operator + namespace: tailscale +rules: + - apiGroups: + - "" + resources: + - secrets + - serviceaccounts + - configmaps + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - update + - apiGroups: + - "" + resources: + - pods/status + verbs: + - update + - apiGroups: + - apps + resources: + - statefulsets + - deployments + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - watch + - create + - update + - deletecollection + - apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - get + - create + - patch + - update + - list + - watch + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - list + - update + - create + - delete +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: proxies + namespace: tailscale +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: operator + namespace: tailscale +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: operator +subjects: + - kind: ServiceAccount + name: operator + namespace: tailscale +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: proxies + namespace: tailscale +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: proxies +subjects: + - kind: ServiceAccount + name: proxies + namespace: tailscale +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: operator + namespace: tailscale +spec: + replicas: 1 + selector: + matchLabels: + app: operator + strategy: + type: Recreate + template: + metadata: + labels: + app: operator + spec: + containers: + - env: + - name: OPERATOR_INITIAL_TAGS + value: "${TS_OP_TAG}" + - name: OPERATOR_HOSTNAME + value: tailscale-operator + - name: OPERATOR_SECRET + value: operator + - name: OPERATOR_LOGGING + value: info + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CLIENT_ID_FILE + value: /oauth/client_id + - name: CLIENT_SECRET_FILE + value: /oauth/client_secret + - name: PROXY_IMAGE + value: tailscale/tailscale:stable + - name: PROXY_TAGS + value: "${TS_OP_TAG}" + - name: APISERVER_PROXY + value: "false" + - name: PROXY_FIREWALL_MODE + value: auto + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_UID + valueFrom: + fieldRef: + fieldPath: metadata.uid + image: tailscale/k8s-operator:stable + imagePullPolicy: Always + name: operator + volumeMounts: + - mountPath: /oauth + name: oauth + readOnly: true + nodeSelector: + kubernetes.io/os: linux + serviceAccountName: operator + volumes: + - name: oauth + secret: + secretName: operator-oauth +--- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + annotations: {} + name: tailscale +spec: + controller: tailscale.com/ts-ingress diff --git a/deployment/k8s/charts/build-bot/values.yaml b/deployment/k8s/charts/build-bot/values.yaml index 8c06e413..87db9998 100644 --- a/deployment/k8s/charts/build-bot/values.yaml +++ b/deployment/k8s/charts/build-bot/values.yaml @@ -17,4 +17,4 @@ dind: cpu: 500m memory: 1Gi -timer: 5000 \ No newline at end of file +timer: 5000 diff --git a/deployment/k8s/charts/competition-api/templates/configmap.yaml b/deployment/k8s/charts/competition-api/templates/configmap.yaml index 4b5e48d6..d3aba880 100644 --- a/deployment/k8s/charts/competition-api/templates/configmap.yaml +++ b/deployment/k8s/charts/competition-api/templates/configmap.yaml @@ -1,3 +1,4 @@ +{{- if .Values.enabled }} apiVersion: v1 kind: ConfigMap metadata: @@ -35,3 +36,4 @@ data: listen_address: "[::]:1323" api_host_and_port: "{{ .Release.Name }}-competition-api.{{ .Release.Namespace }}.svc.cluster.local:1323" +{{- end }} diff --git a/deployment/k8s/charts/competition-api/templates/deployment.yaml b/deployment/k8s/charts/competition-api/templates/deployment.yaml index aef8a884..a955c83d 100644 --- a/deployment/k8s/charts/competition-api/templates/deployment.yaml +++ b/deployment/k8s/charts/competition-api/templates/deployment.yaml @@ -1,3 +1,4 @@ +{{- if .Values.enabled }} apiVersion: apps/v1 kind: Deployment metadata: @@ -34,7 +35,7 @@ spec: valueFrom: secretKeyRef: name: ghcr - key: pat + key: {{ if (lookup "v1" "Secret" .Release.Namespace "ghcr").data.scantron_github_pat }}scantron_github_pat{{ else }}pat{{ end }} volumeMounts: - name: scantron-config mountPath: /etc/scantron/scantron.yaml @@ -52,3 +53,4 @@ spec: emptyDir: {} {{- end }} {{- end }} +{{- end }} diff --git a/deployment/k8s/charts/competition-api/templates/service.yaml b/deployment/k8s/charts/competition-api/templates/service.yaml index b4680967..b0a2c0c9 100644 --- a/deployment/k8s/charts/competition-api/templates/service.yaml +++ b/deployment/k8s/charts/competition-api/templates/service.yaml @@ -1,3 +1,4 @@ +{{- if .Values.enabled }} apiVersion: v1 kind: Service metadata: @@ -14,3 +15,4 @@ spec: nodePort: {{ .Values.service.nodePort }} selector: app: competition-api +{{- end }} diff --git a/deployment/k8s/charts/competition-api/values.yaml b/deployment/k8s/charts/competition-api/values.yaml index 0619d389..e9f3eaea 100644 --- a/deployment/k8s/charts/competition-api/values.yaml +++ b/deployment/k8s/charts/competition-api/values.yaml @@ -1,4 +1,5 @@ # Default values for competition-api chart +enabled: true replicaCount: 1 service: diff --git a/deployment/k8s/charts/coverage-bot/values.yaml b/deployment/k8s/charts/coverage-bot/values.yaml index 8f3ee7b4..598c7a8f 100644 --- a/deployment/k8s/charts/coverage-bot/values.yaml +++ b/deployment/k8s/charts/coverage-bot/values.yaml @@ -16,4 +16,4 @@ dind: memory: 512Mi limits: cpu: 500m - memory: 1Gi \ No newline at end of file + memory: 1Gi diff --git a/deployment/k8s/charts/fuzzer-bot/values.yaml b/deployment/k8s/charts/fuzzer-bot/values.yaml index 6f57e0c6..0f92a865 100644 --- a/deployment/k8s/charts/fuzzer-bot/values.yaml +++ b/deployment/k8s/charts/fuzzer-bot/values.yaml @@ -18,4 +18,4 @@ dind: memory: 1Gi timeout: 900 -timer: 5000 \ No newline at end of file +timer: 5000 diff --git a/deployment/k8s/charts/program-model/values.yaml b/deployment/k8s/charts/program-model/values.yaml index 3859b436..90a550a1 100644 --- a/deployment/k8s/charts/program-model/values.yaml +++ b/deployment/k8s/charts/program-model/values.yaml @@ -1,17 +1,12 @@ enabled: true -image: - repository: program-model - tag: latest - pullPolicy: IfNotPresent - resources: limits: - cpu: 500m - memory: 512Mi + cpu: 300m + memory: 1Gi requests: - cpu: 250m - memory: 256Mi + cpu: 100m + memory: 512Mi dind: resources: @@ -23,4 +18,4 @@ dind: memory: 1Gi logLevel: debug -sleepTime: 5 \ No newline at end of file +sleepTime: 5 diff --git a/deployment/k8s/charts/scheduler/values.yaml b/deployment/k8s/charts/scheduler/values.yaml index f1b36431..d8c8c5ea 100644 --- a/deployment/k8s/charts/scheduler/values.yaml +++ b/deployment/k8s/charts/scheduler/values.yaml @@ -1,4 +1,5 @@ -# Scheduler service configuration +enabled: true + resources: limits: cpu: 500m @@ -6,16 +7,3 @@ resources: requests: cpu: 250m memory: 256Mi - -# Persistence settings -persistence: - crs_scratch: - enabled: true - storageClass: "" # Empty string means use cluster default - size: 1Gi - accessMode: ReadWriteMany - tasks_storage: - enabled: true - storageClass: "" # Empty string means use cluster default - size: 1Gi - accessMode: ReadWriteMany \ No newline at end of file diff --git a/deployment/k8s/charts/seed-gen/values.yaml b/deployment/k8s/charts/seed-gen/values.yaml index dc6a6d79..6152bacc 100644 --- a/deployment/k8s/charts/seed-gen/values.yaml +++ b/deployment/k8s/charts/seed-gen/values.yaml @@ -18,4 +18,4 @@ dind: memory: 512Mi limits: cpu: 500m - memory: 1Gi \ No newline at end of file + memory: 1Gi diff --git a/deployment/k8s/charts/task-downloader/values.yaml b/deployment/k8s/charts/task-downloader/values.yaml index dd0acfeb..b47c913c 100644 --- a/deployment/k8s/charts/task-downloader/values.yaml +++ b/deployment/k8s/charts/task-downloader/values.yaml @@ -1,8 +1,9 @@ -# Service specific configuration +enabled: true -persistence: - tasks_storage: - enabled: true - storageClass: "" # Empty string means use cluster default - size: 1Gi - accessMode: ReadWriteMany \ No newline at end of file +resources: + limits: + cpu: 300m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi diff --git a/deployment/k8s/charts/task-server/templates/ingress.yaml b/deployment/k8s/charts/task-server/templates/ingress.yaml new file mode 100644 index 00000000..3771248e --- /dev/null +++ b/deployment/k8s/charts/task-server/templates/ingress.yaml @@ -0,0 +1,25 @@ +--- +kind: Ingress +apiVersion: networking.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-task-server + annotations: + # TODO: make this configurable + tailscale.com/hostname: "ethereal-logic" + tailscale.com/experimental-forward-cluster-traffic-via-ingress: "true" +spec: + ingressClassName: tailscale + tls: + - hosts: + # TODO: make this configurable + - "ethereal-logic" + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-task-server + port: + number: {{ .Values.service.port }} diff --git a/deployment/k8s/charts/task-server/templates/service.yaml b/deployment/k8s/charts/task-server/templates/service.yaml index f5d6c11f..9b4f05b0 100644 --- a/deployment/k8s/charts/task-server/templates/service.yaml +++ b/deployment/k8s/charts/task-server/templates/service.yaml @@ -10,20 +10,12 @@ metadata: {{- end }} spec: # Set type based on environment - type: {{ if eq $.Values.global.environment "aks" }}LoadBalancer{{ else }}NodePort{{ end }} + type: NodePort ports: - port: {{ .Values.service.port }} targetPort: {{ .Values.port }} protocol: TCP name: http - # Add nodePort only for NodePort service type (minikube) - {{- if and (ne $.Values.global.environment "aks") .Values.service.nodePort }} nodePort: {{ .Values.service.nodePort }} - {{- end }} selector: app: task-server - # Add loadBalancerSourceRanges only for LoadBalancer service type (AKS) - {{- if and (eq $.Values.global.environment "aks") .Values.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: - {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} \ No newline at end of file diff --git a/deployment/k8s/charts/task-server/values.yaml b/deployment/k8s/charts/task-server/values.yaml index 8811cc2e..5c5c2bbe 100644 --- a/deployment/k8s/charts/task-server/values.yaml +++ b/deployment/k8s/charts/task-server/values.yaml @@ -1,12 +1,10 @@ # Basic configuration +enabled: true port: 8000 +workers: 1 # Service configuration service: - type: LoadBalancer port: 8000 annotations: {} # Can be used for cloud-specific LB configurations - # Optional configurations: - # loadBalancerIP: "" # Specify static IP if needed - # loadBalancerSourceRanges: # Restrict source IPs if needed - # - 10.0.0.0/8 \ No newline at end of file + nodePort: 30080 diff --git a/deployment/k8s/charts/tracer-bot/values.yaml b/deployment/k8s/charts/tracer-bot/values.yaml index e335ce2d..73ec1864 100644 --- a/deployment/k8s/charts/tracer-bot/values.yaml +++ b/deployment/k8s/charts/tracer-bot/values.yaml @@ -1,12 +1,6 @@ # Default values for tracer-bot. enabled: true -# Image configuration -image: - repository: fuzzer - tag: latest - pullPolicy: IfNotPresent - # Resource configurations resources: limits: diff --git a/deployment/k8s/templates/common-env.yaml b/deployment/k8s/templates/common-env.yaml index b18eff0a..523119cc 100644 --- a/deployment/k8s/templates/common-env.yaml +++ b/deployment/k8s/templates/common-env.yaml @@ -143,9 +143,24 @@ Service-specific environment variables that utilize the standardized variables - name: BUTTERCUP_TASK_SERVER_HOST value: "0.0.0.0" - name: BUTTERCUP_TASK_SERVER_PORT - value: "8000" + value: "{{ .Values.port | default 8000 }}" - name: BUTTERCUP_TASK_SERVER_WORKERS - value: "1" + value: "{{ .Values.workers | default 1 }}" +- name: BUTTERCUP_TASK_SERVER_COMPETITION_API_URL + valueFrom: + configMapKeyRef: + name: {{ .Release.Name }}-crs-config + key: COMPETITION_API_URL +- name: BUTTERCUP_TASK_SERVER_COMPETITION_API_USERNAME + valueFrom: + configMapKeyRef: + name: {{ .Release.Name }}-crs-config + key: COMPETITION_API_KEY_ID +- name: BUTTERCUP_TASK_SERVER_COMPETITION_API_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-crs-config + key: COMPETITION_API_KEY_TOKEN - name: BUTTERCUP_TASK_SERVER_API_KEY_ID valueFrom: configMapKeyRef: diff --git a/deployment/k8s/templates/secrets.yaml b/deployment/k8s/templates/secrets.yaml index bbfa940d..98469813 100644 --- a/deployment/k8s/templates/secrets.yaml +++ b/deployment/k8s/templates/secrets.yaml @@ -6,6 +6,5 @@ data: {{- $secret := (lookup "v1" "Secret" .Release.Namespace "ghcr") | default dict }} {{- $username := get (get $secret "data") "username" | default "" | b64dec }} {{- $pat := get (get $secret "data") "pat" | default "" | b64dec }} - {{- $email := get (get $secret "data") "email" | default "" | b64dec }} - .dockerconfigjson: {{ printf "{\"auths\":{\"ghcr.io\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" $username $pat $email (printf "%s:%s" $username $pat | b64enc) | b64enc }} + .dockerconfigjson: {{ printf "{\"auths\":{\"ghcr.io\":{\"auth\":\"%s\"}}}" (printf "%s:%s" $username $pat | b64enc) | b64enc }} type: kubernetes.io/dockerconfigjson diff --git a/deployment/k8s/values-aks.template b/deployment/k8s/values-aks.template new file mode 100644 index 00000000..6f86b31a --- /dev/null +++ b/deployment/k8s/values-aks.template @@ -0,0 +1,36 @@ +# Environment selection (minikube or aks) +global: + environment: "aks" + langfuse: + enabled: true + host: "${LANGFUSE_HOST}" + publicKey: "${LANGFUSE_PUBLIC_KEY}" + secretKey: "${LANGFUSE_SECRET_KEY}" + + crs: + api_key_id: ${CRS_KEY_ID} + api_key_token: ${CRS_KEY_TOKEN} + api_key_token_hash: "${CRS_KEY_TOKEN_HASH}" + api_url: "${CRS_URL}" + competition_api_key_id: ${COMPETITION_API_KEY_ID} + competition_api_key_token: ${COMPETITION_API_KEY_TOKEN} + competition_api_url: "${COMPETITION_API_URL}" + +volumes: + tasks_storage: + storageClass: "azurefile-premium" + crs_scratch: + storageClass: "azurefile-premium" + +litellm: + masterKey: "your-secure-master-key" # Generate with: openssl rand -hex 16 + azure: + apiBase: "${AZURE_API_BASE}" + apiKey: "${AZURE_API_KEY}" + openai: + apiKey: "${OPENAI_API_KEY}" + anthropic: + apiKey: "${ANTHROPIC_API_KEY}" + +competition-api: + enabled: true diff --git a/deployment/k8s/values.yaml b/deployment/k8s/values.yaml index 3d5c004a..39bd9ca1 100644 --- a/deployment/k8s/values.yaml +++ b/deployment/k8s/values.yaml @@ -81,116 +81,43 @@ redis: task-server: enabled: true - port: 8000 - service: - # Service type will be overridden based on environment in the template - type: LoadBalancer - port: 8000 - nodePort: 30080 # Used when type is NodePort (minikube) # API authentication is configured with the following credentials: # API Key ID: 515cc8a0-3019-4c9f-8c1c-72d0b54ae561 # API Token: VGuAC8axfOnFXKBB7irpNDOKcDjOlnyB (for client usage) - # These values are set in the deployment via common-env.yaml + # These values are set in the secret/config-map in config.yaml task-downloader: enabled: true - resources: - limits: - cpu: 300m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi scheduler: enabled: true - resources: - limits: - cpu: 300m - memory: 512Mi - requests: - cpu: 150m - memory: 256Mi program-model: enabled: true - sleepTime: 5 - resources: - limits: - cpu: 300m - memory: 1Gi - requests: - cpu: 100m - memory: 512Mi build-bot: replicaCount: 4 enabled: true timer: 5000 logLevel: "DEBUG" - resources: - limits: - cpu: 200m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi fuzzer-bot: enabled: true - timeout: 900 - timer: 5000 - resources: - limits: - cpu: 200m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi coverage-bot: enabled: true - timer: 5000 - resources: - limits: - cpu: 400m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi tracer-bot: enabled: true - resources: - limits: - cpu: 400m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi seed-gen: enabled: true - sleepTime: 60 - pythonPath: "/app/seed-gen/.venv/bin/python" - logLevel: "DEBUG" - resources: - limits: - cpu: 200m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi patcher: enabled: true - resources: - limits: - cpu: 200m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi + +competition-api: + enabled: true # PostgreSQL configuration for LiteLLM litellm-db: @@ -305,18 +232,3 @@ litellm-helm: general_settings: master_key: os.environ/BUTTERCUP_LITELLM_KEY - - - -# Competition API configuration -competition-api: - enabled: true - service: - port: 1323 - resources: - requests: - cpu: 250m - memory: 512Mi - limits: - cpu: 500m - memory: 1Gi diff --git a/deployment/main.tf b/deployment/main.tf index 94cf7ec9..75101b7e 100644 --- a/deployment/main.tf +++ b/deployment/main.tf @@ -1,36 +1,3 @@ -terraform { - required_version = ">=1.0" - - required_providers { - azapi = { - source = "azure/azapi" - version = "2.0.1" - } - azurerm = { - source = "hashicorp/azurerm" - version = "4.7.0" - } - random = { - source = "hashicorp/random" - version = "3.6.3" - } - time = { - source = "hashicorp/time" - version = "0.12.1" - } - } -} - - -provider "azurerm" { - features {} - #Can setup your service principal here, currently commented out to use az cli apply terraform - #subscription_id = "" - #tenant_id = "" - #client_id = "" - #client_secret = "" -} - #resource for random prefixes, helps with unique names and identifiers resource "random_pet" "ssh_key_name" { prefix = "ssh" @@ -116,8 +83,9 @@ resource "azurerm_kubernetes_cluster" "primary" { } } network_profile { - network_plugin = "azure" - network_policy = "azure" + network_plugin = "azure" + network_policy = "azure" + load_balancer_sku = "standard" } oms_agent { log_analytics_workspace_id = azurerm_log_analytics_workspace.aks_logs.id @@ -145,97 +113,3 @@ resource "azurerm_log_analytics_workspace" "aks_logs" { sku = "PerGB2018" retention_in_days = 30 } - -# -# Kubernetes Resources -# - -data "azurerm_kubernetes_cluster" "primary" { - name = azurerm_kubernetes_cluster.primary.name - resource_group_name = azurerm_kubernetes_cluster.primary.resource_group_name -} - -# NOTE: The data block above is used to configured the kubernetes provider -# correctly, by adding a layer of indirectness. This is because of: -# https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#stacking-with-managed-kubernetes-cluster-resources -provider "kubernetes" { - host = data.azurerm_kubernetes_cluster.primary.kube_config[0].host - client_certificate = base64decode(data.azurerm_kubernetes_cluster.primary.kube_config[0].client_certificate) - client_key = base64decode(data.azurerm_kubernetes_cluster.primary.kube_config[0].client_key) - cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.primary.kube_config[0].cluster_ca_certificate) -} - -resource "kubernetes_namespace" "crs-ns" { - metadata { - name = "crs" - } -} - -resource "kubernetes_secret" "ghcr_auth" { - metadata { - name = "ghcr-auth" - namespace = kubernetes_namespace.crs-ns.metadata.0.name - } - type = "kubernetes.io/dockerconfigjson" - data = { - ".dockerconfigjson" = jsonencode({ - "auths" = { - "https://ghcr.io" = { - "auth" : base64encode("${var.github_username}:${var.github_pat}") - } - } - }) - } -} - -resource "kubernetes_deployment" "orchestrator" { - metadata { - name = "orchestrator" - namespace = kubernetes_namespace.crs-ns.metadata.0.name - } - spec { - replicas = 2 - selector { - match_labels = { - app = "orchestrator" - } - } - template { - metadata { - labels = { - app = "orchestrator" - } - } - spec { - image_pull_secrets { - name = kubernetes_secret.ghcr_auth.metadata[0].name - } - container { - image = "ghcr.io/trailofbits/afc-crs-trail-of-bits/orchestrator:latest" - name = "orchestrator-container" - port { - container_port = 8000 - } - } - } - } - } -} - -resource "kubernetes_service" "orchestrator" { - metadata { - name = "orchestrator" - namespace = kubernetes_namespace.crs-ns.metadata.0.name - } - spec { - selector = { - app = kubernetes_deployment.orchestrator.spec.0.template.0.metadata.0.labels.app - } - type = "NodePort" - port { - node_port = 30201 - port = 8000 - target_port = 8000 - } - } -} diff --git a/deployment/outputs.tf b/deployment/outputs.tf index e0590b1a..bc4fba95 100644 --- a/deployment/outputs.tf +++ b/deployment/outputs.tf @@ -6,6 +6,11 @@ output "kubernetes_cluster_name" { value = azurerm_kubernetes_cluster.primary.name } +output "host" { + value = azurerm_kubernetes_cluster.primary.kube_config[0].host + sensitive = true +} + output "client_certificate" { value = azurerm_kubernetes_cluster.primary.kube_config[0].client_certificate sensitive = true @@ -31,11 +36,6 @@ output "cluster_username" { sensitive = true } -output "host" { - value = azurerm_kubernetes_cluster.primary.kube_config[0].host - sensitive = true -} - output "kube_config" { value = azurerm_kubernetes_cluster.primary.kube_config_raw sensitive = true diff --git a/deployment/providers.tf b/deployment/providers.tf new file mode 100644 index 00000000..66c89fe1 --- /dev/null +++ b/deployment/providers.tf @@ -0,0 +1,34 @@ +terraform { + required_version = ">=1.10.5" + + required_providers { + azapi = { + source = "azure/azapi" + version = "2.2.0" + } + helm = { + source = "hashicorp/helm" + version = "2.17.0" + } + azurerm = { + source = "hashicorp/azurerm" + version = "4.17.0" + } + random = { + source = "hashicorp/random" + version = "3.6.3" + } + time = { + source = "hashicorp/time" + version = "0.12.1" + } + } +} + +provider "azurerm" { + features {} + subscription_id = var.ARM_SUBSCRIPTION_ID + tenant_id = var.ARM_TENANT_ID + client_id = var.ARM_CLIENT_ID + client_secret = var.ARM_CLIENT_SECRET +} diff --git a/deployment/variables.tf b/deployment/variables.tf index d094f121..952ab1a4 100644 --- a/deployment/variables.tf +++ b/deployment/variables.tf @@ -1,16 +1,3 @@ -variable "github_username" { - type = string - description = "The username for the GitHub account." - default = "USERNAME" - sensitive = true -} - -variable "github_pat" { - type = string - description = "The personal access token (PAT) for the GitHub account." - sensitive = true -} - variable "resource_group_location" { type = string default = "eastus" @@ -40,3 +27,24 @@ variable "username" { description = "The admin username for the new cluster." default = "azureadmin" } + +variable "ARM_SUBSCRIPTION_ID" { + type = string + description = "Azure subscription ID" +} + +variable "ARM_TENANT_ID" { + type = string + description = "Azure tenant ID" +} + +variable "ARM_CLIENT_ID" { + type = string + description = "Azure client ID" +} + +variable "ARM_CLIENT_SECRET" { + type = string + sensitive = true + description = "Azure client secret" +}