Такие неповторимые SSL-сертификаты на Azure +4


Немного предыстории: почти каждый инженер при разворачивании web проекта сталкивается с вопросом использования и реализации SSL-сертификатов. Я так точно с ним столкнулся) 

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

А я бы хотел чуть подробнее остановиться именно на неудобствах, с которыми столкнулся сам: 

  • Сертификаты необходимо постоянно перевыпускать, а их максимальный срок действия составляет 3 месяца;

  • В случае использования Kubernetes сертификаты приходится хранить в самом кубере и постоянно перегенерировать;

  • Есть целый ряд нюансов с использованием Wildcard сертификатов и их перевыпуском;

  • Некоторые особенности использования протоколов и алгоритмов шифрований.

Регулярно сталкиваясь с этими проблемами, я пришел к своей собственной настройке решения с Let’s Encrypt сертификатами, которой и хотел бы с вами поделиться. 

Последнее время я работаю с определенным стеком технологий, поэтому речь пойдёт об инфраструктурном решении на базе Kubernetes кластеров в контексте облачного провайдера Azure. Есть такое известное решение как cert-manager. Установку лично мне удобнее делать через Helm, поэтому выполняем: 

helm repo add jetstack https://charts.jetstack.io
helm repo update
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.crds.yaml
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.6.1

После чего создаем ClusterIssuer на основе следующего yaml файла:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-cluster-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: p….@gmail.com   #ваш e-mail
    privateKeySecretRef:
      name: letsencrypt-cluster-issuer
    solvers:
      - http01:
          ingress:
            class: nginx

Далее следует 2 варианта развития событий касательно реализации выпуска самих сертификатов:

  • Делать сертификаты через добавление kind: Certificate

  • Делать сертификаты через управление через ваш ingress

Предлагаю рассмотреть оба варианта. 

В первом случае yaml файлы у меня выглядели следующим образом:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: myservice2
  namespace: test
Spec:
  duration: 2160h
  renewBefore: 72h
  dnsNames:
    - myservice2.mydomain.org  #ваш ресурс
  secretName: myservice2-tls
  issuerRef:
    name: letsencrypt-cluster-issuer
    kind: ClusterIssuer

И при этом в ингрессе указывался только  secretName: myservice2-tls в секции tls для конкретного сервиса. 

Кстати, в вышеприведенном yaml файле есть очень удобные параметры: 

  • duration: 48h   - срок действия сертификата в часах;

  • renewBefore: 24h  - за сколько часов до истечении срока действия сертификата можно попробовать обновить уже имеющийся сертификат.

А для любителей консоли приведу пример детального просмотра сертификата:

kubectl describe certificates <имя сертификата> -n <название namespace>

Что же у нас остается по итогу? 

  • Not After - срок годности сертификата; 

  • Not Before - как правило, дата создания сертификата, если вы явно не заполнили это поле в своем Certificate Ресурсе YAML;

  • Renewal Time - временная отметка до истечения срока сертификата.

Схема работы по управлению Let’s Encrypt сертификатами через Ingress показалась мне удобнее и надежнее, так что в последнее время я использую именно её. Помимо добавления secretName и имени хоста в секцию tls необходимо указать/добавить в Ingress yaml файле только аннотации:

annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-cluster-issuer"
    cert-manager.io/renew-before: 72h

Вот и вся магия! Сертификаты автоматически перевыпускаются (в данном примере за 3 суток) до истечении срока действия (в случае с Let’s Encrypt этот срок по умолчанию составляет 90 дней).

Учитывая ограничения бесплатных сертификатов от Let’s Encrypt, на определенном этапе разработки наша команда задумалась над единым полноценным сертификатом, который смог бы защитить не только наш домен, но и поддомены. Разрабатывая проект на Azure, удобным местом для хранения является Azure Key Vault. Для проброса в Kubernetes кластер используется утилита akv2k8s, с которой можно ознакомиться более подробнее по ссылке.

А теперь немного о том, что это и с чем едят

После приобретения сертификата в Azure появляются советы по добавлению его в Azure Key Vault (или сокращенно AKV). В данном этапе нет ничего сложно, необходимо разве что подтвердить права на домен. После прохождения всех этапов подтверждения с “зелеными галочками” ваш сертификат появляется в хранилище ключей (AKV) в “Сикретах”. 

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

