HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
d-shop

НОВОСТИ SSL-сертификаты от Let's Encrypt с cert-manager в Kubernetes

BDFpromo
Оффлайн

BDFpromo

.
.
Регистрация
23.09.18
Сообщения
12.347
Реакции
176
Репутация
0
laqdo7i6ovirl5ciie_qmnbrau4.png


В этой статье я расскажу о том, как автоматизировать заказ и продление сертификатов от Let’s Encrypt (и не только) для Ingress’а в Kubernetes с помощью дополнения cert-manager. Но начну с краткого введения в суть проблемы.

Немного ликбеза


Протокол HTTP, разработанный в начале 90-х годов прошлого века, вошёл в нашу повседневную жизнь настолько плотно, что тяжело представить хотя бы день без его использования. Однако сам по себе он не обеспечивает даже минимальный уровень безопасности при обмене информации между пользователем и веб-сервером. На помощь приходит HTTPS («S» — secure): используя упаковку передаваемых данных в SSL/TLS, этот протокол неплохо себя зарекомендовал в защите информации от перехвата и активно пропагандируется индустрией.

Например, Google с 2014 года позиции «HTTPS везде» и даже понижает приоритет сайтам без него в поисковой выдаче. Не обходит стороной эта «пропаганда» и рядовых потребителей: современные браузеры предупреждают своих пользователей о наличии и корректности SSL-сертификатов у посещаемых сайтов.

srmeilomqktrr7qofwjlgpiw01i.png

mjjopfpjvy84bnbdae_mv6vluw8.png

ddt4vnh-3yseofqyzip5qma-dti.png


Стоимость сертификата для личного сайта начинается с десятков долларов. Не всегда покупка его оправдана и целесообразна. Благо, с конца 2015 года доступна бесплатная альтернатива в виде сертификатов (LE). Этот некоммерческий проект был создан энтузиастами из Mozilla для того, чтобы покрыть большую часть интернет-сайтов шифрованием.

Центр сертификации выписывает сертификаты типа (самые простые среди имеющихся на рынке) сроком действия в 90 дней, и уже не первый год возможен выпуск так называемого wildcard-сертификата на несколько поддоменов.

Для получения сайтом сертификата используются алгоритмы, описанные в протоколе (ACME), созданного специально для Let's Encrypt. При его использовании подтверждение владения доменом осуществляется запросами через размещение определенного HTTP-кода (называется HTTP-01) или установку DNS-записей (DNS-01) — подробнее о них будет ниже.

Cert-manager


— специальный проект для Kubernetes, представляющий собой набор CustomResourceDefinitions (отсюда на минимально поддерживаемую версию K8s — v1.12) для конфигурации CA (удостоверяющих центров) и непосредственного заказа сертификатов. Установка CRD в кластер тривиальна и к применению одного YAML-файла:


kubectl create ns cert-manager
kubectl apply -f

( также и возможность установки с помощью Helm.)

Для инициирования процедуры заказа в кластере должны быть объявлены ресурсы центров сертификации (CA): Issuer или ClusterIssuer, — которые используются для подписи CSR (запросов на выпуск сертификата). Отличие первого ресурса от второго — в области видимости:

  • Issuer может использоваться в рамках одного пространства имен,
  • ClusterIssuer является глобальным объектом кластера.

Практика с cert-manager


№1. Самоподписанный сертификат


Начнем с простейшего случая — заказа самоподписанного сертификата. Такой вариант довольно распространен, например, для динамических тестовых окружений для разработчиков или же в случае использования внешнего балансировщика, терминирующего SSL-трафик.

Ресурс Issuer будет выглядеть так:


apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: selfsigned
spec:
selfSigned: {}

А чтобы выпустить сертификат, необходимо описать ресурс Certificate, где указывается, как произвести выпуск (см. раздел issuerRef ниже) и где размещен приватный ключ (поле secretName). После этого в Ingress потребуется сослаться на этот ключ (см. раздел tls в spec):


