mTLS и service mesh: где шифрование действительно добавляет защиту
<p>Внутри Kubernetes трафик между сервисами часто выглядит «внутренним», а потому по умолчанию считается безопасным. Это опасная модель. Pod может оказаться на другом узле, сеть может быть скомпрометирована, а ошибочно выданные сетевые права позволяют обращаться к соседнему сервису. На этом фоне mTLS — взаимная TLS-аутентификация — решает две конкретные задачи: шифрует соединение и позволяет сторонам удостовериться, с кем они установили связь. Но это не универсальный «режим безопасного Kubernetes».</p> <p>Важно начать с границы ответственности. Kubernetes не включает service mesh как базовую функцию. Сетевые политики Kubernetes определяют, каким Pod разрешены соединения, но шифрование и способ его реализации зависят от CNI либо отдельного продукта. Kubernetes Security Checklist прямо рекомендует NetworkPolicy и допускает service mesh как вариант шифрования внутрикластерного трафика, если выбранный CNI этого не делает. Поэтому слова «включим mTLS» всегда должны сопровождаться названием продукта, моделью его внедрения и владельцем сертификатов.</p> <h2>Что mTLS меняет, а чего не меняет</h2> <p>При обычном TLS клиент проверяет сервер. При mTLS проверяются обе стороны: клиент предъявляет сертификат, сервер предъявляет свой. В service mesh это обычно происходит не в прикладном коде, а в прокси или другом компоненте data plane. Контрольный компонент выдаёт короткоживущие учётные данные и распространяет правила. В результате приложение <code>payments</code> может получить криптографически подтверждаемую идентичность вызывающего, а трафик между точками обработки становится зашифрованным.</p> <p>Это полезно, но это не заменяет следующие уровни:</p> <table> <thead> <tr> <th>Вопрос</th> <th>Что отвечает</th> </tr> </thead> <tbody><tr> <td>Может ли Pod установить сетевое соединение?</td> <td>NetworkPolicy и её реализация CNI</td> </tr> <tr> <td>Кто действительно подключился?</td> <td>mTLS и идентичность workload</td> </tr> <tr> <td>Разрешено ли этому субъекту вызвать именно этот метод?</td> <td>прикладная или mesh-авторизация</td> </tr> <tr> <td>Кто может создать, изменить или прочитать объект Kubernetes?</td> <td>RBAC и защита API</td> </tr> <tr> <td>Что произошло после запуска процесса?</td> <td>журналирование, мониторинг и runtime detection</td> </tr> </tbody></table> <p>Например, сервис <code>frontend</code> имеет разрешённый egress к <code>orders</code>, а <code>orders</code> принимает соединения только от идентичности <code>frontend</code>. Здесь NetworkPolicy уменьшает число возможных путей, а mTLS не позволяет любому Pod, оказавшемуся на допустимом IP-пути, выдать себя за <code>frontend</code>. Однако если у самого <code>frontend</code> есть уязвимость и он легитимно обращается к <code>orders</code>, mTLS не определит, что бизнес-запрос вредоносен. Нужны авторизация, валидация входа и наблюдаемость.</p> <h2>Не начинайте с режима strict на всём кластере</h2> <p>Самый частый риск внедрения — превратить сильную цель в массовый простой. В ряде mesh-реализаций есть миграционный режим, в котором приёмник временно допускает и обычный трафик, и mTLS. В Istio такой режим называется <code>PERMISSIVE</code>; <code>STRICT</code> принимает только mTLS. Это свойство Istio, а не API Kubernetes, поэтому названия и точное поведение обязательно проверяют в документации выбранного продукта.</p> <p>Безопасный порядок работы выглядит так:</p> <ol> <li>Выберите один непубличный сервисный контур и зафиксируйте все его клиенты: workloads, Jobs, интеграции извне кластера, probes, gateways и администрирование.</li> <li>Определите, какая идентичность соответствует каждому workload. Нужен не просто список имён сервисов, а понятная связь «service account / namespace → допустимый сервис».</li> <li>Включите mesh в пилотном namespace и соберите наблюдаемость: сколько соединений уже mTLS, какие остаются plaintext и почему.</li> <li>Исправьте исключения: включите proxy там, где это предусмотрено моделью продукта, либо оформите контролируемый gateway для внешнего клиента. Не маскируйте проблему постоянным глобальным исключением.</li> <li>Лишь после подтверждения всех путей переведите <em>приёмник</em> пилотного сервиса в строгий режим. Затем проверьте доступность, трассы, ошибки TLS и автоматическую ротацию сертификатов.</li> </ol> <p>На практике «сначала permissive, затем strict» — не цель сама по себе, а способ увидеть реальный legacy-трафик до блокировки. Если у выбранного решения нет такого режима, пилот должен быть ещё меньше: отдельный сервис, контролируемые клиенты и заранее подготовленный откат.</p> <h2>Минимальная проверка перед принуждением</h2> <p>До изменения режима владелец сервиса и платформа должны ответить на вопросы ниже не предположениями, а наблюдаемыми данными.</p> <ul> <li>Все ли исходящие клиенты известны, включая CronJob и отладочные утилиты?</li> <li>Все ли конечные точки на пути поддерживают модель mesh: сам workload, ingress/egress gateway, внешняя интеграция?</li> <li>Есть ли отдельный ответственный за корневое доверие, выдачу, срок жизни и отзыв сертификатов?</li> <li>Видна ли в телеметрии идентичность peer и признак шифрования, а не только HTTP-код?</li> <li>Проверяли ли ротацию сертификата в рабочем окружении или реплике production?</li> </ul> <p>Проверка результата должна быть двусторонней. Позитивный тест: <code>frontend</code> вызывает разрешённый API <code>orders</code>, приложение получает ожидаемый ответ, а данные mesh показывают зашифрованное соединение и ожидаемую идентичность клиента. Негативный тест: тестовый Pod с другой service account пытается обратиться к тому же маршруту и получает отказ по политике идентичности либо по NetworkPolicy. Если виден только успешный HTTP 200, это ещё не доказательство mTLS: запрос мог пройти plaintext или обойти data plane.</p> <p>Для Istio минимальная строгая политика приёмника имеет вид ниже. Это <strong>пример именно Istio</strong>, а не универсальный манифест Kubernetes; применять его стоит только в отдельном пилотном namespace после инвентаризации клиентов.</p> <pre><code class="language-yaml">apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: orders-strict namespace: pilot spec: selector: matchLabels: app: orders mtls: mode: STRICT </code></pre> <p>После применения не ограничивайтесь тем, что объект создался. Проверьте, что клиент действительно использует data plane выбранного mesh, что запросы от <code>frontend</code> проходят, а известный plaintext-клиент перестал проходить предсказуемо. Затем отмените или скорректируйте политику в рамках заранее согласованного rollback, если отказ затронул критичный путь.</p> <h2>Сертификаты — не деталь установки</h2> <p>mTLS надёжен ровно настолько, насколько надёжны его идентичности и доверие. Требуется определить:</p> <ul> <li>кто владеет CA и имеет право менять trust bundle;</li> <li>как часто выдаются и автоматически обновляются workload-сертификаты;</li> <li>что происходит при компрометации service account, узла или корневого ключа;</li> <li>где доступны журналы выпуска и ошибок валидации;</li> <li>как проходит смена CA без одновременного отключения всех сервисов.</li> </ul> <p>Нельзя переносить административный корневой сертификат кластера в mesh «для простоты» без отдельной архитектурной оценки. У разных продуктов разные механизмы доверия, хранение ключей и интеграция с внешним PKI. Это отдельная зона контроля: в ней должны быть аудит, ограниченный доступ и проверенный сценарий ротации.</p> <h2>Когда service mesh оправдан</h2> <p>Mesh уместен, когда ценность идентичности workload и шифрования выше его операционной цены: есть много внутренних сервисов, чувствительные данные, несколько команд, требования к аудитам или необходимость вводить авторизацию между сервисами единообразно. Для небольшой системы с несколькими сервисами иногда достаточно TLS в приложении или возможности CNI шифровать трафик, плюс аккуратные NetworkPolicy. Это не «хуже» и не «лучше»: нужно сравнить покрытие угроз, сложность сопровождения и ответственность за инциденты.</p> <p>Итоговая архитектура должна быть слоистой. Сначала минимальные сетевые права, затем криптографическая идентичность и шифрование, затем авторизация действий и полноценная телеметрия. Если хотя бы один из этих уровней не проверен, надпись «mTLS enabled» остаётся настройкой, а не доказанной мерой защиты.</p> <h2>Официальные источники</h2> <ul> <li>Kubernetes Security Checklist: <a href="https://kubernetes.io/docs/concepts/security/security-checklist/">https://kubernetes.io/docs/concepts/security/security-checklist/</a></li> <li>Kubernetes NetworkPolicy: <a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/">https://kubernetes.io/docs/concepts/services-networking/network-policies/</a></li> <li>Istio Security and mTLS: <a href="https://istio.io/latest/docs/concepts/security/">https://istio.io/latest/docs/concepts/security/</a></li> </ul>