feat: add Tailscale subnet router for k8s service CIDR #176

Merged
forgejo_admin merged 1 commit from 175-tailscale-subnet-router into main 2026-03-26 22:19:04 +00:00

Summary

  • Adds Tailscale Connector CRD (subnet router) advertising 10.43.0.0/16 to the tailnet
  • Adds autoApprovers to ACL so tag:k8s nodes' subnet routes are auto-approved
  • Enables external tailnet peers (Mac build agent) to reach k8s ClusterIP services directly

Changes

  • terraform/main.tf: Added kubernetes_manifest.tailscale_subnet_router (Connector CRD, 24 lines)
  • terraform/main.tf: Added autoApprovers.routes block to tailscale_acl.this (7 lines)

tofu plan output

Plan: 1 to add, 1 to change, 0 to destroy.

Test Plan

  • tofu validate passes
  • tofu plan -lock=false shows 1 add, 1 change
  • tofu apply creates subnet router pod
  • tailscale status shows k8s-subnet-router advertising 10.43.0.0/16
  • From Mac: ping 10.43.50.207 (Woodpecker ClusterIP) succeeds
  • Mac Woodpecker agent connects to 10.43.50.207:9000 successfully

Review Checklist

  • Passed tofu fmt and tofu validate
  • No secrets committed
  • No unnecessary file changes (1 file, 2 logical changes)
  • Commit message is descriptive
  • Closes #175
  • Blocks #174 (Mac build agent Salt setup)
  • project-capacitor-mobile — consumer of the Mac build agent
## Summary - Adds Tailscale Connector CRD (subnet router) advertising `10.43.0.0/16` to the tailnet - Adds `autoApprovers` to ACL so `tag:k8s` nodes' subnet routes are auto-approved - Enables external tailnet peers (Mac build agent) to reach k8s ClusterIP services directly ## Changes - `terraform/main.tf`: Added `kubernetes_manifest.tailscale_subnet_router` (Connector CRD, 24 lines) - `terraform/main.tf`: Added `autoApprovers.routes` block to `tailscale_acl.this` (7 lines) ### tofu plan output ``` Plan: 1 to add, 1 to change, 0 to destroy. ``` ## Test Plan - [x] `tofu validate` passes - [x] `tofu plan -lock=false` shows 1 add, 1 change - [ ] `tofu apply` creates subnet router pod - [ ] `tailscale status` shows `k8s-subnet-router` advertising `10.43.0.0/16` - [ ] From Mac: `ping 10.43.50.207` (Woodpecker ClusterIP) succeeds - [ ] Mac Woodpecker agent connects to `10.43.50.207:9000` successfully ## Review Checklist - [x] Passed `tofu fmt` and `tofu validate` - [x] No secrets committed - [x] No unnecessary file changes (1 file, 2 logical changes) - [x] Commit message is descriptive ## Related - Closes #175 - Blocks #174 (Mac build agent Salt setup) - `project-capacitor-mobile` — consumer of the Mac build agent
feat: add Tailscale subnet router for k8s service CIDR
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
47ab84533c
Adds a Tailscale Connector (subnet router) that advertises 10.43.0.0/16
to the tailnet, enabling external peers (Mac build agent) to reach k8s
ClusterIP services directly over WireGuard. Also adds autoApprovers to
the ACL so subnet routes from tag:k8s nodes are auto-approved.

This replaces the non-functional gRPC funnel approach — Tailscale funnels
proxy as HTTP/1.1 which breaks gRPC's HTTP/2 framing. Subnet routing
provides raw TCP access without protocol mangling.

Closes #175

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Owner

PR #176 Review

DOMAIN REVIEW

Tech stack: OpenTofu / Terraform -- Tailscale ACL policy + Kubernetes CRD (Tailscale Connector). Two logical changes in one file (terraform/main.tf), 33 additions, 0 deletions.

Change 1: autoApprovers in ACL policy (lines 93-99)

Correct placement inside the tailscale_acl.this resource's jsonencode block. The autoApprovers.routes structure maps CIDR to tag owners, which is the documented Tailscale ACL schema. This ensures subnet routes advertised by tag:k8s nodes are auto-approved without manual admin console intervention. Sound pattern -- eliminates a manual approval step that would break automation.

