Создание сертификатов для Ingress объектов

1. Цель

Целью данной статьи является создание сертификатов для Ingress объектов, включая этапы:

  1. Создание ресурса Issuer типа SelfSigned в cert-manager;

  2. Создание ресурса Ingress с аннотацией к созданному Issuer, который будет автоматически выпускать сертификаты для Ingress.

2. Cert-manager

cert-manager создает сертификаты TLS для рабочих нагрузок в кластере Kubernetes или в платформе Nova и обновляет сертификаты до истечения срока их действия.

cert-manager может получать сертификаты от различных центров сертификации, включая: StarVault, Let’s Encrypt, HashiCorp Vault, Venafi и частные PKI.

С помощью ресурса сертификатов cert-manager’а, закрытый ключ и сертификат хранятся в Kubernetes Secret, который монтируется приложением Pod или используется контроллером Ingress. При использовании csi-driver, csi-driver-spiffe или istio-csr закрытый ключ генерируется по требованию, перед запуском приложения. Закрытый ключ никогда не покидает узел и не хранится в Kubernetes Secret.

cert-manager уже предустановлен в платформу nova в пространство имен nova-cert-managment

3. Issuers

Issuers и ClusterIssuers - это ресурсы Kubernetes, представляющие центры сертификации (ЦС), которые могут генерировать подписанные сертификаты, выполняя запросы на подписание сертификатов. Все сертификаты cert-manager требуют наличия аннотации к Issuer.

Примером типа Issuer является ЦС. Простой ЦС Issuer выглядит следующим образом:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: ca-issuer
  namespace: sandbox
spec:
  ca:
    secretName: ca-key-pair

Это простой Issuer, который будет подписывать сертификаты на основе закрытого ключа. Сертификат, хранящийся в секрете ca-key-pair, может использоваться для доверия к новым сертификатам, подписанным этим Issuer, в системе инфраструктуры открытых ключей (PKI).

4. SelfSigned Issuers

⚠️ Самоподписанные SelfSigned Issuers обычно полезны для локальной загрузки PKI, что является сложной темой для опытных пользователей. Для безопасного использования в производстве запуск PKI вводит сложные требования к планированию ротации, распределению доверенного хранилища и аварийному восстановлению.

Если вы не планируете запускать собственную PKI, используйте другой Issuer тип.

SelfSigned Issuer не представляет собой центр сертификации как таковой, а вместо этого обозначает, что сертификаты будут "подписывать себя сами", используя заданный закрытый ключ. Другими словами, закрытый ключ сертификата будет использоваться для подписи самого сертификата.

Этот тип Issuer полезен для создания корневого сертификата для пользовательской PKI (Public Key Infrastructure - инфраструктура открытых ключей) или для создания простых специальных сертификатов для быстрого тестирования.

При использовании SelfSigned Issuer следует учитывать важные предостережения, в том числе вопросы безопасности. В общем случае вам лучше использовать ЦС Issuer, а не SelfSigned Issuer. Тем не менее, SelfSigned Issuer очень полезен для первоначальной установки ЦС Issuer.

Примечание: CertificateRequest, ссылающийся на самоподписанный сертификат, должен также содержать аннотацию cert-manager.io/private-key-secret-name, поскольку закрытый ключ, соответствующий CertificateRequest, необходим для подписи сертификата. Эта аннотация добавляется автоматически контроллером сертификатов.

4.1. Развертывание

Поскольку SelfSigned Issuer не зависит ни от какого другого ресурса, его проще всего настроить. В спецификации Issuer должна присутствовать только строка SelfSigned, других настроек не требуется:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: selfsigned-issuer
  namespace: sandbox
spec:
  selfSigned: {}

Пример SelfSigned Issuer для всего кластера:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-cluster-issuer
spec:
  selfSigned: {}

После развертывания вы должны сразу увидеть, что Issuer готов к подписанию:

$ kubectl get issuers  -n sandbox -o wide selfsigned-issuer
NAME                READY   STATUS                AGE
selfsigned-issuer   True                          2m

$ kubectl get clusterissuers -o wide selfsigned-cluster-issuer
NAME                        READY   STATUS   AGE
selfsigned-cluster-issuer   True             3m

4.2. Установка ЦС issuers

Одним из идеальных вариантов использования SelfSigned Issuers является загрузка пользовательского корневого сертификата для частной PKI вместе с ЦС Issuer.

Приведенный ниже манифест создаст SelfSigned Issuer, выпустит корневой сертификат и будет использовать этот корень в качестве ЦС issuer:

apiVersion: v1
kind: Namespace
metadata:
  name: sandbox
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-selfsigned-ca
  namespace: nova-cert-managment
spec:
  isCA: true
  commonName: my-selfsigned-ca
  secretName: root-secret
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
    group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: my-ca-issuer
  namespace: sandbox
spec:
  ca:
    secretName: root-secret

ClusterIssuer "selfsigned-issuer" используется для выпуска корневого Certificate ЦС. Этот сертификат хранится в секрете root-secret, который содержит сам корневой сертификат ca.crt, подписанный сертификат tls.crt (значение которого будет равняться ca.crt, т.к. он самоподписанный) и приватный ключ tls.key. Затем ClusterIssuer "my-ca-issuer" используется для выпуска, а также для подписи сертификатов с использованием только что созданного корневого Certificate ЦС, который вы будете использовать для будущих сертификатов в масштабах всего кластера.

4.3. Точки распространения CRL

Вы также можете указать точки распространения CRL (Certificate revocation list - список отозванных сертификатов) в виде массива строк, каждая из которых идентифицирует местоположение CRL, в котором можно проверить статус отзыва выпущенных сертификатов:

