Перейти к содержанию

Настройка безопасности

Кросс-компонентные настройки безопасности развёрнутой платформы DataHUB. Включает: TLS-сертификаты для внешнего HTTPS, mTLS между серверами и клиентскими адаптерами, секреты Сервера DataHUB (pepper, JWT-ключи, service token), парольную политику, rate-limiting, ограничения firewall, security headers.

Эта статья — для администраторов production-стенда. Концептуальная модель безопасности (защита периметра, изоляция через dh_network, цели атак) — в Архитектура / Безопасность.

Структура раздела


TLS-сертификаты

TLS используется для всего внешнего HTTPS-трафика: личный кабинет (lk.<your-domain>), exchange API (exchange.<your-domain>), административные порты pgAdmin/RabbitMQ (15432/15672). Сертификаты подкладываются в /etc/datahub/nginx/certs/, имена файлов задаются в conf.d/*.conf. Подробно про подключение в nginx — в Reverse Proxy.

Куда кладутся сертификаты

/etc/datahub/nginx/certs/
├── ca.crt, ca.key              # корневой CA (для self-signed и mTLS)
├── server.crt, server.key      # серверный сертификат для админских портов 15432/15672
├── lk.<your-domain>.crt        # сертификат личного кабинета
├── lk.<your-domain>.key
├── exchange.<your-domain>.crt  # сертификат exchange API
├── exchange.<your-domain>.key
└── <client-name>.crt           # клиентские сертификаты для mTLS (по одному на систему)
    <client-name>.key

Права доступа: .crt0644, .key0640, владелец — root.

Способ 1: Self-signed (тест и пилот)

Скрипты лежат в /etc/datahub/nginx/certs/ и используют openssl. Подходят для разработки, демонстраций и пилотных стендов внутри корпоративной сети, где клиентские системы готовы доверять собственному корневому CA.

Корневой CA:

cd /etc/datahub/nginx/certs
sudo ./generate-root-CA.sh

Скрипт выполняет:

openssl req -x509 -newkey rsa:4096 \
  -keyout ca.key -out ca.crt \
  -days 3650 -nodes \
  -subj "/CN=<your-domain>/O=DataHUB/C=RU/ST=Moscow/L=Moscow" \
  -addext "subjectAltName=DNS:<your-domain>"

ca.crt затем добавляется в trust store на стороне всех клиентских систем — иначе они будут выдавать предупреждения о недоверенном сертификате при обращении к платформе.

Серверный сертификат:

sudo ./generate-server-cert.sh

Скрипт генерирует server.key, создаёт CSR и подписывает его корневым CA, получая server.crt. Subject и subjectAltName зашиты в скрипт — отредактируйте их под свои домена перед запуском.

Для дефолтной схемы «два домена» нужно выпустить два серверных сертификата (для lk.<your-domain> и exchange.<your-domain>). Скрипт удобно использовать как шаблон и продублировать с разными -subj и subjectAltName.

Клиентский сертификат для mTLS:

sudo ./generate-client-cert.sh <client-system-name>

Создаёт пару <client-system-name>.key / <client-system-name>.crt, подписанную тем же корневым CA. Файлы передаются администратору клиентской системы. CN сертификата соответствует идентификатору системы в DataHUB — это удобно для аудита.

Способ 2: Let's Encrypt

Готовые скрипты лежат в /etc/datahub/nginx/scripts/letsencrypt/. Подходят для production-стендов с публичным доменным именем, доступным из интернета.

Установка certbot (поддерживает snap/apt/yum):

cd /etc/datahub/nginx/scripts/letsencrypt
sudo chmod +x install_certbot.sh
sudo ./install_certbot.sh

Выпуск сертификата для одного домена:

sudo ADMIN_EMAIL=ssl@<your-domain> ./issue_cert.sh lk.<your-domain>

Скрипт использует webroot-метод HTTP-01: certbot кладёт challenge-файл в /var/www/letsencrypt/.well-known/acme-challenge/, а nginx (через conf.d/http.conf) отдаёт его по http://lk.<your-domain>/.well-known/acme-challenge/.... Сертификат и приватный ключ сохраняются в /etc/letsencrypt/live/lk.<your-domain>/.

Перенос свежевыпущенного сертификата в каталог nginx (с правильными правами):

sudo RENEWED_LINEAGE=/etc/letsencrypt/live/lk.<your-domain> ./copy_cert.sh

Скрипт копирует fullchain.pemlk.<your-domain>.crt и privkey.pemlk.<your-domain>.key в каталог сертификатов nginx (по умолчанию /opt/datahub/nginx/certs/ — отредактируйте CERTS_DIR в скрипте на /etc/datahub/nginx/certs/).

После замены сертификатов перезапустите nginx:

sudo docker restart nginx

Автоматическое обновление. Сертификаты Let's Encrypt действительны 90 дней, обновлять рекомендуется каждые 60. Пример скрипта обновления — cron_reissue_certs_for_<domain>.sh в каталоге letsencrypt/. Скрипт ставится в cron:

0 3 1 * * /etc/datahub/nginx/scripts/letsencrypt/cron_reissue_certs_for_<your-domain>.sh >> /var/log/datahub-cert-renew.log 2>&1

Способ 3: Доверенный CA

Если у заказчика есть собственный сертификат от доверенного публичного или внутреннего CA — достаточно положить его файлы в /etc/datahub/nginx/certs/ под именами, указанными в ssl_certificate / ssl_certificate_key в соответствующем conf.d/*.conf. Никаких скриптов выпускать не нужно.

Минимальные требования к файлам:

  • Сертификат в формате PEM, включая всю цепочку (server cert + intermediate certs).
  • Приватный ключ в формате PEM, не зашифрован паролем (nginx не сможет запросить пароль при автостарте).

Установить корректные права:

sudo chown root:root /etc/datahub/nginx/certs/*.crt /etc/datahub/nginx/certs/*.key
sudo chmod 644 /etc/datahub/nginx/certs/*.crt
sudo chmod 640 /etc/datahub/nginx/certs/*.key

После замены:

sudo docker exec nginx nginx -t
sudo docker exec nginx nginx -s reload

mTLS

mTLS (mutual TLS) обеспечивает взаимную аутентификацию: и сервер, и клиент предъявляют сертификаты, подписанные общим CA. Используется для сценариев, где интегрируемая система должна гарантированно подтвердить свою идентичность перед любым обменом — например, при обмене юридически значимыми документами или при работе в недоверенной сети.

Схема проверки

  1. Клиент отправляет свой сертификат (<client-name>.crt) серверу.
  2. Сервер проверяет его по ca.crt.
  3. Сервер отправляет свой сертификат (exchange.<your-domain>.crt) клиенту.
  4. Клиент проверяет серверный сертификат по ca.crt.

Обе стороны должны доверять одному и тому же ca.crt. Для self-signed схемы — это корневой CA из выпуска сертификатов выше. Для доверенного CA — соответствующий промежуточный CA.

Включение mTLS на nginx

Добавить в нужный server { ... } блок (например, в conf.d/exchange.<your-domain>.conf):

ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client      on;
ssl_verify_depth       2;

Если требуется ограничить mTLS только конкретными location'ами — вынести ssl_verify_client optional на уровень server и проверять $ssl_client_verify внутри location:

ssl_verify_client optional;

location ^~ /api/exchange/secure/ {
    if ($ssl_client_verify != SUCCESS) { return 403; }
    proxy_pass http://backend:80;
}

После правки:

sudo docker exec nginx nginx -t
sudo docker exec nginx nginx -s reload

Выпуск клиентских сертификатов

Для каждой системы, которой разрешён mTLS-доступ — выпустить отдельный клиентский сертификат скриптом generate-client-cert.sh (см. выше). Имя клиента (CN) рекомендуется задавать совпадающим с идентификатором системы в DataHUB, чтобы по логам было видно, кто именно обратился.

Файлы <client-name>.key, <client-name>.crt плюс ca.crt передаются администратору клиентской системы — он подкладывает их в свой адаптер. Для модуля 1С — см. Модули интеграции / 1С / Настройка.


Секреты Сервера DataHUB

Сервер DataHUB использует четыре файла-секрета в /etc/datahub/backend/certs/:

Файл Назначение Последствие компрометации
pepper.txt Серверная соль для хеширования паролей Возможность offline-перебора утёкших хешей
private_key.pem RSA приватный ключ для подписи JWT Подделка произвольных access-токенов
public_key.pem RSA публичный ключ Не критично (но утечка означает, что атакующий знает, чем проверяется подпись)
service_token.jwt Долгоживущий JWT для внутренних сервисных вызовов Возможность выполнять привилегированные операции от лица «service»

Подробности генерации и применения — в статье Сервер DataHUB / Секреты. Здесь — про обращение с ними на production-стенде.

Защита секретов

  • Каталог /etc/datahub/backend/certs/ — доступ только root (0700 на каталог).
  • pepper.txt, private_key.pem, service_token.jwt0600.
  • public_key.pem0644 (он публичный).
  • Бэкап — секреты входят в архив /etc/datahub/, см. Резервное копирование. Архив должен быть зашифрован при отправке во внешнее хранилище.
  • Контейнер монтирует ./certs:/datahub/backend/secrets:rw — права внутри контейнера сохраняются.

Когда менять секреты

Событие Действие
Подозрение на утечку /etc/datahub/backend/certs/ Срочная замена ВСЕХ секретов (см. Ротация секретов)
Уход сотрудника с административными правами на сервер Замена private_key.pem и service_token.jwt (минимум)
Истечение срока service_token.jwt Автоматически перевыпускается backend'ом (1 год)
Плановая ротация (раз в N месяцев) По согласованному регламенту

Парольная политика

Парольная политика конфигурируется в .env блока backend. По умолчанию — корпоративного уровня жёсткости.

Параметры политики

PASSWORD_COMPLEXITY=^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*()_+\-=\[\]{};':"\\|,.<>/?]{12,}$
PASSWORD_COMPLEXITY_DESCRIPTION="Password must contain at least one uppercase, one lowercase, one number, and one special character, and be at least 12 characters long."

PASSWORD_POLICY_HISTORY_SIZE=24         # неповторяемость последних 24 паролей
PASSWORD_POLICY_VALIDITY_DAYS=60        # срок действия пароля

RATE_LIMIT_LOGIN_ATTEMPTS=5             # блокировка после 5 неудачных попыток
RATE_LIMIT_LOGIN_LOCK_TIME=1800000      # автоматическая разблокировка через 30 минут
Параметр Значение по умолчанию Описание
PASSWORD_COMPLEXITY regex, минимум 12 символов Требования к сложности — буква (любой регистр) + цифра + спецсимвол
PASSWORD_POLICY_HISTORY_SIZE 24 Сколько последних паролей запрещено повторять
PASSWORD_POLICY_VALIDITY_DAYS 60 Через сколько дней пароль становится истёкшим
RATE_LIMIT_LOGIN_ATTEMPTS 5 После скольки неудачных попыток аккаунт блокируется
RATE_LIMIT_LOGIN_LOCK_TIME 1800000 мс (30 мин) На сколько блокируется аккаунт после превышения попыток

Согласование с frontend

Frontend имеет собственные UI-валидации пароля (PASSWORD_MIN_LENGHT, PASSWORD_NUMBERS, PASSWORD_UPPERCASE_LOWERCASE, PASSWORD_SPECIAL_SYMBOLS в .env блока frontend). При изменении серверной политики (PASSWORD_COMPLEXITY) обязательно обновить UI-валидации в frontend — иначе пользователь сможет ввести «правильный» по UI пароль, который backend не примет.

См. Frontend.

Изменение политики

Изменения в .env применяются после перезапуска backend:

cd /etc/datahub/backend
sudo docker compose up -d --force-recreate

Существующие пароли не перепроверяются — политика валидируется только при следующей смене пароля пользователем.


Rate-limiting

DataHUB применяет rate-limiting на двух уровнях.

На уровне nginx

В nginx.conf определены две зоны:

  • req_per_ip10r/s (базовый лимит на любой API-эндпоинт с одного IP).
  • auth_per_ip5r/m (жёсткий лимит на эндпоинты /api/auth/*).

При превышении лимита nginx возвращает 429 Too Many Requests с заголовком Retry-After: 60. Конфигурация и применение в location-блоках — в Reverse Proxy.

Зоны включены по умолчанию, отдельная настройка обычно не требуется. Изменение лимитов — правка nginx.conf, требует перезапуска nginx.

На уровне backend (Bucket4j)

Backend использует библиотеку Bucket4j (фильтр на уровне приложения, поверх Caffeine cache). Конфигурация — в application.yml секция bucket4j плюс переменные окружения:

API_AUTH_RATE_LIMIT_REQUESTS_CAPACITY=20         # burst для авторизационных эндпоинтов
API_AUTH_RATE_LIMIT_REQUESTS_CAPACITY_REFILL=20  # refill в минуту
API_RATE_LIMIT_REQUESTS_CAPACITY=200             # burst для остальных API
API_RATE_LIMIT_REQUESTS_CAPACITY_REFILL=100      # refill в минуту

Зачем два уровня:

  • nginx-лимиты работают до того, как запрос попадает в backend. Защищают от грубого DoS и снижают нагрузку на JVM.
  • backend-лимиты видят авторизованный контекст (пользователь, клиентская система) и могут применять более тонкие правила. Также защищают, если nginx обойдён (например, при vpn-доступе напрямую к контейнеру).

При превышении backend возвращает 429 с заголовками X-Rate-Limit-Remaining и X-Rate-Limit-Retry-After-Seconds.

Rate-limiting на отправку OTP

Отдельно ограничена отправка email/SMS токенов подтверждения (защита от спама):

RATE_EMAIL_SENDING_VERIFICATION_TOKEN_CAPACITY=3
RATE_EMAIL_SENDING_VERIFICATION_TOKEN_PERIOD=3                # минут
RATE_EMAIL_SENDING_VERIFICATION_TOKEN_RETRY_AFTER_SECONDS=60

RATE_SMS_SENDING_VERIFICATION_TOKEN_CAPACITY=3
RATE_SMS_SENDING_VERIFICATION_TOKEN_PERIOD=3
RATE_SMS_SENDING_VERIFICATION_TOKEN_RETRY_AFTER_SECONDS=60

— 3 OTP за 3 минуты, после превышения — пауза 60 секунд. Защищает от рассылки спама на email/SMS пользователей за счёт системы.


Firewall и сетевые ограничения

DataHUB по умолчанию изолирует все внутренние сервисы в dh_network — только nginx выставлен наружу. Однако на уровне ОС сервера остаются открытыми порты, которые требуют дополнительной защиты на production.

Минимальный набор открытых портов

Порт Назначение Источник
443 HTTPS — личный кабинет и exchange API Все: интегрируемые системы, администраторы
80 HTTP — редирект на 443 и Let's Encrypt challenge Все
22 SSH — администрирование Только администраторы
15432 pgAdmin (опционально) Только администраторы
15672 RabbitMQ Management Только администраторы
18443 Личный кабинет (только в схеме «один домен, два порта») Все

Ограничения для административных портов

Порты 15432 и 15672 опубликованы наружу — на production к ним должен иметь доступ только администратор. Два варианта:

Вариант A — firewall на уровне ОС. Через ufw или iptables разрешить эти порты только из административной подсети или VPN:

sudo ufw allow from 10.0.0.0/24 to any port 15432
sudo ufw allow from 10.0.0.0/24 to any port 15672
sudo ufw deny 15432
sudo ufw deny 15672

Вариант B — не публиковать наружу вовсе. Убрать строки - "15432:15432" и - "15672:15672" из секции ports: в /etc/datahub/nginx/docker-compose.yml. Доступ к pgAdmin и RabbitMQ Management останется только через SSH-туннель администратора.

После правки docker-compose.yml:

cd /etc/datahub/nginx
sudo docker compose up -d --force-recreate

Опциональные блоки firewall и vpn

В поставке есть опциональные блоки /etc/datahub/firewall/ и /etc/datahub/vpn/. Они адаптируются под инфраструктуру конкретного заказчика и в дефолтной поставке не описаны — конфигурируются индивидуально.


Security headers

В дефолтных конфигах nginx security headers включены на всех публичных серверах (lk.<your-domain>, exchange.<your-domain>, pgAdmin, RabbitMQ Management):

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;

Если требуется временно отключить какой-либо из заголовков (например, HSTS на этапе отладки сертификатов), соответствующая строка закомментировывается в конфиге сервера и применяется sudo docker exec nginx nginx -t && sudo docker exec nginx nginx -s reload.

HSTS — операция в один конец

После первого запроса с заголовком Strict-Transport-Security браузер будет принудительно использовать HTTPS на этом домене в течение max-age секунд (по умолчанию — год). Перед открытием стенда наружу убедитесь, что HTTPS-сертификаты гарантированно работают; иначе клиенты не смогут открыть сайт даже после отката.

X-Frame-Options для pgAdmin

В конфиге pg-admin.conf намеренно стоит SAMEORIGIN вместо DENY — внутри pgAdmin используются iframe для отображения SQL-результатов. Менять на DENY нельзя — сломается UI.


Ротация секретов

Плановая или внеплановая замена секретов на работающей платформе.

Что и как часто менять

Секрет Плановая частота Внеплановая
TLS-сертификаты (Let's Encrypt) Раз в 60 дней (auto) При компрометации ключа
TLS-сертификаты (доверенный CA) По сроку CA (1–2 года) При компрометации ключа
POSTGRES_PASSWORD Раз в год При уходе администратора
RABBITMQ_DEFAULT_PASS Раз в год При уходе администратора
AUTH_SECRET (frontend) Раз в год При компрометации
pepper.txt Не меняем планово (см. предупреждение в Сервер DataHUB) Только при компрометации
private_key.pem / public_key.pem (JWT) Раз в 1–2 года При компрометации
service_token.jwt Автоматически — раз в год После замены JWT-ключей
DATAHUB_ADMIN_PASSWORD Через UI (Profile → Change password) При смене администратора

Процедура замены POSTGRES_PASSWORD

См. предупреждение в PostgreSQL: переменная POSTGRES_PASSWORD в .env блока postgres НЕ меняет пароль уже инициализированной БД. Корректный порядок:

sudo docker exec -it postgres psql -U "$POSTGRES_USER" -d DataHUB -c "ALTER USER datahub WITH PASSWORD 'new-strong-password';"

Затем синхронизировать значение в /etc/datahub/postgres/.env, /etc/datahub/flyway/.env, /etc/datahub/backend/.env. Перезапустить backend:

cd /etc/datahub/backend
sudo docker compose up -d --force-recreate

Процедура замены JWT-ключей и service token

  1. Сохранить старые ключи на случай отката:
sudo cp /etc/datahub/backend/certs/private_key.pem /etc/datahub/backend/certs/private_key.pem.old
sudo cp /etc/datahub/backend/certs/public_key.pem /etc/datahub/backend/certs/public_key.pem.old
sudo cp /etc/datahub/backend/certs/service_token.jwt /etc/datahub/backend/certs/service_token.jwt.old
  1. Перегенерировать ключи и service token:
cd /etc/datahub/backend/scripts
sudo ./generate_keys.sh
sudo ./generate_service_token.sh
  1. Перезапустить backend:
cd /etc/datahub/backend
sudo docker compose up -d --force-recreate
  1. Все пользователи будут разлогинены — их refresh-токены аннулируются. Это ожидаемое поведение.

  2. После убеждения, что система работает (зайти в UI, отправить тестовое сообщение) — удалить старые ключи:

sudo rm /etc/datahub/backend/certs/*.old

Процедура замены pepper

Замена pepper аннулирует ВСЕ существующие хеши паролей

После замены pepper никто не сможет войти под старым паролем. Это аварийная процедура при компрометации pepper.

Порядок:

  1. Сделать резервную копию /etc/datahub/backend/certs/pepper.txt.
  2. Принять решение по политике: сбрасывать всем пароли через UI «забыл пароль» или массово через миграцию БД.
  3. Перегенерировать pepper:
cd /etc/datahub/backend/scripts
sudo ./generate_pepper.sh
  1. Перезапустить backend.
  2. Обеспечить пользователям возможность сбросить пароль (email с инструкцией).

Что дальше