Service Mesh в дикой природе или как не стать жертвой атак

  • Введение

  • Угрозы безопасности в Service Mesh

    • 1. Ошибки конфигурации и человеческий фактор

    • 2. Атаки на контрольную плоскость (control plane)

    • 3. Обход sidecar-прокси (атаки на data plane)

    • 4. Компрометация mTLS и сертификатов

    • 5. Уязвимости Ingress и Egress (границы mesh)

    • 6. Периметр доверия и мульти-тенантность

    • 7. Эксплуатация уязвимостей самого mesh-софта

  • Примеры опасных конфигураций

    • Permissive мTLS в Istio — шифрование по желанию

    • Слишком широкая Authorization Policy

    • Публичный доступ для сервиса в Linkerd

    • Прочие коварные конфиги

  • Практики безопасной настройки Service Mesh

    • Включите строгий mTLS повсеместно

    • Применяйте политики авторизации «по-умолчанию закрыто»

    • Защитите Ingress и Egress

    • Минимизируйте права и возможности POD«ов

    • Обеспечьте наблюдаемость и аудит

    • Правильно работайте с сертификатами и ключами

    • Обновляйтесь и будуте в «безопасности»

    • Практикуйте принцип «Defense in Depth»

    • Автоматизируйте проверку конфигураций

  • Опыт реальных компаний

    • Airbnb

    • Lyft

  • Заключение

  • Источники

Введение

Всё, как я люблю, начнем с основ, а закончим непонятным.

c4d2e822f69ed2ae247abcbbb83bb053.png

Service mesh — это слой инфраструктуры, который берёт на себя сетевое взаимодействие между микросервисами. Проекты Istio и Linkerd — одни из самых популярных service mesh для Kubernetes. Они обещают нам »из коробки» мьютуальный (язык сломаешь) TLS (mTLS) шифрование трафика, аутентификацию сервисов, гибкое управление трафиком и тонкий контроль доступа — словом, всё для реализации принципов Zero Trust. Звучит отлично, безопасность, наблюдаемость и контроль без правки кода приложений. Но, как известно, «самое прочное железо бесполезно, если дверь установлена петлями наружу». Проще говоря, неправильная конфигурация или недопонимание работы mesh способно свести на нет все преимущества. Согласно одному отчёту, до 55% инцидентов безопасности в Kubernetes связаны с неверной конфигурацией –, а service mesh добавляет ещё свой пласт YAML-манифестов и настроек. Для ибшника это означает — чтобы успешно внедрить Istio или Linkerd, надо знать, где нас подстерегают грабли.

В этом посте мы глубоко погрузимся в темы безопасности service mesh на примере Istio и Linkerd. Разберём основные угрозы: от банальных ошибок в YAML до атакующих, пытающихся обойти sidecar-прокси или скомпрометировать mTLS. Посмотрим на конкретные примеры уязвимых конфигураций — да-да, будет немного страшных YAML-ов. Обсудим лучшие практики: как настроить mesh безопасно — политики, мониторинг, ротация сертификатов, ограничения прав. И немного заглянем в реальные кейсы — как крупные компании (Airbnb и Lyft) подходят к безопасности service mesh, какие уроки они вынесли.

Угрозы безопасности в Service Mesh

Service mesh решает много проблем, но приносит и новые векторы атак. Рассмотрим основные угрозы и уязвимости, характерные для Istio/Linkerd.

53e505fe0db4864ba93a9ab1ed22723e.png

1. Ошибки конфигурации и человеческий фактор

Это враг №1. Service mesh — это десятки CRD и сотни опций, неправильная комбинация которых может открыть дырку в обороне. Например, не включили строгий mTLS — и прокси примут незашифрованный трафик от кого угодно. Или написали слишком разрешающую политику — и невольно открыли доступ лишним сервисам. Порой достаточно опечатки или неверного значения по умолчанию. В 2017 небольшая конфигурационная ошибка обрушила пол-интернета (привет AWS outage) –, а неправильный YAML в mesh может компрометировать безопасность всего кластера. Kubernetes и Istio стараются валидировать ресурсы, но они не всесильны, Mesh позволяет описать политику, которая формально валидна, но не делает то, что вы думаете.

2. Атаки на контрольную плоскость (control plane)

