Add Pulumi for tailnet IaC management (#15)
## Summary - Manage tail8d86e.ts.net ACLs, tags, and DNS via Pulumi + Python - State stored in Pulumi Cloud (free tier) to avoid circular dependency - OAuth authentication via 1Password for secure credential management - New mise tasks: `tailnet-preview`, `tailnet-up` ## Architecture Two-layer approach: - **Layer 1 (Pulumi)**: Tailnet-wide config (ACLs, tags, DNS) - **Layer 2 (Ansible)**: Node-local `tailscale serve` config (unchanged) ## Test plan - [x] Exported current ACL from Tailscale API - [x] Imported existing ACL into Pulumi state - [x] Verified `mise run tailnet-preview` shows no changes - [x] Verified `mise run tailnet-up` applies successfully 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.tail8d86e.ts.net/eblume/blumeops/pulls/15
This commit is contained in:
parent
72c2dd7096
commit
3f4e40f3ae
13 changed files with 231 additions and 0 deletions
10
pulumi/.gitignore
vendored
Normal file
10
pulumi/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Python
|
||||
.venv/
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# uv
|
||||
uv.lock
|
||||
|
||||
# Pulumi
|
||||
*.pyc
|
||||
2
pulumi/Pulumi.tail8d86e.yaml
Normal file
2
pulumi/Pulumi.tail8d86e.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
config:
|
||||
tailscale:tailnet: tail8d86e.ts.net
|
||||
6
pulumi/Pulumi.yaml
Normal file
6
pulumi/Pulumi.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name: blumeops-tailnet
|
||||
runtime:
|
||||
name: python
|
||||
options:
|
||||
toolchain: uv
|
||||
description: Tailnet configuration for tail8d86e.ts.net
|
||||
18
pulumi/__main__.py
Normal file
18
pulumi/__main__.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
"""Pulumi program to manage tail8d86e.ts.net tailnet configuration."""
|
||||
|
||||
import pulumi
|
||||
import pulumi_tailscale as tailscale
|
||||
from pathlib import Path
|
||||
|
||||
# Read the HuJSON policy file
|
||||
policy_path = Path(__file__).parent / "policy.hujson"
|
||||
policy_content = policy_path.read_text()
|
||||
|
||||
# Manage the ACL - this completely overwrites the tailnet's ACL policy
|
||||
acl = tailscale.Acl(
|
||||
"tailnet-acl",
|
||||
acl=policy_content,
|
||||
)
|
||||
|
||||
# Export useful info
|
||||
pulumi.export("acl_id", acl.id)
|
||||
104
pulumi/policy.hujson
Normal file
104
pulumi/policy.hujson
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Example/default ACLs for unrestricted connections.
|
||||
{
|
||||
// Declare static groups of users. Use autogroups for all users or users with a specific role.
|
||||
// "groups": {
|
||||
// "group:example": ["alice@example.com", "bob@example.com"],
|
||||
// },
|
||||
|
||||
// Define the tags which can be applied to devices and by which users.
|
||||
// "tagOwners": {
|
||||
// "tag:example": ["autogroup:admin"],
|
||||
// },
|
||||
|
||||
// Define grants that govern access for users, groups, autogroups, tags,
|
||||
// Tailscale IP addresses, and subnet ranges.
|
||||
"grants": [
|
||||
// Allow all connections.
|
||||
// Comment this section out if you want to define specific restrictions.
|
||||
{
|
||||
"src": ["*"],
|
||||
"dst": ["*"],
|
||||
"ip": ["*"],
|
||||
},
|
||||
|
||||
// Allow users in "group:example" to access "tag:example", but only from
|
||||
// devices that are running macOS and have enabled Tailscale client auto-updating.
|
||||
// {"src": ["group:example"], "dst": ["tag:example"], "ip": ["*"], "srcPosture":["posture:autoUpdateMac"]},
|
||||
],
|
||||
|
||||
// Define postures that will be applied to all rules without any specific
|
||||
// srcPosture definition.
|
||||
// "defaultSrcPosture": [
|
||||
// "posture:anyMac",
|
||||
// ],
|
||||
|
||||
// Define device posture rules requiring devices to meet
|
||||
// certain criteria to access parts of your system.
|
||||
// "postures": {
|
||||
// // Require devices running macOS, a stable Tailscale
|
||||
// // version and auto update enabled for Tailscale.
|
||||
// "posture:autoUpdateMac": [
|
||||
// "node:os == 'macos'",
|
||||
// "node:tsReleaseTrack == 'stable'",
|
||||
// "node:tsAutoUpdate",
|
||||
// ],
|
||||
// // Require devices running macOS and a stable
|
||||
// // Tailscale version.
|
||||
// "posture:anyMac": [
|
||||
// "node:os == 'macos'",
|
||||
// "node:tsReleaseTrack == 'stable'",
|
||||
// ],
|
||||
// },
|
||||
|
||||
// Define users and devices that can use Tailscale SSH.
|
||||
"ssh": [
|
||||
// Allow all users to SSH into their own devices in check mode.
|
||||
// Comment this section out if you want to define specific restrictions.
|
||||
{
|
||||
"action": "check",
|
||||
"src": ["autogroup:member"],
|
||||
"dst": ["autogroup:self"],
|
||||
"users": ["autogroup:nonroot", "root"],
|
||||
},
|
||||
// Allow Erich to ssh on to the homelab server.
|
||||
{
|
||||
"src": ["blume.erich@gmail.com"],
|
||||
"dst": ["tag:homelab"],
|
||||
"users": ["autogroup:nonroot"],
|
||||
"action": "check",
|
||||
"checkPeriod": "12h0m0s",
|
||||
},
|
||||
],
|
||||
|
||||
"tagOwners": {
|
||||
// Grafana service host tag
|
||||
"tag:grafana": ["autogroup:admin"],
|
||||
|
||||
// This tag applies to instances which are meant to be accessible in my homelab. These instances can be SSH'ed in to by any member of the admin autogroup.
|
||||
"tag:homelab": ["autogroup:admin"],
|
||||
|
||||
// Kiwix, a local wiki server. I use it to create mirrors of wikipedia.
|
||||
"tag:kiwix": ["autogroup:admin"],
|
||||
|
||||
// Service tag for forgejo, scm host and code forge
|
||||
"tag:forge": ["autogroup:admin"],
|
||||
|
||||
// devpi pypi index
|
||||
"tag:devpi": ["autogroup:admin"],
|
||||
|
||||
// Loki log collection
|
||||
"tag:loki": ["autogroup:admin"],
|
||||
|
||||
// This tag is applied to resources modified by blumeops-pulumi IaC
|
||||
"tag:blumeops": ["autogroup:admin"],
|
||||
},
|
||||
|
||||
// Test access rules every time they're saved.
|
||||
// "tests": [
|
||||
// {
|
||||
// "src": "alice@example.com",
|
||||
// "accept": ["tag:example"],
|
||||
// "deny": ["100.101.102.103:443"],
|
||||
// },
|
||||
// ],
|
||||
}
|
||||
8
pulumi/pyproject.toml
Normal file
8
pulumi/pyproject.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[project]
|
||||
name = "blumeops-tailnet"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"pulumi>=3.0.0",
|
||||
"pulumi-tailscale>=0.24.0",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue