# 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/` -> ACL check + проверка `expires_at` + создание записи `sessions` + старт отдельного контейнера. 4. Пользователь редиректится на `/s//`. 5. Traefik отправляет `/s//...` в конкретный runtime контейнер по dynamic labels. 6. Runtime страница шлёт heartbeat в `/api/sessions//touch`. 7. Фоновый cleanup завершает сессии при idle > 30 минут. При prewarm: - создаются warm-контейнеры с маршрутом `/svc//`; - клик по плитке использует 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/`. - Аудит: события входа и создания сессий в `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:///admin ``` ## 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: " \ -H "Cookie: portal_auth=; csrf_token=" \ -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: " \ -H "Cookie: portal_auth=; csrf_token=" \ -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: " \ -H "Cookie: portal_auth=; csrf_token=" \ -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: " \ -H "Cookie: portal_auth=; csrf_token=" \ -d '{"service_ids":[1,2]}' ``` ## 7. Ограничения MVP - Нет отдельной UI-админки (есть admin API). - TTL неактивности основан на heartbeat runtime-страницы. - Для production стоит добавить Alembic-миграции, rate limiting и централизованный логинг.