Files
IPWhiteList/docs/architecture.md
T

7.6 KiB
Raw Blame History

IP WhiteList — Архитектура (актуально, 2026-05-30 14:43)

Ветка sonnet, репо: gitea.services.ngcloud.ru/Nail/ipwhitelist-app.git
Код: /home/naeel/ipwhitelist-app


Стек

Слой Технология
Сервер Node.js + Express 4
Шаблоны EJS (server-side, без SPA)
БД PostgreSQL, клиент pg (pool)
Безопасность helmet, express-rate-limit, csrf-csrf, express-session
JWT jsonwebtoken (mock RS256 / OIDC validation)
Авторизация Keycloak (OIDC) или mock при локальной разработке

Структура модулей

server.js                  — точка входа: init + подключение роутеров (131 строк)
src/
  auth.js                  — аутентификация: OIDC или mock, session middleware
  config.js                — MOCK_USERS, backUrl()
  db.js                    — pg pool (checkConnection)
  queries.js               — все SQL-запросы к БД
  validators.js            — validateCIDR, aggregateCIDRs
  middleware/
    csrf.js                — initCsrf() → { doubleCsrfProtection, generateCsrfToken }
    rateLimit.js           — mutationLimiter (30/мин), exportLimiter (20/мин)
  routes/
    auth.js                — /login /callback /logout /dev-login
    entries.js             — / /add /edit/:id /delete/:id
    admin.js               — /audit /admin /admin/limit/:companyId
    export.js              — /export
views/
  login.ejs                — форма входа (mock) или заглушка (OIDC)
  dev-login.ejs            — тестовый вход (пресеты + произвольные поля)
  index.ejs                — список записей пользователя / admin-обзор
  admin.ejs                — admin-панель (все компании, лимиты)
  audit.ejs                — лог операций
sql/
  schema.sql               — companies, whitelist_entries (soft delete), audit_log

Схема авторизации

GET /login
  ┌─ OIDC-режим (KC_CLIENT_ID задан) ──────────────────────────────────────┐
  │  state → сессия                                                         │
  │  redirect → keycloak.nubes.ru/realms/cloud/.../auth                    │
  │       ↓ KK перенаправляет на /callback                                 │
  │  GET /callback → проверить state → exchangeCode → токен KK             │
  │                → userFromPayload → req.session.user → redirect /        │
  └─────────────────────────────────────────────────────────────────────────┘
  ┌─ Mock-режим (KC_CLIENT_ID не задан) ───────────────────────────────────┐
  │  render login.ejs (выбрать из MOCK_USERS)                               │
  │  POST /login → CSRF → req.session.user → redirect /                    │
  └─────────────────────────────────────────────────────────────────────────┘

GET /dev-login  (DEV_MODE=true или DEV_SECRET задан)
  render dev-login.ejs (пресеты MOCK_USERS + произвольные поля)
  POST /dev-login → CSRF → req.session.user → redirect /
  ⚠ Работает в ОБОИХ режимах — для тестирования с разными ролями

GET /logout
  session.destroy() + clearCookie('connect.sid')
  OIDC: redirect → keycloak logout endpoint
  Mock: redirect → /login

Все защищённые роуты: auth.middleware
  req.session.user       → req.user              (основной путь)
  Authorization: Bearer  → verify JWT → req.user  (API-клиенты)
  cookie 'jwt' (legacy)  → verify → session → req.user (плавная миграция)
  нет ничего             → GET: redirect /login, остальное: 401

JWT claims (auth-api, из реального токена портала)

auth-api выпускает свой JWT — не Keycloak напрямую.
Issuer: "auth-api", алгоритм: RS256.
Наш сервис принимает и валидирует этот токен.

Поле Тип Пример Что используем
ClientID string "WZ01325" req.user.clientId
company_id UUID "3e64aac6-..." req.user.companyId, FK в БД
company_name string "Тест" req.user.companyName
email string "user@example.com" req.user.email (аудит)
login string "user@example.com" fallback для email
sub UUID "0199e325-..." fallback для companyId
iss string "auth-api" проверяется при валидации
exp number ~12 часов автоматически

Признак admin: clientId === ADMIN_CLIENT_ID (env ADMIN_CLIENT_ID, default WZ01112).
isAdmin не приходит в JWT — см. questions.md.


Маршруты

Метод Путь Доступ Лимит
GET /healthz public
GET /.well-known/jwks.json public
GET /export public 20/мин
GET/POST /login public
GET /callback public
GET /logout public
GET/POST /dev-login public (с guard)
GET / auth
POST /add /edit/:id /delete/:id auth 30/мин
GET /audit /admin auth + admin
POST /admin/limit/:companyId auth + admin 30/мин

Env-переменные

Обязательные в продакшене

Переменная Описание
DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASS PostgreSQL
SESSION_SECRET Секрет сессии (≥ 32 символа)
CSRF_SECRET Секрет CSRF (≥ 32 символа)
KC_CLIENT_ID client_id в Keycloak realm cloud
KC_CLIENT_SECRET client_secret
APP_URL Внешний URL приложения (для redirect_uri)

Опциональные

Переменная Default Описание
KC_BASE_URL https://keycloak.nubes.ru/realms/cloud Keycloak realm base URL
ADMIN_CLIENT_ID WZ01112 WZ-номер администратора
PORT 3000 HTTP-порт
NODE_ENV production включает secure cookie, trust proxy
DEV_MODE false true → /dev-login без пароля
DEV_SECRET Ключ для /dev-login в staging

БД (dev)

Host: write.bde8229b-1381-4330-b24b-727ad73fcb44.dev.nubes.ru
DB:   ipwhitelist
User: super

Схема: sql/schema.sqlcompanies, whitelist_entries (soft delete), audit_log + индексы.


Деплой (текущий)

  • URL: https://white.nodejsk8s.dev.nubes.ru
  • k8s namespace: whitelist
  • Сессии: in-memory (MemoryStore) — не масштабируется, нужен Redis при multi-pod
  • Режим: ожидает KC_CLIENT_ID/KC_CLIENT_SECRET для перехода с mock на OIDC