Настройка безопасности
Кросс-компонентные настройки безопасности развёрнутой платформы DataHUB. Включает: TLS-сертификаты для внешнего HTTPS, mTLS между серверами и клиентскими адаптерами, секреты Сервера DataHUB (pepper, JWT-ключи, service token), парольную политику, rate-limiting, ограничения firewall, security headers.
Эта статья — для администраторов production-стенда. Концептуальная модель безопасности (защита периметра, изоляция через dh_network, цели атак) — в Архитектура / Безопасность.
Структура раздела
- TLS-сертификаты — три способа выпуска (self-signed, Let's Encrypt, доверенный CA).
- mTLS — взаимная аутентификация между сервером и клиентскими адаптерами.
- Секреты Сервера DataHUB — расположение, ротация, последствия компрометации.
- Парольная политика — сложность, история, срок действия.
- Rate-limiting — защита от brute-force и DoS.
- Firewall и сетевые ограничения — что должно быть открыто, что закрыто.
- Security headers — HSTS, X-Frame-Options, X-Content-Type-Options.
- Ротация секретов — порядок плановой замены.
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
Права доступа: .crt — 0644, .key — 0640, владелец — root.
Способ 1: Self-signed (тест и пилот)
Скрипты лежат в /etc/datahub/nginx/certs/ и используют openssl. Подходят для разработки, демонстраций и пилотных стендов внутри корпоративной сети, где клиентские системы готовы доверять собственному корневому CA.
Корневой CA:
Скрипт выполняет:
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 на стороне всех клиентских систем — иначе они будут выдавать предупреждения о недоверенном сертификате при обращении к платформе.
Серверный сертификат:
Скрипт генерирует server.key, создаёт CSR и подписывает его корневым CA, получая server.crt. Subject и subjectAltName зашиты в скрипт — отредактируйте их под свои домена перед запуском.
Для дефолтной схемы «два домена» нужно выпустить два серверных сертификата (для lk.<your-domain> и exchange.<your-domain>). Скрипт удобно использовать как шаблон и продублировать с разными -subj и subjectAltName.
Клиентский сертификат для mTLS:
Создаёт пару <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
Выпуск сертификата для одного домена:
Скрипт использует 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 (с правильными правами):
Скрипт копирует fullchain.pem → lk.<your-domain>.crt и privkey.pem → lk.<your-domain>.key в каталог сертификатов nginx (по умолчанию /opt/datahub/nginx/certs/ — отредактируйте CERTS_DIR в скрипте на /etc/datahub/nginx/certs/).
После замены сертификатов перезапустите 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
После замены:
mTLS
mTLS (mutual TLS) обеспечивает взаимную аутентификацию: и сервер, и клиент предъявляют сертификаты, подписанные общим CA. Используется для сценариев, где интегрируемая система должна гарантированно подтвердить свою идентичность перед любым обменом — например, при обмене юридически значимыми документами или при работе в недоверенной сети.
Схема проверки
- Клиент отправляет свой сертификат (
<client-name>.crt) серверу. - Сервер проверяет его по
ca.crt. - Сервер отправляет свой сертификат (
exchange.<your-domain>.crt) клиенту. - Клиент проверяет серверный сертификат по
ca.crt.
Обе стороны должны доверять одному и тому же ca.crt. Для self-signed схемы — это корневой CA из выпуска сертификатов выше. Для доверенного CA — соответствующий промежуточный CA.
Включение mTLS на nginx
Добавить в нужный server { ... } блок (например, в conf.d/exchange.<your-domain>.conf):
Если требуется ограничить 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;
}
После правки:
Выпуск клиентских сертификатов
Для каждой системы, которой разрешён 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.jwt—0600.public_key.pem—0644(он публичный).- Бэкап — секреты входят в архив
/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:
Существующие пароли не перепроверяются — политика валидируется только при следующей смене пароля пользователем.
Rate-limiting
DataHUB применяет rate-limiting на двух уровнях.
На уровне nginx
В nginx.conf определены две зоны:
req_per_ip—10r/s(базовый лимит на любой API-эндпоинт с одного IP).auth_per_ip—5r/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:
Опциональные блоки 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:
Процедура замены JWT-ключей и service token
- Сохранить старые ключи на случай отката:
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
- Перегенерировать ключи и service token:
- Перезапустить backend:
-
Все пользователи будут разлогинены — их refresh-токены аннулируются. Это ожидаемое поведение.
-
После убеждения, что система работает (зайти в UI, отправить тестовое сообщение) — удалить старые ключи:
Процедура замены pepper
Замена pepper аннулирует ВСЕ существующие хеши паролей
После замены pepper никто не сможет войти под старым паролем. Это аварийная процедура при компрометации pepper.
Порядок:
- Сделать резервную копию
/etc/datahub/backend/certs/pepper.txt. - Принять решение по политике: сбрасывать всем пароли через UI «забыл пароль» или массово через миграцию БД.
- Перегенерировать pepper:
- Перезапустить backend.
- Обеспечить пользователям возможность сбросить пароль (email с инструкцией).
Что дальше
- Reverse Proxy — детали подключения TLS-сертификатов в nginx.
- Сервер DataHUB — детали секретов и rate-limit на уровне backend.
- Настройка уведомлений — email-уведомления (используются для сброса паролей).
- Production-настройка — итоговый чек-лист готовности.
- Архитектура / Безопасность — общая модель безопасности платформы.