---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: selfsigned-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: selfsigned
commonName: "yet-another.website"
dnsNames:
- "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: app
spec:
tls:
- hosts:
- "yet-another.website"
secretName: tls-secret
rules:
- host: "yet-another.website"
http:
paths:
- path: /
backend:
serviceName: app
servicePort: 8080

Через несколько секунд после добавления этих ресурсов в кластер сертификат будет выписан. Увидеть соответствующий отчёт можно в выводе команды:


kubectl -n app describe certificate selfsigned-crt
...
Normal GeneratedKey 5s cert-manager Generated a new private key
Normal Requested 5s cert-manager Created new CertificateRequest resource "selfsigned-crt-4198958557"
Normal Issued 5s cert-manager Certificate issued successfully

Если посмотреть на сам ресурс секрета, то в нём лежат:

  • приватный ключ tls.key,
  • корневой сертификат ca.crt,
  • наш самоподписанный сертификат tls.crt.

Содержимое этих файлов можно увидеть с помощью утилиты openssl, например, так:


kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website

Стоит отметить, что в общем случае сертификату, выписанному с помощью такого Issuer, подключаемые клиенты доверять не будут. Причина проста: он не имеет CA (см. ). Чтобы этого избежать, нужно указать в Certificate путь до файла секрета, где содержится ca.crt. Таковым может быть и корпоративный CA организации — чтобы подписать выпускаемые для Ingress сертификаты ключом, уже используемым для нужд других серверных служб/информационных систем.

№2. Сертификат Let’s Encrypt с HTTP-валидацией


Для выпуска сертификатов LE, как упоминалось ранее, доступны владения доменом: HTTP-01 и DNS-01.

Первый подход (HTTP-01) в запуске небольшого веб-сервера отдельным deployment’ом, который будет отдавать в интернет по ссылке из наличия API к DNS-серверу, где размещены записи домена. Issuer с помощью указанных токенов создает TXT-записи на домене, которые потом получает в ходе подтверждения ACME-сервер. Среди официально поддерживаемых DNS-провайдеров — CloudFlare, AWS Route53, Google CloudDNS и другие, в том числе и собственная реализация ( ).

Примечание: у Let’s Encrypt существуют довольно на запросы к ACME-серверам. Чтобы не попасть в длительный бан, для отладки рекомендуется использовать тип сертификата (отличие только в ACME-сервере).

Итак, опишем ресурсы:


apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: le-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: letsencrypt
commonName: yet-another.website
dnsNames:
- yet-another.website

Обратите внимание, что в качестве server у acme (в Issuer) указан адрес staging-сервера. Заменить его на боевой можно будет позже.

Применив эту конфигурацию, проследим весь путь заказа:

  1. Создание Certificate породило новый ресурс CertificateRequest:


    kubectl -n app describe certificate le-crt
    ...
    Created new CertificateRequest resource "le-crt-1127528680"
  2. В его описании — отметка о создании Order:


    kubectl -n app describe certificaterequests le-crt-1127528680

    Created Order resource app/le-crt-1127528680-1805948596
  3. В Order описано, с какими параметрами проходит проверка и какой у неё текущий статус. Такая проверка осуществляется ресурсом Challenge:


    kubectl -n app describe order le-crt-1127528680-1805948596

    Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
  4. Наконец, в подробностях этого ресурса содержится информация о статусе самой проверки:


    kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
    ...
    Reason: Successfully authorized domain
    ...
    Normal Started 2m45s cert-manager Challenge scheduled for processing
    Normal Presented 2m45s cert-manager Presented challenge using http-01 challenge mechanism
    Normal DomainVerified 2m22s cert-manager Domain "yet-another.website" verified with "http-01" validation

Если все условия были соблюдены (т.е. домен доступен снаружи, нет бана со стороны LE…) — меньше, чем через минуту, сертификат будет выпущен. В случае успеха в выводе describe certificate le-tls появится запись Certificate issued successfully.

Теперь можно смело менять адрес сервера на боевой (https://acme-v02.api.letsencrypt.org/directory) и перезаказывать уже настоящие сертификаты, подписанные не Fake LE Intermediate X1, а Let's Encrypt Authority X3.

Для этого сначала потребуется удалить ресурс Certificate: иначе никакие процедуры заказа не активируются, потому что сертификат уже есть и он актуален. Удаление секрета приведет к его немедленному возврату с сообщением в describe certificate:


Normal PrivateKeyLost 44s cert-manager Lost private key for CertificateRequest "le-crt-613810377", deleting old resource

Остается применить «боевой» манифест для Issuer с уже описанным выше Certificate (он не изменился):


apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx

После получения сообщения Certificate issued successfully в describe проверим его:


kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

№3. Wildcard LE с валидацией через DNS


Усложним задачу, выписав сертификат сразу на все поддомены сайта и воспользовавшись на этот раз DNS-проверкой (через CloudFlare).

Для начала получим в панели управления CloudFlare токен для работы через API:

  1. Profile → API Tokens → Create Token.
  2. Выставляем права доступа следующим образом:
    • Permissions:
      • Zone — DNS — Edit
      • Zone — Zone — Read
    • Zone Resources:
      • Include — All Zones
  3. Копируем полученный после сохранения токен (например: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M).

Создадим Secret, в котором будет храниться этот токен, и сошлемся на него в Issuer:


apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token
type: Opaque
stringData:
api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: le-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: letsencrypt
commonName: yet-another.website
dnsNames:
- "yet-another.website"
- "*.yet-another.website"

(Не забудьте про использование staging, если экспериментируете!)

Пройдем процедуру подтверждения владения доменом:


kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
Presented: true
Processing: true
Reason: Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 54s cert-manager Challenge scheduled for processing
Normal Presented 53s cert-manager Presented challenge using dns-01 challenge mechanism

В панели появится TXT-запись:

pohkjc19ui1olxdlfeaiuuqm3ne.png


… а через некоторое время статус сменится на:


Domain "yet-another.website" verified with "dns-01" validation

Убедимся в том, что сертификат валиден для любого поддомена:


kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
DNS:*.yet-another.website, DNS:yet-another.website

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


apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- selector:
dnsNames:
- "*.yet-another.website"
dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
- selector:
dnsNames:
- "yet-another.website"
http01:
ingress:
class: nginx

Если применить эту конфигурацию, будут созданы два ресурса Challenge:


kubectl -n app describe orders le-crt-613810377-1285319347

Normal Created 3m29s cert-manager Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"
Normal Created 3m29s cert-manager Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"

№4. Использование специальных аннотаций Ingress


Помимо прямого пути по созданию сертификатов в cert-manager есть возможность воспользоваться компонентом под названием и явно не создавать ресурсы Certificate. Идея заключается в том, что с помощью специальных аннотаций Ingress’а сертификат будет автоматически заказан с помощью указанного в них Issuer. В результате получается примерно следующий ресурс Ingress’а:


apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- "yet-another.website"
secretName: tls-secret
rules:
- host: "yet-another.website"
http:
paths:
- path: /
backend:
serviceName: app
servicePort: 8080

Для корректной работы тут достаточно только наличия Issuer’а, то есть создавать на одну сущность меньше.

Кроме того, существует аннотация — kubernetes.io/tls-acme: "true", — которая требует задания Issuer по умолчанию при установке cert-manager с помощью параметров Helm (или в параметрах запуска контейнера менеджера).

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

Вместо заключения


Путём несложных манипуляций с CRD мы научились выписывать автопродляемые, самоподписанные и бесплатные SSL-сертификаты от проекта Let’s Encrypt для доменов сайтов, запущенных в рамках Ingress’ов в Kubernetes-кластерах.

В статье приведены примеры решения наиболее частых в нашей практике задач. Однако функции cert-manager не ограничиваются описанными выше возможностями. На сайте утилиты можно найти примеры работы с другими сервисами — например, или же (issuers).

P.S.


Читайте также в нашем блоге:

  • « »;
  • « »;
  • « ».
[/URL]
 
Сверху Снизу