Istio-ядро (istiod) или компоненты Linkerd могут стать мишенью. Это высокопривилегированные сервисы: они распределяют сертификаты, конфигурации, управляют всеми прокси. Если злоумышленник найдёт уязвимость в istiod или проникнет в него, он потенциально сможет перехватить или подменить трафик всего mesh.

Были прецеденты, например, уязвимость, позволявшая вызвать отказ в обслуживании istiod без аутентификации (Stack exhaustion DoS) исправлялась в Istio — ТЫК. Control plane нужно обновлять вовремя (позже поговорим об этом) и строго ограничивать доступ к нему. Кроме того, подмена контрольной плоскости — теоретический, но пугающий сценарий. Если атакующий сумеет заставить прокси верить фальшивому istiod, он сможет управлять ими. Поэтому при многокластерных установках часто рекомендуют изолировать контрольную плоскость — например, вынести istiod в отдельный кластер, изолированный от рабочих нагрузок.

3. Обход sidecar-прокси (атаки на data plane)

Service mesh полагается на то, что весь трафик проходит через прокси (Envoy в Istio, Linkerd-proxy в Linkerd). Однако »плохое» приложение может попытаться обойти этот «слой контроля». Kubernetes-под и его sidecar разделяют сеть и пространство процессов, так что полного изоляционного барьера между приложением и прокси нет. Если приложение обладает достаточными правами, оно может отключить iptables-правила перенаправления и начать ходить напрямую, минуя proxy.

Например, под с Capability NET_ADMIN или NET_RAW вполне способен снести или изменить правила перенаправления Istio на своих интерфейсах — и трафик больше не будет «прозрачно» идти через sidecar — ТЫК. Istio по-умолчанию использует UID 1337 для Envoy-процесса и если запустить основное приложение с тем же UID 1337, оно может избежать перенаправления (iptables думает, что это уже прокси). Без должного контроля разработчики (или злоумышленники, эксплуатировавшие уязвимость в приложении) могут намеренно исключить свой под из-под контроля mesh.

Linkerd здесь немного выигрышнее: его proxy написан на Rust и не запускается с UID 0, а iptables обычно ставит init-контейнер. Тем не менее, общая рекомендация — не давать приложениям лишних прав в поде. В идеале, включить Istio CNI, чтобы iptables-правила ставились на уровне узла без NET_ADMIN в самом поде, и применять Pod Security Policy/Admission Control, запрещающие запуск привилегированных контейнеров или подозрительных UID.

4. Компрометация mTLS и сертификатов

Сам по себе mTLS — хорошее средство «от всего» — sidecar-прокси автоматически шифруют трафик и аутентифицируют друг друга сертификатами, избавляя нас от необходимости доверять сети. Но спросим себя: — »Где хранятся эти сертификаты и ключи? »

В Istio за выдачу сертификатов отвечает компонент Citadel (часть istiod) — он генерирует ключи и загружает их в sidecar через SDS (Secret Discovery Service). Ключ хранится в памяти прокси. Значит, если злоумышленник сумел выполнить код в контейнере Envoy или получил дамп памяти, он может вытащить приватный ключ и сертификат сервиса. С этим он может выдавать себя за сервис в mesh, пока сертификат действителен. Также, если скомпрометирован корневой сертификат (CA) mesh, всё рушится — злоумышленник может подделывать кого угодно.

В Linkerd доверенная сторона — это «trust anchor» (корневой сертификат), и с ним те же проблемы. Срок жизни рабочих сертификатов обычно ограничен (в Istio по умолчанию ~90 дней, в Linkerd по умолчанию 24 часа для выдаваемых сертификатов), но ротация должна работать как часы, иначе через N дней вы сами DoS«нете свой mesh просроченными сертификатами.

Кстати, у Istio по-умолчанию в старых версиях mTLS режим Permissive — прокси принимают и незашифрованные соединения тоже, чтобы постепенно внедрять шифрование. Если админ не включил Strict, злоумышленник может просто не использовать TLS и общаться с сервисом в открытую. Поэтому mTLS — не панацея, если не защитить ключи и правильно всё настроить. Нужно применять принцип наименьших привилегий, разграничивать доступ сервисов (Authentication+Authorization, о чём ниже) и иметь план на случай компрометации сертификатов (отзыв, замена).

5. Уязвимости Ingress и Egress (границы mesh)

Обычно Istio разворачивают Ingress Gateway — специальный Envoy, принимающий внешний трафик. Ошибка в настройке ingress способна выставить наружу то, что не должно быть видно. Как пример — открыть все пути на сервис без аутентификации, забыть проверить JWT-токены на входе, включить поддержку HTTP без TLS там, где ожидался только HTTPS.