...
spec:
  selfSigned:
    crlDistributionPoints:
    - "http://example.com"

4.4. Предостережения

4.4.1. Доверие

Клиенты, использующие SelfSigned сертификаты, не могут доверять им, не имея предварительно сертификатов, что может быть затруднительно, если клиент находится в другом пространстве имен, чем сервер.

Это ограничение можно устранить, используя trust-manager для распространения ca.crt в другие пространства имен.

Безопасной альтернативы решению проблемы распространения хранилищ доверия не существует. Можно использовать сертификат "TOFU" (trust-on-first-use), но такой подход уязвим для атак типа "человек посередине" (man-in-the-middle).

4.4.2. Срок действия сертификата

Одним из побочных эффектов самоподписания сертификата является то, что его Subject DN и Issuer DN идентичны. Раздел 4.1.2.4 стандарта X.509 RFC 5280 требует, чтобы:

Поле издателя ДОЛЖНО содержать непустое отличительное имя (DN).

Однако самоподписанные сертификаты по умолчанию не имеют DN субъекта. Если вы не зададите вручную Subject DN сертификата, то Issuer DN будет пустым, и сертификат будет технически недействительным.

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

Чтобы избежать этого, обязательно задайте Subject для самоподписных сертификатов. Это можно сделать, задав spec.subject объекту сертификат в cert-manager, который будет выпущен SelfSigned Issuer.

Начиная с версии 1.3, cert-manager будет выдавать предупреждающее событие Kubernetes типа BadConfig, если обнаружит, что сертификат создается SelfSigned Issuer, у которого пустой Issuer DN.

5. Аннотация к Ingress ресурсу

Частым случаем использования cert-manager является запрос сертификатов, подписанных TLS, для защиты ваших Ingress ресурсов. Это можно сделать, просто добавив аннотации к вашим ресурсам Ingress, и cert-manager облегчит создание ресурса сертификата для вас. За это отвечает небольшой подкомпонент cert-manager, ingress-shim.

5.1. Как это работает

Подкомпонент ingress-shim следит за ресурсами Ingress в вашем кластере. Если он обнаружит Ingress с аннотациями, описанными в разделе Поддерживаемые аннотации, он проверит, что в том же пространстве имен, где находится ресурс Ingress, существует ресурс Certificate с именем, указанным в поле tls.secretName и настроенным, как описано в этом Ingress.

Приведенный ниже манифест создаст Ingress ресурс с привязкой к ранее созданному Issuer my-ca-issuer:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # добалвение аннотации, указывающей какой _Issuer_ ипсользовать
    cert-manager.io/issuer: my-ca-issuer
  name: my-ingress
  namespace: sandbox
spec:
  rules:
  - host: example.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: myservice
            port:
              number: 80
  tls: # < Размещение хоста в конфигурации TLS будет определять, что будет содержаться в subjectAltNames сертификата
  - hosts:
    - example.com
    secretName: myingress-cert # < cert-manager будет хранить сертификат в этом секрете store.

5.2. Поддерживаемые аннотации

Вы можете указать следующие аннотации для ресурсов Ingress, чтобы вызвать автоматическое создание ресурсов сертификатов:

  • cert-manager.io/issuer: имя Issuer, который должен выпустить сертификат, необходимый для данного Ingress

⚠️ Эта аннотация не предполагает наличие Issuer с привязкой к пространству имен. По умолчанию будет использоваться cert-manager.io Issuer, однако в случае внешних типов Issuers это должно быть использовано как для типов Issuers с привязкой к пространству имен, так и для типов Issuers с привязкой ко всему кластеру.

⚠️ Если используется Issuer для пространства имен, то он должен находиться в том же пространстве имен, что и ресурс Ingress.

  • cert-manager.io/cluster-issuer: имя ClusterIssuer для получения сертификата, необходимого для этого Ingress. Не имеет значения, в каком пространстве имен находится ваш Ingress, так как ClusterIssuers - это ресурс с привязкой ко всему кластеру.

⚠️ Эта аннотация является сокращением для ссылки на cert-manager.io ClusterIssuer без необходимости указывать группу и тип. Она не предназначена для указания внешнего Issuer с кластерной привязкой - для этого используйте аннотацию cert-manager.io/issuer.

  • cert-manager.io/issuer-kind: тип внешнего ресурса Issuer, например AWSPCAIssuer. Это необходимо только для не стандартных Issuer.

  • cert-manager.io/issuer-group: группа API контроллера внешнего Issuer, например awspca.cert-manager.io. Это необходимо только для не стандартных Issuer.

  • cert-manager.io/common-name: (необязательно) эта аннотация позволяет настроить spec.commonName для генерируемого сертификата.

Другие поддерживаемые опциональные аннотации вы можете найти на оффициальном сайте.

5.3. Генерация нескольких сертификатов с несколькими Ingress

Если вам нужно сгенерировать сертификаты из нескольких Ingress, убедитесь, что они имеют аннотацию Issuer. Помимо аннотации, необходимо, чтобы каждый Ingress обладал уникальным именем tls.secretName

5.4. Устранение неполадок

Если после применения аннотаций ingress-shim ресурс Certificate не создается, проверьте, установлен ли хотя бы cert-manager.io/issuer или cert-manager.io/cluster-issuer. Если вы хотите использовать kubernetes.io/tls-acme: "true", убедитесь, что проверили все шаги выше, и, возможно, захотите поискать ошибки в логах cert-manager pod, если они не устранены.