Add ringtail DeviceTags and homelab-to-homelab SSH rule (#210)

## Summary
- Add `ringtail` DeviceTags Pulumi resource with `tag:homelab` + `tag:blumeops` (matching indri/sifaka pattern)
- Remove the bootstrap `ringtail_key` auth key — ringtail is already on the tailnet
- Add SSH ACL rule allowing `tag:homelab` → `tag:homelab` SSH, unblocking cross-host management (e.g., ringtail running ansible against indri)

## Deployment and Testing
- [ ] `mise run tailnet-preview` — dry run, confirm diff
- [ ] `mise run tailnet-up` — apply
- [ ] From ringtail: `ssh indri 'hostname'` — should succeed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/210
This commit is contained in:
Erich Blume 2026-02-18 21:48:11 -08:00
commit 630ebcd12d
3 changed files with 24 additions and 13 deletions

View file

@ -0,0 +1 @@
Add ringtail DeviceTags to Pulumi and allow homelab-to-homelab Tailscale SSH for cross-host ansible/management.

View file

@ -70,6 +70,18 @@ sifaka_tags = tailscale.DeviceTags(
], ],
) )
# ringtail - NixOS gaming/compute workstation
# Managed by this IaC after initial bootstrap via auth key.
ringtail = tailscale.get_device(name="ringtail.tail8d86e.ts.net")
ringtail_tags = tailscale.DeviceTags(
"ringtail-tags",
device_id=ringtail.node_id,
tags=[
"tag:homelab", # Server role - allows SSH from workstations and homelab peers
"tag:blumeops", # Managed by this IaC
],
)
# ============== Auth Keys ============== # ============== Auth Keys ==============
# Auth key for Fly.io proxy container (public reverse proxy) # Auth key for Fly.io proxy container (public reverse proxy)
@ -82,26 +94,16 @@ flyio_key = tailscale.TailnetKey(
expiry=7776000, # 90 days expiry=7776000, # 90 days
) )
# Auth key for ringtail (gaming/compute workstation, NixOS)
# Used during bootstrap: `tailscale up --auth-key=<key>`
# Once ringtail is on the tailnet, add DeviceTags resource for ongoing management.
ringtail_key = tailscale.TailnetKey(
"ringtail-key",
reusable=False,
ephemeral=False,
preauthorized=True,
tags=["tag:homelab", "tag:blumeops"],
expiry=86400, # 24 hours - single use for bootstrap
)
# ============== Exports ============== # ============== Exports ==============
pulumi.export("acl_id", acl.id) pulumi.export("acl_id", acl.id)
pulumi.export("policy_hash", policy_hash) pulumi.export("policy_hash", policy_hash)
pulumi.export("flyio_authkey", flyio_key.key) pulumi.export("flyio_authkey", flyio_key.key)
pulumi.export("ringtail_authkey", ringtail_key.key)
pulumi.export("indri_device_id", indri.node_id) pulumi.export("indri_device_id", indri.node_id)
pulumi.export("indri_tags", indri_tags.tags) pulumi.export("indri_tags", indri_tags.tags)
pulumi.export("sifaka_device_id", sifaka.node_id) pulumi.export("sifaka_device_id", sifaka.node_id)
pulumi.export("sifaka_tags", sifaka_tags.tags) pulumi.export("sifaka_tags", sifaka_tags.tags)
pulumi.export("ringtail_device_id", ringtail.node_id)
pulumi.export("ringtail_tags", ringtail_tags.tags)

View file

@ -124,6 +124,14 @@
"users": ["autogroup:nonroot"], "users": ["autogroup:nonroot"],
"checkPeriod": "12h0m0s", "checkPeriod": "12h0m0s",
}, },
// Homelab can SSH to homelab (for ansible, cross-host management)
// Tagged devices can't do interactive "check" auth, so use "accept".
{
"action": "accept",
"src": ["tag:homelab"],
"dst": ["tag:homelab"],
"users": ["autogroup:nonroot"],
},
], ],
// ============== Auto Approvers ============== // ============== Auto Approvers ==============