11 августа 2026

Скил network-exposure-baseline — ограниченная привязка как умолчание

Как скил network-exposure-baseline классифицирует каждую сетевую привязку по четырём уровням, блокирует публичную доступность без обоснования и встраивается как проверочный шлюз в /dr-plan, /dr-do и /dr-archive.

Публичная доступность по умолчанию — это уязвимость по умолчанию. В экосистеме Arcana произошёл инцидент: Redis 7.x слушал на 0.0.0.0:6379 без аутентификации, а Postgres работал с listen_addresses='*' и стандартным паролем — оба были доступны из публичного интернета, что привело к жалобе регулятора. Первопричина: docker compose по умолчанию привязывается к 0.0.0.0, и не было никакого шлюза в CI. Скил network-exposure-baseline нужен, чтобы не допустить таких ошибок.

Скил загружается перед любым изменением, которое затрагивает порты Docker Compose, адреса bind в Redis, listen_addresses в Postgres, значения ListenStream в сокетах systemd или правила файрвола. К чисто внутреннему рефакторингу без сетевых изменений скил не применяется.

Четыре уровня

Каждая сетевая привязка попадает в один из четырёх уровней. Уровень 0 — Unix-сокет или отсутствие опубликованного порта: всегда разрешено, обоснование не нужно. Уровень 1 — петлевой адрес: 127.0.0.1, ::1 и их IPv6-маппированные варианты: всегда разрешено. Уровень 2 — диапазон CGNAT Tailscale (100.64.0.0/10): только внутри меша по определению, всегда разрешено.

Уровень 3 охватывает всё остальное: 0.0.0.0, ::, маска *, конкретные публичные IPv4-адреса, приватные диапазоны RFC1918 и link-local-адреса. RFC1918 тоже относится к уровню 3, потому что статический анализатор не может доказать, что привязка к приватному адресу безопасна: адрес из приватного диапазона, прокинутый на публично маршрутизируемый интерфейс, — это реальная уязвимость. Петлевой адрес и CGNAT Tailscale безопасны по конструкции; всё остальное требует явного обоснования от человека.

Формат обоснования и срок действия

Привязка уровня 3 требует двух дополнительных полей в docker-compose.yml: x-exposure-justification с описанием реального смягчения риска и x-exposure-expires с датой не позднее 90 дней в будущем. Срок действия — обязателен. Без принудительного истечения исключения накапливаются и забываются. Отсутствующий или истекший срок — ошибка, не предупреждение.

Для конфигов, которые не являются compose (Redis, Postgres, сокеты systemd), та же информация указывается в строке комментария: # exposure: justified expires=YYYY-MM-DD — причина.

Краткая форма портов в docker-compose (например, "5432:5432" без IP-адреса хоста) — это неявная привязка к 0.0.0.0, ошибка по умолчанию. Переменные интерполяции вроде ${TAILNET_IP}:443:443 разворачиваются из окружения перед классификацией; если переменная не задана и нет значения по умолчанию, анализатор выдаёт предупреждение и пропускает — он не может знать значение времени выполнения.

Интеграция в конвейер

Шлюз работает на четырёх стадиях конвейера. При /dr-prd раздел Network Exposure Baseline обязателен в PRD: декларация уровней и обоснования для уровня 3. При /dr-plan выдаётся предупреждение, если план затрагивает сетевые поверхности без явной классификации. При /dr-do любая новая привязка к 0.0.0.0 или краткая форма порта без обоснования блокирует шаг. При /dr-archive каждая привязка уровня 3 должна иметь действующее обоснование.

Канонический инструмент проверки — dev-tools/network-exposure-check.sh. Расхождение между документом скила и скриптом — это ошибка; обновлять нужно оба одновременно.

Общая картина — в посте что такое Datarim. О паттернах серверных операций, которые опираются на правильную настройку привязок, рассказывает пост о скиле infra-automation.