A Kubernetes controller that provisions a Tailscale-based Gateway.
- A Kubernetes cluster with access to create cluster-scoped resources
- Gateway API CRDs (
gateway.networking.k8s.io/v1) - Tailscale OAuth client credentials with scopes that allow key creation
Ensure the Gateway API CRDs are installed. If not present, install a stable release:
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yamlVerify:
kubectl get crd gateways.gateway.networking.k8s.io httproutes.gateway.networking.k8s.ioCreate the Secret the controller consumes for environment configuration in the tailscale-system namespace:
kubectl create namespace tailscale-system
kubectl -n tailscale-system create secret generic tailscale-gateway-controller \
--from-literal=TAILSCALE_OAUTH_CLIENT_ID=<client-id> \
--from-literal=TAILSCALE_OAUTH_CLIENT_SECRET=<client-secret>Optional values:
TAILSCALE_IMAGE: Override the Tailscale daemon image used by the gateway pods
See Environment Configuration below for details on each variable.
Apply the provided Kustomize overlay to install the controller, RBAC,
ServiceAccount, metrics Service, and the GatewayClass named tailscale:
kubectl apply -k https://github.com/shikanime-studio/tailscale-gateway/manifests/gatewayCheck status:
kubectl -n tailscale-system get deploy/tailscale-gateway-controller,svc/tailscale-gateway-controller-metrics
kubectl get gatewayclass tailscaleWith the controller running, create a Gateway bound to the tailscale
GatewayClass and HTTPRoutes. Example manifests are provided:
kubectl apply -k ./manifests/demo
kubectl -n tailscale-gateway-demo get gateway demo
kubectl -n tailscale-gateway-demo get httproute demoWhen reconciliation succeeds, the Gateway status reports Ready and the
controller.
TAILSCALE_OAUTH_CLIENT_ID: OAuth client ID with scopes allowing key creationTAILSCALE_OAUTH_CLIENT_SECRET: OAuth client secretTAILSCALE_TAGS: Comma-separated device tags applied to generated auth keys (e.g.tag:gateway,tag:proxy)TAILSCALE_IMAGE: Tailscale daemon image (defaulttailscale/tailscale:latest)
The controller reads these via the internal/config package. Tags are parsed
from TAILSCALE_TAGS and default to tag:gateway when unset.
- The controller checks for a Secret named
<gateway-name>in the Gateway namespace. - If the Secret contains
authkey, it is left unchanged. - If missing, the controller generates a non-reusable, ephemeral, preauthorized
auth key using the official Tailscale client
(
tailscale.com/client/tailscale). - Tags for the key are sourced from
TAILSCALE_TAGS.
- Tailscale client for API: https://github.com/tailscale/tailscale-client-go-v2
- OAuth clients and scopes: https://tailscale.com/kb/1215/oauth-clients