Чтобы кластер Кубернейтс мог использовать этот сертификат нужно выдать права и установить разрешение.

Для этого получаем identityProfile.kubeletidentity.objectId кластера командой:

az aks show -g <RG> -n <AKS_name>

Где RG - группа ресурсов, в которой находится кластер, а AKS_name - имя вашего кластера.

Следующим шагом необходимо скопировать identityProfile.kubeletidentity.objectId. Затем подставляем полученные значения в команду разрешения доступа к секретам:

az keyvault set-policy --name <имя AKV> --object-id  <полученное на предыдущем шаге значение> --secret-permissions get

После этого можно приступить к установке akv2k8s (через helm или другие удобные для вас способы). Установку смотрел тут.

По официальной документации делаем синхронизацию нашего сертификата из Azure Key Vault в Secret в конкретном namespace в Kubernetes. Вот мой пример yaml файла:

apiVersion: spv.no/v1
kind: AzureKeyVaultSecret
metadata:
  name: wildcard-cert #любое имя - то как будет называться 
  namespace: default
spec:
  vault:
    name: SandboxKeyVault  #имя хранилище вашего сертификата в Azure
    object:
      name: name_object_id #имя object id из Azure AKV конкретного серификата
      type: secret
  output:
    secret:
      name: wildcard-cert # любое имя как будет называться secret в вашем namespace
      type: kubernetes.io/tls
      chainOrder: ensureserverfirst # очень важная строка!!!

Хотелось бы отметить, что последняя строка была крайне важной, я не сразу нашел это решение. Поэтому сертификат прокидывался в кубер корректно,  но не работал. И мне потребовалось какое-то время, чтобы разобраться в этой проблеме. 

Оказалось, при экспорте сертификата PFX из Key Vault сертификат сервера иногда оказывается в конце цепочки, а не в начале, как и должно быть. Если эти параметры используются вместе, например, с ingress-nginx, сертификат не будет загружен и вернется к значению по умолчанию. Установив chainOrder на ensureserverfirst сертификат сервера будет перемещен первым в цепочке. При детальном рассмотрении сертификата мне удалось выявить, что цепочка выстраивается в следующем порядке:

  1. Intermediate

  2. Root

  3. Server

Поговорив про техническую составляющую и настройку, вернёмся к тонкостям именно ажуровских сертификатов.

В Azure есть возможность заказа сертификата. Сертификаты поставляются от GoDaddy. Тут доступно два варианта: 

  • Сертификат отдельно для конкретного домена (поддомена);

  • Wildcard сертификат. 

Первый вариант нас не устраивал, так что мы выбрали второй, надеясь, что он сможет защитить все наши приложения и сервисы. Но даже тут имел место быть ряд нюансов. 

Wildcard сертификат, который поставляется на Azure, защищает только поддомены первого уровня. Т.е. если у нас есть домен с именем mydomail.com, то Wildcard сертификат Azure подходит только для поддоменов первого уровня вида .mydomain.com! Например, для ресурсов service1.mydomain.com, service2.mydomain.com, service3.mydomain.com сертификат подойдёт. Но, если нам необходим service1.test.mydomain.com или mail.service1.mydomain.com, то тут сертификат идет мимо. Какие в целом могут быть тут варианты? 

  • Покупать отдельные сертификаты wildcard для всех необходимых поддоменов; 

  • Добавлять SAN (Subject Alternative Name) записи в сертификат.

Первый вариант, думаю, вряд ли кого устроит. Разработки, как и поддоменов второго уровня, может быть огромное количество и платить за wildcard сертификат для каждого поддомена (.service1.mydomain.com, *.dev.mydomain.com…) не самый разумный выход.

А вот по второму варианту я довольно долго общался со службой поддержки Azure. Прошел все стадии отрицания, разочарования и гнева, поняв в самом конце, что поддержка возможности SAN для сертификатов у них до сих пор не реализована.

