Skip to content

Конфигурация Nginx и SSL ​

1. Назначение и архитектурное положение Nginx ​

В архитектуре распределённой микросервисной платформы «UrFU HeatBalance» веб-сервер Nginx выполняет роль пограничного обратного прокси-сервера (Perimeter Reverse Proxy) и демилитаризованной зоны (DMZ). Nginx является единственным публичным компонентом инфраструктуры, принимающим трафик из внешней сети Интернет по портам 80 (HTTP) и 443 (HTTPS).

Основные функции узла Nginx: ​

  1. SSL/TLS-терминация: централизованная криптографическая обработка входящих соединений с разгрузкой вычислительных мощностей микросервисов (TLS Offloading).
  2. Селективная маршрутизация доменов: разграничение трафика основного производственного интерфейса (heatbalance.ru), портала документации (heatbalance.online) и контура непрерывной интеграции (ci.heatbalance.online).
  3. Маршрутизация путей (Path Routing): распределение запросов между контейнером статического фронтенда React и точкой входа API (YARP Gateway).
  4. Проксирование WebSockets: поддержка полнодуплексного протокола WebSocket для интерактивной передачи логов сборки Jenkins CI/CD в реальном времени.
  5. Аппаратная оптимизация и сжатие: сжатие текстовых и JSON-ответов алгоритмом Gzip на лету, кэширование статических ассетов и поддержка постоянных соединений (Keep-Alive).
  6. Защита периметра (Security Hardening): внедрение защитных заголовков OWASP, лимитирование объёмов входящего трафика и защита от фрагментированных атак.

2. Топология доменных имён и маршрутизация ​

Инфраструктура платформы обслуживает три доменные зоны с чётким разделением ответственности:

Доменное имяПротоколНаправление проксированияОписание и назначение
heatbalance.ru
www.heatbalance.ru
HTTPShttp://frontend:80
http://gateway:8080
Основной производственный портал: веб-интерфейс React SPA, API шлюз, аутентификация OpenIddict.
heatbalance.online
www.heatbalance.online
HTTPShttp://docs:80Официальный портал технической документации VitePress (контейнер heatbalance-docs).
ci.heatbalance.onlineHTTPShttp://host.docker.internal:8080Защищённый контур CI/CD: панель управления Jenkins, приём вебхуков от GitHub, мониторинг конвейеров сборки.
Все домены (порт 80)HTTP301 -> 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;: активация системного вызова ядра Linux sendfile(), позволяющего передавать статические файлы (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: ​

  1. host.docker.internal:8080: доступ изнутри контейнера Docker к порту демона Jenkins, работающего на хостовой операционной системе Linux.
  2. WebSocket-туннелирование (Upgrade и Connection): гарантирует стабильное обновление консольного вывода сборки в веб-интерфейсе Jenkins без обрывов соединений.
  3. client_max_body_size 50M;: разрешает передачу крупных артефактов и пакетов вебхуков от GitHub.
  4. 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 ​

  1. X-Frame-Options "SAMEORIGIN": блокирует отображение страниц платформы внутри тегов <frame>, <iframe> или <embed> на сторонних сайтах, исключая возможность проведения атак типа Clickjacking (кража кликов).
  2. X-Content-Type-Options "nosniff": запрещает веб-браузерам определять MIME-тип ресурса путём анализа содержимого (MIME-sniffing), принуждая строго следовать заголовку Content-Type. Это предотвращает выполнение вредоносных скриптов, замаскированных под изображения.
  3. 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-email

5.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 reload

6.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";.

Разработано в рамках проекта моделирования тепловых балансов и промышленного мониторинга