Linkerd не предоставляет собственного ingress-контроллера, но mesh-прокси всё равно участвуют в обработке входящего трафика после ingress-контроллера Kubernetes. Если ingress-прокси пропускает что-то лишнее, дальше mesh уже не спасёт. С Egress аналогично, Istio умеет ограничивать исходящий трафик (например, режим REGISTRY_ONLY запрещает обращения к внешним хостам, не перечисленным в ServiceEntry). Но по умолчанию стоит ALLOW_ANY — любой внешний адрес доступен. Это значит, что скомпрометированное приложение внутри mesh может свободно слить данные на любой внешний сервер или скачать эксплоит — proxy его не остановит.

Более того, даже если включён REGISTRY_ONLY, злоумышленник всё ещё может найти способ обойти настройку. Например, создаст свой ServiceEntry с wildcard-хостом или воспользуется сервисом типа ExternalName, чтобы опять получить доступ вовне — ТЫК.

Разработчики могут из лучших побуждений добавить ServiceEntry для «удобства» (например, открыть доступ ко всем *.example[.]com), а злоумышленник этим окном воспользуется.

Если закрывать тему, то ingress/egress точки требуют особого внимания. Надо явно прописывать, что можно и нельзя, а также рассматривать использование отдельного узла выхода — Egress Gateway, через который направлять весь внешний трафик для мониторинга и фильтрации.

6. Периметр доверия и мульти-тенантность

Service mesh часто внедряют в компаниях с множеством сервисов и команд. Возникает вопрос: — »Насколько вы доверяете всем участникам mesh? »

По-умолчанию, если сервис подключён к mesh, он получает некий уровень доверия — у него есть сертификат, он может шифровать соединения и потенциально общаться с другими. Если это один общий mesh на весь кластер, то без дополнительных политик любой сервис может обратиться к любому (пусть шифрованно и аутентифицированно, но сама возможность есть). В среде с разными командами или особенно с внешними пользователями (мульти-тенант платформы типа SaaS) — это риск.

Исследования Wiz показали, как в cloud-сервисе атакующий, запустив свой код, сумел через Istio-прокси выйти к сервисам других арендаторов — из-за того, что среда полагалась на mesh, но не изолировала достаточно пулы ресурсов — ТЫК.

В Istio есть механизм AuthorizationPolicy как эквивалент межсервисного файрвола, а в Linkerd с версии 2.11+ появилась своя модель Default deny/allow policies. Но их ещё надо правильно применить — и не забыть, что сеть Kubernetes тоже может помочь. Например, дополнительно ограничить трафик между неймспейсами стандартными NetworkPolicy (сетевая политика перехватит трафик, даже если mesh по какой-то причине пропустит).

7. Эксплуатация уязвимостей самого mesh-софта

Не стоит забывать, что Istio и Linkerd — сложные программы (написанные, соответственно, на Go/Cpp и Rust). В них тоже бывают баги. Envoy (сердце Istio) — очень хороший прокси, но время от времени у него появляются CVE.

Например, нашумевшая уязвимость HTTP/2 (CVE-2023–44487, «Rapid Reset») позволяла завалить многие прокси потоком запросов — Linkerd, кстати, оказался устойчив благодаря особенностям реализации. Были проблемы с фильтрами Envoy, с обработкой JWT — перечислять можно долго. Атака на уязвимость data plane может привести к DoS (отказу в обслуживании) или, в худшем случае, удалённому выполнению кода в прокси. Хотя таких RCE не припомню, но теоретически возможно. Control plane Istio тоже имел CVE, например упомянутый DoS, и другие.

Фух, пора немного передохнуть, а то мы уже перечислили пугающий список угроз. Что со всем этим делать? Для начала — не отчаиваться. Да, service mesh добавляет новую сложность, но при грамотной настройке он значительно усилит безопасность ваших микросервисов, обеспечивая возможности, которых без mesh трудно достичь (например, mTLS «просто работает», детальный аудит кто к кому ходит, и т.д.). Нужно лишь знать слабые места и строить оборону вглубь.

Примеры опасных конфигураций

Ниже несколько реальных (и упрощённых) примеров YAML-манифестов для Istio и Linkerd, которые могут противоречить целям безопасности. Разберём, что в них не так.

Данная часть была написана с использованием ChatGPT-o3-mini-high, ведь найти реальные уязвимые манифесты было достаточно проблематично. А показать неправильные настройки — важно, для понимания темы.

Permissive мTLS в Istio — шифрование по желанию

Представим, вы установили Istio и хотите постепенно включать mTLS. По-умолчанию Istio находится в режиме PERMISSIVE, что означает, что прокси принимают как mTLS, так и обычный незашифрованный HTTP. Если клиент пришлёт TLS сертификат — отлично, установим защищенное соединение. Не пришлёт — ну что ж, пустим как есть. Это удобно для миграции, но если оставить так в продакшене — шифрование становится опциональным. Злоумышленник внутри вашей сети может просто не использовать TLS и общаться с сервисами, притворившись «старым» клиентом. Исправляется это ресурсом PeerAuthentication с режимом STRICT.

Опасная конфигурация может выглядеть так:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: enable-permissive-mtls
  namespace: prod-apps
spec:
  mtls:
    mode: PERMISSIVE

Мы явно включили Permissive-режим для namespace prod-apps. Это значит, что сервисы в этом неймспейсе будут принимать нешифрованный трафик. Возможно, админ думал, что »ну mTLS же включён, Permissive — это частично защищённый режим».

Но нет, Permissive нужен только временно, на этапе миграции. В боевом режиме такой YAML — беда в безопасности. Любой клиент, умеющий достучаться по IP до подов prod-apps, может общаться без сертификата. Правильно было бы использовать mode: STRICT повсеместно, когда все сервисы готовы, а лучше — включать строгий mTLS сразу глобально (например, PeerAuthentication с STRICT в istio-system на весь mesh). Инструменты вроде Snowcat — ТЫК сразу укажут на namespaces с Permissive как на проблему.

Слишком широкая Authorization Policy

Istio позволяет задавать политики авторизации — кто и что может вызывать. Это отлично для Zero Trust, если сами политики не содержат логических дыр.

Рассмотрим вот такую политику:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-with-exception
  namespace: payments
spec:
  action: ALLOW
  rules:
  - to:
    - operation:
        notPaths: ["/admin"]

Эта политика говорит:»разрешить всё, кроме пути /admin». На первый взгляд, звучит разумно, мы хотим закрыть чувствительный админский эндпоинт, а остальное — пусть работает. Проблема в том, что такой подход опасен по-умолчанию. Если разработчики завтра добавят новый чувствительный эндпоинт, скажем /superadmin или /bh-cat, то политика его пропустит, потому что она блокирует только URL, начинающиеся на /admin. Получается модель безопасности «от противного», разрешено всё, что не перечислено как запрещённое.

При правильной настройке следует писать AuthorizationPolicy правила позитивно, перечисляя разрешённые пути, методы, сервисы. Либо сначала создать политику-DENY для конкретного чувствительного маршрута, а затем ALLOW для остального –, но нужно тщательно следить, чтобы новые функции тоже попадали под защиту. Общий совет от экспертов и ChatGPT — избегать notPaths и подобных негативных критериев ТЫК. Лучше несколько явных правил на конкретные пути, чем одно »разрешить всё кроме X».

Публичный доступ для сервиса в Linkerd

В Linkerd модель безопасности другая, по-умолчанию весь трафик разрешён (policy mode all-unauthenticated), чтобы не ломать существующие связи при установке. Но вы можете включить режим по-умолчанию »deny» и открывать сервисы явно с помощью ресурсов Server и ServerAuthorization. Рассмотрим пример, когда администратор хочет открыть некий веб-сервис для доступа извне (например, фронтенд, которому должны стучаться пользователи):

apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
  name: web-open-to-world
  namespace: prod-apps
spec:
  server:
    name: web-http
  client:
    unauthenticated: true
    networks:
      - cidr: 0.0.0.0/0
      - cidr: ::/0

Эта конфигурация взята из документации Linkerd и означает:»разрешить неаутентифицированным клиентам из любых сетей доступ к серверу web-http».

Проще говоря, сделать сервис общедоступным, без требования mTLS. В некоторых случаях это нужно (например, публичный веб-портал). Но это также значит, что любой клиент, даже не являющийся частью вашего mesh, может подключиться. Если где-то внешняя защита не сработает (скажем, неверно настроен Ingress контроллер или открыты лишние порты), этот YAML потенциально даст доступ извне прямо до сервиса.

Из-за этого мы теряем преимущества аутентификации внутри mesh — здесь unauthenticated: true явно отключает проверку идентичности клиента. А поле networks: 0.0.0.0/0 расширяет допуск на все возможные подсети (включая интернет). Если такая политика применена по ошибке не к тому сервису, мы получим дыру в безопасности.

Ну хоть что-то у нас в безопасности

Представьте, кто-то скопировал этот манифест для удобства, забыл сузить cidr до внутренней сети — и вот, внутренний API внезапно доступен всему миру, готовьтесь к DDoS.

Из-за подобных настроек в Linkerd нужно с осторожностью обращаться с ServerAuthorization. По возможности держать all-authenticated режим по-умолчанию, чтобы внутренняя коммуникация шла только между прокси Linkerd (с проверкой сертификатов). А когда открываете что-то наружу, контролируйте CIDR (например, ограничьтесь подсетью вашего ingress).

Кстати, по-умолчанию, если ничего не настроить, Linkerd автоматически в режиме all-unauthenticated — что, как мы упомянули, разрешает всё подряд. Ибшнику стоит это знать и сразу при деплое Linkerd поменять default policy на более строгую (например, cluster-authenticated) и затем явно открывать нужные порты. Так вы избежите ситуации, когда кто-то разворачивает новый сервис, забывает задать политику, а по-умолчанию он открыт кому попало.

Прочие коварные конфиги

Есть и другие примеры, когда конфигурация mesh может выстрелить в ногу (ссылки на материалы про них в конце статьи):

  • Превышение доверия к сервис-аккаунтам;

  • Отсутствие валидации сертификатов внешних сервисов;

  • EnvoyFilter и Sidecar ресурсы в Istio.

Практики безопасной настройки Service Mesh

Настроить service mesh безопасно — задача многогранная. Она включает и корректную конфигурацию самого mesh, и общие DevSecOps-подходы.

Ниже я собрал best practices — своего рода чеклист для ибшника, отвечающего за Istio или Linkerd в продакшене.

94dceeb256dfa4e9ed332b889bc57e46.png

Включите строгий mTLS повсеместно

Шифрование и аутентификация сервис-сервис по TLS — основа Zero Trust. В Istio включите режим STRICT для всех namespace (PeerAuthentication без исключений). Можно задать одну политику default на весь mesh. Убедитесь, что нет legacy с Permissive.

В Linkerd установите default policy all-authenticated или даже cluster-authenticated (чтобы меж-кластер не пускать без явного разрешения) вместо all-unauthenticated по-умолчанию. Это гарантирует, что никакой трафик не пройдет незашифрованным и незаверенным. Каждый прокси будет требовать сертификат соседа. Так, если какой-то левый POD без sidecar попытается стучаться к сервису, он будет отвергнут.

Исключением разве что будет ingress-прокси, который принимает внешние подключения — он, понятно, будет принимать TLS от внешних клиентов по сертификату доверенному публичному или тому, что вы настроили. Но внутри кластера — всё должно идти через mTLS.

Применяйте политики авторизации «по-умолчанию закрыто»

Настроив шифрование, важно задать правила, кто кому может звонить. Без этого mesh как бы говорит — «Ладно, я удостоверился, что «service A» — это действительно сервис А, но пущу его куда угодно внутри, раз он свой». Такой модели недостаточно. В Istio используйте AuthorizationPolicy, например, можно для каждого сервиса (или группы) создать политику, разрешающую вход только от определённых сервис-аккаунтов или из определённых неймспейсов.

Хорошая практика — сначала создать DENY all по-умолчанию на namespace, а потом для нужных сервисов ALLOW от конкретных клиентов. Если DENY all неудобно, то хотя бы не полагайтесь на отсутствие политики = полный доступ (по-умолчанию, если никаких ALLOW-политик нет, Istio пустит запрос — ТЫК). Лучше явно описать разрешения. В Linkerd аналогично, можете установить default policy = deny (или all-authenticated) и далее создавать ServerAuthorization только для разрешённых коммуникаций.

Например, разрешить payments обращаться к billing, monitoring-сервису Prometheus разрешить обращаться к метрикам всех, и т.д. Да, это работа — зато при компрометации одного сервиса злоумышленник не сможет разгуливать по всему mesh, прокси будут блокировать неожиданные вызовы.

Защитите Ingress и Egress

Для входящего трафика:

  • Убедитесь, что ваш Istio Ingress Gateway настроен с TLS (лучше полностью отключить прием обычного HTTP, если только он не нужен для редиректа на HTTPS);

  • Используйте RequestAuthentication+AuthorizationPolicy на ingress, чтобы валидировать JWT токены или другие auth-схемы для входящих запросов — иначе вы шифруете, а кто приходит не проверяете.

Для исходящего трафика:

  • Ограничьте egress — в Istio, как минимум, установите outboundTrafficPolicy: REGISTRY_ONLY глобально, чтобы никакой левый адрес не достижим без явного ServiceEntry;

  • Пустить все внешние вызовы через Egress Gateway;

Egress Gateway — это по сути отдельный Envoy, через который проксируется выход наружу. На него можно навесить Firewall rules, мониторинг, и он отделяет внешний трафик от общего east-west. Даже если сервис скомпрометирован, ему придётся идти через egress-gateway, где можно отследить аномалию или отфильтровать.

  • В Linkerd собственного egress-механизма нет, но можно добиться похожего эффекта network policy или в будущем использовать внешние proxy вне кластера;

Главное — знать, что выходит из вашего mesh.

  • Настройте логирование DNS-запросов или подключите мониторинг соединений, чтобы неожиданно не обнаружить, что из пода бухгалтерии идёт трафик на какой-нибудь api[.]easymoneydogecoin[.]com.

Минимизируйте права и возможности POD«ов

Мы уже говорили про NET_ADMIN — постарайтесь не запускать лишних привилегий в контейнерах.

В Istio-сетапе включите Istio CNI — тогда не понадобится initContainer с iptables от рута в каждом поде, и sidecar-прокси можно запускать с меньшими правами.

В Kubernetes применяйте ограничения: PSP/OPA-гейткиперы, запрещающие использовать UID 1337 в контейнерах (Istio-прокси на Linux обычно именно с таким UID), запрещающие CAP_NET_ADMIN в контейнерах приложений. Это предотвращает трюки с обходом.

Кроме того, ограничьте кто может изменять настройки mesh. Особенно обычным разработчикам, деплоящим приложения, скорее всего не нужны права создавать ресурсы типа ServiceEntry, Gateway, EnvoyFilter, Sidecar и т.п. Пусть эти привилегии будут только у платформенной команды или автоматизированного процесса через GitOps. Иначе кто-то по незнанию (или злому умыслу) откроет дырку.

Можно внедрить OPA Gatekeeper с готовыми правилами: например, запретить в продакшене Deployment«ам аннотацию sidecar.istio.io/disableInjection: "true", чтобы никто не вывел свой под из mesh-инфраструктуры, либо запрещать устанавливать PeerAuthentication с Permissive.

Обеспечьте наблюдаемость и аудит

Как понять, что mesh работает как задумано? Только наблюдая за ним.

Включите логирование важных событий. Istio имеет телеметрию, которая в сочетании с инструментами типа Kiali — ТЫК покажет, какие запросы были отклонены политиками (фактически аудит попыток нарушить правила). Метрики Envoy могут показать метрики отказов по причине RBAC_DENY.

В Linkerd есть удобный linkerd tap для интерактивного просмотра трафика, но убедитесь, что доступ к tap защищён RBAC (по-умолчанию только админ), ведь tap показывает сырой трафик запросов.

Также следите за сертификатами, в Istio метрики есть время до истечения сертификата, чтобы вы не забыли про ротацию CA. Если mesh поддерживает remote logging — используйте это. Например, Envoy можно настроить на вывод access log, и все прокси будут писать, кто к ним подключался и с каким SPIFFE-ID.

Правильно работайте с сертификатами и ключами

Mesh сам генерирует и распределяет мютуальные TLS-серты, но вы отвечаете за Root CA. В продакшене стоит использовать собственный корневой сертификат, может быть, интегрированный с корпоративным PKI (например, Istio умеет работать с внешним CA через Istio CSR API или вообще через SPIRE). Срок жизни корневого сертификата Istio по умолчанию 10 лет — лучше уменьшить и проводить ротацию root CA хотя бы раз в год-два. У Istio есть процедура смены корневого сертификата без остановки работы. Ею нужно воспользоваться задолго до экспирации.

Для Linkerd trust anchor тоже нужно продумать замену до истечения (Linkerd предоставляет инструкции, хотя и требует перезагрузки прокси).

