Skip to content

feat(network): mint WireGuard peer keys for a non-public path to OpenBao - #53

Merged
nbrieussel merged 3 commits into
mainfrom
feature/openbao-wireguard-tunnel
Aug 11, 2026
Merged

feat(network): mint WireGuard peer keys for a non-public path to OpenBao#53
nbrieussel merged 3 commits into
mainfrom
feature/openbao-wireguard-tunnel

Conversation

@nbrieussel

@nbrieussel nbrieussel commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Terraform's vault provider (05-secrets/openbao/{bootstrap,managed}) no longer needs the public gateway route for its own sake — it authenticates through a self-hosted WireGuard tunnel instead. Human OIDC/UI login (gitops repo's services/platform/openbao/config) is untouched and stays public.

  • New 04-vpn/wireguard domain — explicitly temporary (see its README for why neither 01-iam/ nor 05-secrets/ quite fits a WireGuard peer identity yet). Mints one keypair per peer + the tunnel server's own keypair via OJFord/wireguard.
  • terraform apply generates a ready-to-import wg-quick config per peer (data.wireguard_config_document + local_sensitive_file, written to generated/*.conf, gitignored) — no manual key assembly.
  • wg_endpoint defaults to wg.scalepack.fr:30820, a hostname external-dns keeps pointed at whichever node currently hosts the tunnel server — survives this cluster's daily destroy/rebuild with zero manual intervention.
  • 05-secrets/openbao/{bootstrap,managed}/version.tf: the vault provider now uses the same hostname as the public route (https://openbao.scalepack.fr/) — split-DNS (gitops companion PR) resolves it through the tunnel while up, or the real public route otherwise. Same address either way.
  • 10-cluster/scaleway/main.tf: dedicated scaleway_instance_security_group on the node pool — Scaleway's auto-managed default group ships with zero inbound rules and blocks all direct traffic to a node's public IP, discovered live while debugging the NodePort exposure.
  • New mise run vpn-generate / vpn-up / vpn-down tasks — wg-quick's own bare-name config lookup doesn't find Terraform-generated configs, these handle that plus always forcing the correct Terraform workspace first.

Companion PR in gitops: IntegratedDynamic/gitops#22 (tunnel server, Gateway exposure, split-DNS, generic Gateway passthrough).

Design notes / dead ends ruled out live

  • Routing tunnel traffic to OpenBao's ClusterIP at the kernel level (net.ipv4.ip_forward + iptables MASQUERADE) — blocked by Kapsule's kubelet not allowlisting that sysctl, and Scaleway's kubelet_args API refusing to widen it for this cluster's k8s version at all. Replaced with an application-layer proxy (gitops side).
  • Exposing the tunnel through the shared Envoy Gateway's LoadBalancer — Scaleway's cloud-controller-manager silently drops any non-TCP Service port when building the LB, so a UDPRoute there is never reachable no matter how correctly configured. Moved to a hardened NodePort instead (externalTrafficPolicy: Local + explicit NetworkPolicy + the dedicated security group above).
  • A dedicated proxy-openbao:8200 sidecar bypassing the Gateway entirely for Terraform's API access — reasoned as avoiding Gateway/TLS dependency for a machine credential, but that didn't hold up: OpenBao's HTTPRoute has no path restriction, so it routes through the generic Gateway passthrough exactly like the UI does. Removed in favor of one generic mechanism.

Test plan

  • terraform validate/plan clean on all touched roots
  • terraform providers lock regenerated for both platforms where providers changed
  • Live end-to-end, repeatedly, across a real cluster rebuild: WireGuard handshake, curl to OpenBao through the tunnel, a real terraform plan on 05-secrets/openbao/managed through the tunnel (both the direct-port and final consolidated-hostname address), Grafana OIDC login through the tunnel (proves the split-DNS + Gateway-passthrough design generalizes beyond OpenBao)
  • Not merging yet — branch used for live iteration/testing, per explicit instruction

Nicolas Brieussel and others added 3 commits August 11, 2026 11:03
Mints a WireGuard server keypair plus one keypair per human/CI peer,
renders full wg-quick configs per peer (server pubkey, allowed IPs,
persistent keepalive, split-DNS pointing scalepack.fr at the tunnel's own
address). Endpoint is a stable external-dns hostname
(wg.scalepack.fr:30820), not a raw node IP, so it survives node/cluster
churn.

Peer private keys are never bulk-written to disk: `peer_confs` is a
sensitive output, and mise's vpn-generate/vpn-up/vpn-down tasks extract
and manage exactly one peer's own file at a time — anyone running
`terraform apply` on this root doesn't end up with every peer's plaintext
key on their machine.

Goal: eliminate OpenBao's public gateway route as a dependency for this
repo's own `vault` provider, without a third-party control plane (no
Tailscale). Human OIDC/UI login via Dex stays on the public route,
untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZMCuQ5t9hdrmUctLk7hEv
Kapsule's default "default security group" allows zero inbound traffic.
Adds a scaleway_instance_security_group with a default-drop stance plus
one explicit allow rule for UDP/30820 (the WireGuard NodePort), attached
to the node pool via security_group_id — an in-place update, not a pool
recreate (verified against the provider's Go source: that field isn't
ForceNew, despite an adjacent field's ForceNew warning in the docs being
easy to misattribute to it).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZMCuQ5t9hdrmUctLk7hEv
…-paste

The `vault` provider in 05-secrets/openbao/managed now defaults to
openbao.scalepack.fr resolving through the WireGuard tunnel's split-DNS
(same hostname either way — falls back to the real public route if the
tunnel's down, which is fine, that route still exists for human OIDC/UI
login). A commented-out direct port-forward address is kept as a
documented fallback for breaking any future tunnel/OpenBao chicken-and-egg
deadlock.

Both the WireGuard server private key and the CI peer's private key are
now read straight from 04-vpn/wireguard's state via terraform_remote_state
instead of hand-copied between files, matching this repo's existing
convention for cross-root secrets. Also syncs full rendered peer configs
into OpenBao (apps/wireguard/confs) for self-service retrieval without
needing local Terraform state.

data_json_wo_version bumped 1->2 on both new WireGuard KV secrets: write-
only diffing can't detect a raft snapshot restoring OpenBao from before
these were first written (confirmed live 2026-08-10 after a full cluster
rebuild), so the version bump is what re-triggers the write.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZMCuQ5t9hdrmUctLk7hEv
@nbrieussel
nbrieussel force-pushed the feature/openbao-wireguard-tunnel branch from c7a9833 to f70d292 Compare August 11, 2026 09:04
@nbrieussel
nbrieussel merged commit 6c03117 into main Aug 11, 2026
3 of 8 checks passed
@nbrieussel
nbrieussel deleted the feature/openbao-wireguard-tunnel branch August 11, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant