Files
Stend_mont/README.md

252 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Portal Stand Access (MVP)
MVP-портал доступа к стендам в Proxmox через единый вход `https://stend.4mont.ru`.
## 1. Архитектура
- `traefik`: входная точка TLS, маршрутизация на API и динамические сессионные контейнеры.
- `api` (FastAPI): auth, ACL, админ API, создание runtime-сессий, cleanup.
- `db` (PostgreSQL): пользователи, сервисы, ACL, сессии, аудит.
- `portal-kiosk` image: Chromium kiosk + noVNC (для WEB target).
- `portal-rdp-proxy` image: xfreerdp + websockify/noVNC proxy до удаленного RDP host:port.
- `portal-universal-runtime` image: универсальный runtime (WEB/RDP), используется общим warm pool `UNIVERSAL_POOL_SIZE`.
- Опциональный prewarm pool:
- глобальный fallback `PREWARM_POOL_SIZE`;
- приоритетный `warm_pool_size` на каждом сервисе в админке.
Поток:
1. Пользователь логинится на `/`.
2. Dashboard показывает разрешённые сервисы.
3. Клик по `/go/<slug>` -> ACL check + проверка `expires_at` + создание записи `sessions` + старт отдельного контейнера.
4. Пользователь редиректится на `/s/<session_id>/`.
5. Traefik отправляет `/s/<session_id>/...` в конкретный runtime контейнер по dynamic labels.
6. Runtime страница шлёт heartbeat в `/api/sessions/<session_id>/touch`.
7. Фоновый cleanup завершает сессии при idle > 30 минут.
При prewarm:
- создаются warm-контейнеры с маршрутом `/svc/<slug>/`;
- клик по плитке использует prewarmed runtime без задержки cold start;
- сессии в БД создаются, но контейнеры остаются пулом (без стопа на каждую сессию).
- в `/admin` есть:
- отдельные разделы Users / WEB / RDP (список + форма выбранной записи);
- `pool size` на сервис;
- кнопка `Prewarm now`;
- health `running/desired` по каждому пулу.
- авто-генерация `slug` из названия (поддержка кириллицы -> латиница).
## 2. Схема БД
Основные таблицы:
- `users`
- `services`
- `user_service_access`
- `sessions`
Дополнительно:
- `audit_logs`
SQL-схема: `scripts/schema.sql`.
## 3. Безопасность
- Пароли: `argon2` (`passlib[argon2]`).
- Cookie auth: `HttpOnly`, `Secure`, `SameSite=Strict`.
- CSRF:
- формы (`/login`) через hidden token + cookie;
- admin JSON API через `X-CSRF-Token`.
- Проверки при каждом запросе:
- пользователь `active=true`;
- `expires_at > now()`;
- ACL на `/go/<slug>`.
- Аудит: события входа и создания сессий в `audit_logs`.
## 4. Файлы
- `docker-compose.yml`
- `traefik/traefik.yml`
- `traefik/dynamic/security.yml`
- `app/main.py`
- `kiosk/Dockerfile`, `kiosk/entrypoint.sh`
- `rdp-proxy/Dockerfile`, `rdp-proxy/entrypoint.sh`
## 5. Запуск
```bash
cp .env.example .env
mkdir -p traefik/letsencrypt
touch traefik/letsencrypt/acme.json
chmod 600 traefik/letsencrypt/acme.json
```
Собрать runtime образы:
```bash
docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image
```
Поднять систему:
```bash
docker compose up -d --build
```
Проверка:
```bash
docker compose ps
docker compose logs -f api traefik
```
Дефолтный админ берётся из `.env` (`ADMIN_USERNAME`, `ADMIN_PASSWORD`).
## 5.1 Развертывание На Другом Сервере (Gitea)
Пример для чистого Ubuntu-сервера.
1. Установить Docker + Compose plugin:
```bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin git
sudo usermod -aG docker $USER
newgrp docker
```
2. Клонировать проект из Gitea:
```bash
git clone http://gitea.ruslan.xyz/ruslan/Stend_mont.git
cd Stend_mont
```
3. Подготовить конфиг:
```bash
cp .env.example .env
mkdir -p traefik/letsencrypt
touch traefik/letsencrypt/acme.json
chmod 600 traefik/letsencrypt/acme.json
```
4. Отредактировать `.env`:
- `PUBLIC_HOST` -> домен нового стенда (например `stend.example.com`)
- `LETSENCRYPT_EMAIL` -> рабочий email
- `ADMIN_USERNAME`, `ADMIN_PASSWORD` -> учетка администратора
- `DATABASE_URL` при необходимости оставить как есть (внутренний docker postgres)
5. Собрать и запустить:
```bash
docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image
docker compose up -d --build
```
6. Проверка:
```bash
docker compose ps
docker compose logs -f api traefik
```
Если домен уже смотрит на этот сервер и 80/443 открыты, админка будет доступна по:
```text
https://<PUBLIC_HOST>/admin
```
## 5.2 Вариант С Nginx Proxy Manager (NPM)
Если на сервере уже используется NPM, внешний трафик можно вести через него, а Traefik оставить только внутренним роутером проекта.
В этом репозитории уже настроены внутренние порты:
- `127.0.0.1:2288 -> traefik:443` (HTTPS upstream)
- `127.0.0.1:8288 -> traefik:80` (HTTP upstream, технический)
Важно: Traefik убирать нельзя, он нужен для динамических маршрутов сессий (`/s/*`, `/svc/*`).
Как настроить NPM:
1. Создать `Proxy Host` для домена (например `stend.example.com`):
- `Forward Hostname / IP`: `127.0.0.1`
- `Forward Port`: `2288`
- `Scheme`: `https`
- включить `Websockets Support`
2. На вкладке SSL в NPM выпустить/подключить сертификат для домена.
3. В `.env` проекта оставить:
- `PUBLIC_HOST=stend.example.com`
4. Перезапустить проект:
```bash
docker compose up -d --build
```
После этого вход в портал и сессии будут работать через NPM, а Traefik останется внутренним компонентом.
## 6. Примеры admin API
1) Создать сервис WEB:
```bash
curl -k -X POST "https://stend.4mont.ru/api/admin/services" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <csrf_from_cookie>" \
-H "Cookie: portal_auth=<cookie>; csrf_token=<csrf>" \
-d '{"name":"CRM","slug":"crm","type":"WEB","target":"http://192.168.1.10:3000","active":true}'
```
2) Создать сервис RDP:
```bash
curl -k -X POST "https://stend.4mont.ru/api/admin/services" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <csrf_from_cookie>" \
-H "Cookie: portal_auth=<cookie>; csrf_token=<csrf>" \
-d '{"name":"Windows Desktop","slug":"win-rdp1","type":"RDP","target":"192.168.1.60:3389","active":true}'
```
Для RDP `target` также поддерживает креды и параметры:
```text
rdp://user:password@192.168.1.60:3389?domain=AD&sec=nla
```
3) Создать пользователя:
```bash
curl -k -X POST "https://stend.4mont.ru/api/admin/users" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <csrf_from_cookie>" \
-H "Cookie: portal_auth=<cookie>; csrf_token=<csrf>" \
-d '{"username":"user1","password":"Passw0rd!","expires_at":"2026-12-31T23:59:59+00:00","active":true}'
```
4) Назначить ACL:
```bash
curl -k -X PUT "https://stend.4mont.ru/api/admin/users/2/acl" \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: <csrf_from_cookie>" \
-H "Cookie: portal_auth=<cookie>; csrf_token=<csrf>" \
-d '{"service_ids":[1,2]}'
```
## 7. Ограничения MVP
- Нет отдельной UI-админки (есть admin API).
- TTL неактивности основан на heartbeat runtime-страницы.
- Для production стоит добавить Alembic-миграции, rate limiting и централизованный логинг.