369 lines
14 KiB
Markdown
369 lines
14 KiB
Markdown
|
|
# 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 1–3 (tunnel VPS-end, edge, DNS) → Tasks 2–5 ✓; 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 4–5 (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>
|