Skip to content

aneviaro/tailscale-setup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tailscale IaC

This repository contains a Terraform scaffold for managing a Tailscale tailnet.

The tracked files are sanitized for public-repo readiness. Live exports, local tfvars, Terraform state, and credentials are ignored.

Recommended Direction

Manage these items with Terraform:

  • Full tailnet policy file via tailscale_acl.
  • Tailnet settings via tailscale_tailnet_settings.
  • MagicDNS, global DNS, split DNS, and search paths.
  • Device tags and enabled subnet routes once device names are known.
  • Webhooks and log streaming if you send events to Slack, Discord, SIEM, or storage.
  • Short-lived auth keys only for automation bootstrap, preferably generated by CI rather than committed.

Keep these out of Terraform state when possible:

  • Long-lived pre-auth keys.
  • OAuth client secrets.
  • Per-machine runtime flags such as advertised routes; Terraform can enable advertised routes, but each node still has to advertise them locally.

Security Improvements

  1. Prefer grants over legacy acls for new policy work. Grants are the forward-looking syntax and support network plus application-layer capabilities.
  2. Keep the policy deny-by-default. Avoid broad * to * rules except as a temporary import bridge.
  3. Require policy tests for every important allow and deny path. Terraform validates the policy before applying when tests are present.
  4. Put servers, routers, CI runners, and app connectors behind tags. Avoid user-owned identity for unattended infrastructure.
  5. Use tagOwners narrowly. Admin-owned tags are fine for small tailnets; larger setups should delegate tag ownership by group.
  6. Use autoApprovers for subnet routers and exit nodes only with tagged infrastructure, not individual users, where possible.
  7. For Tailscale SSH, separate self-device access from server access. Use check for privileged server logins and avoid blanket root.
  8. Turn on externally managed ACLs once Terraform owns the policy to prevent console edits from drifting.
  9. Turn on device approval, device auto-updates, key expiry, HTTPS, and network flow logging if your plan and client fleet support them.
  10. Route changes through pull requests with terraform fmt, terraform validate, and terraform plan.

Adoption Plan

  1. Export the current live state with make export-current.
  2. Generate ignored local values with make write-current-tfvars, or edit terraform/terraform.tfvars manually.
  3. Import existing managed resources before applying.
  4. Run terraform plan and resolve drift.
  5. Add policy tests while preserving current behavior.
  6. Remove broad/stale rules in a separate change.
  7. Enable acls_externally_managed_on after the policy matches production.

For live drift-free adoption, terraform/terraform.tfvars can point at the ignored exported policy:

policy_file = "../exports/current/acl.hujson"

The generated terraform/terraform.tfvars file is ignored because it can contain real device names, tailnet names, tags, and internal Tailscale IPs.

Run Terraform through the wrapper so .env.local is sourced without committing credentials:

make init
make plan

make fmt formats tracked Terraform files only. Use make fmt-local if you also want to format ignored local files such as terraform/terraform.tfvars.

If make validate fails while loading the Tailscale provider schema, refresh the local Terraform runtime/provider cache before treating it as an HCL problem. A known local failure mode is a provider binary that installs but cannot instantiate.

The Tailscale provider needs one of:

  • TAILSCALE_API_KEY
  • TAILSCALE_OAUTH_CLIENT_ID plus TAILSCALE_OAUTH_CLIENT_SECRET

Useful Imports

Generate current import commands from the exported device snapshot:

make print-imports

Or import the current exported resources directly:

make import-current

Core imports:

terraform -chdir=terraform import tailscale_acl.policy acl
terraform -chdir=terraform import tailscale_tailnet_settings.this tailnet_settings
terraform -chdir=terraform import tailscale_dns_preferences.this dns_preferences
terraform -chdir=terraform import 'tailscale_dns_nameservers.global[0]' dns_nameservers

Device-specific imports use stable node IDs where possible:

terraform -chdir=terraform import 'tailscale_device_tags.by_name["server.example.ts.net"]' nodeidCNTRL
terraform -chdir=terraform import 'tailscale_device_subnet_routes.by_name["router.example.ts.net"]' nodeidCNTRL

References

About

This is my obfuscated tailscale setup IaC

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors