From be7b56d7d49ddd7c144218855a7de843449cb493 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Mon, 11 May 2026 23:52:32 +0000 Subject: [PATCH] Codify Tailscale ACL with tag:homelab scaffolding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings the tailnet policy file under OpenTofu management. Keeps the existing default-allow stance and SSH/Funnel defaults intact while adding forward-looking structure that no-ops until devices wear the new tag: - `tagOwners` for `tag:homelab` (only autogroup:admin can apply it) - SSH rule `autogroup:member -> tag:homelab` for `nickv` and `root` Phase 2 (separate PR after gandalf advertises tag:homelab) will replace the `*:*` acls rule with restrictive admin/tag-scoped rules. Tightening in the same change as introducing tagging would leave the operator unable to reach gandalf via Tailscale. OAuth client `opentofu-homelab` needs the `acl` (read+write) scope — added in the Tailscale admin UI before this lands. Applied locally with `tofu import tailscale_acl.main acl` followed by in-place update: 0 added, 1 changed, 0 destroyed. --- homelab/tailscale.tf | 62 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) 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"] + }, + ] + }) +}