Add tag:edge to Tailscale ACL, rotate OAuth client, fix edge server type #424

Merged
ldraney merged 2 commits from edge-node-apply-fixes into main 2026-06-13 12:53:48 +00:00
Owner

Summary

Fixes discovered during tofu apply of the hetzner-edge module (PR #420). Edge node is live at 178.156.129.142 on edge-proxy.tail5b443a.ts.net.

Changes

  • Added tag:edge to tagOwners and edge→k8s ACL grant in networking module
  • Added tags = ["tag:edge"] to tailscale_tailnet_key.edge resource
  • Changed default server type from cax11 (ARM, EU-only) to cpx11 (x86, Ashburn-available)
  • Rotated Tailscale OAuth client credentials to new client on correct tailnet

Test Plan

  • tofu apply -target=module.networking — ACL updated
  • tofu apply -target=module.hetzner_edge — server + tailnet key created
  • Edge node joined correct tailnet (tail5b443a.ts.net)
  • Edge node visible to all tailnet peers
  • DNS A record for palinks.app → 178.156.129.142 (post-merge)

Review Checklist

  • No secrets in plaintext (all GPG-encrypted in pillar)
  • Terraform state consistent with live infrastructure
  • ACL grants follow least-privilege (edge only reaches k8s)
  • Closes #419
  • Depends on PRs #420-#423 (all merged)
## Summary Fixes discovered during `tofu apply` of the hetzner-edge module (PR #420). Edge node is live at `178.156.129.142` on `edge-proxy.tail5b443a.ts.net`. ## Changes - Added `tag:edge` to `tagOwners` and edge→k8s ACL grant in networking module - Added `tags = ["tag:edge"]` to `tailscale_tailnet_key.edge` resource - Changed default server type from `cax11` (ARM, EU-only) to `cpx11` (x86, Ashburn-available) - Rotated Tailscale OAuth client credentials to new client on correct tailnet ## Test Plan - [x] `tofu apply -target=module.networking` — ACL updated - [x] `tofu apply -target=module.hetzner_edge` — server + tailnet key created - [x] Edge node joined correct tailnet (`tail5b443a.ts.net`) - [x] Edge node visible to all tailnet peers - [ ] DNS A record for palinks.app → 178.156.129.142 (post-merge) ## Review Checklist - [x] No secrets in plaintext (all GPG-encrypted in pillar) - [x] Terraform state consistent with live infrastructure - [x] ACL grants follow least-privilege (edge only reaches k8s) ## Related Notes - Closes #419 - Depends on PRs #420-#423 (all merged)
Add tag:edge to Tailscale ACL, rotate OAuth client, fix edge server type
Some checks failed
ci/woodpecker/push/terraform Pipeline was successful
ci/woodpecker/pr/terraform Pipeline failed
4e5206bd25
- Add tag:edge to ACL tagOwners and edge->k8s grant in networking module
- Add tags = ["tag:edge"] to tailscale_tailnet_key in hetzner-edge module
- Change default server type from cax11 (ARM, EU-only) to cpx11 (x86, Ashburn)
- Rotate Tailscale OAuth client credentials to new client on correct tailnet

Closes #419

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

PR #424 Review

DOMAIN REVIEW

Tech stack: Terraform (HCL) + Salt (GPG-encrypted YAML pillar). Reviewing against Terraform style, k8s security, and secrets handling checklists.

Tailscale ACL policy (networking/main.tf):

  • tag:edge added to tagOwners with autogroup:admin ownership -- consistent with existing tag:k8s pattern.
  • Edge-to-k8s ACL grant (src=tag:edge, dst=tag:k8s, ip=*) follows least privilege: edge can only reach k8s-tagged nodes, not admin devices or the broader tailnet. Good scoping for a reverse proxy role.
  • Comment on the grant block clearly documents intent. Placement in the grants list is logically ordered.

Tailnet key tagging (hetzner-edge/main.tf):

  • tags = ["tag:edge"] on the tailscale_tailnet_key.edge resource ensures the edge node joins the tailnet with the correct tag, which the ACL grant requires. Without this, the node would be untagged and the ACL would not match. Correct and necessary.

Server type change (hetzner-edge/variables.tf):

  • cax11 (ARM, EU-only) to cpx11 (x86, Ashburn-available). Operational reason documented in PR body. No correctness concern.

Credential rotation (salt/pillar/secrets/platform.sls):

  • Both tailscale_oauth_client_id and tailscale_oauth_client_secret GPG ciphertext blobs replaced. All values remain GPG-encrypted; no plaintext secrets in the diff. The #!yaml|gpg shebang is intact for Salt GPG renderer.

Root variables (terraform/variables.tf):

  • Spurious blank line added at EOF. Non-functional, but tofu fmt should be the arbiter -- see nit below.

BLOCKERS

None.

  • No plaintext secrets in the diff -- all credentials are GPG-encrypted.
  • No unvalidated user input (infrastructure-as-code, no user-facing input paths).
  • No DRY violations -- the ACL grant pattern is consistent with existing grants.
  • No scope creep -- every change is directly related to making the edge node functional.

NITS

  1. Trailing blank line in terraform/variables.tf: The diff adds a blank line at EOF. Run tofu fmt to normalize -- it will either keep or strip it consistently. Minor, but keeps the repo clean.
  2. ip = ["*"] on edge-to-k8s grant: This allows all ports. For a reverse proxy this is reasonable (it needs to reach whatever k8s services expose), but if the edge proxy will only ever hit HTTP/HTTPS, consider tightening to ip = ["80", "443"] in a future iteration. Not a blocker for this PR since the proxy needs may vary.

SOP COMPLIANCE

  • PR body has Summary, Changes, Test Plan, Related sections
  • Test plan includes concrete verification steps (tofu apply targets, tailnet visibility check)
  • No secrets or credentials in plaintext
  • No .env files committed
  • All changes are in scope (ACL, tagging, server type, credential rotation -- all related to edge node apply)
  • Commit message is descriptive
  • Closes #419 referenced in PR body
  • Dependency chain (#420-#423) documented

PROCESS OBSERVATIONS

  • This PR is the final piece of a 5-PR chain (#420-#424) to stand up the edge node. Clean decomposition into discrete PRs (module, secrets, wiring, fixes).
  • Test plan notes DNS A record as post-merge, which is appropriate -- the edge node IP needs to be stable first.
  • Credential rotation as part of the same PR that fixes the edge node is pragmatic (avoids a separate PR for just the secret swap).

VERDICT: APPROVED

## PR #424 Review ### DOMAIN REVIEW **Tech stack:** Terraform (HCL) + Salt (GPG-encrypted YAML pillar). Reviewing against Terraform style, k8s security, and secrets handling checklists. **Tailscale ACL policy (`networking/main.tf`):** - `tag:edge` added to `tagOwners` with `autogroup:admin` ownership -- consistent with existing `tag:k8s` pattern. - Edge-to-k8s ACL grant (`src=tag:edge, dst=tag:k8s, ip=*`) follows least privilege: edge can only reach k8s-tagged nodes, not admin devices or the broader tailnet. Good scoping for a reverse proxy role. - Comment on the grant block clearly documents intent. Placement in the grants list is logically ordered. **Tailnet key tagging (`hetzner-edge/main.tf`):** - `tags = ["tag:edge"]` on the `tailscale_tailnet_key.edge` resource ensures the edge node joins the tailnet with the correct tag, which the ACL grant requires. Without this, the node would be untagged and the ACL would not match. Correct and necessary. **Server type change (`hetzner-edge/variables.tf`):** - `cax11` (ARM, EU-only) to `cpx11` (x86, Ashburn-available). Operational reason documented in PR body. No correctness concern. **Credential rotation (`salt/pillar/secrets/platform.sls`):** - Both `tailscale_oauth_client_id` and `tailscale_oauth_client_secret` GPG ciphertext blobs replaced. All values remain GPG-encrypted; no plaintext secrets in the diff. The `#!yaml|gpg` shebang is intact for Salt GPG renderer. **Root variables (`terraform/variables.tf`):** - Spurious blank line added at EOF. Non-functional, but `tofu fmt` should be the arbiter -- see nit below. ### BLOCKERS None. - No plaintext secrets in the diff -- all credentials are GPG-encrypted. - No unvalidated user input (infrastructure-as-code, no user-facing input paths). - No DRY violations -- the ACL grant pattern is consistent with existing grants. - No scope creep -- every change is directly related to making the edge node functional. ### NITS 1. **Trailing blank line in `terraform/variables.tf`:** The diff adds a blank line at EOF. Run `tofu fmt` to normalize -- it will either keep or strip it consistently. Minor, but keeps the repo clean. 2. **`ip = ["*"]` on edge-to-k8s grant:** This allows all ports. For a reverse proxy this is reasonable (it needs to reach whatever k8s services expose), but if the edge proxy will only ever hit HTTP/HTTPS, consider tightening to `ip = ["80", "443"]` in a future iteration. Not a blocker for this PR since the proxy needs may vary. ### SOP COMPLIANCE - [x] PR body has Summary, Changes, Test Plan, Related sections - [x] Test plan includes concrete verification steps (tofu apply targets, tailnet visibility check) - [x] No secrets or credentials in plaintext - [x] No .env files committed - [x] All changes are in scope (ACL, tagging, server type, credential rotation -- all related to edge node apply) - [x] Commit message is descriptive - [x] Closes #419 referenced in PR body - [x] Dependency chain (#420-#423) documented ### PROCESS OBSERVATIONS - This PR is the final piece of a 5-PR chain (#420-#424) to stand up the edge node. Clean decomposition into discrete PRs (module, secrets, wiring, fixes). - Test plan notes DNS A record as post-merge, which is appropriate -- the edge node IP needs to be stable first. - Credential rotation as part of the same PR that fixes the edge node is pragmatic (avoids a separate PR for just the secret swap). ### VERDICT: APPROVED
Update hetzner-edge docs: cpx11 server type and live IPv4
All checks were successful
ci/woodpecker/push/terraform Pipeline was successful
ci/woodpecker/pr/terraform Pipeline was successful
ci/woodpecker/pull_request_closed/terraform Pipeline was successful
063196cc39
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ldraney deleted branch edge-node-apply-fixes 2026-06-13 12:53:48 +00:00
Sign in to join this conversation.
No description provided.