Тема оформления
Конфигурация Nginx и SSL
1. Назначение и архитектурное положение Nginx
В архитектуре распределённой микросервисной платформы «UrFU HeatBalance» веб-сервер Nginx выполняет роль пограничного обратного прокси-сервера (Perimeter Reverse Proxy) и демилитаризованной зоны (DMZ). Nginx является единственным публичным компонентом инфраструктуры, принимающим трафик из внешней сети Интернет по портам 80 (HTTP) и 443 (HTTPS).
Основные функции узла Nginx:
- SSL/TLS-терминация: централизованная криптографическая обработка входящих соединений с разгрузкой вычислительных мощностей микросервисов (TLS Offloading).
- Селективная маршрутизация доменов: разграничение трафика основного производственного интерфейса (
heatbalance.ru), портала документации (heatbalance.online) и контура непрерывной интеграции (ci.heatbalance.online). - Маршрутизация путей (Path Routing): распределение запросов между контейнером статического фронтенда React и точкой входа API (YARP Gateway).
- Проксирование WebSockets: поддержка полнодуплексного протокола WebSocket для интерактивной передачи логов сборки Jenkins CI/CD в реальном времени.
- Аппаратная оптимизация и сжатие: сжатие текстовых и JSON-ответов алгоритмом Gzip на лету, кэширование статических ассетов и поддержка постоянных соединений (
Keep-Alive). - Защита периметра (Security Hardening): внедрение защитных заголовков OWASP, лимитирование объёмов входящего трафика и защита от фрагментированных атак.
2. Топология доменных имён и маршрутизация
Инфраструктура платформы обслуживает три доменные зоны с чётким разделением ответственности:
| Доменное имя | Протокол | Направление проксирования | Описание и назначение |
|---|---|---|---|
heatbalance.ruwww.heatbalance.ru | HTTPS | http://frontend:80http://gateway:8080 | Основной производственный портал: веб-интерфейс React SPA, API шлюз, аутентификация OpenIddict. |
heatbalance.onlinewww.heatbalance.online | HTTPS | http://docs:80 | Официальный портал технической документации VitePress (контейнер heatbalance-docs). |
ci.heatbalance.online | HTTPS | http://host.docker.internal:8080 | Защищённый контур CI/CD: панель управления Jenkins, приём вебхуков от GitHub, мониторинг конвейеров сборки. |
| Все домены (порт 80) | HTTP | 301 -> https://$host$request_uri | Безусловный редирект всего незашифрованного входящего трафика на защищённый протокол HTTPS. |
3. Детальный разбор конфигурации nginx/nginx.conf
Файл конфигурации расположен в репозитории по пути nginx/nginx.conf и монтируется в рабочий контейнер heatbalance-nginx.
3.1. Глобальные директивы и оптимизация ввода-вывода
nginx
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;worker_connections 1024;: максимальное число одновременных рабочих соединений на один рабочий процесс Nginx. Обеспечивает параллельную обработку свыше 1000 одновременных сетевых сессий.sendfile on;: активация системного вызова ядра Linuxsendfile(), позволяющего передавать статические файлы (JS, CSS, изображения) напрямую с диска в сетевой сокет, минуя копирование данных в адресное пространство приложения.keepalive_timeout 65;: интервал удержания постоянного TCP-соединения между клиентом и прокси-сервером, снижающий накладные расходы на повторное рукопожатие TLS.
3.2. Конфигурация потокового сжатия Gzip
Для минимизации объёма сетевого трафика и ускорения загрузки клиентского интерфейса на мобильных или низкоскоростных каналах включён модуль потокового сжатия:
nginx
# Настройки сжатия Gzip
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;gzip_comp_level 6;: оптимальный баланс между степенью компрессии данных (до 70–80% для JSON и JavaScript) и нагрузкой на центральный процессор сервера.gzip_vary on;: добавление HTTP-заголовкаVary: Accept-Encoding, предотвращающего отправку сжатых ответов кэширующими прокси-серверами клиентам, не поддерживающим Gzip.gzip_types: перечень MIME-типов данных, подлежащих сжатию (текстовые стили, скрипты, структуры данных API). Бинарные форматы (изображения PNG/WebP, PDF-отчёты) исключены, так как они уже сжаты на уровне формата.
3.3. Принудительное перенаправление HTTP на HTTPS
Все входящие соединения по незащищённому порту 80 принудительно трансформируются в защищённые с сохранением запрошенного пути и параметров строки запроса:
nginx
# Редирект всего входящего трафика HTTP на HTTPS
server {
listen 80;
server_name heatbalance.ru www.heatbalance.ru heatbalance.online www.heatbalance.online ci.heatbalance.online;
location / {
return 301 https://$host$request_uri;
}
}- Код ответа
301 Moved Permanentlyинструктирует браузеры и поисковые системы кэшировать безопасный адрес и выполнять последующие переходы напрямую по протоколу HTTPS.
3.4. Виртуальный хост портала документации (heatbalance.online)
Конфигурация домена документации направляет входящие запросы к специализированному контейнеру heatbalance-docs, отдающему скомпилированный статический сайт VitePress:
nginx
# Портал технической документации на heatbalance.online
server {
listen 443 ssl;
server_name heatbalance.online www.heatbalance.online;
ssl_certificate /etc/nginx/ssl/online_fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/online_private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Заголовки безопасности OWASP
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
client_max_body_size 50M;
location / {
proxy_pass http://docs:80;
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;
}
}3.5. Виртуальный хост Jenkins CI/CD (ci.heatbalance.online)
Конфигурация субдомена автоматизации обеспечивает изолированный защищённый доступ к серверу Jenkins, запущенному на хосте:
nginx
# Безопасный доступ HTTPS к панели Jenkins CI/CD
server {
listen 443 ssl;
server_name ci.heatbalance.online;
ssl_certificate /etc/nginx/ssl/online_fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/online_private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
client_max_body_size 50M;
location / {
proxy_pass http://host.docker.internal:8080;
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;
proxy_set_header X-Forwarded-Port $server_port;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 90s;
}
}Ключевые параметры шлюза CI/CD:
host.docker.internal:8080: доступ изнутри контейнера Docker к порту демона Jenkins, работающего на хостовой операционной системе Linux.- WebSocket-туннелирование (
UpgradeиConnection): гарантирует стабильное обновление консольного вывода сборки в веб-интерфейсе Jenkins без обрывов соединений. client_max_body_size 50M;: разрешает передачу крупных артефактов и пакетов вебхуков от GitHub.proxy_read_timeout 90s;: увеличенное время ожидания ответа при длительных синхронных операциях Jenkins.
3.6. Виртуальный хост производственного портала (heatbalance.ru)
Основной блок конфигурации распределяет клиентские запросы между статическим интерфейсом и шлюзом API:
nginx
server {
listen 443 ssl;
server_name heatbalance.ru www.heatbalance.ru;
# SSL-сертификаты (монтируются с хоста)
ssl_certificate /etc/nginx/ssl/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
# Заголовки безопасности OWASP
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
client_max_body_size 50M;
# 1. Отдача клиентского веб-приложения React SPA
location / {
proxy_pass http://frontend:80;
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;
}
# 2. Проксирование API-запросов к шлюзу YARP API Gateway
location /api/ {
proxy_pass http://gateway:8080/api/;
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;
}
# 3. Эндпоинты авторизации OpenIddict / OAuth 2.0
location /connect/ {
proxy_pass http://gateway:8080/connect/;
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;
}
# 4. Эндпоинты пользовательского интерфейса ASP.NET Core Identity
location /Identity/ {
proxy_pass http://gateway:8080/Identity/;
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;
}
}4. Стандарты безопасности и криптографические профили
Для соответствия современным отраслевым стандартам информационной безопасности (PCI DSS, рекомендациям OWASP и NIST) в Nginx реализован усиленный криптографический профиль:
4.1. Протоколы и шифронаборы
- Запрет уязвимых протоколов: протоколы
SSLv2,SSLv3,TLSv1.0иTLSv1.1полностью отключены. Разрешены только современные протоколыTLSv1.2иTLSv1.3. - Forward Secrecy (Совершенная прямая секретность): все применяемые наборы шифров основаны на алгоритмах Диффи-Хеллмана на эллиптических кривых (
ECDHE) и классическом Диффи-Хеллмане (DHE). Компрометация закрытого ключа сервера в будущем не позволяет расшифровать ранее записанный трафик. - Аутентифицированное шифрование (AEAD): использование режимов
AES-GCMгарантирует одновременную конфиденциальность и целостность передаваемых пакетов без накладных расходов на вычисление отдельного HMAC.
4.2. Заголовки безопасности OWASP
X-Frame-Options "SAMEORIGIN": блокирует отображение страниц платформы внутри тегов<frame>,<iframe>или<embed>на сторонних сайтах, исключая возможность проведения атак типа Clickjacking (кража кликов).X-Content-Type-Options "nosniff": запрещает веб-браузерам определять MIME-тип ресурса путём анализа содержимого (MIME-sniffing), принуждая строго следовать заголовкуContent-Type. Это предотвращает выполнение вредоносных скриптов, замаскированных под изображения.X-XSS-Protection "1; mode=block": активирует встроенный фильтр межсайтового скриптинга в браузерах устаревших поколений с блокировкой отображения страницы при обнаружении атаки.
5. Управление SSL-сертификатами Let's Encrypt
Платформа использует бесплатные доверенные сертификаты центра сертификации Let's Encrypt с автоматизированным жизненным циклом через утилиту Certbot.
5.1. Первичное получение сертификатов
Для первичного выпуска сертификатов при остановленном веб-сервере используется режим автономной аутентификации (standalone):
bash
# Установка Certbot на хост-сервер Ubuntu
sudo apt update && sudo apt install -y certbot
# 1. Сертификат для основного домена платформы
sudo certbot certonly --standalone \
-d heatbalance.ru \
-d www.heatbalance.ru \
--email admin@heatbalance.ru \
--agree-tos --no-eff-email
# 2. Сертификат для вторичного домена и субдомена CI/CD
sudo certbot certonly --standalone \
-d heatbalance.online \
-d www.heatbalance.online \
-d ci.heatbalance.online \
--email admin@heatbalance.ru \
--agree-tos --no-eff-email5.2. Структура каталогов и монтирование в Docker
На хост-сервере сертификаты размещаются в служебной директории Jenkins /var/lib/jenkins/app-config/nginx/ssl или в стандартном каталоге Let's Encrypt:
bash
# Создание рабочей директории для сертификатов
sudo mkdir -p /var/lib/jenkins/app-config/nginx/ssl
# Копирование / создание символических ссылок для heatbalance.ru
sudo cp -L /etc/letsencrypt/live/heatbalance.ru/fullchain.pem /var/lib/jenkins/app-config/nginx/ssl/fullchain.crt
sudo cp -L /etc/letsencrypt/live/heatbalance.ru/privkey.pem /var/lib/jenkins/app-config/nginx/ssl/private.key
# Копирование / создание символических ссылок для heatbalance.online
sudo cp -L /etc/letsencrypt/live/heatbalance.online/fullchain.pem /var/lib/jenkins/app-config/nginx/ssl/online_fullchain.crt
sudo cp -L /etc/letsencrypt/live/heatbalance.online/privkey.pem /var/lib/jenkins/app-config/nginx/ssl/online_private.key
# Установка прав доступа (чтение только для владельца и группы)
sudo chmod 600 /var/lib/jenkins/app-config/nginx/ssl/*.key
sudo chmod 644 /var/lib/jenkins/app-config/nginx/ssl/*.crtВ файле docker-compose.yml эти пути монтируются в контейнер heatbalance-nginx в режиме «только для чтения» (:ro):
yaml
services:
nginx:
image: nginx:alpine
container_name: heatbalance-nginx
volumes:
- ${NGINX_CONF_PATH:-./nginx/nginx.conf}:/etc/nginx/nginx.conf:ro
- ${NGINX_SSL_PATH:-./nginx/ssl}:/etc/nginx/ssl:ro
ports:
- "80:80"
- "443:443"5.3. Автоматическое продление сертификатов (Zero-Downtime Auto-Renew)
Срок действия сертификатов Let's Encrypt составляет 90 дней. Для их автоматического продления настраивается регулярное задание cron на хосте, выполняющее продление и горячую перезагрузку конфигурации Nginx без остановки контейнеров:
bash
# Редактирование заданий суперпользователя
sudo crontab -eДобавьте следующую строку для ежедневной проверки в 03:30 ночи:
bash
30 3 * * * certbot renew --quiet --deploy-hook "cp -L /etc/letsencrypt/live/heatbalance.ru/fullchain.pem /var/lib/jenkins/app-config/nginx/ssl/fullchain.crt && cp -L /etc/letsencrypt/live/heatbalance.ru/privkey.pem /var/lib/jenkins/app-config/nginx/ssl/private.key && cp -L /etc/letsencrypt/live/heatbalance.online/fullchain.pem /var/lib/jenkins/app-config/nginx/ssl/online_fullchain.crt && cp -L /etc/letsencrypt/live/heatbalance.online/privkey.pem /var/lib/jenkins/app-config/nginx/ssl/online_private.key && docker exec heatbalance-nginx nginx -s reload"6. Диагностика, аудит логов и устранение неисправностей
6.1. Проверка синтаксиса и перезагрузка «на лету»
Перед применением любых изменений конфигурации выполняется обязательное тестирование синтаксической корректности директив:
bash
# Проверка синтаксиса Nginx внутри контейнера
docker exec heatbalance-nginx nginx -t
# Ожидаемый вывод:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# Горячая перезагрузка конфигурации без разрыва активных соединений
docker exec heatbalance-nginx nginx -s reload6.2. Мониторинг журналов запросов и ошибок
Журналы Nginx доступны через стандартный поток логирования Docker:
bash
# Просмотр последних 100 строк логов доступа и ошибок в реальном времени
docker logs -f --tail 100 heatbalance-nginx
# Фильтрация ошибок 5xx (сбои внутренних бэкенд-сервисов)
docker logs heatbalance-nginx 2>&1 | grep " 50[0-9] "
# Фильтрация ошибок 4xx (ошибки авторизации, ненайденные пути)
docker logs heatbalance-nginx 2>&1 | grep " 40[0-9] "6.3. Типовые неисправности и сценарии локализации
| Код ошибки / Симптом | Вероятная причина | Инженерное решение |
|---|---|---|
| 502 Bad Gateway | Контейнер upstream (frontend:80 или gateway:8080) остановлен, перезагружается или не входит в общую сеть heatbalance_network. | Выполнить docker ps -a, проверить логи упавшего контейнера через docker logs heatbalance-gateway, убедиться в совпадении имён сервисов в docker-compose.yml. |
| 504 Gateway Timeout | Сложный теплотехнический расчёт или генерация многостраничного отчёта PDF превысили лимит времени ожидания прокси. | Увеличить таймауты в location /api/: proxy_read_timeout 120s;, proxy_connect_timeout 30s;. |
| 413 Request Entity Too Large | Пользователь загрузил технологический датасет или телеметрический лог размером более установленного лимита. | Проверить директиву client_max_body_size 50M; в соответствующем блоке server файла nginx.conf. |
| SSL Handshake Failed / Certificate Expired | Истёк срок действия сертификата Let's Encrypt либо повреждён файл закрытого ключа. | Проверить сроки действия через openssl x509 -in /var/lib/jenkins/app-config/nginx/ssl/fullchain.crt -noout -dates, запустить ручное обновление certbot renew --force-renewal. |
| WebSocket connection to 'ci.heatbalance.online' failed | Отсутствуют директивы апгрейда соединения в блоке проксирования Jenkins. | Убедиться в наличии строк proxy_set_header Upgrade $http_upgrade; и proxy_set_header Connection "upgrade";. |