quality / commitlint (push) Skipped
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 57s
quality / api (push) Failing after 30s
CD / quality (push) Failing after 1m36s
CD / publish (push) Skipped
- Updated environment configurations to include CDN Manager in the RETURN_TO_ALLOWLIST. - Enhanced target app resolution to recognize CDN-related hosts. - Added CDN Manager to the application switcher and updated relevant documentation. - Included tests to verify the correct mapping of CDN hosts. Co-authored-by: Cursor <[email protected]>
238 lines
9.1 KiB
Markdown
238 lines
9.1 KiB
Markdown
# Развёртывание Auth Portal + Traefik (один Docker Compose)
|
||
|
||
Один стек: **Traefik** (HTTPS, Let's Encrypt DNS-01 через Cloudflare) + **auth-portal** (API + SPA, SQLite).
|
||
|
||
Образ приложения: `git.shx.one/denozord/auth-portal` (тот же манифест публикуется как `authportal`)
|
||
Runtime: `node:22-alpine`, API + SPA + SQLite в одном процессе.
|
||
Пайплайн: [`.gitea/README.md`](../.gitea/README.md), [`docs/releasing.md`](releasing.md).
|
||
Публичный 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.shx.one`.
|
||
|
||
> Если на сервере уже крутится другой 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.shx.one/denozord/auth-portal/raw/branch/main/deploy/docker-compose.traefik.yml
|
||
curl -fsSL -o .env \
|
||
https://git.shx.one/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` приложений **и** OIDC issuer |
|
||
| `RETURN_TO_ALLOWLIST` | Origins SSO / App Switcher (`.shnt.top`, `https://dns.shnt.top`, …) |
|
||
| `OIDC_ISSUER` | Опционально; если пусто — берётся `ISSUER`. Публичный URL IdP для Technitium |
|
||
| `OIDC_RSA_PRIVATE_KEY` | Опционально PKCS8 PEM; иначе RSA-ключ в SQLite (`./data`) |
|
||
|
||
Опционально: `AUTH_IMAGE_TAG`, `TRAEFIK_IMAGE_TAG`, `TRAEFIK_HTTP_PORT`, `TRAEFIK_HTTPS_PORT`.
|
||
|
||
В production cookie refresh с флагом `Secure` — нужен HTTPS.
|
||
|
||
### Проверка OIDC после старта
|
||
|
||
```bash
|
||
curl -fsS https://auth.shnt.top/.well-known/openid-configuration | head
|
||
curl -fsS https://auth.shnt.top/.well-known/jwks.json | head
|
||
```
|
||
|
||
Админка: `https://auth.shnt.top/admin/oidc` — создать client для Technitium.
|
||
Полная инструкция SSO: [integrate-technitium.md](integrate-technitium.md).
|
||
|
||
---
|
||
|
||
## 3. Запуск (один compose)
|
||
|
||
```bash
|
||
cd /opt/auth-portal
|
||
docker login git.shx.one
|
||
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).
|
||
|
||
---
|
||
|
||
## Связка с приложениями
|
||
|
||
### JWT apps (CFDM / VPS / BGP / FW)
|
||
|
||
```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), [integrate-evobgp.md](integrate-evobgp.md), [integrate-evofirewall.md](integrate-evofirewall.md), [integrate-cdnmanager.md](integrate-cdnmanager.md).
|
||
Logout SSO: `https://auth.shnt.top/logout`.
|
||
|
||
### Technitium DNS (OIDC)
|
||
|
||
Portal — IdP; Technitium — RP. После `docker compose up`:
|
||
|
||
1. В `.env` добавьте origin DNS в `RETURN_TO_ALLOWLIST` (например `https://dns.shnt.top`).
|
||
2. `curl https://auth.shnt.top/.well-known/openid-configuration` — должен отвечать JSON.
|
||
3. Admin → OIDC-клиенты → redirect `https://dns.shnt.top/sso/callback`.
|
||
4. В Technitium SSO: Metadata = `https://auth.shnt.top/.well-known/openid-configuration`.
|
||
|
||
Важно: Technitium резолвит IdP **своим** DNS — A/AAAA для `auth.shnt.top` должна быть видна с DNS-сервера.
|
||
Детали: [integrate-technitium.md](integrate-technitium.md).
|
||
|
||
---
|
||
|
||
## Бэкап
|
||
|
||
```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` |
|
||
| Technitium «Failed to reach SSO provider» | A/AAAA `auth.*` в самом Technitium; `curl` с хоста DNS к discovery URL |
|
||
| OIDC discovery 404 | образ без OIDC; `ISSUER`/`OIDC_ISSUER` = публичный HTTPS URL; Traefik Host |
|
||
|
||
```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`** выше.
|