Никогда не оставляйте продакшен на self-signed CA «как в туториале» навечно

Также, защищайте секреты в istio-system (если вы не external CA) — там хранятся ключи корневого сертификата. Дайте права на них только самым необходимым.

Кстати, будет полезно уменьшить TTL рабочих сертификатов (в Istio можно настроить через MeshConfig). Чем короче жизнь сертификата — тем меньше окно, когда компрометированный ключ будет действовать. Но не перестарайтесь, слишком частая ротация — нагрузка на control plane и риск сбоев. Стандарт 24 часа — 72 часа для workload cert — нормально. И убедитесь, что включена проверка отозванных сертификатов (CRL) при использовании внешнего CA, иначе отозвали вы компрометированный сертификат, а прокси об этом не знают.

Обновляйтесь и будуте в «безопасности«ё

Это тривиально, но необходимо.

Следите за новыми версиями Istio/Linkerd и установленного proxy. Разработчики mesh регулярно исправляют уязвимости и выпускают security bulletins. Обязательно подписывайтесь на рассылки безопасности Istio или CNCF. Если выяснилось, что в вашей версии баг, позволяющий, скажем, обойти авторизацию или обрушить прокси — планируйте немедленный апгрейд. В крупных компаниях mesh обновляют весьма часто: например, Pinterest встроил обновление service mesh в свой PaaS, чтобы разработчики получали свежие версии «под капотом» без усилий — ТЫК.

Практикуйте принцип «Defense in Depth»

Один только сервис меш не должен быть единственной линией обороны. Как отмечает документация Istio, mesh не рассматривается как полноценный брандмауэр для выхода и не заменяет всех традиционных средств защиты.

Лучше комбинировать, используйте сетевые политики K8s параллельно с mesh-политиками — чтобы даже при обходе прокси, пакет не ушёл по сети — ТЫК.

Используйте Pod Security Standards (OPA) — чтобы никто не мог запустить привилегированный контейнер и вскрыть узел изнутри.

Внешний WAF/API Gateway перед ingress mesh — чтобы фильтровать и аномалии на уровне запроса (mesh сам по себе не анализирует содержимое HTTP, он оперирует на уровне L4/L7 маршрутизации и идентификации). Также продолжайте сканировать уязвимости в коде приложений — mesh не спасёт, если у вас RCE в каждом втором сервисе, хотя усложнит атакующему распространение. Помните, что service mesh — это надстройка, усиливающая вашу безопасность, но не волшебный щит.

Автоматизируйте проверку конфигураций

Вручную уследить за десятками YAML-ов сложновато, я в Вас верю, но давайте будем реалистами. Для этого нам понадобятся инструменты, помогающие найти опасные паттерны. Пользуйтесь утилитой istioctl analyze — ТЫК для проверки конфигурации Istio — она ловит многие ошибки (например, правило, ссылающееся на несуществующий сервис, или конфликты).

Для глубокой безопасности существуют проекты вроде Snowcat, которого мы сегодня касались. Он умеет выявлять именно отступления от best practices безопасности. Например, найдет все namespace с Permissive mTLS, или policy с notPaths, или DestinationRule без проверки сертификата, и т.д. Kiali тоже подсвечивает проблемы конфигурации.

Для Linkerd, кстати, тоже появляются линтеры ТЫК –, но там и самих настроек меньше.

Опыт реальных компаний

Теория — это хорошо, но как крупные команды справляются с задачей обезопасить mesh на практике? Рассмотрим несколько примеров и публичных кейсов.

Airbnb

Они серьёзно подошли к вопросу изоляции и управляемости mesh. В своем выступлении на IstioCon они рассказали, что развернули Istio в режиме External Control Plane и flat network — ТЫК. Control plane (istiod) у них вынесен в отдельный кластер, а данные (прокси) работают в других. Это дало лучшую изоляцию, даже если что-то в рабочем кластере пойдёт не так (компрометация узла, атака и т.д.), istiod остаётся вне досягаемости, в безопасном окружении. Это также упростило обновления Istio без воздействия на приложения.

Кроме того, Airbnb внедрили многоуровневый mesh — у них есть sandbox-окружение, где тестируются новые фичи mesh и политики перед выкатом в прод. Такой подход (multi-tier mesh) уменьшает «радиус взрыва» изменений: все новые политики безопасности обкатываются на тестовом уровне, имитирующем прод, но без влияния на реальных пользователей — ТЫК.

