Mtls что это
Перейти к содержимому

Mtls что это

  • автор:

Поддержка mTLS в своём Service Mesh: чему мы научились

Салют! На связи команда платформы Авито, сегодня будем рассказывать про service mesh.

У Авито самописный service mesh — сначала это был Netramesh, который потом трансформировался в собственный control-plane и envoy в качестве data-plane. В начале этого года я добавлял в него поддержку mTLS, а сейчас мы успешно раскатываем это решение для межсервисного взаимодействия.

Взаимная аутентификация делает сервисы безопаснее и помогает защитить передаваемые по сети данные. Такая фича уже есть в разных готовых решениях. Тем не менее, я узнал для себя ряд интересных моментов при создании своей реализации и теперь хотел бы поделиться этим опытом с вами.

Подходы к аутентификации сервисов в разных решениях для service mesh

В зависимости от того, как устроен service mesh, будет отличаться и подход к авторизации сервисов. Например, если он построен на умных клиентах, то логику аутентификации нужно будет реализовывать в них. Это значит, что придётся подумать, как обеспечить ротацию и обновление секретов, которые используются для «подтверждения личности сервиса».

Имеющиеся опенсорсные решения для service mesh обычно применяют другой подход — использование sidecar proxy рядом с каждым экземпляром сервиса. Ниже я проведу небольшой обзор того, как устроена аутентификация в распространённых решениях для service mesh.

Istio

Для межсервисной аутентификации в Istio предлагается один из двух способов: mutual TLS для аутентификации сервисов и использование JWT-токенов для аутентификации на уровне запросов.

Первый способ предполагает заворачивание всего обмена между сервисами в TLS-туннель. Он устанавливается между двумя sidecar proxy и аутентифицирует клиентскую и серверную сторону по TLS-сертификатам. Сертификаты содержат идентификаторы сервисов, а управление ими берёт на себя control plane компонент istiod. Авторизация сервисов, т.е. правила, по которым определяется, какие сервисы могут друг с другом взаимодействовать, задаётся набором политик в CRD. Политики могут распространяться как на весь меш, так и на отдельные сервисы.

Для более простой и безопасной миграции Istio даёт возможность использовать PERMISSIVE режим. В нём разрешается использовать и TLS- и plaintext-трафик.

Если mutual TLS работает на уровне транспорта, то JWT-токены— на уровне запросов. Второй способ предполагает, что приложение должно будет получить JWT-токен от одного из провайдеров и использовать его на каждый запрос. Такой подход позволяет более гибко настраивать политики разграничения доступа на уровне конкретных эндпоинтов и методов.

Consul Service Mesh

Консул использует похожий подход как и в Istio для контроля доступа сервисов внутри меша — Intentions. Есть Identity-based Intentions, которые работают на уровне соединений (L4) и Application-Aware Intentions на уровне отдельных запросов (L7).

Подход Identity-based Intentions при установке соединений между сайдкарами использует тот же mTLS. Внутри сертификатов задаётся идентификатор сервисов, соответствующий спецификации SPIFFE X.509 Identity Document (о ней я ещё буду говорить дальше). Сертификаты управляются консулом, а хранятся в Vault, с котором у него есть встроенная интеграция.

Application-Aware Intentions позволяют задать политики авторизации на основе параметров HTTP-запросов дополнительно к аутентификации на L4. Enterprise-версия Consul Service Mesh предлагает ещё более гибкие возможности авторизации.

Linkerd

Control plane Linkerd является Certificate Authority, который выпускает сертификаты, привязанные к сервис-аккаунту, и распространяет их до data-plane компонентов. Компоненты, в свою очередь, используют сертификаты для установки TLS-сессии и взаимной аутентификации сервисов. Политики авторизации задаются через CRD на уровне отдельных сервисов.

Open Service Mesh

Sandbox-проект CNCF. Здесь также используется envoy sidecar в качестве data-plane. Для аутентификации сервисов использует mTLS. Подписывать и выпускать сертификаты может отдельный компонент Tresor, возможна интеграция с Vault или cert-manager. Распространяет сертификаты control plane, используя протокол SDS.

Traefik Mesh

Достаточно легковесное решение по сравнению с Istio, которое использует несколько иной подход внедрения service mesh в работу сервисов. Разворачивается в виде даемонсета. Трафик сервисов заворачивается на него за счёт либо явных походов на сервисы через DNS имена вида ..traefik.mesh, либо настройки search в resolv.conf для всех контейнеров с сервисами. Так как межсервисный трафик проходит через proxy только со стороны клиента, обеспечить прозрачный для сервисов механизм аутентификации Traefik Mesh не может.

Для чего нам нужен mTLS в service mesh

У нас собственный service mesh, организованный по принципе sidecar proxy, и самописный control plane. Сейчас это нечто сложившееся исторически, про появление решения можно почитать тут. Я же расскажу про контекст, который мы имели на момент внедрения аутентификации.

Одна из задач, которую нужно было решить, — это запрет ходить в сервисы сторонним клиентам, условно говоря, просто из сети дата-центра. Контроль доступа на уровне отдельных методов, в целом, пока не требовался. Все запросы имеют заголовок X-Source, по которому эту логику можно реализовать в самих сервисах в случае необходимости.

Вторым важным для нас моментом была возможность использовать открытые сети в межсервисном обмене. Это позволяет использовать вместо внутреннего облака публичные и не беспокоиться о том, насколько чувствительные данные передаются внутри.

То, что весь трафик сервисов завёрнут на sidecar proxy, позволяло организовать транспортную аутентификацию абсолютно прозрачно для сервисов. Как мы увидели из предыдущего раздела, практически единственным решением является использование mTLS между sidecar proxy при межсервисном обмене. Давайте вкратце посмотрим, что это такое.

Mutual TLS — это TLS соединение, в котором верифицируется не только серверный, но и клиентский сертификат. В процессе установления соединения стороны обмениваются сертификатами и публичными ключами, которые в них находятся. Далее каждая сторона может проверить целостность сертификата — ведь он передавался по открытой сети — за счёт проверки подписи на открытом ключе CA, которые эти сертификаты выпустил. Обычно, конечно, есть некая цепочка CA, вплоть до корневого CA.

После этого стороны убеждаются, что противоположная сторона имеет закрытый ключ, соответствующий его сертификату. Для этого стороны обмениваются сообщениями, зашифрованными открытыми ключами из сертификатов. Их корректная расшифровка подтверждает верификацию личностей каждой из сторон. После согласования параметров соединения и генерации сессионных ключей устанавливается зашифрованная TLS-сессия.

Таким образом, mTLS между сервисами решает сразу две проблемы: проблему аутентификации и проблему защиты передаваемых данных от прослушивания и подделки.

Межсервисная авторизация

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

Envoy, который мы используем в качестве сайдкаров, позволяет это сделать через параметр match_subject_alt_names в блоке конфигурации TLS на листенере:

Чтобы сформировать эту конфигурацию, control plane должен знать обо всех потребителях сервиса и их идентификаторах. У нас это уже было реализовано — в тех CRD, которые деплоятся вместе с сервисом, все эти зависимости перечислены. Изначально в кластера их деплоит PaaS, который требует от каждого сервиса явного указания зависимостей в app.toml.

Бесшовная ротация секретов в Envoy

Для поддержки mTLS в service mesh важно, чтобы data plane компоненты могли автоматически и при этом бесшовно получать и обновлять сертификаты и ключи. Ротировать их лучше достаточно часто, чтобы снизить риск компрометации. В нашем случае срок жизни такого сертификата составляет часы.

Для загрузки сертификатов envoy использует SDS, который является частью XDS-протокола. XDS — это стандартизированный API поверх GRPC, который envoy использует для обновления свой конфигурации. Поддержка SDS в control plane или в каком-либо другом компоненте означает, что envoy может запросить нужный ему сертификат и бесшовно обновить своё внутреннее состояние.

В качестве SDS и инфраструктуры управления идентификаторами и сертификатами мы использовали Spire. Оказалось что его можно достаточно легко встроить в service mesh.

Что такое Spire

Spire — это эталонная реализация SPIFFE. SPIFFE — это стандарт, описывающий подход к идентификации и управлению секретами для инфраструктурных компонентов. SPIFFE и Spire, по сути, — ответ на проблемы масштабирования подходов к аутентификации компонентов в микросервисной среде.

Инсталляция spire cостоит из агентов, каждый из которых запускается на ноде кластера и сервера:

Сервер предоставляет API для администратора, который управляет набором идентификаторов сервиса (SPIFFE ID) и правилами их выдачи компонентам (Селекторы). Он же является CA для выпускаемых им сертификатов (SVID).

Агент запускается на ноде, регистрируется и подтверждает свою identity у spire-сервера. Он безопасно запрашивает у сервера SVID и хранит их у себя. Агент — это точка входа для sidecar proxy, которые запрашивают у него свои SVID. Он является SDS-сервером, который предоставляет доступ через UNIX-сокет. Почему через UNIX-сокет? Об этом я расскажу чуть ниже, сначала — про стандарт SPIFFE.

Что такое SPIFFE

SPIFFE — это набор спецификаций, решающий проблему идентификации и аутентификации приложений в динамичной микросервисной среде.

  • Как приложения идентифицируются. Для этого используется SPIFFE ID — структурированная в форме URI строка, которая уникально идентифицирует экземпляр приложения. Она состоит из имени trust domain и path.
  • Как SPIFFE ID кодируются в криптографически верифицируемый документ (SVID). Это может быть определённым образом сформированный сертификат или JWT-токен, которые содержат SPIFFE ID.
  • Как SVID выпускаются или перевыпускаются (Workload API): принципиально не требует никакой аутентификации со стороны приложения, идентифицируя его при помощи out-of-band механизмов.

Workload API стоит рассмотреть отдельно.а мой взгляд, он достаточно интересно решает проблему первоначального получения SVID тем компонентом, у которого пока нет никакого секрета, чтобы аутентифицировать себя.

Первичное получение SVID: проблема курицы и яйца

Представим такую ситуацию.

Человек получает свой первый паспорт. Для этого ему нужно прийти в паспортный стол с фотографией и назвать свои данные или предъявить свидетельство о рождении. Будущий паспорт — первый документ человека с фотографией, поэтому ему нечего предъявить сотруднику паспортного стола, чтобы удостоверить свою личность. Теоретически, он может договориться с кем-то и поменяться своими данными. Тогда он получит паспорт со своей фотографией, но чужим именем, и обманет систему.

Здесь и кроется проблема первичного получения идентифицирующего документа. В Spire её успешно решает Workload API.

Выше я акцентировал внимание на том, что Workload API предоставляется в пределах машины через UNIX-сокет, вот как это работает:

  1. Приложение обращается к Workload API агента через UNIX-сокет, запрашивает SVID. В случае envoy это происходит по протоколу SDS.
  2. Агент через вызов getsockopt с опцией SO_PEERCRED получает PID, UID и GID клиентского процесса. В дальнейшем он использует их для получения дополнительной информации о процессе.
  3. Агент берёт PID процесса, получает имя контейнера через cgroup, идёт на /pods локального kubelet, получает спеку пода и использует её для формирования селекторов: имя пода, ноды, неймспейс, имя сервис-аккаунта и т.д.
  4. Затем агент отдаёт тот SVID, который наиболее точно матчится на набор селекторов, зарегистрированных заранее через Registration API сервера.

Селектор может выглядеть так: выдавать указанный spiffeID только процессу, запущенному в неймспейсе X.

В дальнейшем envoy использует этот SVID в качестве клиентского сертификата при установлении TLS-соединения до сервиса, в котором включён mTLS.

Eventual consistency конфигурации service mesh и переходные режимы работы mTLS

Конфигурация service-mesh является eventual-consistent системой. Для нас это значило то, что легко может возникнуть ситуация, когда принимающий запросы сервис ожидает подключения по TLS, а сервис-клиент ещё не получил актуальную конфигурацию и работает в обычном режиме. Это привело бы к потерянным запросам при включении mTLS, т.к. сервис-получатель ожидал бы TLS соединения от клиента, который его установить ещё не готов.

Для того, чтобы разрешить эту проблему мы ввели несколько режимов, через которые последовательно проходит сервис в момент включения mTLS.

В режиме INITIAL, сервис готов получать TLS запросы, но принимает запросы от всех клиентов по-прежнему в plain-тексте. После этого сервис переходит в режим PERMISSIVE, в котором все его клиенты отправляют запросы по TLS. При этом он по-прежнему готов принимать и plain-текст запросы. Такой переходный режим позволяет «дождаться» пока все envoy-сайдкары клиентских сервисов получат актуальные конфигурации от control-plan и перейдёт на использование TLS. Финальный режим STRICT отключает возможность принимать запросы без использования TLS.

Отключение mTLS приводит к таким же проблемам сходимости конфигурации, поэтому оно происходит также через переключение режимов, но в обратном порядке. Переход через эти режимы реализован внутри PaaS, прозрачно для конечных потребителей.

Подведём итоги

Существуют разные подходы к авторизации в разных service-mesh решениях. Условно их можно разделить на две категории — авторизация на уровне транспорта (mTLS) и авторизация на уровне запросов. На своём собственном service-mesh мы реализовали первое решение с помощью возможностей envoy по бесшовной ротации сертификатов/ключей (SDS) и данных о связях между сервисами в нашем PaaS. А для решения проблемы управления и выдачи сертификатов использовали Spire — это интересный проект CNCF, который реализует спецификацию SPIFFE.

Если вам захотелось узнать о теме чуть больше — можете посмотреть запись доклада моего коллеги Алексея Егорова, он рассказал о нашем решении немного с другой точки зрения.

  • Блог компании AvitoTech
  • Информационная безопасность

Что такое mTLS

Обновлено и опубликовано

Опубликовано: 15.10.2023

протокол, основанный на TLS с усиленной безопасностью. Он включает дополнительную аутентификацию клиента с помощью сертификата. Таким образом, при обмене данными, клиент проверяет сервер, а сервер — клиента. Полное название Mutual TLS, а в переводе, Взаимный TLS.

Принцип работы у mTLS следующий: есть две пары сертификатов и, соответственно, два закрытых ключа. Один закрытый ключ находится на сервере и позволяет расшифровывать данные, которые шифруются открытым ключом, как при обычной работе TLS. Второй закрытый ключ устанавливается на клиенте, а сервер при ответах клиенту также шифрует данные его открытым ключом. Таким образом, к серверу могут обращаться только те клиенты, у которых есть закрытый ключ для расшифровки данных. В этом заключается основной смысл mTLS.

Для mTLS, как и для TLS используются сертификаты стандарта x509. Их можно сгенерировать самому, например, с помощью утилиты openssl, купить или получить бесплатно от центра сертификации Let’s Encrypt.

mTLS может быть настроен для защиты внутреннего ресурса, к которому должны подключаться только сотрудники компании или доверенные сервисы. Примеры реализации:

  • Защищенный https на NGINX или Apache.
  • Защита Service Mesh с использованием istio.
  • Конфиденциальность при общении в Skype for Business.

Основные недостатки mTLS:

  1. Повышенная нагрузка. Шифровка и дешифровка — операции, которые сильно нагружают систему.
  2. Несовместимость. Не все клиенты могут быть настроены для взаимного TLS шифрования.

Дополнительно про Mutual TLS можно почитать на Википедии (только на английском).

Service Entry

ServiceEntry enables adding additional entries into Istio’s internal service registry, so that auto-discovered services in the mesh can access/route to these manually specified services. A service entry describes the properties of a service (DNS name, VIPs, ports, protocols, endpoints). These services could be external to the mesh (e.g., web APIs) or mesh-internal services that are not part of the platform’s service registry (e.g., a set of VMs talking to services in Kubernetes). In addition, the endpoints of a service entry can also be dynamically selected by using the workloadSelector field. These endpoints can be VM workloads declared using the WorkloadEntry object or Kubernetes pods. The ability to select both pods and VMs under a single service allows for migration of services from VMs to Kubernetes without having to change the existing DNS names associated with the services.

The following example declares a few external APIs accessed by internal applications over HTTPS. The sidecar inspects the SNI value in the ClientHello message to route to the appropriate external service.

v1alpha3 v1beta1

The following configuration adds a set of MongoDB instances running on unmanaged VMs to Istio’s registry, so that these services can be treated as any other service in the mesh. The associated DestinationRule is used to initiate mTLS connections to the database instances.

v1alpha3 v1beta1

and the associated DestinationRule

v1alpha3 v1beta1

The following example uses a combination of service entry and TLS routing in a virtual service to steer traffic based on the SNI value to an internal egress firewall.

v1alpha3 v1beta1

And the associated VirtualService to route based on the SNI value.

v1alpha3 v1beta1

The virtual service with TLS match serves to override the default SNI match. In the absence of a virtual service, traffic will be forwarded to the wikipedia domains.

The following example demonstrates the use of a dedicated egress gateway through which all external service traffic is forwarded. The ’exportTo’ field allows for control over the visibility of a service declaration to other namespaces in the mesh. By default, a service is exported to all namespaces. The following example restricts the visibility to the current namespace, represented by “.”, so that it cannot be used by other namespaces.

v1alpha3 v1beta1

Define a gateway to handle all egress traffic.

v1alpha3 v1beta1

And the associated VirtualService to route from the sidecar to the gateway service ( istio-egressgateway.istio-system.svc.cluster.local ), as well as route from the gateway to the external service. Note that the virtual service is exported to all namespaces enabling them to route traffic through the gateway to the external service. Forcing traffic to go through a managed middle proxy like this is a common practice.

v1alpha3 v1beta1

The following example demonstrates the use of wildcards in the hosts for external services. If the connection has to be routed to the IP address requested by the application (i.e. application resolves DNS and attempts to connect to a specific IP), the resolution mode must be set to NONE .

v1alpha3 v1beta1

The following example demonstrates a service that is available via a Unix Domain Socket on the host of the client. The resolution must be set to STATIC to use Unix address endpoints.

v1alpha3 v1beta1

For HTTP-based services, it is possible to create a VirtualService backed by multiple DNS addressable endpoints. In such a scenario, the application can use the HTTP_PROXY environment variable to transparently reroute API calls for the VirtualService to a chosen backend. For example, the following configuration creates a non-existent external service called foo.bar.com backed by three domains: us.foo.bar.com:8080, uk.foo.bar.com:9080, and in.foo.bar.com:7080

v1alpha3 v1beta1

With HTTP_PROXY=http://localhost/ , calls from the application to http://foo.bar.com will be load balanced across the three domains specified above. In other words, a call to http://foo.bar.com/baz would be translated to http://uk.foo.bar.com/baz .

The following example illustrates the usage of a ServiceEntry containing a subject alternate name whose format conforms to the SPIFFE standard:

v1alpha3 v1beta1

The following example demonstrates the use of ServiceEntry with a workloadSelector to handle the migration of a service details.bookinfo.com from VMs to Kubernetes. The service has two VM-based instances with sidecars as well as a set of Kubernetes pods managed by a standard deployment object. Consumers of this service in the mesh will be automatically load balanced across the VMs and Kubernetes.

v1alpha3 v1beta1

Assuming there is also a Kubernetes deployment with pod labels app: details using the same service account details , the following service entry declares a service spanning both VMs and Kubernetes:

v1alpha3 v1beta1

ServiceEntry

ServiceEntry enables adding additional entries into Istio’s internal service registry.

The hosts associated with the ServiceEntry. Could be a DNS name with wildcard prefix.

  1. The hosts field is used to select matching hosts in VirtualServices and DestinationRules.
  2. For HTTP traffic the HTTP Host/Authority header will be matched against the hosts field.
  3. For HTTPs or TLS traffic containing Server Name Indication (SNI), the SNI value will be matched against the hosts field.

NOTE 1: When resolution is set to type DNS and no endpoints are specified, the host field will be used as the DNS name of the endpoint to route traffic to.

NOTE 2: If the hostname matches with the name of a service from another service registry such as Kubernetes that also supplies its own set of endpoints, the ServiceEntry will be treated as a decorator of the existing Kubernetes service. Properties in the service entry will be added to the Kubernetes service if applicable. Currently, only the following additional properties will be considered by istiod :

  1. subjectAltNames: In addition to verifying the SANs of the service accounts associated with the pods of the service, the SANs specified here will also be verified.

The virtual IP addresses associated with the service. Could be CIDR prefix. For HTTP traffic, generated route configurations will include http route domains for both the addresses and hosts field values and the destination will be identified based on the HTTP Host/Authority header. If one or more IP addresses are specified, the incoming traffic will be identified as belonging to this service if the destination IP matches the IP/CIDRs specified in the addresses field. If the Addresses field is empty, traffic will be identified solely based on the destination port. In such scenarios, the port on which the service is being accessed must not be shared by any other service in the mesh. In other words, the sidecar will behave as a simple TCP proxy, forwarding incoming traffic on a specified port to the specified destination endpoint IP/host. Unix domain socket addresses are not supported in this field.

The ports associated with the external service. If the Endpoints are Unix domain socket addresses, there must be exactly one port.

Specify whether the service should be considered external to the mesh or part of the mesh.

Service resolution mode for the hosts. Care must be taken when setting the resolution mode to NONE for a TCP port without accompanying IP addresses. In such cases, traffic to any IP on said port will be allowed (i.e. 0.0.0.0: ).

One or more endpoints associated with the service. Only one of endpoints or workloadSelector can be specified.

Applicable only for MESH_INTERNAL services. Only one of endpoints or workloadSelector can be specified. Selects one or more Kubernetes pods or VM workloads (specified using WorkloadEntry ) based on their labels. The WorkloadEntry object representing the VMs should be defined in the same namespace as the ServiceEntry.

A list of namespaces to which this service is exported. Exporting a service allows it to be used by sidecars, gateways and virtual services defined in other namespaces. This feature provides a mechanism for service owners and mesh administrators to control the visibility of services across namespace boundaries.

If no namespaces are specified then the service is exported to all namespaces by default.

The value “.” is reserved and defines an export to the same namespace that the service is declared in. Similarly the value “*” is reserved and defines an export to all namespaces.

For a Kubernetes Service, the equivalent effect can be achieved by setting the annotation “networking.istio.io/exportTo” to a comma-separated list of namespace names.

If specified, the proxy will verify that the server certificate’s subject alternate name matches one of the specified values.

NOTE: When using the workloadEntry with workloadSelectors, the service account specified in the workloadEntry will also be used to derive the additional subject alternate names that should be verified.

ServicePort

ServicePort describes the properties of a specific port of a service.

A valid non-negative integer port number.

The protocol exposed on the port. MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS. TLS implies the connection will be routed based on the SNI header to the destination without terminating the TLS connection.

Label assigned to the port.

The port number on the endpoint where the traffic will be received. If unset, default to number .

ServiceEntry.Location

Location specifies whether the service is part of Istio mesh or outside the mesh. Location determines the behavior of several features, such as service-to-service mTLS authentication, policy enforcement, etc. When communicating with services outside the mesh, Istio’s mTLS authentication is disabled, and policy enforcement is performed on the client-side as opposed to server-side.

Signifies that the service is external to the mesh. Typically used to indicate external services consumed through APIs.

Signifies that the service is part of the mesh. Typically used to indicate services added explicitly as part of expanding the service mesh to include unmanaged infrastructure (e.g., VMs added to a Kubernetes based service mesh).

ServiceEntry.Resolution

Resolution determines how the proxy will resolve the IP addresses of the network endpoints associated with the service, so that it can route to one of them. The resolution mode specified here has no impact on how the application resolves the IP address associated with the service. The application may still have to use DNS to resolve the service to an IP so that the outbound traffic can be captured by the Proxy. Alternatively, for HTTP services, the application could directly communicate with the proxy (e.g., by setting HTTP_PROXY) to talk to these services.

Assume that incoming connections have already been resolved (to a specific destination IP address). Such connections are typically routed via the proxy using mechanisms such as IP table REDIRECT/ eBPF. After performing any routing related transformations, the proxy will forward the connection to the IP address to which the connection was bound.

Use the static IP addresses specified in endpoints (see below) as the backing instances associated with the service.

Attempt to resolve the IP address by querying the ambient DNS, asynchronously. If no endpoints are specified, the proxy will resolve the DNS address specified in the hosts field, if wildcards are not used. If endpoints are specified, the DNS addresses specified in the endpoints will be resolved to determine the destination IP address. DNS resolution cannot be used with Unix domain socket endpoints.

Attempt to resolve the IP address by querying the ambient DNS, asynchronously. Unlike DNS , DNS_ROUND_ROBIN only uses the first IP address returned when a new connection needs to be initiated without relying on complete results of DNS resolution, and connections made to hosts will be retained even if DNS records change frequently eliminating draining connection pools and connection cycling. This is best suited for large web scale services that must be accessed via DNS. The proxy will resolve the DNS address specified in the hosts field, if wildcards are not used. DNS resolution cannot be used with Unix domain socket endpoints.

Was this information useful?
Yes No

Do you have any suggestions for improvement?

[Перевод] Как автоматизировать выдачу сертификатов для развертываний Kubernetes с помощью Autocert 02.11.2022 18:01

При публичном веб-браузинге TLS-аутентификация происходит лишь в одном направлении — свои сертификаты показывает только сервер. Передача публичных веб-страниц без аутентификации клиента вполне логична, но не в случае Kubernetes. Если другие субъекты будут получать доступ к уязвимой информации в сервисах/кластерах, то будет логично валидировать личность и таких субъектов тоже.

Повсеместное использование TLS — одна из рекомендаций разработчиков Kubernetes по повышению безопасности и надёжности кластеров.

«TLS должен быть включён у каждого поддерживающего его компонента, чтобы предотвратить сниффинг трафика, проверять идентификацию сервера и (в случае взаимного TLS) проверять идентификацию клиента».

В случае доступа клиентов к уязвимым данным в сервисах/кластерах логично валидировать и личность таких клиентов. Это называется взаимным TLS (mutual TLS, mTLS).

Что такое mutual TLS?

Mutual TLS (mTLS), также называемый двусторонней аутентификацией — это процесс обеспечения безопасности, при котором субъекты перед выполнением обмена данными аутентифицируют друг друга. Это значит, что для установки соединения клиент с сервером должны обменяться своими сертификатами, верифицировать их и использовать. Аутентификация при помощи mTLS — наилучший способ повышения безопасности Kubernetes, он гарантирует, что только аутентифицированные субъекты смогут обмениваться данными с вашими кластерами.

В отличие от VPN и SDN, реализовать mTLS очень просто. Есть только одно препятствие: вам нужны сертификаты, выпущенные вашим собственным сертифицирующим органом (certificate authority, CA). Создание и эксплуатация CA, выпуск сертификатов и их обновление до истечения срока действия может быть сложной задачей. Упрощают весь этот процесс Autocert и Smallstep Certificate Manager.

Небольшое предисловие об инструментах, которые мы будем использовать

▍ Autocert

Autocert — это admission webhook, выполняющий перехват и патчинг запросов на создание pod при помощи YAML для инъецирования init container и sidecar, занимающихся получением и обновлением сертификатов. Иными словами, это опенсорсный аддон Kubernetes, автоматически инъецирующий TLS-сертификаты непосредственно в контейнеры.

Autocert полезен, когда вы не доверяете складу данных, в котором хранятся Kubernetes TLS Secrets (часто это etcd ). В зависимости от конфигурации склада данных, данные могут быть зашифрованы или не зашифрованы. При помощи Autocert сертификаты инъецируются непосредственно в контейнеры, а приватные ключи никогда не передаются по сети и не сохраняются в etcd .

Для получения сертификата достаточно лишь сообщить autocert имя рабочей нагрузки при помощи аннотации pod autocert.step.sm/name . Autocert выпустит сертификат для pod, сделав его доступным в var/run/autocert.step.sm , и будет его обновлять. Однако для выпуска сертификатов требуется certificate authority. И здесь на помощь приходит Smallstep Certificate Manager. В данном руководстве мы расскажем, как сконфигурировать autocert для использования Certificate Manager в качестве вышестоящего CA.

▍ Smallstep Certificate Manager

Smallstep Certificate Manager — это расширяемая платформа для инфраструктуры публичных ключей (public key infrastructure, PKI) DevSecOps, обеспечивающая работу certificate authority. Также он предоставляет функции уведомлений и алертов об истечении срока работы, дэшборда управления, Active Revocation и другие возможности. При помощи Smallstep Certificate Manager вы сможете с лёгкостью выпускать приватные сертификаты TLS и SSH для всех рабочих нагрузок/разработчиков.

Строго говоря, Kubernetes не имеет в комплекте CA. Он имеет точки интеграции, позволяющие использовать любой CA. Поэтому мы будем использовать Smallstep Certificate Manager, поскольку его создают и поддерживают разработчики Autocert.

Перед началом работы

Прежде чем переходить к главному, вам нужно:

  1. Создать бесплатный аккаунт Smallstep Certificate Manager
  2. Создать Certificate Authority в Certificate Manager, который будет использоваться в качестве вышестоящего CA
  3. Установить step CLI. Для взаимодействия с Certificate Manager через терминал вам понадобится команда CLI step на локальной машине. Она используется для многих стандартных криптоопераций. Список всех команд можно посмотреть здесь.

Настройка Autocert

▍ 1. Bootstrap с выбранным CA

Бутстреппинг с выбранным Authority конфигурирует вашу рабочую станцию так, чтобы она доверяла корневому сертификату CA. Выполните следующую команду:

step ca bootstrap --ca-url [your CA URL] \\ --fingerprint [your CA fingerprint] \\ --install

▍ 2. Добавление Provisioner

Provisioner — это методы, используемые для верификации подлинности запросов на подписание сертификатов и свидетельствующие об идентификации сервиса или человека, выполняющего запрос. Autocert требует JWK provisioner. Показанная ниже команда создаёт стандартный JWK provisioner с именем autocert :

step ca provisioner add autocert --create

Необходимо будет указать пароль для шифрования приватного ключа provisioner.

▍ 3. Создание ConfigMaps и секрета для Autocert

В Kubernetes создайте пространство имён для autocert:

kubectl create ns step

Результат выполнения команды :

namespace/step created

Используйте тот же пароль, который вы ввели при создании provisioner, чтобы создать секрет.

kubectl -n step create secret generic autocert-password --from-file=password=autocert-password.txt

Результат выполнения команды :

secret/autocert-password created

Теперь создайте ConfigMap , содержащую папку конфигурации:

kubectl -n step create configmap config --from-file $(step path)/config`

Результат выполнения команды :

configmap/config created

Сделаем то же самое для папки certs , содержащей корневой сертификат нашего CA:

kubectl -n step create configmap certs --from-file $(step path)/certs

Результат выполнения команды :

configmap/certs created

▍ 4. Развёртывание Autocert

Скачайте YAML-конфигурацию Autocert:

curl -O 

Измените caUrl в ConfigMap autocert-config только что скачанного файла .yaml. Замените значение https://ca.step.svc.cluster.local на URL вашего Certificate Manager authority, например, https://autocert.areed.ca.smallstep.com .

Затем выполните следующую команду:

kubectl apply -f 

Результат выполнения команды :

clusterrole.rbac.authorization.k8s.io/autocert-controller created clusterrolebinding.rbac.authorization.k8s.io/autocert-controller created

Теперь давайте развернём Autocert. Выполните следующую команду:

kubectl apply -f 02-autocert.yaml

Результат выполнения команды :

service/autocert created configmap/autocert-config created deployment.apps/autocert created

Далее давайте развернём admission webhook. В этом блоке в качестве части клиентской конфигурации для Autocert мы включаем корневой сертификат CA (в base64).

cat webhooks: - name: autocert.step.sm sideEffects: None admissionReviewVersions: ["v1beta1"] clientConfig: service: name: autocert namespace: step path: "/mutate" caBundle: $(cat $(step path)/certs/root_ca.crt | base64 | tr -d '\\n') rules: - operations: ["CREATE"] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"] namespaceSelector: matchLabels: autocert.step.sm: enabled EOF

Результат выполнения:

mutatingwebhookconfiguration.admissionregistration.k8s.io/autocert-webhook-config created

Теперь Autocert добавлен в кластер и сконфигурирован. Можно выполнить следующую команду, чтобы убедиться в том, что pod-ы autocert помечены правильно.

kubectl -n step get deployment/autocert

Использование Autocert для включения mTLS между сервером и клиентом

Давайте создадим тестовое приложение, которое будет использовать Autocert. Это веб-сервер уровня «Hello World», использующий взаимную TLS-сертификацию.

Так как эта среда находится в пространстве имён по умолчанию, пометим её, чтобы приказать Autocert выпускать и обновлять сертификаты для новых pod-ов аннотацией autocert.step.sm/name :

kubectl label namespace default autocert.step.sm=enabled

Результат выполнения команды :

namespace/default labeled

Чтобы протестировать систему, мы создадим среду с аннотацией pod-а autocert.step.sm/name . В этом примере используется имя localhost , потому что мы будем выполнять тестирование со своей рабочей станции.

Результат выполнения :

deployment.apps/hello-mtls created

Для тестирования перенаправим localhost:8443 на порт 443 pod-а.

kubectl port-forward deploy/hello-mtls 8443:443

Результат выполнения команды :

Forwarding from 127.0.0.1:8443 -> 443 Forwarding from [::1]:8443 -> 443

В течение следующих этапов оставим это запущенным на фоне.

Теперь выпустим сертификат клиента, подписанный нашим CA. Это нужно, чтобы аутентифицироваться на тестовом сервере «Hello mTLS».

step ca certificate linda.ikechukwu@smallstep.com demo.crt demo.key

После этого у вас должно получить проверить, что всё это работает:

curl --cacert $(step path)/certs/root_ca.crt \\ --cert demo.crt --key demo.key \\ 

Результат выполнения команды :

Hello linda.ikechukwu@smallstep.com

Заключение

Обеспечение безопасности кластера Kubernetes может быть сложной задачей. Вне зависимости от сетевой архитектуры и политик, автоматизация безопасности всегда повышает безопасность и надёжность рабочей нагрузки кластера.

Autocert вместе с Smallstep Certificate Manager упрощает выпуск сертификатов для среды Kubernetes. Для того, чтобы начать выпускать TLS-сертификаты Kubernetes своих микросервисов и защититься от злоумышленников, достаточно лишь немного YAML.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *