quality / changes (push) Successful in 5s
quality / api (push) Skipped
quality / commitlint (push) Skipped
quality / docker-check (push) Skipped
CD / update-wiki (push) Successful in 5s
quality / web (push) Successful in 57s
CD / quality (push) Successful in 1m9s
CD / publish (push) Successful in 1m39s
- Introduced a new Access Denied route to handle unauthorized access. - Updated routing logic to redirect to the Access Denied page when necessary. - Enhanced authentication checks to prevent infinite redirect loops and improve user experience. - Adjusted API client to handle JWT rejection scenarios more gracefully.
165 lines
6.1 KiB
Markdown
165 lines
6.1 KiB
Markdown
# Развёртывание CDN Manager + Traefik (один Docker Compose)
|
||
|
||
Один стек: **Traefik** (HTTPS, Let's Encrypt DNS-01 через Cloudflare) + **cdnmanager** (API + SPA, SQLite).
|
||
|
||
| | |
|
||
|---|---|
|
||
| Образ | `git.shx.one/denozord/cdnmanager` (алиас `cdn-manager`) |
|
||
| Runtime | `node:22-alpine`, API + SPA + SQLite |
|
||
| Публичный URL | `https://cdn.shnt.top` |
|
||
| Compose | [`deploy/docker-compose.traefik.yml`](../deploy/docker-compose.traefik.yml) |
|
||
| Env-шаблон | [`deploy/env.traefik.example`](../deploy/env.traefik.example) |
|
||
| SSO | [`integrate-auth-portal.md`](./integrate-auth-portal.md) |
|
||
| Релизы | [`releasing.md`](./releasing.md) |
|
||
|
||
Документация 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) → cdnmanager: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`.
|
||
5. Для SSO — работающий auth-portal с тем же `JWT_SECRET` / `AUTH_ISSUER`.
|
||
|
||
> Если на сервере уже крутится другой Traefik на 80/443 — либо остановите его, либо смените `TRAEFIK_HTTP_PORT` / `TRAEFIK_HTTPS_PORT`. Два Traefik на одних портах не запустятся.
|
||
|
||
---
|
||
|
||
## 1. Cloudflare: токены и DNS
|
||
|
||
### Два разных токена
|
||
|
||
| Переменная | Куда | Назначение |
|
||
|------------|------|------------|
|
||
| `CF_DNS_API_TOKEN` | контейнер **Traefik** | ACME DNS-01 (TXT `_acme-challenge`) |
|
||
| `CLOUDFLARE_API_TOKEN` | контейнер **app** | Zone DNS Edit + Zone Read для sync флота |
|
||
|
||
Можно один токен с нужными правами продублировать в обе переменные, либо разделить (рекомендуется least privilege).
|
||
|
||
[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 → ваша зона. Не Global API Key, не в Git.
|
||
|
||
### DNS-запись
|
||
|
||
| Type | Name | Content | Proxy |
|
||
|------|------|---------|-------|
|
||
| `A` / `AAAA` | `cdn` | IP VPS | **DNS only** (серое облако) |
|
||
|
||
```bash
|
||
dig +short cdn.shnt.top A
|
||
```
|
||
|
||
Traefik для сертификата создаёт TXT `_acme-challenge.cdn…` через Cloudflare API.
|
||
|
||
---
|
||
|
||
## 2. Файлы на сервере
|
||
|
||
```bash
|
||
mkdir -p /opt/cdnmanager/data
|
||
cd /opt/cdnmanager
|
||
|
||
# из репозитория:
|
||
# cp deploy/docker-compose.traefik.yml docker-compose.yml
|
||
# cp deploy/env.traefik.example .env
|
||
|
||
curl -fsSL -o docker-compose.yml \
|
||
https://git.shx.one/denozord/CDNManager/raw/branch/main/deploy/docker-compose.traefik.yml
|
||
curl -fsSL -o .env \
|
||
https://git.shx.one/denozord/CDNManager/raw/branch/main/deploy/env.traefik.example
|
||
|
||
nano .env # заполнить секреты
|
||
```
|
||
|
||
### Обязательные переменные в `.env`
|
||
|
||
| Переменная | Назначение |
|
||
|------------|------------|
|
||
| `CF_DNS_API_TOKEN` | Cloudflare token для ACME DNS-01 (Traefik) |
|
||
| `LETSENCRYPT_EMAIL` | Email для Let's Encrypt |
|
||
| `CDN_DOMAIN` | Хост в Traefik rule, по умолчанию `cdn.shnt.top` |
|
||
| `CLOUDFLARE_API_TOKEN` | Token приложения для DNS sync |
|
||
| `AUTH_JWT_SECRET` / `JWT_SECRET` | = `JWT_SECRET` auth-portal (HS256) |
|
||
| `AUTH_ISSUER` | `https://auth.shnt.top` |
|
||
| `AUTH_PORTAL_URL` | URL UI портала |
|
||
|
||
Опционально: `CDN_IMAGE_TAG`, `TRAEFIK_IMAGE_TAG`, `TRAEFIK_HTTP_PORT`, `TRAEFIK_HTTPS_PORT`, `AUTH_AUDIT_INGEST_SECRET`, `AUTH_REQUIRED` (default `true`).
|
||
|
||
На стороне портала добавьте origin в `RETURN_TO_ALLOWLIST` (`https://cdn.shnt.top`) и выдайте app **`cdn`** + права `cdn:*`. App Switcher URL: тот же origin.
|
||
|
||
**Важно:** `AUTH_JWT_SECRET` в CDN Manager **должен совпадать** с `JWT_SECRET` auth-portal, `AUTH_ISSUER` — с `ISSUER` портала. Иначе после SSO UI зацикливается / «Страница не отвечает».
|
||
|
||
---
|
||
|
||
## 3. Запуск
|
||
|
||
```bash
|
||
cd /opt/cdnmanager
|
||
docker login git.shx.one
|
||
docker compose pull
|
||
docker compose up -d
|
||
docker compose ps
|
||
docker compose logs -f --tail=100
|
||
```
|
||
|
||
Проверка:
|
||
|
||
```bash
|
||
curl -fsS https://cdn.shnt.top/health
|
||
# {"status":"ok",…}
|
||
```
|
||
|
||
Откройте `https://cdn.shnt.top` → редирект на auth-portal → handoff на `/auth/callback`.
|
||
|
||
---
|
||
|
||
## 4. Обновление
|
||
|
||
```bash
|
||
cd /opt/cdnmanager
|
||
docker compose pull
|
||
docker compose up -d
|
||
```
|
||
|
||
Миграции SQLite применяются при старте API. Volume `./data` сохраняется.
|
||
|
||
---
|
||
|
||
## 5. Остановка
|
||
|
||
```bash
|
||
docker compose down
|
||
# с удалением ACME volume (сертификаты перевыпустятся):
|
||
# docker compose down -v
|
||
```
|
||
|
||
Данные SQLite в `./data` на хосте остаются, пока не удалите каталог.
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
| Симптом | Причина |
|
||
|---------|---------|
|
||
| Порт 80/443 занят | другой Traefik/nginx; смените порты или хост |
|
||
| ACME timeout | `CF_DNS_API_TOKEN` без DNS Edit; Cloudflare Proxy на A-записи (нужен DNS only) |
|
||
| SSO loop / 401 | разный `AUTH_JWT_SECRET` / `AUTH_ISSUER` vs portal |
|
||
| 403 «Нет доступа» | у пользователя нет app `cdn` |
|
||
| return_to rejected | `https://cdn.shnt.top` не в `RETURN_TO_ALLOWLIST` портала |
|
||
| Sync CF падает | пустой/`CLOUDFLARE_API_TOKEN` без Zone DNS Edit |
|
||
|
||
Без Traefik (plain `docker run` / простой Compose): [`deploy-docker.md`](./deploy-docker.md).
|