- POST /login-token: принимает реальный JWT (не только clientId) - auth.js: verifyAnyToken — mock + JWKS (Keycloak Nubes) одновременно - auth.js: JWKS предзагружается всегда, даже в mock-режиме - devops-deploy.md: полная production-инструкция для DevOps - .env.example: production-ready шаблон - README.md: актуальный (v0.5.13, архитектура, тесты, ссылки) - .gitignore: очищен от дубликатов
251 lines
8.2 KiB
Markdown
251 lines
8.2 KiB
Markdown
# Деплой ipwhitelist — PRODUCTION
|
|
|
|
> Репозиторий: `https://gitea.services.ngcloud.ru/Nail/ipwhitelist-app.git` (публичный)
|
|
> Стек: Node.js 20+ / Express 4 / EJS / PostgreSQL 16+
|
|
> Требования: доступ к Keycloak Nubes (realm `cloud`), зарегистрированный OIDC-клиент
|
|
|
|
---
|
|
|
|
## 1. Подготовка сервера
|
|
|
|
```bash
|
|
# Node.js 20+
|
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
|
apt install -y nodejs git nginx certbot python3-certbot-nginx postgresql
|
|
|
|
# Код
|
|
git clone https://gitea.services.ngcloud.ru/Nail/ipwhitelist-app.git /opt/ipwhitelist
|
|
cd /opt/ipwhitelist
|
|
npm install --production
|
|
```
|
|
|
|
---
|
|
|
|
## 2. PostgreSQL
|
|
|
|
```bash
|
|
# Создать пользователя и БД
|
|
sudo -u postgres psql -c "CREATE USER ipwhitelist WITH PASSWORD '${PASSWORD}';"
|
|
sudo -u postgres psql -c "CREATE DATABASE ipwhitelist OWNER ipwhitelist;"
|
|
|
|
# Применить схему
|
|
PGPASSWORD="${PASSWORD}" psql -h localhost -U ipwhitelist -d ipwhitelist -f /opt/ipwhitelist/sql/schema.sql
|
|
|
|
# Настройка: слушать только localhost (безопасность)
|
|
grep -c "^listen_addresses = 'localhost'" /etc/postgresql/16/main/postgresql.conf \
|
|
|| echo "listen_addresses = 'localhost'" >> /etc/postgresql/16/main/postgresql.conf
|
|
systemctl restart postgresql
|
|
```
|
|
|
|
---
|
|
|
|
## 3. .env (PRODUCTION)
|
|
|
|
```bash
|
|
cp /opt/ipwhitelist/.env.example /opt/ipwhitelist/.env
|
|
chmod 600 /opt/ipwhitelist/.env
|
|
```
|
|
|
|
```ini
|
|
# ── База данных ──
|
|
DB_HOST=localhost
|
|
DB_PORT=5432
|
|
DB_NAME=ipwhitelist
|
|
DB_USER=ipwhitelist
|
|
DB_PASS=<сгенерировать: openssl rand -base64 24>
|
|
DB_SSLMODE=disable # localhost — шифрование не нужно
|
|
|
|
# ── Аутентификация ──
|
|
DEV_MODE=false # ОБЯЗАТЕЛЬНО false в production
|
|
ADMIN_CLIENT_ID=WZ01112 # clientId пользователя-администратора
|
|
|
|
# ── Keycloak / OIDC (данные от DevOps) ──
|
|
KC_CLIENT_ID=<client_id> # зарегистрированный OIDC-клиент
|
|
KC_CLIENT_SECRET=<secret> # секрет клиента
|
|
KC_BASE_URL=https://keycloak.nubes.ru/realms/cloud
|
|
APP_URL=https://<домен> # публичный URL приложения (без слеша)
|
|
|
|
# ── Сессия ──
|
|
SESSION_SECRET=<openssl rand -base64 32>
|
|
CSRF_SECRET=<openssl rand -base64 32>
|
|
|
|
# ── Лимит записей по умолчанию ──
|
|
DEFAULT_LIMIT=15
|
|
|
|
# ── Порт (для nginx upstream) ──
|
|
PORT=3001
|
|
|
|
# ── API_BASE — куда UI-слой ходит за API (обычно localhost) ──
|
|
API_BASE=http://localhost:3001
|
|
```
|
|
|
|
**Генерация секретов:**
|
|
```bash
|
|
openssl rand -base64 32 # для SESSION_SECRET
|
|
openssl rand -base64 32 # для CSRF_SECRET
|
|
openssl rand -base64 24 # для DB_PASS
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Keycloak — регистрация OIDC-клиента
|
|
|
|
**Что сделать в админке Keycloak (realm `cloud`):**
|
|
|
|
1. Создать клиента:
|
|
- Client ID: `<домен>` (например `white.nubes.ru`)
|
|
- Client authentication: ON
|
|
- Standard flow: ON
|
|
- Valid redirect URIs: `https://<домен>/callback`
|
|
- Web origins: `https://<домен>` (или `+`)
|
|
|
|
2. Забрать `Client secret` → в .env как `KC_CLIENT_SECRET`
|
|
|
|
3. **Mapper'ы** (создать на клиенте, тип `User Attribute → Token Claim`):
|
|
|
|
| Claim name | User attribute | Назначение |
|
|
|---|---|---|
|
|
| `ClientID` | `client_id` | ID компании (может быть через запятую) |
|
|
| `company_id` | `company_id` | ID компании в БД |
|
|
| `company_name` | `company_name` | Название компании |
|
|
|
|
4. **Пользователи** — создать в Keycloak с заполненными атрибутами `client_id`, `company_id`, `company_name`.
|
|
|
|
**Формат multi-company:** `client_id = "WZ11125,WZ03816"` — первое значение до запятой = активная компания. Приложение парсит автоматически.
|
|
|
|
---
|
|
|
|
## 5. nginx + HTTPS
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name <домен>;
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name <домен>;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/<домен>/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/<домен>/privkey.pem;
|
|
|
|
# Security headers
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-Frame-Options "DENY" always;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3001;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header X-Forwarded-Host $host;
|
|
}
|
|
}
|
|
```
|
|
|
|
```bash
|
|
# Получить сертификат
|
|
certbot --nginx -d <домен>
|
|
|
|
# Проверить авто-обновление
|
|
certbot renew --dry-run
|
|
```
|
|
|
|
---
|
|
|
|
## 6. PM2 — автозапуск
|
|
|
|
```bash
|
|
npm i -g pm2
|
|
|
|
# Запуск
|
|
pm2 start /opt/ipwhitelist/server.js --name ipwhitelist --update-env
|
|
|
|
# Автостарт при перезагрузке
|
|
pm2 startup systemd
|
|
pm2 save
|
|
|
|
# Проверка
|
|
pm2 status
|
|
pm2 logs ipwhitelist --lines 5
|
|
# Должно быть: [auth] OIDC — Keycloak
|
|
# Если [auth] MOCK — значит KC_CLIENT_ID не задан
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Порядок ввода в эксплуатацию
|
|
|
|
```bash
|
|
# 1. Убедиться что DEV_MODE=false и заданы KC_*
|
|
grep -E "DEV_MODE|KC_CLIENT_ID|KC_CLIENT_SECRET" /opt/ipwhitelist/.env
|
|
|
|
# 2. Перезапустить
|
|
pm2 restart ipwhitelist --update-env
|
|
|
|
# 3. Проверить логи — должен быть OIDC-режим
|
|
pm2 logs ipwhitelist --lines 3 --nostream
|
|
|
|
# 4. Проверить health
|
|
curl -s https://<домен>/healthz
|
|
# → OK
|
|
|
|
# 5. Проверить SSO: открыть https://<домен>/login в браузере
|
|
# → редирект на Keycloak
|
|
# → после входа → редирект обратно на главную
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Проверка
|
|
|
|
| URL | Ожидаемый результат |
|
|
|---|---|
|
|
| `https://<домен>/healthz` | `OK` |
|
|
| `https://<домен>/` | Редирект на Keycloak login (если нет сессии) |
|
|
| `https://<домен>/login` | Редирект на Keycloak (если `DEV_MODE=false`) |
|
|
| `https://<домен>/export` | CIDR-список (text/plain) |
|
|
| `https://<домен>/api/v1/entries` | 401 (нужен Bearer) |
|
|
| `https://<домен>/callback?code=...` | Обмен code → сессия → редирект на `/` |
|
|
|
|
---
|
|
|
|
## 9. Обновление
|
|
|
|
```bash
|
|
cd /opt/ipwhitelist
|
|
git pull
|
|
npm install --production
|
|
pm2 restart ipwhitelist --update-env
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Переменные окружения (справочно)
|
|
|
|
| Переменная | Обязательна | По умолчанию | Описание |
|
|
|---|---|---|---|
|
|
| `DB_HOST` | да | — | Хост PostgreSQL |
|
|
| `DB_PORT` | нет | 5432 | Порт PostgreSQL |
|
|
| `DB_NAME` | да | — | Имя БД |
|
|
| `DB_USER` | да | — | Пользователь БД |
|
|
| `DB_PASS` | да | — | Пароль БД |
|
|
| `DB_SSLMODE` | нет | disable | `require` для внешнего подключения |
|
|
| `DEV_MODE` | нет | true | `false` в production (ОБЯЗАТЕЛЬНО) |
|
|
| `ADMIN_CLIENT_ID` | нет | WZ01112 | clientId администратора |
|
|
| `KC_CLIENT_ID` | да* | — | OIDC client ID (обязателен для SSO) |
|
|
| `KC_CLIENT_SECRET` | да* | — | OIDC client secret |
|
|
| `KC_BASE_URL` | нет | `https://keycloak.nubes.ru/realms/cloud` | Keycloak realm URL |
|
|
| `APP_URL` | да* | — | Публичный URL приложения (для OIDC redirect) |
|
|
| `SESSION_SECRET` | да | — | Подпись сессионных cookie |
|
|
| `CSRF_SECRET` | нет | — | CSRF-токены |
|
|
| `PORT` | нет | 3000 | Порт Express |
|
|
| `DEFAULT_LIMIT` | нет | 15 | Лимит записей по умолчанию |
|
|
| `API_BASE` | нет | `http://localhost:PORT` | URL для UI→API запросов |
|
|
|
|
\* — обязательны при `DEV_MODE=false`
|