89f705c5336667768d677d1d13e512b11bfbdbb6
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-kioskimage: Chromium kiosk + noVNC (для WEB target).portal-rdp-proxyimage: xfreerdp + websockify/noVNC proxy до удаленного RDP host:port.portal-universal-runtimeimage: универсальный runtime (WEB/RDP), используется общим warm poolUNIVERSAL_POOL_SIZE.- Опциональный prewarm pool:
- глобальный fallback
PREWARM_POOL_SIZE; - приоритетный
warm_pool_sizeна каждом сервисе в админке.
- глобальный fallback
Поток:
- Пользователь логинится на
/. - Dashboard показывает разрешённые сервисы.
- Клик по
/go/<slug>-> ACL check + проверкаexpires_at+ создание записиsessions+ старт отдельного контейнера. - Пользователь редиректится на
/s/<session_id>/. - Traefik отправляет
/s/<session_id>/...в конкретный runtime контейнер по dynamic labels. - Runtime страница шлёт heartbeat в
/api/sessions/<session_id>/touch. - Фоновый cleanup завершает сессии при idle > 30 минут.
При prewarm:
- создаются warm-контейнеры с маршрутом
/svc/<slug>/; - клик по плитке использует prewarmed runtime без задержки cold start;
- сессии в БД создаются, но контейнеры остаются пулом (без стопа на каждую сессию).
- в
/adminесть:- отдельные разделы Users / WEB / RDP (список + форма выбранной записи);
pool sizeна сервис;- кнопка
Prewarm now; - health
running/desiredпо каждому пулу. - авто-генерация
slugиз названия (поддержка кириллицы -> латиница).
2. Схема БД
Основные таблицы:
usersservicesuser_service_accesssessions
Дополнительно:
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.ymltraefik/traefik.ymltraefik/dynamic/security.ymlapp/main.pykiosk/Dockerfile,kiosk/entrypoint.shrdp-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-сервера.
- Установить 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
- Клонировать проект из Gitea:
git clone http://gitea.ruslan.xyz/ruslan/Stend_mont.git
cd Stend_mont
- Подготовить конфиг:
cp .env.example .env
mkdir -p traefik/letsencrypt
touch traefik/letsencrypt/acme.json
chmod 600 traefik/letsencrypt/acme.json
- Отредактировать
.env:
PUBLIC_HOST-> домен нового стенда (напримерstend.example.com)LETSENCRYPT_EMAIL-> рабочий emailADMIN_USERNAME,ADMIN_PASSWORD-> учетка администратораDATABASE_URLпри необходимости оставить как есть (внутренний docker postgres)
- Собрать и запустить:
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
Если домен уже смотрит на этот сервер и 80/443 открыты, админка будет доступна по:
https://<PUBLIC_HOST>/admin
6. Примеры admin API
- Создать сервис 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}'
- Создать сервис 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
- Создать пользователя:
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}'
- Назначить 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 и централизованный логинг.
Description
Languages
Python
54.3%
HTML
31.9%
Shell
8.2%
CSS
4.4%
Dockerfile
1.2%