Files
Stend_mont/README.md

9.0 KiB
Raw Blame History

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. Запуск

cp .env.example .env
mkdir -p traefik/letsencrypt
touch traefik/letsencrypt/acme.json
chmod 600 traefik/letsencrypt/acme.json

Собрать runtime образы:

docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image

Поднять систему:

docker compose up -d --build

Проверка:

docker compose ps
docker compose logs -f api traefik

Дефолтный админ берётся из .env (ADMIN_USERNAME, ADMIN_PASSWORD).

5.1 Развертывание На Другом Сервере (Gitea)

Пример для чистого Ubuntu-сервера.

  1. Установить Docker + Compose plugin:
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
  1. Клонировать проект из Gitea:
git clone http://gitea.ruslan.xyz/ruslan/Stend_mont.git
cd Stend_mont
  1. Подготовить конфиг:
cp .env.example .env
mkdir -p traefik/letsencrypt
touch traefik/letsencrypt/acme.json
chmod 600 traefik/letsencrypt/acme.json
  1. Отредактировать .env:
  • PUBLIC_HOST -> домен нового стенда (например stend.example.com)
  • LETSENCRYPT_EMAIL -> рабочий email
  • ADMIN_USERNAME, ADMIN_PASSWORD -> учетка администратора
  • DATABASE_URL при необходимости оставить как есть (внутренний docker postgres)
  1. Собрать и запустить:
docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image
docker compose up -d --build
  1. Проверка:
docker compose ps
docker compose logs -f api traefik

Если домен уже смотрит на этот сервер и 80/443 открыты, админка будет доступна по:

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
  1. На вкладке SSL в NPM выпустить/подключить сертификат для домена.

  2. В .env проекта оставить:

  • PUBLIC_HOST=stend.example.com
  1. Перезапустить проект:
docker compose up -d --build

После этого вход в портал и сессии будут работать через NPM, а Traefik останется внутренним компонентом.

6. Примеры admin API

  1. Создать сервис WEB:
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}'
  1. Создать сервис RDP:
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 также поддерживает креды и параметры:

rdp://user:password@192.168.1.60:3389?domain=AD&sec=nla
  1. Создать пользователя:
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}'
  1. Назначить ACL:
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 и централизованный логинг.