HTTPS настроен, но API-шлюз в Docker доступен по IP и порту: что проверить
Определите, где работает прокси, ограничьте публикацию порта backend и проверьте IPv4, IPv6 и правила Docker. Выполните проверку без учётных данных с тайм-аутом и повторите её после пересоздания контейнера.
После настройки домена и сертификата у API-шлюза в Docker браузер показывает HTTPS. Но это ещё не означает, что все запросы проходят через обратный прокси. Если прежний адрес IP-сервера:3000 по-прежнему доступен извне, через него можно обойти ограничения и журналирование на уровне прокси. Даже собственная аутентификация шлюза не означает, что этот дополнительный вход закрыт.
Материал предназначен для администраторов собственного шлюза и рассматривает участок «публичный домен → обратный прокси с HTTPS → контейнер шлюза». Предполагается обычная сеть Docker bridge. Для host, macvlan, прямой маршрутизации и других особых режимов нужна отдельная проверка. Порт 3000 приведён только для примера: это не утверждение о настройках какого-либо продукта. Мы не меняем рабочую инфраструктуру и не выдаём локальную имитацию за проверку реального развёртывания.
1. Определите, где работает обратный прокси
Запишите домен, среду выполнения прокси, имя сервиса шлюза, порт внутри контейнера и опубликованный порт хоста. Две распространённые схемы требуют разных адресов backend:
| Где работает прокси | Пример адреса шлюза для прокси | Нужна ли публикация порта шлюза на хосте |
|---|---|---|
| Прокси работает непосредственно на хосте, шлюз — в контейнере | 127.0.0.1:3000 | Можно опубликовать порт только на loopback-адресе хоста для локального прокси |
| Прокси и шлюз находятся в одной контролируемой bridge-сети Compose | gateway:3000 | Для связи между контейнерами публикация порта шлюза обычно не нужна |
| Прокси и шлюз работают на разных машинах | Проверенный частный адрес и порт | Адрес прослушивания и доступ выбирают по реальной схеме; loopback здесь копировать нельзя |
Во второй строке gateway должен быть настоящим именем сервиса, а 3000 — портом внутри контейнера. В общей сети Compose сервисы доступны по имени. IP контейнера может измениться при пересоздании, поэтому его не следует жёстко прописывать в настройках.[2] Адрес 127.0.0.1 внутри контейнера прокси обычно указывает на сам этот контейнер, а не на хост или соседний контейнер.
Процесс шлюза должен слушать интерфейс контейнера, доступный прокси. Если он слушает только собственный loopback, прокси из той же сети всё равно может не подключиться. Прослушивание всех интерфейсов внутри контейнера и публикация порта на публичном интерфейсе хоста — разные действия. Первое также позволяет подключаться другим контейнерам общей сети, поэтому не включайте в неё посторонние и недоверенные сервисы.
2. Закройте ненужный публичный вход к backend
Проверьте привязки портов работающего контейнера и файлы развёртывания. Редактирование файла без обновления контейнера не меняет действующую конфигурацию автоматически.
В записи 3000:3000 адрес хоста не ограничен. Она не означает «доступно только локально». Docker прямо предупреждает, что публикация порта по умолчанию может открыть его для внешних подключений.[1] Если прокси работает на хосте, такую привязку можно заменить на 127.0.0.1:3000:3000. Если прокси и шлюз находятся в одной контролируемой контейнерной сети, обычно достаточно удалить публикацию порта шлюза на хосте и обращаться к нему по имени сервиса. Нужные публичные порты самого прокси сохраняют.
До изменения убедитесь, что у вас остаётся надёжный административный доступ, сохраните старую конфигурацию и запланируйте допустимый перерыв. После обновления контейнера снова проверьте фактические привязки: одного перезапуска процесса приложения недостаточно. Если используются несколько файлов Compose, проверьте итоговую объединённую конфигурацию. Добавление loopback-привязки в новый файл не доказывает, что прежняя публичная привязка исчезла.
Не пропустите три ограничения:
- Проверяйте IPv4 и IPv6 отдельно. При определённых настройках Docker публикация без явного IP хоста может принимать подключения и через IPv6-адрес хоста. Одной проверки IPv4 недостаточно.[1]
- Docker документирует проблему версий до 28.0.0: машины в том же сегменте второго уровня могли обращаться к портам, опубликованным на localhost. Для старого развёртывания нельзя считать loopback-привязку достаточным доказательством: обновите Docker до поддерживаемой версии и проверьте доступ извне.[1]
- В Linux правило «ufw запрещает 3000» само по себе не доказывает недоступность порта Docker. Согласно документации Docker, перенаправление контейнерного трафика может происходить до обработки обычных правил ufw.[3] Сначала уберите ненужную публикацию, затем проверьте действующие правила выбранного firewall backend Docker и облачные группы безопасности. Не отключайте управление межсетевым экраном Docker ради эксперимента.
Удаление публикации на хосте не изолирует сервис от администратора хоста или контейнеров общей сети. Особые настройки прямой маршрутизации также меняют доступность. Цель — ограничить ненужный внешний доступ, а не объявить контейнер полностью изолированной границей безопасности.
3. Проверяйте два участка соединения по отдельности
HTTPS от публичного клиента до обратного прокси и соединение от прокси до шлюза нужно проверять отдельно. Действительный сертификат на первом участке не подтверждает шифрование второго. Внутренний HTTP допустимо рассматривать только для явно контролируемой сети на одной машине; через недоверенную сеть используйте проверяемый TLS или аутентифицированный зашифрованный канал.
Для обычного развёртывания Caddy с автоматическим HTTPS на публичном домене проверьте A/AAAA-записи, доступность прокси для внешней проверки, возможность привязать нужные порты, а также доступность для записи и постоянное хранение каталога с данными сертификатов.[4] Распространённая схема по умолчанию использует 80/443. У DNS challenge и других способов проверки свои условия: не всякая схема требует одинакового открытия порта 80. Условия, необходимые для продления сертификата, должны сохраняться и после его первого выпуска.
Особенно проверьте устаревшую AAAA-запись. IPv4 может работать, а IPv6 — вести на прежнюю машину, вызывая ошибки у части клиентов. Проверяйте сертификат по доменному имени. Несовпадение имени в сертификате при обращении по IP не означает, что порт закрыт. Не отключайте проверку сертификатов, чтобы получить успешный результат.
В клиенте следует сразу указывать проверенный HTTPS-адрес API. Перенаправление с HTTP не защищает Authorization или тело, уже отправленные по HTTP. Не передавайте настоящий ключ на HTTP-адрес в расчёте на последующий редирект. Для существующих ошибок сертификата используйте проверку TLS; изменение параметров модели здесь не поможет.
4. Проверьте доступ извне без учётных данных и с тайм-аутом
С устройства, с которого вы вправе проводить проверку и которое не является самим сервером, проверьте доменный вход, затем прежний порт backend на хосте. Обращайтесь только к контролируемым вами адресам, не сканируйте чужие сети. Сначала выберите подтверждённый документацией диагностический путь, доступный только для чтения и не запускающий генерацию. В примере /healthz — заменяемый путь: он есть не у каждого шлюза.
Пример для Windows PowerShell:
curl.exe -q --silent --show-error --connect-timeout 3 --max-time 10 --output NUL --write-out "http=%{http_code}\n" https://api.example.com/healthz
Замените домен и путь своими проверенными значениями. В Linux/macOS используйте curl, а вместо NUL — /dev/null. Параметр -q стоит первым, чтобы не подгружать дополнительные настройки из личного файла curl. Команда не передаёт Key, Cookie или тело запроса, не следует перенаправлениям и не повторяет запрос автоматически. На подключение отведено не более 3 секунд, на весь запрос — не более 10 секунд. Проверка HTTPS-сертификата остаётся включённой.[5] Существующие корпоративные прокси могут влиять на маршрут: фиксируйте устройство и окружение прокси, не обходя сетевую политику организации.
Для старого backend замените только целевой адрес, протокол и безопасный путь на те, которые ранее действительно были опубликованы, например на прежний HTTP-порт хоста. Не добавляйте настоящие учётные данные «для проверки связи». IPv6-адрес в URL заключают в квадратные скобки. Отдельно запишите результаты для домена, IPv4-входа backend и, если применимо, IPv6-входа.
| Результат | Что он подтверждает | Следующее действие |
|---|---|---|
| HTTPS-домен возвращает ожидаемый статус здоровья | Этот запрос дошёл до соответствующего HTTP-сервиса | Подтвердите назначение по журналам, затем проверяйте аутентификацию и функции |
| HTTPS-домен возвращает 502/503 | Некоторый HTTP-слой вернул ошибку; это не равнозначно сбою сертификата | Проверьте адрес backend, прослушивание, сеть и журналы прокси |
| Старый порт backend возвращает 200, 401, 403 или 404 | С этого источника по адресу всё ещё получен HTTP-ответ | Если внешний прямой доступ запрещён, проверка не пройдена; по журналам отличите backend от промежуточного устройства |
| Получен 301/302/307/308 | Получен ответ с перенаправлением | Проверьте адрес назначения; редирект не означает закрытый порт |
| Нет HTTP-ответа, соединение отклонено или истекло время ожидания | На этом маршруте не получен HTTP-ответ | Сверьте прослушивание, правила и источник; один отказ не доказывает недоступность из всех сетей |
В команде нет --fail, поэтому HTTP 4xx/5xx могут сопровождаться кодом завершения curl 0. Смотрите и на http=, и на сообщения об ошибках. Значение http=000 лишь указывает, что обычный HTTP-ответ не получен: причиной могут быть DNS, TLS, соединение или тайм-аут. Отсутствие ошибки curl не доказывает исправность сервиса, а отсутствие 200 — закрытие порта.
Команда проверена на локальном имитаторе HTTP: обычные и ошибочные статусы, отсутствие перехода по редиректу, отказ соединения и тайм-аут. Также проверено, что запросы не содержат Authorization, Cookie или тела. Эта имитация не проверяет публичные сертификаты, настоящий межсетевой экран Docker или модели поставщиков. Платные API не вызывались.
5. Повторите проверку после пересоздания контейнера
Сначала убедитесь, что прокси достигает backend, затем закройте старый вход и повторите внешнюю проверку. При неудаче откатывайте к проверенной предыдущей конфигурации. Не открывайте backend всему интернету с отключённой аутентификацией ради срочного восстановления. Если обновление затрагивает данные шлюза, отдельно подготовьте резервную копию и восстановление.
В запись проверки включите время по Пекину, версии Docker и прокси, сетевой режим, фактические привязки портов, источник проверки, результаты IPv4/IPv6 и соответствующие запросы в журналах прокси и шлюза. Ключи и полные пользовательские данные в запись не включают.
Перед вводом в работу подтвердите каждый пункт:
- Сертификат соответствует нужному имени хоста; путь продления и постоянное хранилище сертификатов работают.
- Прокси достигает шлюза, а внешний источник не может обратиться к порту backend в обход предусмотренного входа.
- На интерфейсе, требующем аутентификации, запрос без учётных данных или с неверными данными отклоняется ожидаемым образом. Открытый health-check со статусом 200 не означает сбой аутентификации; проверка аутентификации не заменяет сетевую проверку закрытого порта.
- Проверенный маршрут к имитатору, который не может переключиться на платный upstream, обрабатывает обычный и потоковый ответ. Возможности настоящей модели требуют отдельной контролируемой проверки, а не одного health-check.
- После пересоздания контейнеров повторно проверены привязки портов, разрешение имён сервисов, хранилище сертификатов и внешний доступ. Файлы развёртывания соответствуют работающей конфигурации, а результат не зависит от временной ручной настройки.
Если обычный запрос работает, а потоковый — нет, переходите к диагностике потокового ответа. Эта проверка касается входа, соединений и границ доступа; она не подтверждает качество модели или правильность тарификации.
Официальные источники и границы применимости
- Docker: Port publishing and mapping: адрес публикации, IPv6, ограничения localhost в старых версиях и прямая маршрутизация.
- Docker Compose: Networking: имена сервисов, внутренние порты и изменение адресов после пересоздания.
- Docker: Packet filtering and firewalls: различия обработки правил Docker и ufw.
- Caddy: Automatic HTTPS: домен, порты, хранилище и управление сертификатами.
- Официальное руководство curl: загрузка конфигурации, тайм-ауты и вывод кода ответа.
Источники проверены 20 октября 2026 года. Страницы документации Docker в текущем окружении не открылись из-за ошибки соединения; соответствующие положения сверены с Markdown-файлами официального репозитория документации Docker. Для других сетевых драйверов, firewall backend и управляемых прокси необходимо проверить фактическую версию и настройки. Реальное производственное развёртывание в рамках этой статьи не выполнялось.