Change 2: kubernetes_manifest.tailscale_subnet_router (lines 113-131)

  • Uses tailscale.com/v1alpha1 Connector CRD, which is the correct API version for the Tailscale k8s operator's subnet router feature.
  • Connector is a cluster-scoped CRD -- no namespace in metadata is correct.
  • depends_on correctly references both helm_release.tailscale_operator (CRD must exist before the CR) and tailscale_acl.this (auto-approver must be in place before routes are advertised). This ordering prevents a race where the route would be advertised but not auto-approved.
  • hostname field gives the node a deterministic name in the tailnet (k8s-subnet-router), which aids operational visibility in tailscale status.

Observations on the CIDR 10.43.0.0/16:

The CIDR 10.43.0.0/16 appears in three places across the repo: salt/pillar/firewall.sls:26, the new autoApprovers block, and the new advertiseRoutes block. The two new occurrences in this PR are tightly coupled (they must match) and co-located in the same file 27 lines apart. A local would be reasonable but is not a blocker given the tight proximity and the fact that the Salt pillar reference is in a different IaC domain entirely. The inline comments clearly label the value's purpose.

No field_manager block needed: Unlike NetworkPolicy resources (fixed in PR #156), the Connector CRD is a brand-new resource with no pre-existing server-side state, so no field manager conflict is expected.

BLOCKERS

None.

This is a pure infrastructure change (Terraform CRD + ACL policy). There is no application code, no user input, no secrets, no auth logic. The BLOCKER criteria do not apply:

  • No new functionality requiring test coverage (infrastructure is validated by tofu plan + post-apply verification, which is documented in the Test Plan).
  • No user input to validate.
  • No secrets in code (the CIDR is a well-known cluster network range, not a credential).
  • No DRY violation in auth/security paths.

NITS

  1. Consider a locals block for the service CIDR: 10.43.0.0/16 appears twice in this PR (line 97 and line 124). A locals { k8s_service_cidr = "10.43.0.0/16" } would create a single source of truth within the Terraform layer. Low priority given the co-location and clear comments.

  2. Connector CRD API version is v1alpha1: This is the current correct version for the Tailscale operator, but alpha APIs can change without notice. Worth a comment or a pinned operator version check. The operator is already pinned at 1.94.2 (line 19), which mitigates this -- just noting for future upgrade awareness.

SOP COMPLIANCE

  • Branch named after issue: 175-tailscale-subnet-router matches issue #175
  • PR body has: Summary, Changes, Test Plan, Related -- all present and well-structured
  • Related section references parent issue: "Closes #175", also notes blocking relationship to #174
  • Related references plan slug: project-capacitor-mobile referenced as the consumer
  • tofu fmt and tofu validate confirmed passing (per PR checklist)
  • tofu plan -lock=false output included: "Plan: 1 to add, 1 to change, 0 to destroy"
  • No secrets committed -- CIDR is public infrastructure knowledge, not a credential
  • No unnecessary file changes -- 1 file, 2 tightly related logical changes
  • Commit message is descriptive

PROCESS OBSERVATIONS

  • Deployment frequency: Clean single-purpose PR that unblocks #174 (Mac build agent). Fast merge path.
  • Change failure risk: Low. The depends_on ordering is correct, the CRD is well-documented, and the ACL auto-approver is idempotent. Rollback is straightforward (tofu destroy -target=kubernetes_manifest.tailscale_subnet_router).
  • Post-apply verification items in Test Plan are unchecked: The tofu apply, tailscale status, and connectivity checks are correctly left unchecked (pre-merge). These should be verified after apply.
  • Scope is tight: Two related changes, one file, one issue. No scope creep.

VERDICT: APPROVED

## PR #176 Review ### DOMAIN REVIEW **Tech stack**: OpenTofu / Terraform -- Tailscale ACL policy + Kubernetes CRD (Tailscale Connector). Two logical changes in one file (`terraform/main.tf`), 33 additions, 0 deletions. **Change 1: `autoApprovers` in ACL policy (lines 93-99)** Correct placement inside the `tailscale_acl.this` resource's `jsonencode` block. The `autoApprovers.routes` structure maps CIDR to tag owners, which is the documented Tailscale ACL schema. This ensures subnet routes advertised by `tag:k8s` nodes are auto-approved without manual admin console intervention. Sound pattern -- eliminates a manual approval step that would break automation. **Change 2: `kubernetes_manifest.tailscale_subnet_router` (lines 113-131)** - Uses `tailscale.com/v1alpha1` Connector CRD, which is the correct API version for the Tailscale k8s operator's subnet router feature. - Connector is a cluster-scoped CRD -- no `namespace` in metadata is correct. - `depends_on` correctly references both `helm_release.tailscale_operator` (CRD must exist before the CR) and `tailscale_acl.this` (auto-approver must be in place before routes are advertised). This ordering prevents a race where the route would be advertised but not auto-approved. - `hostname` field gives the node a deterministic name in the tailnet (`k8s-subnet-router`), which aids operational visibility in `tailscale status`. **Observations on the CIDR `10.43.0.0/16`**: The CIDR `10.43.0.0/16` appears in three places across the repo: `salt/pillar/firewall.sls:26`, the new `autoApprovers` block, and the new `advertiseRoutes` block. The two new occurrences in this PR are tightly coupled (they must match) and co-located in the same file 27 lines apart. A `local` would be reasonable but is not a blocker given the tight proximity and the fact that the Salt pillar reference is in a different IaC domain entirely. The inline comments clearly label the value's purpose. **No `field_manager` block needed**: Unlike NetworkPolicy resources (fixed in PR #156), the Connector CRD is a brand-new resource with no pre-existing server-side state, so no field manager conflict is expected. ### BLOCKERS None. This is a pure infrastructure change (Terraform CRD + ACL policy). There is no application code, no user input, no secrets, no auth logic. The BLOCKER criteria do not apply: - No new functionality requiring test coverage (infrastructure is validated by `tofu plan` + post-apply verification, which is documented in the Test Plan). - No user input to validate. - No secrets in code (the CIDR is a well-known cluster network range, not a credential). - No DRY violation in auth/security paths. ### NITS 1. **Consider a `locals` block for the service CIDR**: `10.43.0.0/16` appears twice in this PR (line 97 and line 124). A `locals { k8s_service_cidr = "10.43.0.0/16" }` would create a single source of truth within the Terraform layer. Low priority given the co-location and clear comments. 2. **Connector CRD API version is `v1alpha1`**: This is the current correct version for the Tailscale operator, but alpha APIs can change without notice. Worth a comment or a pinned operator version check. The operator is already pinned at `1.94.2` (line 19), which mitigates this -- just noting for future upgrade awareness. ### SOP COMPLIANCE - [x] Branch named after issue: `175-tailscale-subnet-router` matches issue #175 - [x] PR body has: Summary, Changes, Test Plan, Related -- all present and well-structured - [x] Related section references parent issue: "Closes #175", also notes blocking relationship to #174 - [x] Related references plan slug: `project-capacitor-mobile` referenced as the consumer - [x] `tofu fmt` and `tofu validate` confirmed passing (per PR checklist) - [x] `tofu plan -lock=false` output included: "Plan: 1 to add, 1 to change, 0 to destroy" - [x] No secrets committed -- CIDR is public infrastructure knowledge, not a credential - [x] No unnecessary file changes -- 1 file, 2 tightly related logical changes - [x] Commit message is descriptive ### PROCESS OBSERVATIONS - **Deployment frequency**: Clean single-purpose PR that unblocks #174 (Mac build agent). Fast merge path. - **Change failure risk**: Low. The `depends_on` ordering is correct, the CRD is well-documented, and the ACL auto-approver is idempotent. Rollback is straightforward (`tofu destroy -target=kubernetes_manifest.tailscale_subnet_router`). - **Post-apply verification items in Test Plan are unchecked**: The `tofu apply`, `tailscale status`, and connectivity checks are correctly left unchecked (pre-merge). These should be verified after apply. - **Scope is tight**: Two related changes, one file, one issue. No scope creep. ### VERDICT: APPROVED
forgejo_admin deleted branch 175-tailscale-subnet-router 2026-03-26 22:19:04 +00:00
Sign in to join this conversation.
No description provided.