7.6 KiB
7.6 KiB
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.sql — companies, 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