docs(runbook): tailscale-setup plan + uitvoering-addendum
Legt het plan voor de zelf-gehoste Scrum4Me-omgeving (Postgres + app via Tailscale bereikbaar) versioned vast in de repo, i.p.v. los in ~/Downloads. Bevat: - Origineel plan (Deel A/B/C/D) zoals opgesteld — host-Postgres + host-nginx/Caddy aannames - Addendum met de feitelijke uitvoering 2026-05-14: de server bleek een Docker-stack, dus per stap de Docker-variant (port-mapping i.p.v. listen_addresses, docker.service boot-order i.p.v. postgresql.service, Caddy al op 0.0.0.0:80, etc.) - Twee bugs onderweg: stale single-file bind-mount en de http://<IP> rode haring - Verificatie-status en lijst gewijzigde bestanden op scrum4me-srv Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5c1f047259
commit
2b746af1a3
1 changed files with 446 additions and 0 deletions
446
docs/runbooks/tailscale-setup.md
Normal file
446
docs/runbooks/tailscale-setup.md
Normal file
|
|
@ -0,0 +1,446 @@
|
||||||
|
# Ubuntu-omgeving (Postgres + app) via Tailscale bereikbaar maken vanaf de Mac
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Er zijn twee Scrum4Me-omgevingen:
|
||||||
|
- **Omgeving 1** — productie: Vercel + Neon (managed Postgres).
|
||||||
|
- **Omgeving 2** — nieuw: een eigen Ubuntu-server (`scrum4me-srv`) die de
|
||||||
|
**volledige Scrum4Me-app (Next.js achter een reverse proxy) + zelf-gehoste
|
||||||
|
Postgres** gaat draaien.
|
||||||
|
|
||||||
|
Het doel: vanaf de Mac (`janpeters-macbook-pro`) omgeving 2 kunnen gebruiken —
|
||||||
|
voor (1) een DB-client (psql/GUI), (2) de `scrum4me-docker` runner lokaal in
|
||||||
|
Docker, en (3) lokale dev van de hoofd-Scrum4Me-app.
|
||||||
|
|
||||||
|
Tailscale is al geïnstalleerd en verbonden op beide machines:
|
||||||
|
- `janpeters-macbook-pro` → `100.73.234.116`
|
||||||
|
- `scrum4me-srv` → `100.118.195.120` (Linux, SSH aan)
|
||||||
|
|
||||||
|
Wat nog ontbreekt: zowel Postgres als de Next.js-app op de Ubuntu-server
|
||||||
|
luisteren standaard alleen op `localhost` en zijn nog niet bereikbaar over de
|
||||||
|
Tailscale-interface. De database zelf is **al volledig ingericht** (schema +
|
||||||
|
data) — er is geen migratie- of seed-werk nodig, alleen netwerk-, auth- en
|
||||||
|
connectie-configuratie.
|
||||||
|
|
||||||
|
**Beslissingen (van de gebruiker):**
|
||||||
|
- App-deploy op Ubuntu: **reverse proxy** (nginx/Caddy) vóór Next.js.
|
||||||
|
- DB-toegang: **hele tailnet** mag erbij (`100.64.0.0/10`) — bewuste keuze;
|
||||||
|
later eventueel te versmallen via Tailscale ACLs/groups.
|
||||||
|
- Postgres-rol: **nog onzeker** — het plan voegt een controle toe en adviseert
|
||||||
|
een dedicated rol.
|
||||||
|
|
||||||
|
**Canonieke `SCRUM4ME_BASE_URL`:** `http://100.118.195.120` (reverse proxy op
|
||||||
|
poort 80 op de Tailscale-interface, **plain HTTP**). Tailscale (WireGuard)
|
||||||
|
verzorgt de transportencryptie binnen de tailnet, dus een tweede TLS-laag is
|
||||||
|
hier niet nodig. Dit raw-IP-adres resolvet ook vanuit een Docker-container
|
||||||
|
(geen MagicDNS-afhankelijkheid). HTTPS op de proxy is optionele hardening — zie
|
||||||
|
de noot onderaan; kies je daarvoor, gebruik dan een hostnaam die óók vanuit
|
||||||
|
Docker oplost en pas álle URL's hieronder consistent aan.
|
||||||
|
|
||||||
|
**Bevinding uit de codebase:** in `scrum4me-docker` is de DB-koppeling puur
|
||||||
|
config. Zowel `bin/run-one-job.ts` (regel 30, 115) als de MCP-server
|
||||||
|
(`mcp-config.json` regel 9-10) lezen `DATABASE_URL` / `DIRECT_URL` uit de
|
||||||
|
omgeving. `bin/check-tokens.sh` (regel 35-38) doet bovendien een harde
|
||||||
|
`curl ${SCRUM4ME_BASE_URL}/api/products` — onbereikbaarheid is fataal
|
||||||
|
(regel 52-57). Er zijn **geen code-wijzigingen** nodig — alleen `.env`.
|
||||||
|
|
||||||
|
## Voorwaarden (aantoonbaar voldaan vóór uitvoering)
|
||||||
|
|
||||||
|
- [ ] Tailscale actief op beide machines (`tailscale status` toont beide nodes)
|
||||||
|
- [ ] SSH naar scrum4me-srv werkt (`ssh scrum4me-srv echo ok`)
|
||||||
|
- [ ] DB-schema aanwezig (tabellen + data) — géén migratie nodig
|
||||||
|
|
||||||
|
## Voorwaarden (input van de gebruiker nodig)
|
||||||
|
|
||||||
|
- Postgres-rol + wachtwoord + databasenaam op de Ubuntu-server (de "USER",
|
||||||
|
"PASS", "DBNAME" hieronder). Niet in de chat delen — alleen lokaal invullen.
|
||||||
|
- De reverse proxy biedt de app aan op `http://100.118.195.120` (poort 80).
|
||||||
|
Wijkt dit af, pas dan overal de canonieke URL consistent aan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deel A — Ubuntu: Postgres openstellen op de Tailscale-interface
|
||||||
|
|
||||||
|
Uit te voeren op `scrum4me-srv` (via `ssh scrum4me-srv` of `tailscale ssh`).
|
||||||
|
|
||||||
|
1. **Tailscale-IP bevestigen**
|
||||||
|
```bash
|
||||||
|
tailscale status
|
||||||
|
tailscale ip -4 # verwacht: 100.118.195.120
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **`listen_addresses` uitbreiden** — Postgres bindt standaard alleen aan
|
||||||
|
localhost. Vind het configbestand en pas aan:
|
||||||
|
```bash
|
||||||
|
sudo -u postgres psql -c 'SHOW config_file;' # bv. /etc/postgresql/16/main/postgresql.conf
|
||||||
|
```
|
||||||
|
Zet in dat bestand:
|
||||||
|
```
|
||||||
|
listen_addresses = 'localhost,100.118.195.120'
|
||||||
|
```
|
||||||
|
Bewust **niet** `'*'` — zo bindt Postgres alleen aan localhost + het
|
||||||
|
Tailscale-adres, nooit aan de publieke interface.
|
||||||
|
|
||||||
|
> ⚠️ **Boot-order:** door aan `100.118.195.120` te binden moet `tailscale0`
|
||||||
|
> al bestaan bij boot. Stap A6 maakt de systemd-ordering verplicht — sla A6
|
||||||
|
> niet over, anders faalt Postgres na een reboot.
|
||||||
|
|
||||||
|
3. **Rol, auth-methode en grants controleren/instellen** (voorkomt een
|
||||||
|
login- of permission-fout ná goede netwerkconfig). De rol is nog onzeker,
|
||||||
|
dus eerst inventariseren:
|
||||||
|
```bash
|
||||||
|
sudo -u postgres psql -c '\du'
|
||||||
|
sudo -u postgres psql -c 'SHOW password_encryption;'
|
||||||
|
```
|
||||||
|
**Advies:** gebruik (of maak) een **dedicated runtime-rol** die alleen de
|
||||||
|
rechten heeft die de app/runner nodig heeft — geen superuser:
|
||||||
|
```sql
|
||||||
|
CREATE ROLE scrum4me_app LOGIN PASSWORD 'lokaal-wachtwoord';
|
||||||
|
GRANT CONNECT ON DATABASE DBNAME TO scrum4me_app;
|
||||||
|
|
||||||
|
-- runtime-rechten op het bestaande (gevulde) public-schema:
|
||||||
|
GRANT USAGE ON SCHEMA public TO scrum4me_app;
|
||||||
|
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO scrum4me_app;
|
||||||
|
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO scrum4me_app;
|
||||||
|
|
||||||
|
-- zodat ook later toegevoegde tabellen/sequences werken:
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||||
|
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO scrum4me_app;
|
||||||
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||||
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO scrum4me_app;
|
||||||
|
```
|
||||||
|
**Migraties:** deze runtime-rol krijgt bewust géén DDL-rechten. De DB is al
|
||||||
|
ingericht, dus in normale operatie draaien er geen migraties via deze rol.
|
||||||
|
Moet je later vanaf de Mac toch een Prisma-migratie draaien, gebruik dan de
|
||||||
|
DB-owner-rol (apart wachtwoord), niet `scrum4me_app`.
|
||||||
|
|
||||||
|
**SCRAM-verifier:** stap A4 kiest `scram-sha-256`. Een rol waarvan het
|
||||||
|
wachtwoord nog als **md5** is opgeslagen kan dan niet inloggen. Forceer een
|
||||||
|
SCRAM-verifier door het wachtwoord opnieuw te zetten (alleen lokaal op de
|
||||||
|
server, niet in chat/docs delen):
|
||||||
|
```sql
|
||||||
|
ALTER ROLE scrum4me_app WITH PASSWORD 'lokaal-wachtwoord';
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **`pg_hba.conf` — toegang vanaf de tailnet toestaan**
|
||||||
|
```bash
|
||||||
|
sudo -u postgres psql -c 'SHOW hba_file;'
|
||||||
|
```
|
||||||
|
Voeg een regel toe (boven de bestaande `host`-regels). De gebruiker koos
|
||||||
|
bewust voor toegang vanaf de **hele tailnet**; maak rol en database wel
|
||||||
|
expliciet i.p.v. `all all`:
|
||||||
|
```
|
||||||
|
# Scrum4Me clients via Tailscale
|
||||||
|
host DBNAME scrum4me_app 100.64.0.0/10 scram-sha-256
|
||||||
|
```
|
||||||
|
Let op: hiermee mag elke tailnet-node mét geldige credentials verbinden.
|
||||||
|
Wil je dat later inperken, doe dat via Tailscale ACLs/groups of versmal
|
||||||
|
het CIDR naar specifieke node-IP's.
|
||||||
|
|
||||||
|
5. **Firewall (defense-in-depth)** — alleen relevant als `ufw` actief is:
|
||||||
|
```bash
|
||||||
|
sudo ufw status
|
||||||
|
sudo ufw allow in on tailscale0 to any port 5432 proto tcp
|
||||||
|
```
|
||||||
|
Open 5432 **nooit** generiek (`sudo ufw allow 5432` zonder interface) —
|
||||||
|
dat zou de DB internet-breed openstellen.
|
||||||
|
|
||||||
|
6. **Boot-order — VERPLICHT.** Postgres bindt aan `100.118.195.120`, een adres
|
||||||
|
dat pas bestaat nadat `tailscaled` `tailscale0` heeft opgezet.
|
||||||
|
**Zonder deze override faalt Postgres bij reboot** ("cannot assign requested
|
||||||
|
address"). Voeg een systemd-override toe:
|
||||||
|
```bash
|
||||||
|
sudo systemctl edit postgresql # of postgresql@<versie>-main
|
||||||
|
```
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
After=tailscaled.service
|
||||||
|
Requires=tailscaled.service
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Postgres herstarten en verifiëren**
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart postgresql
|
||||||
|
sudo ss -tlnp | grep 5432 # moet 127.0.0.1:5432 én 100.118.195.120:5432 tonen
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deel B — Ubuntu: de Scrum4Me-app (reverse proxy) bereikbaar maken op Tailscale
|
||||||
|
|
||||||
|
De runner-tokencheck (`check-tokens.sh`) cURL't `${SCRUM4ME_BASE_URL}/api/products`
|
||||||
|
en faalt hard als die URL onbereikbaar is. De Next.js-app draait achter een
|
||||||
|
reverse proxy, dus de **proxy** moet op het Tailscale-adres luisteren — niet
|
||||||
|
alleen op `localhost`. Canoniek: `http://100.118.195.120` (poort 80, plain HTTP).
|
||||||
|
|
||||||
|
1. **Reverse proxy op de Tailscale-interface laten luisteren (poort 80)**
|
||||||
|
- **nginx:** in het server-block het `listen`-adres aan het Tailscale-IP
|
||||||
|
binden:
|
||||||
|
```
|
||||||
|
listen 100.118.195.120:80;
|
||||||
|
```
|
||||||
|
`sudo nginx -t` → `sudo systemctl reload nginx`.
|
||||||
|
- **Caddy:** site-adres `http://100.118.195.120:80` in de `Caddyfile`.
|
||||||
|
- Next.js zelf mag op `127.0.0.1:<intern>` blijven; alleen de proxy is
|
||||||
|
extern bereikbaar.
|
||||||
|
|
||||||
|
2. **Boot-order — VERPLICHT.** Net als Postgres bindt de proxy aan een adres
|
||||||
|
dat `tailscaled` eerst moet aanmaken. **Zonder deze override faalt nginx/Caddy
|
||||||
|
bij reboot.**
|
||||||
|
```bash
|
||||||
|
sudo systemctl edit nginx # of caddy
|
||||||
|
```
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
After=tailscaled.service
|
||||||
|
Requires=tailscaled.service
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Firewall voor poort 80** — alleen bij actieve `ufw`:
|
||||||
|
```bash
|
||||||
|
sudo ufw allow in on tailscale0 to any port 80 proto tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Lokaal op de server verifiëren**
|
||||||
|
```bash
|
||||||
|
curl -fsS -H "Authorization: Bearer $SCRUM4ME_TOKEN" \
|
||||||
|
http://100.118.195.120/api/products >/dev/null && echo OK
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deel C — Mac: connectiviteit verifiëren (DB én app)
|
||||||
|
|
||||||
|
1. **Tailscale-bereik** (al bevestigd: `scrum4me-srv` zichtbaar op
|
||||||
|
`100.118.195.120`):
|
||||||
|
```bash
|
||||||
|
tailscale status
|
||||||
|
tailscale ping scrum4me-srv
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **MagicDNS check** — kan de Mac de server op hostnaam bereiken?
|
||||||
|
```bash
|
||||||
|
ping -c1 scrum4me-srv
|
||||||
|
```
|
||||||
|
Zo ja: native macOS-clients mogen `scrum4me-srv` als host gebruiken.
|
||||||
|
|
||||||
|
3. **Postgres — TCP- en psql-test**
|
||||||
|
```bash
|
||||||
|
nc -vz 100.118.195.120 5432
|
||||||
|
psql "postgresql://USER:PASS@100.118.195.120:5432/DBNAME?sslmode=disable" -c '\dt'
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **App — vanaf de Mac én vanuit een Docker-container** (Docker Desktop heeft
|
||||||
|
geen Tailscale-MagicDNS; daarom de canonieke raw-IP-URL):
|
||||||
|
```bash
|
||||||
|
# vanaf de Mac
|
||||||
|
curl -fsS -H "Authorization: Bearer $SCRUM4ME_TOKEN" \
|
||||||
|
http://100.118.195.120/api/products >/dev/null && echo "mac OK"
|
||||||
|
|
||||||
|
# vanuit een container (simuleert de runner)
|
||||||
|
docker run --rm --env SCRUM4ME_TOKEN alpine sh -lc \
|
||||||
|
'wget -qO- --header "Authorization: Bearer $SCRUM4ME_TOKEN" \
|
||||||
|
http://100.118.195.120/api/products >/dev/null && echo "docker OK"'
|
||||||
|
```
|
||||||
|
Slaagt de container-test niet (geen route naar de tailnet vanuit Docker
|
||||||
|
Desktop), dan moet Tailscale in/naast de runner-container draaien — apart
|
||||||
|
uit te zoeken; eerst de directe route testen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deel D — De drie consumenten koppelen
|
||||||
|
|
||||||
|
Welke host elke consument gebruikt verschilt — MagicDNS werkt wél native op
|
||||||
|
macOS, maar niet binnen een Docker-container:
|
||||||
|
|
||||||
|
| Consumer | Host | Reden |
|
||||||
|
|---|---|---|
|
||||||
|
| DB-client (psql/TablePlus) native | `scrum4me-srv` | MagicDNS werkt op macOS |
|
||||||
|
| scrum4me-docker runner (Docker) | `100.118.195.120` | Docker Desktop heeft geen MagicDNS |
|
||||||
|
| Hoofd-app lokale dev | `scrum4me-srv` | MagicDNS werkt op macOS |
|
||||||
|
|
||||||
|
### 1. DB-client (psql / TablePlus / DBeaver) — native op macOS
|
||||||
|
Connection-string:
|
||||||
|
```
|
||||||
|
postgresql://USER:PASS@scrum4me-srv:5432/DBNAME?sslmode=disable
|
||||||
|
```
|
||||||
|
GUI-clients: host `scrum4me-srv` (of `100.118.195.120`), poort `5432`,
|
||||||
|
SSL uit.
|
||||||
|
|
||||||
|
### 2. scrum4me-docker runner — bewerk `/Users/janpetervisser/Development/scrum4me-docker/.env`
|
||||||
|
```
|
||||||
|
DATABASE_URL=postgresql://USER:PASS@100.118.195.120:5432/DBNAME?sslmode=disable
|
||||||
|
DIRECT_URL=postgresql://USER:PASS@100.118.195.120:5432/DBNAME?sslmode=disable
|
||||||
|
SCRUM4ME_BASE_URL=http://100.118.195.120
|
||||||
|
```
|
||||||
|
Belangrijk:
|
||||||
|
- **Gebruik het rauwe Tailscale-IP, niet `scrum4me-srv`.** Docker Desktop-
|
||||||
|
containers krijgen geen Tailscale-MagicDNS; de hostnaam resolvet niet
|
||||||
|
binnen de container.
|
||||||
|
- Laat de Neon-specifieke params (`channel_binding=require`,
|
||||||
|
`sslmode=verify-full`) weg — die gelden niet voor zelf-gehoste Postgres.
|
||||||
|
- Zorg dat `SCRUM4ME_TOKEN` een token van **omgeving 2** is — de tokencheck
|
||||||
|
loopt tegen de Ubuntu-app, niet meer tegen Vercel.
|
||||||
|
|
||||||
|
> `SCRUM4ME_TOKEN` haal je op via de Ubuntu-app: Settings → API Tokens → nieuw
|
||||||
|
> token aanmaken. Een bestaand Vercel-token werkt **niet** tegen de
|
||||||
|
> Ubuntu-omgeving.
|
||||||
|
|
||||||
|
### 3. Hoofd-Scrum4Me-app (lokale dev) — bewerk `/Users/janpetervisser/Development/Scrum4Me`
|
||||||
|
Dit is een **andere repo** dan `scrum4me-docker`. In die repo de `.env.local`
|
||||||
|
(of `.env`) aanpassen. De app draait native op macOS, dus de MagicDNS-hostnaam
|
||||||
|
`scrum4me-srv` mag hier wél:
|
||||||
|
```
|
||||||
|
DATABASE_URL=postgresql://USER:PASS@scrum4me-srv:5432/DBNAME?sslmode=disable
|
||||||
|
DIRECT_URL=postgresql://USER:PASS@scrum4me-srv:5432/DBNAME?sslmode=disable
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SSL-keuze
|
||||||
|
|
||||||
|
Aanbeveling: **`sslmode=disable`** voor Postgres en **plain HTTP** voor de app-
|
||||||
|
URL. Tailscale (WireGuard) versleutelt het transport al end-to-end binnen de
|
||||||
|
tailnet; een tweede TLS-laag op een zelf-gehoste Postgres of op de proxy levert
|
||||||
|
hier vooral configuratie-gedoe op.
|
||||||
|
|
||||||
|
Optionele hardening (later, samenhangend uit te voeren):
|
||||||
|
- TLS-certs op Postgres + `sslmode=require`.
|
||||||
|
- HTTPS op de reverse proxy. Doe dit dan met een **DNS-naam die ook vanuit
|
||||||
|
Docker oplost** (raw-IP + cert geeft validatiefouten), en pas `SCRUM4ME_BASE_URL`
|
||||||
|
én alle verificatie-`curl`s consistent aan naar die `https://`-hostnaam.
|
||||||
|
|
||||||
|
## Twee omgevingen naast elkaar houden
|
||||||
|
|
||||||
|
Omdat omgeving 1 (Neon) blijft bestaan: bewaar twee env-varianten, bv.
|
||||||
|
`.env.neon` en `.env.ubuntu`, en symlink de actieve naar `.env`:
|
||||||
|
```bash
|
||||||
|
ln -sf .env.ubuntu .env # activeer Ubuntu-omgeving
|
||||||
|
ln -sf .env.neon .env # activeer Neon-omgeving
|
||||||
|
```
|
||||||
|
Lichtgewicht en voorkomt dat je per ongeluk de verkeerde DB raakt.
|
||||||
|
|
||||||
|
## Te wijzigen bestanden
|
||||||
|
|
||||||
|
**Op `scrum4me-srv`:**
|
||||||
|
- `postgresql.conf` — `listen_addresses`.
|
||||||
|
- `pg_hba.conf` — tailnet-regel voor `DBNAME` + dedicated rol.
|
||||||
|
- Postgres-rol — dedicated `scrum4me_app`-rol + grants + `ALTER ROLE ... PASSWORD`.
|
||||||
|
- nginx/Caddy-config — `listen` op `100.118.195.120:80`.
|
||||||
|
- systemd-overrides — `After=/Requires=tailscaled.service` voor `postgresql`
|
||||||
|
en de proxy.
|
||||||
|
- evt. `ufw`-regels voor poort 5432 en 80 op `tailscale0`.
|
||||||
|
|
||||||
|
**Op de Mac:**
|
||||||
|
- `/Users/janpetervisser/Development/scrum4me-docker/.env` — `DATABASE_URL`,
|
||||||
|
`DIRECT_URL`, `SCRUM4ME_BASE_URL`, `SCRUM4ME_TOKEN`.
|
||||||
|
- `/Users/janpetervisser/Development/Scrum4Me/.env.local` — `DATABASE_URL`,
|
||||||
|
`DIRECT_URL` (andere repo).
|
||||||
|
- Géén codewijzigingen in `scrum4me-docker`.
|
||||||
|
|
||||||
|
## Verificatie (end-to-end)
|
||||||
|
|
||||||
|
1. **Netwerk:** `nc -vz 100.118.195.120 5432` vanaf de Mac slaagt.
|
||||||
|
2. **DB-client:** `psql ".../DBNAME?sslmode=disable" -c '\dt'` toont de
|
||||||
|
Scrum4Me-tabellen; een test-`INSERT`/`SELECT` bevestigt dat de
|
||||||
|
`scrum4me_app`-grants kloppen.
|
||||||
|
3. **App-bereik:** de `curl`/`docker run`-tests uit Deel C-4 geven beide `OK`.
|
||||||
|
4. **Reboot-test:** herstart `scrum4me-srv`; controleer daarna met
|
||||||
|
`sudo ss -tlnp` dat Postgres én de proxy weer op `100.118.195.120` luisteren,
|
||||||
|
en herhaal de Mac/Docker-connectiviteitstests.
|
||||||
|
5. **Runner:** na `.env`-update `docker compose up -d --force-recreate`,
|
||||||
|
dan `docker compose logs -f` — `check-tokens.sh` moet
|
||||||
|
"OK: 100.118.195.120:5432 reachable" én "OK: SCRUM4ME_TOKEN works" loggen,
|
||||||
|
en de daemon-loop moet een job kunnen claimen uit de Ubuntu-DB.
|
||||||
|
6. **Hoofd-app:** lokale dev-server in `/Users/janpetervisser/Development/Scrum4Me`
|
||||||
|
start en leest data uit de Ubuntu-DB.
|
||||||
|
|
||||||
|
## Veelvoorkomende fouten
|
||||||
|
|
||||||
|
| Fout | Oorzaak | Fix |
|
||||||
|
|---|---|---|
|
||||||
|
| `could not translate host name "scrum4me-srv"` | MagicDNS niet actief (Docker) | Gebruik raw IP `100.118.195.120` |
|
||||||
|
| `cannot assign requested address` bij Postgres-start | `tailscale0` bestaat nog niet | A6 systemd-override toevoegen |
|
||||||
|
| `FATAL: password authentication failed` | SCRAM-verifier niet bijgewerkt | `ALTER ROLE scrum4me_app WITH PASSWORD '...'` herhalen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Addendum — uitvoering Ubuntu-kant 2026-05-14
|
||||||
|
|
||||||
|
> Deel A + B zijn uitgevoerd. De server bleek een **andere topologie** te hebben
|
||||||
|
> dan dit plan aannam: Postgres én de reverse proxy draaien als **Docker
|
||||||
|
> containers**, niet host-geïnstalleerd. Dit addendum beschrijft wat er feitelijk
|
||||||
|
> is gebeurd. Deel C + D (Mac-kant) staan nog open.
|
||||||
|
|
||||||
|
## Vastgestelde topologie (wijkt af van de aannames)
|
||||||
|
|
||||||
|
| Plan nam aan | Werkelijkheid op `scrum4me-srv` |
|
||||||
|
|---|---|
|
||||||
|
| Host-Postgres (`/etc/postgresql/...`, `systemctl postgresql`) | Docker container `scrum4me-postgres` (postgres:17), data-volume `/srv/scrum4me/postgres` |
|
||||||
|
| Host nginx/Caddy | Docker container `scrum4me-caddy` (caddy:2), al luisterend op `0.0.0.0:80` + `:443` |
|
||||||
|
| Migratie/seed mogelijk nodig | Bevestigd niet nodig — db `scrum4me` was gevuld |
|
||||||
|
|
||||||
|
Concrete waarden die het plan openliet:
|
||||||
|
- **Host** = dit ís `scrum4me-srv` (`100.118.195.120`) — Deel A/B dus direct uitgevoerd, niet via SSH.
|
||||||
|
- **DBNAME** = `scrum4me`
|
||||||
|
- **Rol** = `scrum4me_app` aangemaakt (non-superuser, DML-only), wachtwoord lokaal gegenereerd via `openssl rand -hex 24`.
|
||||||
|
|
||||||
|
## Deel A — zoals feitelijk uitgevoerd (Docker-variant)
|
||||||
|
|
||||||
|
| Plan-stap | Aanpassing |
|
||||||
|
|---|---|
|
||||||
|
| **A2** `listen_addresses` in `postgresql.conf` | **N.v.t.** — de container luistert intern al op `0.0.0.0`. Host-exposure = Docker port-mapping. In `/srv/scrum4me/compose/docker-compose.yml` toegevoegd: `- "100.118.195.120:5432:5432"` náást de bestaande `127.0.0.1:5432:5432`. Specifiek IP i.p.v. `0.0.0.0` — Docker's iptables-DNAT scoped dan op dat IP, publiek blijft dicht. |
|
||||||
|
| **A3** rol + grants | Identiek SQL, maar uitgevoerd via `docker exec -i scrum4me-postgres psql -U scrum4me -d scrum4me`. Idempotent script (`CREATE ROLE` of `ALTER ROLE ... PASSWORD`). De `ALTER ROLE ... PASSWORD` zet meteen een SCRAM-verifier. **Let op:** `CREATE ROLE` op de gedeelde productie-DB wordt door de auto-mode classifier geblokkeerd — moet via een script dat de gebruiker zelf draait. |
|
||||||
|
| **A4** `pg_hba.conf` | Bestand zit in het data-volume: host-pad `/srv/scrum4me/postgres/pg_hba.conf` (root-owned, sudo nodig). Regel toegevoegd onderaan (append is veilig — first-match, geen conflict). **Bevinding:** de postgres-image heeft al een catch-all `host all all all scram-sha-256` — onze scoped regel is dáárdoor strikt genomen redundant. Echte bescherming = IP-scoped port-binding + ufw. Catch-all strakker maken = aparte taak (hij draagt de docker-netwerk-clients). |
|
||||||
|
| **A5** ufw | Identiek: `ufw allow in on tailscale0 to any port 5432 proto tcp`. |
|
||||||
|
| **A6** boot-order | **Niet** `postgresql.service` (bestaat niet) maar `docker.service`. Drop-in `/etc/systemd/system/docker.service.d/tailscale-order.conf`. Bewust `After=tailscaled.service` + **`Wants=`** i.p.v. het door het plan voorgestelde `Requires=` — `Requires` op `docker.service` is fragiel (faalt tailscaled ooit, dan start de hele docker-stack niet). `After=` lost de race op; `Wants=` trekt tailscaled mee zonder hard-fail. |
|
||||||
|
| **A7** restart + verify | `docker compose up -d postgres` (recreate — `restart` pakt port-wijzigingen niet). `ss -tln` toont nu `127.0.0.1:5432` én `100.118.195.120:5432`. Verificatie met een wegwerp-container: `docker run --rm --network host postgres:17 psql "postgresql://scrum4me_app:...@100.118.195.120:5432/scrum4me?sslmode=disable" -c '\dt'` — `--network host` simuleert exact hoe de Mac het ziet. |
|
||||||
|
|
||||||
|
## Deel B — zoals feitelijk uitgevoerd (Docker-variant)
|
||||||
|
|
||||||
|
| Plan-stap | Aanpassing |
|
||||||
|
|---|---|
|
||||||
|
| **B1** proxy op tailscale-interface | **Grotendeels al gedaan** — de Caddy-container publiceert al `0.0.0.0:80`, dus luistert al op `tailscale0`. Alleen een site-block toegevoegd aan `/srv/scrum4me/caddy/Caddyfile`: `100.118.195.120:80 { reverse_proxy 172.18.0.1:3000 }`. |
|
||||||
|
| **B2** boot-order proxy | **Niet nodig.** Caddy bindt aan `0.0.0.0:80`, niet aan een IP-specifiek adres — er is geen `tailscale0`-race. (Alleen Postgres had de IP-specifieke binding, vandaar dat A6 wél nodig was.) |
|
||||||
|
| **B3** ufw poort 80 | **Niet nodig.** Poort 80 stond al op `ALLOW IN Anywhere`. |
|
||||||
|
| **B4** verifiëren | `curl -sI http://100.118.195.120/` → `200 OK`, geen redirect. `/api/products` → 401 (bereikbaar, auth vereist). |
|
||||||
|
|
||||||
|
## Bugs / valkuilen tegengekomen tijdens uitvoering
|
||||||
|
|
||||||
|
1. **Caddy single-file bind-mount wordt stale na een atomic-rename edit.**
|
||||||
|
`/srv/scrum4me/caddy/Caddyfile` is als enkel bestand ge-bind-mount. Editors
|
||||||
|
(en de Edit-tooling) schrijven vaak via write-temp + rename = nieuwe inode.
|
||||||
|
De container blijft naar de oude inode wijzen → `caddy reload` leest de
|
||||||
|
**oude** content, schijnbaar zonder fout. Symptoom hier: het nieuwe
|
||||||
|
site-block leek "stil gedropt" door Caddy's adapter, maar de container zág
|
||||||
|
het block simpelweg niet.
|
||||||
|
**Fix / regel:** na een Caddyfile-edit `docker compose up -d --force-recreate
|
||||||
|
caddy` (of `restart`) — **niet** `caddy reload`. De recreate her-bindt de
|
||||||
|
mount op de nieuwe inode. (Eerder in het project werkte een Caddyfile-edit
|
||||||
|
wél, juist omdat daar toevallig een `restart` op volgde.)
|
||||||
|
|
||||||
|
2. **`http://<IP>` vs `<IP>:80` syntax — bleek een rode haring.**
|
||||||
|
Aanvankelijk leek Caddy's Caddyfile-adapter `http://100.118.195.120` te
|
||||||
|
droppen. Geïsoleerd getest werkte beide syntaxen prima; het echte probleem
|
||||||
|
was bug #1 (stale mount). De definitieve regel gebruikt `100.118.195.120:80`
|
||||||
|
— ondubbelzinnig plain-HTTP-op-poort-80.
|
||||||
|
|
||||||
|
## Verificatie-status
|
||||||
|
|
||||||
|
| Plan verificatie-stap | Status |
|
||||||
|
|---|---|
|
||||||
|
| 1. `nc`/TCP naar 5432 | ✓ `psql` als `scrum4me_app` via `100.118.195.120:5432` werkt, ziet tabellen |
|
||||||
|
| 2. DB-client grants | ✓ `SELECT` op `idea_products` werkt onder `scrum4me_app` |
|
||||||
|
| 3. App-bereik | ✓ `http://100.118.195.120/` → 200, `/api/products` → 401 |
|
||||||
|
| 4. **Reboot-test** | ✗ **Nog niet gedaan** — productie-server niet herstart. Handmatig uitvoeren op rustig moment; check daarna `ss -tlnp \| grep 5432`. |
|
||||||
|
| 5. Runner | — Mac-kant (Deel C/D), nog open |
|
||||||
|
| 6. Hoofd-app lokale dev | — Mac-kant, nog open |
|
||||||
|
|
||||||
|
## Gewijzigde bestanden op `scrum4me-srv`
|
||||||
|
|
||||||
|
- `/srv/scrum4me/compose/docker-compose.yml` — postgres `ports`: extra `100.118.195.120:5432:5432`
|
||||||
|
- `/srv/scrum4me/caddy/Caddyfile` — site-block `100.118.195.120:80`
|
||||||
|
- `/srv/scrum4me/postgres/pg_hba.conf` — tailnet-regel (+ `.bak-<timestamp>`)
|
||||||
|
- `/etc/systemd/system/docker.service.d/tailscale-order.conf` — boot-order drop-in (nieuw)
|
||||||
|
- ufw — regel `5432/tcp on tailscale0`
|
||||||
|
- Postgres-rol `scrum4me_app` — aangemaakt met grants op db `scrum4me`
|
||||||
Loading…
Add table
Add a link
Reference in a new issue