До последнего хотелось верить, что такого не может быть на Azure…  У их конкурентов (AWS Amazon) сертификаты, предоставляемые AWS Certificate Manager (ACM), поддерживают до 10 альтернативных имен субъектов, включая подстановочные знаки. Таким образом можно было придумать 10 поддоменов с подстановочным знаком *, а также запросить увеличение квоты на AWS до 100 к.

На финал я оставил небольшой рассказ о том, как можно использовать сертификаты с сервисом FrontDoor на Azure.

Что же такое Azure Front Door (AFD)?

В моем представлении, это глобальная масштабируемая точка входа, которая использует глобальную пограничную сеть Microsoft для распределения входного трафика и перенаправления его к соответствующим конечным точкам (веб приложениям и сервисам). Front Door работает на уровне 7 (уровень HTTP/HTTPS). Front Door будет направлять ваши клиентские запросы на доступный сервер приложения из пула. Серверная часть приложения — это любая служба с выходом в Интернет, размещенная внутри или за пределами Azure. 

Пример с сайта документации https://docs.microsoft.com/
Пример с сайта документации https://docs.microsoft.com/

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

Как вы уже догадались, входной трафик на Azure Front Door может поступать как по Http, так и по https. В случае выбора https есть возможность генерации сертификата на самом сервисе Azure Front Door, подгрузки собственного сертификата или синхронизации имеющегося у вас сертификата в Azure Key Vault (чтобы служба Frontdoor получила доступ к Key Vault, необходимо настроить нужные разрешения).

Рекомендую использовать последний вариант и выбрать последнюю версию сикрета, тогда при автопродлении и перегенерации сертификата вам ничего делать не придётся: всё само подтянется. Подключив сертификат из AKV получится такая картина:

Еще одна фишка: особенности перенаправлении трафика с Azure Front Door на AKS. 

В случае с http никаких проблем не возникает. Но есть один нюанс: при создании пула ресурсов и указании вашего внешнего IP адреса AKS кластера, поле “заголовок узла серверного компонента” оставляйте пустым! Оно заполнится автоматически теми показателями, которые вы оставили в поле “ip или имя узла”.

Например, у вас есть доменный wildcard сертификат присоединенный через AKV как к сервису FrontDoor, так и прокинутый в AKS кластер через akv2k8s. Для всех ваших приложений и сервисов, доступных через FrontDoor, имя интерфейсного узла (и содержание CNAME записи в DNS) будет следующим: 

  • *.mydomain.com  – с пустым полем “заголовок узла серверного компонента” 

  • Ваш внешний IP адрес AKS будет иметь  правило перенаправления http по умолчанию на /

В таком случае все ваши сервисы формата *.mydomain.com  будут работать) 

На этом можно закончить настройку.

Но в некоторых случаях куда как интереснее настроить перенаправления трафика именно по https с AFD на AKS. Для корректной работы Azure Front Door в настройках серверного пула нужно обязательно указывать именно DNS имя соответствующее вашему AKS кластеру (это связано с SNI и hc), иначе ничего не сработает.

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

Тут же отмечу, что из соображений безопасности стоит обратить внимание на возможности настройки (ограничения) разрешения подключения к AKS только с IP адресов Azure Front Door в вашей Network Security Group для AKS (рис. ниже).

Дополнительно в ингрессе AKS возможно указать разрешение принимать соединения только с заголовком именно вашего Azure Front Door по ID (подробнее о параметре X-Azure-FDID).

Чем всё это дело закончилось и что я могу посоветовать?

  1. Azure не пишут подробно о возможностях и проблемах своих сертификатов. Но стоит отдать должное, они довольно оперативно вернули деньги за купленный нами сертификат. 

  2. Мы продолжаем использовать Lets Encrypt в разработке. Да, у него есть свои минусы, но это не самый худший вариант. 

  3. Если планируете создавать много различных поддоменов и вам необходимы разные уровни для ваших ресурсов на проектах, то присмотритесь к “Wildcard (иногда его называют Multidomain) with SAN” сертификатам от сторонних поставщиков. Вы вполне сможете импортировать его в Azure и полноценно использовать в своей работе.

  4. Azure Front Door - достаточно неплохой сервис при грамотной его настройке, рекомендую к использованию!




К сожалению, не доступен сервер mySQL