# autopass — Deploy pe Dokploy LXC 103 ## Descriere Gateway RAR AUTOPASS (Python / FastAPI) care preia prezentări de service-auto și le declară la RAR AUTOPASS (Legea 142/2023). Deployat pe Dokploy / LXC 103 ca serviciu Docker Compose. - **Domenii:** `autopass.romfast.ro` (public, site IIS dedicat) + `autopass.roa.romfast.ro` (prin wildcard) - **Staging:** branch `staging` → `autopass-test.roa.romfast.ro` — vezi [autopass-staging.md](autopass-staging.md) - **Server:** LXC 103 (10.0.20.167) - **Deployment:** Dokploy → Docker Compose, provider **Custom Git** - **Repo:** `git@gitea.romfast.ro:romfast/rar-autopass.git`, branch **`main`** ### Arhitectură ``` Browser → VM201 IIS (TLS, site roa-apps) → Traefik LXC103 (HTTP :80) → api (uvicorn :8010) ↓ worker + autoheal ↓ SQLite /data/autopass.db ``` Servicii Docker Compose (`docker-compose.yml`, un singur image): - `api` — FastAPI/uvicorn pe `:8010`, expus prin Traefik (entrypoint `web`) - `worker` — procesează coada; partajează volumul `autopass-data` și `AUTOPASS_CREDS_KEY` - `autoheal` — restartează `worker`-ul când probe-ul îl marchează unhealthy (proces agățat) --- ## Deploy în Dokploy UI ### 1. Creare Service 1. https://dokploy.romfast.ro → **Services** → **Create Service** → **Docker Compose** 2. Name: `autopass`, Server: **LXC 103 (local)** ### 2. Provider — Custom Git (NU Gitea nativ) Integrarea nativă **Gitea** dă "Failed to fetch repositories: Unauthorized" (token OAuth expirat în Dokploy). Folosește tab-ul **` Git`**: - Repository URL: `git@gitea.romfast.ro:romfast/rar-autopass.git` - Branch: `main` - Compose Path: `./docker-compose.yml` - SSH: adaugă deploy key-ul afișat de Dokploy în Gitea → repo → Settings → Deploy Keys ### 3. Environment (în Dokploy, NU în .env — e gitignored) | Variabilă | Valoare | |-----------|---------| | `AUTOPASS_CREDS_KEY` | cheie Fernet, **partajată api↔worker** (vezi mai jos) | | `AUTOPASS_REQUIRE_API_KEY` | `true` pentru prod | ```bash # Generare AUTOPASS_CREDS_KEY: python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" ``` Fără cheie, compose pică explicit (`:?seteaza AUTOPASS_CREDS_KEY`). ### 4. Domain **Domains** → service **`api`**, port **`8010`**, domain `autopass.roa.romfast.ro`. TLS-ul public se termină la IIS (VM 201), deci în Dokploy e suficient entrypoint `web`. ### 5. Autodeploy (deploy automat la commit) Toggle **Autodeploy** = ON nu e suficient — trebuie webhook în Gitea (Custom Git nu-l creează automat): 1. Dokploy → General → copiază **Webhook URL** (`https://dokploy.romfast.ro/api/deploy/compose/`) 2. Gitea → repo → Settings → Webhooks → Add Webhook → Gitea: - Target URL: *(URL-ul din Dokploy)*, Method `POST`, Content Type `application/json` - Branch filter: `main`, trigger Push Events, Active - **Test Delivery** → trebuie `200` 3. Doar ce e `git push`-uit pe `main` declanșează deploy. --- ## Rutare (de ce NU trebuie regulă IIS per-app) IIS site `roa-apps` (VM 201) are binding wildcard `*.roa.romfast.ro` (80+443) și o regulă URL Rewrite catch-all care forwardează tot către Traefik: ```xml ... ``` ARR are `preserveHostHeader=True`, deci Host-ul original ajunge la Traefik, care rutează după `Host(...)`. Un app nou sub `*.roa` merge automat — **nu** adaugi nimic în IIS, doar domeniul în Dokploy. --- ## Probleme întâlnite la primul deploy (și fix-urile) ### 1. Gitea Unauthorized în Dokploy **Simptom:** "Failed to fetch repositories: Unauthorized", dropdown Repository gol. **Cauză:** token-ul OAuth al integrării native Gitea în Dokploy e expirat/revocat. **Fix:** folosește provider **Custom Git** (vezi pasul 2). Alternativ: regenerează OAuth app / PAT în Gitea și reconectează în Dokploy → Settings → Git. ### 2. 404 la `https://autopass.roa.romfast.ro/` — api în crash-loop **Simptom:** site-ul răspunde **404** (nu 502). `docker ps` → `api` în `Restarting (1)`, worker + autoheal healthy. **Cauză:** `ModuleNotFoundError: No module named 'itsdangerous'`. `SessionMiddleware` (`app/main.py`) îl cere, dar lipsea din `requirements.txt` — era doar instalat local în dev, **nu** tras tranzitiv în imaginea Docker. Worker-ul nu importă SessionMiddleware, de-aia el era healthy. **Fix:** adăugat `itsdangerous==2.2.0` în `requirements.txt`, push → autodeploy a reconstruit imaginea (commit `412102b`). > 404 vs 502: 404 = niciun backend sănătos / niciun router care să răspundă; 502 = portul > din Traefik ≠ portul containerului. Aici api nu pornea deloc → 404. ### 3. (Nu era o problemă) `/` → 303 După fix, root-ul redirectează `303 → /login` (auth pe sesiune). E comportament normal, nu eroare. --- ## Verificare post-deploy ```bash # Stare containere ssh root@10.0.20.201 "pct exec 103 -- docker ps --format '{{.Names}} -> {{.Status}}' | grep autopass" # Așteptat: api Up (healthy), worker Up (healthy), autoheal Up (healthy) # API direct în Traefik (host header), pe LXC 103 ssh root@10.0.20.201 'pct exec 103 -- sh -lc "curl -s -o /dev/null -w \"%{http_code}\n\" -H \"Host: autopass.roa.romfast.ro\" http://localhost/healthz"' # Așteptat: 200 # Lanț complet de pe VM 201 (loopback, TLS real) # autopass.roa.romfast.ro -> HTTP 303 (redirect /login) = OK # Logs api dacă pică ssh root@10.0.20.201 "pct exec 103 -- docker logs \$(ssh ... ) --tail 40" ``` --- ## Atenție: deploy de TEST, nu producție `docker-compose.yml` are hardcodat: - `AUTOPASS_RAR_ENV: test` - `AUTOPASS_WORKER_SEND_ENABLED: "false"` — worker-ul **NU** trimite efectiv la RAR. Pentru producție (trimiteri reale către RAR, Legea 142/2023) acestea trebuie schimbate explicit — preferabil prin Environment în Dokploy, nu hardcodat în compose. --- ## DNS ``` autopass.romfast.ro A 188.26.14.103 autopass.roa.romfast.ro A 188.26.14.103 ``` `autopass.roa…` e acoperit de wildcard `*.roa.romfast.ro`. `autopass.romfast.ro` e single-name → are site IIS + cert propriu (vezi mai jos). --- ## Domeniu public `autopass.romfast.ro` (site IIS dedicat) Spre deosebire de `*.roa`, domeniul public single-name are nevoie de **site IIS propriu** pe VM 201 (model identic cu `roa-qr` — pattern din `setup-new-iis-sites.ps1`). **IIS (VM 201, 10.0.20.122)** — Site ID **7**, nume `autopass`: - HTTP `:80` + HTTPS `:443` hostHeader `autopass.romfast.ro`, SNI (SslFlags 1) - `C:\inetpub\autopass\web.config` = catch-all rewrite → `http://10.0.20.167/{R:1}` (Traefik LXC 103) cu `X-Forwarded-Proto/Host` + `X-Real-IP` **Cert SSL — win-acme HTTP-01, auto-renew** (la fel ca site-urile 1–5): ```powershell C:\Tools\win-acme\wacs.exe --source iis --siteid 7 --host autopass.romfast.ro ` --validationmode http-01 --validation selfhosting --installation iis ` --store certificatestore --accepttos --emailaddress admin@romfast.ro ``` - Validation `selfhosting` (c7d5e050) — ascultă pe http.sys, **nu** e blocat de catch-all-ul din web.config. Install `iis` (ea6a5be3) → re-leagă automat binding-ul SNI la renew. - Renewal: `[IIS] autopass, autopass.romfast.ro`, Scheduled Task "win-acme renew", next ~2026-08-23. - Monitorizat în `monitor-ssl-certificates.sh` (Site ID 7) ca safety-net. **Pas necesar în Dokploy** (Traefik rutează după Host → trebuie domeniul adăugat explicit): > Dokploy → service `autopass` → **Domains** → **Add Domain** > - Host: `autopass.romfast.ro`, Service: **`api`**, Container Port: **`8010`**, Path: `/` > - **HTTPS: OFF** (entrypoint `web`) — TLS-ul public se termină la IIS; Certificate: None > Save → Traefik hot-reload. Înainte de pas: `https://autopass.romfast.ro/` → **404** (Traefik > n-are router pe acest Host). După pas: **200**.