MakerFLOSS_Troubleshooting/docs/superpowers/plans/2026-06-28-tappaas-vps-publishing.md
sjat 2e3f38cb3a plan: TaPPaaS VPS-side publishing implementation
Bite-sized tasks for the AnsibleBaobabV4 changes: flossfw wg1 peer +
keypair, *.tappaas wildcard cert, catch-all delegate route, public DNS.
VPS-side deliverable = valid-TLS 502; end-to-end gated on TaPPaaS-side plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 10:33:10 +02:00

368 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TaPPaaS VPS Publishing (VPS side) — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Wire the makerfloss VPS to publish `*.tappaas.makerfloss.eu` — add a `wg1` peer for the TaPPaaS FLOSSFirewall, issue the wildcard cert, add the catch-all route to the backend, and publish the public DNS — reusing the proven mf01 pattern.
**Architecture:** All changes are additive edits to `AnsibleBaobabV4/host_vars/makerfloss.yml` (plus one vault entry), deployed with the repo's existing plays. TLS terminates at the VPS; a catch-all Traefik router forwards plain HTTP over `wg1` to the FLOSSFirewall/Caddy backend at `10.13.0.9:80`. This plan delivers the **VPS edge only** — end-to-end `200` awaits the separate TaPPaaS-side plan, so the success criterion here is a valid-TLS `502` (route works, backend not yet up).
**Tech Stack:** Ansible (baobab roles `baobab.wireguard_server`, `baobab.traefik`, `baobab.gandi_dns`), Traefik (Gandi DNS-01 ACME), WireGuard, `wireguard-tools`.
## Global Constraints
- **Target repo/branch:** changes land in `~/Projects/AnsibleBaobabV4` on **`main`**. That repo currently has unrelated WIP on `feat/loki-review-staleness-alert` — before starting, get these commits onto `main` cleanly (stash/commit the WIP, or branch off `main` and fast-forward `main` when done). Do **not** mix this work into the loki feature branch.
- **All ansible commands run from** `~/Projects/AnsibleBaobabV4` (repo root) using `.venv/bin/ansible-playbook`.
- **Inventory + limit always:** `-i inventories/prod/hosts.yml --limit makerfloss`.
- **Vault is auto-loaded** from `~/.ansible/vault-keys/prod.txt` via `ansible.cfg` (`vault_identity_list = prod@...`). Never pass `--vault-id` manually; never print vault plaintext into a commit or log.
- **Idempotency (repo rule):** every deploy task runs the play **twice**; the second run must report `changed=0`.
- **Peer naming/addressing (decided):** peer name `flossfw`, `wg1` IP **`10.13.0.9/32`** (confirmed free; taken = `.1 .2 .3 .5 .6 .8`).
- **Test hostname:** use `whoami.tappaas.makerfloss.eu` throughout — any label under the wildcard works; nothing needs to exist at the backend for the VPS-side checks.
- **Reach path:** all verification targets the **public VPS** (`makerfloss.eu` / `88.99.32.236`, SSH `:7576`) — no makerspace tunnel needed (access.md path B2).
- **Never commit secrets.** The keypair lives only in the encrypted vault.
## File Structure
- `~/Projects/AnsibleBaobabV4/group_vars/all/90-secrets.vault.yml` *(modify, encrypted)* — adds `flossfw` keypair under `vault_wireguard_makerfloss_peers`.
- `~/Projects/AnsibleBaobabV4/host_vars/makerfloss.yml` *(modify)* — adds, in four distinct sections: the `wg1` peer, the wildcard cert set, the catch-all dynamic route, and the public DNS records.
No new files; no TaPPaaS-side files (separate plan).
---
### Task 1: Generate the FLOSSFirewall WireGuard keypair and store it in the vault
**Files:**
- Modify: `~/Projects/AnsibleBaobabV4/group_vars/all/90-secrets.vault.yml` (encrypted)
**Interfaces:**
- Produces: vault var `vault_wireguard_makerfloss_peers.flossfw.public_key` (and `.private_key`), referenced by Task 2.
- [ ] **Step 1: Ensure `wg` is available**
Run:
```bash
command -v wg || sudo apt-get install -y wireguard-tools
```
Expected: a path like `/usr/bin/wg`.
- [ ] **Step 2: Generate the keypair (private never leaves tmpfs until vaulted)**
Run:
```bash
umask 077
wg genkey | tee /dev/shm/flossfw.key | wg pubkey > /dev/shm/flossfw.pub
echo "PRIV=$(cat /dev/shm/flossfw.key)"
echo "PUB=$(cat /dev/shm/flossfw.pub)"
```
Expected: two base64 strings (44 chars ending `=`).
- [ ] **Step 3: Add the entry to the vault**
Run:
```bash
cd ~/Projects/AnsibleBaobabV4
.venv/bin/ansible-vault edit group_vars/all/90-secrets.vault.yml
```
Under the existing `vault_wireguard_makerfloss_peers:` mapping, add (paste the real values from Step 2 in place of `PRIV`/`PUB`):
```yaml
flossfw:
private_key: "PRIV"
public_key: "PUB"
```
Save and close the editor.
- [ ] **Step 4: Verify the entry decrypts and parses**
Run:
```bash
cd ~/Projects/AnsibleBaobabV4
.venv/bin/ansible-vault view group_vars/all/90-secrets.vault.yml | grep -A3 'flossfw:'
```
Expected: the `flossfw:` block with `private_key`/`public_key` printed. (This is local stdout only — do not redirect to a file.)
- [ ] **Step 5: Shred the tmp keys**
Run:
```bash
shred -u /dev/shm/flossfw.key /dev/shm/flossfw.pub 2>/dev/null; rm -f /dev/shm/flossfw.key /dev/shm/flossfw.pub
```
Expected: files gone (`ls /dev/shm/flossfw.* ` → no such file). The keys now live only in the vault.
- [ ] **Step 6: Commit**
```bash
cd ~/Projects/AnsibleBaobabV4
git add group_vars/all/90-secrets.vault.yml
git commit -m "secrets: add flossfw wireguard keypair for tappaas wg1 peer"
```
---
### Task 2: Add the FLOSSFirewall as a `wg1` peer and deploy
**Files:**
- Modify: `~/Projects/AnsibleBaobabV4/host_vars/makerfloss.yml` (`wireguard_server_peers`)
**Interfaces:**
- Consumes: `vault_wireguard_makerfloss_peers.flossfw.public_key` (Task 1).
- Produces: VPS `wg1` allows `10.13.0.9/32` — the backend address Task 4 routes to.
- [ ] **Step 1: Add the peer entry**
In `host_vars/makerfloss.yml`, append to the `wireguard_server_peers:` list (after the `mf01` entry), matching the existing style exactly:
```yaml
- name: "flossfw"
public_key: "{{ vault_wireguard_makerfloss_peers.flossfw.public_key }}"
allowed_ips: "10.13.0.9/32"
managed: true
description: "TaPPaaS FLOSSFirewall (Caddy reverse proxy, split-tunnel)"
generate_config: false # client config deployed on the TaPPaaS side
```
- [ ] **Step 2: Deploy the WireGuard server config**
Run:
```bash
cd ~/Projects/AnsibleBaobabV4
.venv/bin/ansible-playbook play_setup.yml -i inventories/prod/hosts.yml --limit makerfloss -t wireguard-server,firewall
```
Expected: PLAY RECAP with `failed=0`, `changed>=1`.
- [ ] **Step 3: Confirm idempotence (run again)**
Run the exact command from Step 2 again.
Expected: PLAY RECAP `changed=0`.
- [ ] **Step 4: Verify the peer is live on the VPS**
Run:
```bash
ssh -p 7576 sjat@makerfloss.eu "sudo wg show wg1 allowed-ips" | grep -F '10.13.0.9/32'
```
Expected: one line containing the `flossfw` public key and `10.13.0.9/32`. (No handshake yet — the FLOSSFirewall isn't up; that's expected.)
- [ ] **Step 5: Commit**
```bash
cd ~/Projects/AnsibleBaobabV4
git add host_vars/makerfloss.yml
git commit -m "wg1: add flossfw peer (10.13.0.9) for tappaas publishing"
```
---
### Task 3: Add the `*.tappaas` wildcard cert set and issue it
**Files:**
- Modify: `~/Projects/AnsibleBaobabV4/host_vars/makerfloss.yml` (`traefik_wildcard_sets`)
**Interfaces:**
- Produces: a valid `*.tappaas.makerfloss.eu` cert on the VPS Traefik, used by Task 4's router (`tls: {}`).
- [ ] **Step 1: Add the wildcard set**
In `host_vars/makerfloss.yml`, append to `traefik_wildcard_sets:` (after the `mf01` entry):
```yaml
- main: "tappaas.makerfloss.eu"
sans: ["*.tappaas.makerfloss.eu"]
```
- [ ] **Step 2: Deploy Traefik (triggers ACME via the anchor router)**
Run:
```bash
cd ~/Projects/AnsibleBaobabV4
.venv/bin/ansible-playbook play_containers.yml -i inventories/prod/hosts.yml --limit makerfloss -t traefik
```
Expected: PLAY RECAP `failed=0`; the traefik container is recreated.
- [ ] **Step 3: Confirm idempotence (run again)**
Run the Step 2 command again.
Expected: PLAY RECAP `changed=0`.
- [ ] **Step 4: Watch ACME issue the cert**
Run (wait up to ~90s for DNS-01 to complete):
```bash
ssh -p 7576 sjat@makerfloss.eu "docker logs traefik 2>&1 | grep -i tappaas | tail -20"
```
Expected: ACME log lines referencing `tappaas.makerfloss.eu` / `*.tappaas.makerfloss.eu` with no error; eventually a "certificate obtained" style entry.
- [ ] **Step 5: Verify the served cert (by IP + SNI — no DNS needed yet)**
Run:
```bash
echo | openssl s_client -connect 88.99.32.236:443 -servername whoami.tappaas.makerfloss.eu 2>/dev/null | openssl x509 -noout -ext subjectAltName
```
Expected: SAN list contains `*.tappaas.makerfloss.eu`.
- [ ] **Step 6: Commit**
```bash
cd ~/Projects/AnsibleBaobabV4
git add host_vars/makerfloss.yml
git commit -m "traefik: add *.tappaas.makerfloss.eu wildcard cert set"
```
---
### Task 4: Add the catch-all delegate route to the backend and deploy
**Files:**
- Modify: `~/Projects/AnsibleBaobabV4/host_vars/makerfloss.yml` (`traefik_extra_dynamic_files`)
**Interfaces:**
- Consumes: the wildcard cert (Task 3) via `tls: {}`; the `10.13.0.9` peer (Task 2).
- Produces: VPS routes `*.tappaas``http://10.13.0.9:80`.
- [ ] **Step 1: Add the dynamic route file**
In `host_vars/makerfloss.yml`, add a new key under `traefik_extra_dynamic_files:` (sibling of `mf01-delegate.yml`). **Keep the regex single-quoted** — a YAML error here breaks the whole file provider:
```yaml
tappaas-delegate.yml: |
http:
routers:
tappaas-wildcard:
rule: 'HostRegexp(`^.+\.tappaas\.makerfloss\.eu$`)'
entryPoints:
- websecure
service: tappaas-backend
tls: {}
services:
tappaas-backend:
loadBalancer:
passHostHeader: true
servers:
- url: "http://10.13.0.9:80"
```
- [ ] **Step 2: Deploy Traefik**
Run:
```bash
cd ~/Projects/AnsibleBaobabV4
.venv/bin/ansible-playbook play_containers.yml -i inventories/prod/hosts.yml --limit makerfloss -t traefik
```
Expected: PLAY RECAP `failed=0`.
- [ ] **Step 3: Confirm idempotence (run again)**
Run the Step 2 command again.
Expected: PLAY RECAP `changed=0`.
- [ ] **Step 4: Verify the dynamic file parsed (no file-provider error)**
Run:
```bash
ssh -p 7576 sjat@makerfloss.eu "docker logs traefik 2>&1 | grep -iE 'error|tappaas-delegate' | tail -20"
```
Expected: no parse/error lines mentioning `tappaas-delegate.yml`.
- [ ] **Step 5: Verify the route matches (502 = success here)**
Run:
```bash
curl -sk -o /dev/null -w '%{http_code}\n' -H 'Host: whoami.tappaas.makerfloss.eu' https://88.99.32.236/
```
Expected: `502` (or `504`). The router matched and tried the backend; `10.13.0.9:80` isn't up yet, which is the expected VPS-side end state. A `404` would mean the route didn't match — investigate before committing.
- [ ] **Step 6: Commit**
```bash
cd ~/Projects/AnsibleBaobabV4
git add host_vars/makerfloss.yml
git commit -m "traefik: route *.tappaas to flossfw backend over wg1"
```
---
### Task 5: Publish the public DNS records and deploy
**Files:**
- Modify: `~/Projects/AnsibleBaobabV4/host_vars/makerfloss.yml` (`gandi_dns_records`)
**Interfaces:**
- Produces: public A records `tappaas` and `*.tappaas``88.99.32.236`.
- [ ] **Step 1: Add the records**
In `host_vars/makerfloss.yml`, append to `gandi_dns_records:` (after the `*.mf01` entry), matching the existing style:
```yaml
- name: "tappaas"
type: A
ttl: 300
values: ["88.99.32.236"]
- name: "*.tappaas"
type: A
ttl: 300
values: ["88.99.32.236"]
```
- [ ] **Step 2: Deploy DNS**
Run:
```bash
cd ~/Projects/AnsibleBaobabV4
.venv/bin/ansible-playbook play_dns.yml -i inventories/prod/hosts.yml --limit makerfloss -t gandi-dns
```
Expected: PLAY RECAP `failed=0`; task output shows the two records added.
- [ ] **Step 3: Confirm idempotence (run again)**
Run the Step 2 command again.
Expected: PLAY RECAP `changed=0` (records already present).
- [ ] **Step 4: Verify resolution**
Run (allow up to the 300s TTL to propagate):
```bash
dig +short whoami.tappaas.makerfloss.eu @1.1.1.1
dig +short tappaas.makerfloss.eu @1.1.1.1
```
Expected: both return `88.99.32.236`.
- [ ] **Step 5: Commit**
```bash
cd ~/Projects/AnsibleBaobabV4
git add host_vars/makerfloss.yml
git commit -m "dns: publish tappaas + *.tappaas A records to the VPS"
```
---
### Task 6: VPS-side integration verification (no code change)
**Files:** none — verification gate.
- [ ] **Step 1: End-to-end VPS edge check**
Run:
```bash
curl -sk -o /dev/null -w 'http=%{http_code} cert_subject=%{ssl_verify_result}\n' https://whoami.tappaas.makerfloss.eu/
echo | openssl s_client -connect whoami.tappaas.makerfloss.eu:443 -servername whoami.tappaas.makerfloss.eu 2>/dev/null | openssl x509 -noout -ext subjectAltName | grep -F '*.tappaas.makerfloss.eu'
```
Expected: real public DNS resolves to the VPS, TLS validates against the `*.tappaas` wildcard, and HTTP returns `502`/`504` (backend not yet up). This is the **completed VPS-side deliverable**.
- [ ] **Step 2: Record the result**
Note in the session/PR: VPS edge ready; `200 OK` is gated on the **TaPPaaS-side plan** (FLOSSFirewall WireGuard client + Caddy plain-HTTP backend on `10.13.0.9:80` + internal DNS). That plan is blocked on identifying the TaPPaaS config repo (spec Open items). Optionally add a short note to `MakerFLOSS_Troubleshooting/runbooks/` pointing at this pattern (mirror of `publishing-services-mf01.md`).
---
## Self-Review
**Spec coverage (VPS scope):**
- Component A.1 wg1 peer → Task 2 ✓ (keypair prereq → Task 1 ✓)
- A.2 wildcard cert → Task 3 ✓
- A.3 catch-all route → Task 4 ✓
- A.4 public DNS → Task 5 ✓
- Phasing steps 13 (tunnel VPS-end, edge, DNS) → Tasks 25 ✓; integration gate → Task 6 ✓
- Isolation: split-tunnel/`AllowedIPs` and the `tcp/80 from 10.13.0.1` firewall are **TaPPaaS-side** — correctly deferred to the other plan, not in scope here.
- Phasing steps 45 (internal DNS, makerspace LAN) → TaPPaaS-side plan, out of scope. **Noted gap by design.**
**Placeholder scan:** No TBD/TODO/"handle errors" placeholders; `PRIV`/`PUB` in Task 1 are explicitly real-value substitutions generated in-step. ✓
**Type/name consistency:** peer name `flossfw`, IP `10.13.0.9`, vault path `vault_wireguard_makerfloss_peers.flossfw.public_key`, service `tappaas-backend`, router `tappaas-wildcard`, dynamic file `tappaas-delegate.yml`, hostname `whoami.tappaas.makerfloss.eu` — all consistent across tasks. ✓
</content>