Lyft

Компания, которая создала Envoy, фактически прародителя многих service mesh. У них один из самых больших mesh (если не самый большой) — тысячи сервисов, миллиарды запросов. Культура безопасности у них строится вокруг автоматизации и проверки на каждом шаге. Например, все изменения конфигурации Envoy проходят строгий code review и тесты — уронить mesh значит уронить Lyft, цена огромна. Lyft публиковала, что для предотвращения ошибок они разрабатывали внутренние инструменты статической проверки конфигов, а staging-среду сделали «первоклассной»: любую новую фичу mesh прогоняют через staging, изолируя трафик с помощью специальных заголовков, чтобы не затронуть остальных.

Заключение

Внедряя Istio или Linkerd, легко увлечься всеми их фичами и пропустить из виду вопросы безопасности. Но, как мы разобрали, service mesh способен значительно усилить безопасность микросервисов, если соблюсти ряд условий.

Кратко напомним самое главное:

  • Всегда шифруйте и аутентифицируйте сервис-сервис трафик (mTLS) — и делайте это строго, без «исключений из правил». Это базовый слой Zero Trust, который mesh предоставляет почти без усилий, грех не воспользоваться полностью;

  • Явно контролируйте, кто с кем общается, с помощью политик. Принцип наименьших привилегий: если сервису не нужно звать другой — запретите. Лучше добавить разрешение потом, чем пытаться выяснить, как злоумышленник прокрался через три сервиса по цепочке;

  • Следите за конфигами — ошибка в YAML может быть фатальной. Проверяйте их автоматически (istioctl, линтеры), ограничьте, кто может вносить изменения, и ревьюйте на предмет безопасности. Документируйте, почему открыта та или иная дырочка (если уж надо), и не забудьте закрыть, когда надобность отпала;

  • Защитите среду выполнения. Не позволяйте контейнерам вырываться из-под контроля (NET_ADMIN и проч.), обновляйте proxy и контрольную плоскость, держите секреты (сертификаты) под надёжной защитой. Регулярно обновляйте mesh — каждая версия обычно укрепляет безопасность и закрывает обнаруженные уязвимости;

  • Мониторинг и аудит — ваши лучшие друзья. Логи, метрики, трейсинг — используйте всё, чтобы видеть картину: кто стучится, где отказы, где попытки обойти;

  • Многоуровневая оборона. Не полагайтесь только на mesh. Комбинируйте с NetworkPolicy, проверкой приложенческих токенов, web application firewall — чем больше барьеров, тем выше шанс остановить атаку на раннем этапе. Service mesh прекрасно вписывается в Zero Trust архитектуру, но он — часть её, а не вся она.

Ну и напоследок вовлекайте разработчиков и DevOps-ов в тему безопасности mesh. Часто уязвимости появляются не от злого умысла, а от незнания. Проводите обучения, пишите гайды (подобно этому): почему нельзя ставить Permissive, почему важно не отключать sidecar даже в отладочных целях на проде, и т.д. Культура DevSecOps в microservices подразумевает, что безопасность — ответственность каждого, просто вы даёте им правильные инструменты (policy-as-code, шаблоны, автоматические проверки).

Источники

  1. Официальные рекомендации Istio по безопасности: Istio Security Best Practices — обязательно к прочтению, там много тонкостей, например, ограничения перехвата трафика и советы по использованию Network Policy вместе с Istio;

  2. Разбор типовых ошибок конфигурации Istio и как их избежать: статья Tetrate Istio Configuration Security: How to avoid misconfigurations — помогает понять, почему валидный на вид YAML может привести к неожиданному поведению;

  3. Блог Praetorian о сканере Snowcat и обзоре рисков Istio: «Introducing Snowcat: Istio Security Scanner» — содержит отличные примеры (несколько мы привели) опасных шаблонов и почему они опасны;

  4. Кейс-стади Istio: IstioCon презентация Airbnb — поможет понять организационные меры;

  5. Статья Solo.io о случаях обхода Istio и выводах: Best practices for secure Istio deployment — ценна тем, что разбирает реальные атаки и подчеркивает важные настройки (UID, NetworkPolicy и др.);

  6. Статья от Wiz: SAPwned: SAP AI vulnerabilities expose customers» cloud environments and private AI artifacts — показывает их анализ и опыт;

  7. Инструмент Snowcat

  8. Инструмент Kiali

© Habrahabr.ru