diff --git a/homelab/tailscale.tf b/homelab/tailscale.tf index 5b47ceb..3c42e38 100644 --- a/homelab/tailscale.tf +++ b/homelab/tailscale.tf @@ -14,3 +14,65 @@ resource "tailscale_dns_nameservers" "global" { resource "tailscale_dns_preferences" "main" { magic_dns = true } + +# Tailnet policy file. Codifies the current default-allow stance and adds +# forward-looking structure (tag:homelab, SSH for tagged servers) that no-ops +# until devices actually advertise the tag. +# +# DO NOT tighten the `*:*` rule in the same change that introduces tagging. +# Switching to a restrictive policy before any device wears tag:homelab will +# leave the operator unable to reach gandalf over Tailscale. Sequence: +# 1. (this resource) Codify allow-all + tagOwners + SSH-to-tag-homelab. +# 2. Advertise tag:homelab on gandalf (`tailscale up --advertise-tags=tag:homelab`) +# and approve the tag in the admin UI. +# 3. Replace the `*:*` rule with restrictive admin/tag-scoped rules. +resource "tailscale_acl" "main" { + acl = jsonencode({ + tagOwners = { + # Servers in the home lab (gandalf today, Pis later). Only tailnet + # admins can apply this tag — prevents a random tailnet member from + # spoofing a homelab node. + "tag:homelab" = ["autogroup:admin"] + } + + acls = [ + # Phase 1: keep the default-allow rule so nothing breaks for current + # devices. Replace with restrictive rules in a follow-up PR after + # gandalf is tagged. + { + action = "accept" + src = ["*"] + dst = ["*:*"] + }, + ] + + ssh = [ + # Tailscale SSH default: members can SSH into their own devices + # (the "check" action requires reauth). Carried over from the + # admin UI default. + { + action = "check" + src = ["autogroup:member"] + dst = ["autogroup:self"] + users = ["autogroup:nonroot", "root"] + }, + # Forward-looking: members can SSH into homelab-tagged servers as + # `nickv` or `root`. No-op until a device wears tag:homelab. + { + action = "accept" + src = ["autogroup:member"] + dst = ["tag:homelab"] + users = ["nickv", "root"] + }, + ] + + nodeAttrs = [ + # Tailscale Funnel — let members expose their own devices to the + # public internet. Carried over from the admin UI default. + { + target = ["autogroup:member"] + attr = ["funnel"] + }, + ] + }) +}