# Развёртывание Auth Portal + Traefik (один Docker Compose) Один стек: **Traefik** (HTTPS, Let's Encrypt DNS-01 через Cloudflare) + **auth-portal** (API + SPA, SQLite). Образ приложения: `git.shts.su/denozord/auth-portal` Публичный URL: `https://auth.shnt.top` Compose: [`deploy/docker-compose.traefik.yml`](../deploy/docker-compose.traefik.yml) Env-шаблон: [`deploy/env.traefik.example`](../deploy/env.traefik.example) Документация Traefik: [Expose Docker](https://doc.traefik.io/traefik/expose/docker/basic/), [ACME DNS challenge](https://doc.traefik.io/traefik/https/acme/). ``` Internet → :80/:443 (Traefik) → auth-portal:8080 ↑ Cloudflare DNS TXT (ACME) ``` ## Предпосылки 1. Зона домена в Cloudflare (например `shnt.top`). 2. Docker Engine + Compose plugin на VPS. 3. Свободные порты **80** и **443** на хосте (этот стек сам поднимает Traefik). 4. `docker login git.shts.su`. > Если на сервере уже крутится другой Traefik на 80/443 — либо остановите его, либо смените `TRAEFIK_HTTP_PORT` / `TRAEFIK_HTTPS_PORT` (и проброс снаружи). Два Traefik на одних портах не запустятся. --- ## 1. Cloudflare: токен и DNS ### API-токен [API Tokens → Create Token](https://dash.cloudflare.com/profile/api-tokens) — шаблон **Edit zone DNS** или Custom: | Permission | Access | |------------|--------| | Zone → DNS | Edit | | Zone → Zone | Read (желательно) | **Zone Resources:** Include → Specific zone → ваша зона. Токен → `CF_DNS_API_TOKEN` в `.env` стека. Не Global API Key, не в Git. ### DNS-запись | Type | Name | Content | Proxy | |------|------|---------|-------| | `A` / `AAAA` | `auth` | IP VPS | **DNS only** (серое облако) | ```bash dig +short auth.shnt.top A ``` Traefik для сертификата создаёт TXT `_acme-challenge.auth…` через Cloudflare API (HTTP-01 на :80 не обязателен). --- ## 2. Файлы на сервере ```bash mkdir -p /opt/auth-portal/data cd /opt/auth-portal # из репозитория или curl: # deploy/docker-compose.traefik.yml → docker-compose.yml # deploy/env.traefik.example → .env curl -fsSL -o docker-compose.yml \ https://git.shts.su/denozord/auth-portal/raw/branch/main/deploy/docker-compose.traefik.yml curl -fsSL -o .env \ https://git.shts.su/denozord/auth-portal/raw/branch/main/deploy/env.traefik.example nano .env # заполнить секреты ``` ### Обязательные переменные в `.env` | Переменная | Назначение | |------------|------------| | `CF_DNS_API_TOKEN` | Cloudflare token для ACME DNS-01 (env контейнера **Traefik**) | | `LETSENCRYPT_EMAIL` | Email для Let's Encrypt | | `JWT_SECRET` | HS256; тот же секрет в VPS Tracker / CFDM (`AUTH_JWT_SECRET`) | | `ADMIN_PASSWORD` | Пароль bootstrap-админа (только при пустой БД) | | `AUTH_DOMAIN` | Хост в Traefik rule, по умолчанию `auth.shnt.top` | | `ISSUER` | `https://auth.shnt.top` — совпадает с `AUTH_ISSUER` приложений | | `RETURN_TO_ALLOWLIST` | Origins SSO (например `.shnt.top`) | Опционально: `AUTH_IMAGE_TAG`, `TRAEFIK_IMAGE_TAG`, `TRAEFIK_HTTP_PORT`, `TRAEFIK_HTTPS_PORT`. В production cookie refresh с флагом `Secure` — нужен HTTPS. --- ## 3. Запуск (один compose) ```bash cd /opt/auth-portal docker login git.shts.su docker compose pull docker compose up -d docker compose ps docker compose logs -f --tail=100 ``` Сервисы: | Service | Контейнер | Роль | |---------|-----------|------| | `traefik` | `auth-portal-traefik` | :80 → HTTPS, ACME, роутинг | | `app` | `auth-portal` | приложение на внутренней сети `auth-portal`, порт 8080 | Первая выдача сертификата обычно 30–90 с. ### Проверка ```bash curl -fsS https://auth.shnt.top/health echo | openssl s_client -connect auth.shnt.top:443 -servername auth.shnt.top 2>/dev/null \ | openssl x509 -noout -issuer -dates -subject docker compose logs traefik 2>&1 | grep -iE 'acme|certificate|cloudflare|error' ``` ### Обновление / остановка ```bash cd /opt/auth-portal docker compose pull docker compose up -d # остановка (тома и ./data сохраняются) docker compose down # НЕ делайте down -v без бэкапа — сотрёт ACME (auth_portal_traefik_letsencrypt) ``` --- ## Что внутри compose - Сеть Docker **`auth-portal`** (внутренняя, создаётся стеком). - Том **`auth_portal_traefik_letsencrypt`** → `/letsencrypt/acme.json`. - Том хоста **`./data`** → SQLite портала. - Labels на `app`: `Host(AUTH_DOMAIN)`, `entrypoints=websecure`, `certresolver=letsencrypt`, backend port `8080`. - `CF_DNS_API_TOKEN` только у сервиса `traefik`. Полный файл: [`deploy/docker-compose.traefik.yml`](../deploy/docker-compose.traefik.yml). --- ## Связка с приложениями ```env AUTH_REQUIRED=true AUTH_JWT_SECRET=<тот же JWT_SECRET> AUTH_ISSUER=https://auth.shnt.top AUTH_PORTAL_URL=https://auth.shnt.top ``` UI: ```env VITE_AUTH_ENABLED=true VITE_AUTH_PORTAL_URL=https://auth.shnt.top ``` См. [integrate-vps-tracker.md](integrate-vps-tracker.md), [integrate-cfdm.md](integrate-cfdm.md). Logout SSO: `https://auth.shnt.top/logout`. --- ## Бэкап ```bash # SQLite cp /opt/auth-portal/data/app.db /opt/auth-portal/data/app.db.bak-$(date +%F) # ACME docker run --rm -v auth_portal_traefik_letsencrypt:/data -v "$PWD:/backup" alpine \ tar czf /backup/traefik-acme-$(date +%F).tgz -C /data . ``` Откат образа: в `.env` `AUTH_IMAGE_TAG=vX.Y.Z` → `docker compose pull && docker compose up -d`. --- ## Troubleshooting | Симптом | Что проверить | |---------|----------------| | `Bind for 0.0.0.0:80/443 failed` | Другой Traefik/nginx занимает порты | | ACME / нет HTTPS | `CF_DNS_API_TOKEN`, права Zone:DNS:Edit, логи `docker compose logs traefik` | | `invalid credentials` | Не Global Key; зона в scope токена; пробелы в `.env` | | Gateway Timeout / 404 | `docker compose ps`; labels; сеть `auth-portal` | | Login OK, SSO в app падает | `JWT_SECRET` / `ISSUER` | | `return_to` rejected | `RETURN_TO_ALLOWLIST` | | Cookie не держится | HTTPS; `NODE_ENV=production` | ```bash docker compose logs traefik 2>&1 | grep -iE 'acme|certificate|cloudflare|error' docker compose logs app --tail=50 ``` --- ## Альтернатива: Docker CLI (без compose) Если нужен ручной запуск — создайте сеть и два контейнера с теми же env/labels, что в compose. Предпочтительный путь — **один `docker compose up -d`** выше.