В чем основная проблема: если вы деплоите self‑hosted n8n (или любое Node.js‑приложение) в Docker и пытаетесь подключить GigaChat API то, скорее всего, натнетесь на ошибку SSL‑верификации. Статья о том, как это починить правильно, а не костылём с отключением всех сертификатов безопасности NODE_TLS_REJECT_UNAUTHORIZED=0, который советуют на каждом втором форуме.

Бонусом будет история про галлюцинацию, из‑за которой GigaChat начал называть всех клиентов «Иваном».

Контекст: что мы строили

Self‑hosted стек на VPS:

n8n в Docker — оркестратор воркфлоу

PostgreSQL в Docker — база данных (сессии, лиды, кэш)

GigaChat API (Сбер) — LLM для чат‑виджета на сайте

Gemini Flash (Google) — вторая модель для задач скоринга

GigaChat нужен для обработки входящих сообщений на русском языке с привязкой к 152-ФЗ: если пользователь отправляет персональные данные (телефон, email), система должна обнаружить PII и обработать их отдельным контуром. GigaChat выбрали потому что данные не уходят за рубеж — серверы Сбера находятся в РФ.

Симптом: UNABLE_TO_VERIFY_LEAF_SIGNATURE

Стек подняли, завели в n8n узел HTTP Request, указали эндпоинт https://gigachat.devices.sberbank.ru/api/v1/chat/completions, передали авторизационный заголовок и тестовый JSON. Жмем кнопку запускаи нода сразу валится в красную ошибку:

Error: unable to verify the first certificate

    at TLSSocket.onConnectSecure (node:_tls_wrap:1674:34)

    code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'

Суть проблемы упирается в устройство цепочки доверия. Сертификаты для доменов Сбера подписывает Головной удостоверяющий центр Минцифры России. При этом рантайм Node.js при сборке зашивает в себя эталонный список корневых сертификатов NSS от Mozilla Foundation. Поскольку российский удостоверяющий центр туда не включен, при валидации цепочки TLS‑хендшейка клиент Node.js просто берет и завершает соединение.

Вредный совет из интернета: почему нельзя отключать TLS

Первый порыв при виде красной ошибки сертификата, просто взять и выключить проверку сертификатов, и выставить NODE_TLS_REJECT_UNAUTHORIZED=0 в секции environment docker‑compose.

#docker-compose.yml — НЕ ДЕЛАЙТЕ ТАК

environment:

- NODE_TLS_REJECT_UNAUTHORIZED=0

На раннем этапе тестирования, отложив правильную настройку сертификатов на потом, я сделал так же. Спустя пару месяцев выяснилось, что временная директива тихо прижилась на боевом сервере.

Опасность такого подхода заключается в том, что флаг действует глобально на уровне всего процесса Node.js. Контейнер n8n прекращает валидацию цепочек сертификатов не только для эндпоинтов GigaChat, но и для обращений к базам данных, внешним вебхукам и Telegram Bot API. При компрометации маршрутизации трафика злоумышленник сможет осуществить атаку Man‑in‑the‑Middle и перехватить рабочие Bearer‑токены.

Настройка сертификатов Минцифры в Docker

Чтобы вернуть серверу безопасность, нам нужно было научить рантайм Node.js распознавать подпись Минцифры на общих основаниях.

Сборка бандла на сервере занимает четыре строчки в терминале:

mkdir -p /opt/certs

curl -k -s https://gu-st.ru/content/Other/doc/russian\_trusted\_root\_ca.cer > /opt/certs/russian_trusted_root_ca.crt

echo "" >> /opt/certs/russian_trusted_root_ca.crt

curl -k -s https://gu-st.ru/content/Other/doc/russian\_trusted\_sub\_ca.cer >> /opt/certs/russian_trusted_sub_ca.crt

chmod 644 /opt/certs/russian_trusted_root_ca.crt

В docker‑compose.yml прокидываем файл через volume и объявляем переменную окружения:

services:

  n8n:

    image: n8nio/n8n:latest

    volumes:

      - /opt/certs/russian_trusted_root_ca.crt:/home/node/certs/russian_trusted_root_ca.crt:ro

    environment:

      - NODE_EXTRA_CA_CERTS=/home/node/certs/russian_trusted_root_ca.crt

После рестарта (docker compose up -d) не стоит проверять статус через консольный openssl s_client — утилита берет сертификаты из системного каталога ОС и переменную Node.js просто не увидит.

Проверяем прямо внутри контейнера:

docker exec -it n8n node -e "require('https').get('https://gigachat.devices.sberbank.ru', (res) => console.log('TLS code:', res.statusCode)).on('error', console.error)"

Пришел ответ 200 или 403? Отлично, значит TLS завелся, сертификаты подцепились нормально.

Особенность с переменными $env в n8n 1.x

С токенами в n8n 1.x есть отдельная засада. Если дернуть $env.GIGACHAT_TOKEN прямо в выражении ноды, n8n выдаст ошибку: ExpressionError: access to env vars denied. В первой ветке прямой доступ к переменным окружения из нод просто заблокировали по умолчанию.

Конечно, можно открыть доступ переменной N8N_BLOCK_ENV_ACCESS_IN_NODE=false, но лучше не костылить и нормально завести ключ через Header Auth в меню Credentials.

Бонус — Синдром Ивана

И напоследок — смешной баг, на котором мы знатно споткнулись («синдром Ивана»). Мы поставили GigaChat чистить текст от персональных данных‑ убрать телефоны и почту перед тем, как отправлять сообщения дальше...

В системном промпте стояла задача: если клиент оставил контакты, достать его имя в поле first_name.

Прикол вскрылся на безымянных лидах. Пишет человек: «Добрый вечер, сколько стоит создание агента для атосервиса?». Имени в сообщении нет. Но модель обязана отдать валидный JSON, строковое поле пустоту не терпит, а null она возвращать не умеет. В итоге GigaChat не нашел ничего лучше, как вписать самое частое русское имя из своего датасета — Иван.

Наш тестовый бот начал приветствовать абсолютно всех пользователей фразой «Здравствуйте, Иван!», включая женщин и юрлиц. Пришлось переписать промпт с жестким запретом на домысливание и добавить на выходе ноду Code с проверкой имени.

Итог

Если хотитие добавить в n8n российские сервисы — не отключайте проверку сертификатов костылями. Добавить сертификаты Минцифры занимает три минуты, зато криптографический контур остается целым.


Исходники docker‑compose и готовый воркфлоу для n8n выложены в открытом репозитории: n8n‑gigachat‑starter‑kit.

Комментарии (0)