Как посмотреть состояние сертификата?

# kubectl -n default describe certificate example-com
...
Status:
  Acme:
    Authorizations:
      Account:  https://acme-v01.api.letsencrypt.org/acme/reg/22442061
      Domain:   example.com
      Uri:      https://acme-v01.api.letsencrypt.org/acme/challenge/qJA9MGCZnUnVjAgxhoxONvDnKAsPatRILJ4n0lJ7MMY/4062050823
      Account:  https://acme-v01.api.letsencrypt.org/acme/reg/22442061
      Domain:   admin.example.com
      Uri:      https://acme-v01.api.letsencrypt.org/acme/challenge/pW2tFKLBDTll2Gx8UBqmEl846x5W-YpBs8a4HqstJK8/4062050808
      Account:  https://acme-v01.api.letsencrypt.org/acme/reg/22442061
      Domain:   www.example.com
      Uri:      https://acme-v01.api.letsencrypt.org/acme/challenge/LaZJMM9_OKcTYbEThjT3oLtwgpkNfbHVdl8Dz-yypx8/4062050792
  Conditions:
    Last Transition Time:  2018-04-02T18:01:04Z
    Message:               Certificate issued successfully
    Reason:                CertIssueSuccess
    Status:                True
    Type:                  Ready
Events:
  Type     Reason                 Age                 From                     Message
  ----     ------                 ----                ----                     -------
  Normal   PrepareCertificate     1m                cert-manager-controller  Preparing certificate with issuer
  Normal   PresentChallenge       1m                cert-manager-controller  Presenting http-01 challenge for domain example.com
  Normal   PresentChallenge       1m                cert-manager-controller  Presenting http-01 challenge for domain www.example.com
  Normal   PresentChallenge       1m                cert-manager-controller  Presenting http-01 challenge for domain admin.example.com
  Normal   SelfCheck              1m                cert-manager-controller  Performing self-check for domain admin.example.com
  Normal   SelfCheck              1m                cert-manager-controller  Performing self-check for domain example.com
  Normal   SelfCheck              1m                cert-manager-controller  Performing self-check for domain www.example.com
  Normal   ObtainAuthorization    55s               cert-manager-controller  Obtained authorization for domain example.com
  Normal   ObtainAuthorization    54s               cert-manager-controller  Obtained authorization for domain admin.example.com
  Normal   ObtainAuthorization    53s               cert-manager-controller  Obtained authorization for domain www.example.com

Как получить список сертификатов?

# kubectl get certificate --all-namespaces
NAMESPACE          NAME                            AGE
default            example-com                     13m

Какие виды сертификатов поддерживаются?

На данный момент модуль устанавливает следующие ClusterIssuer:

  • letsencrypt
  • letsencrypt-staging
  • selfsigned
  • selfsigned-no-trust

Работает ли старая аннотация TLS-acme?

Да, работает! Специальный компонент (cert-manager-ingress-shim) видит эти аннотации и на их основании автоматически создает ресурсы Certificate (в тех же namespace, что и Ingress-ресурсы с аннотациями).

Важно! При использовании аннотации Certificate создается «прилинкованным» к существующему Ingress-ресурсу, и для прохождения Challenge НЕ создается отдельный Ingress, а вносятся дополнительные записи в существующий. Это означает, что если на основном Ingress’е настроена аутентификация или whitelist — ничего не выйдет. Лучше не использовать аннотацию и переходить на Certificate.

Важно! При переходе с аннотации на Certificate нужно удалить Certificate, который был создан по аннотации. Иначе по обоим Certificate будет обновляться один Secret, и это может привести к достижению лимита запросов Let’s Encrypt.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/tls-acme: "true"           # Вот она, аннотация!
  name: example-com
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: example.com
    http:
      paths:
      - backend:
          service:
            name: site
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: www.example.com                    # Дополнительный домен.
    http:
      paths:
      - backend:
          service:
            name: site
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: admin.example.com                  # Еще один дополнительный домен.
    http:
      paths:
      - backend:
          service:
            name: site
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - example.com
    - www.example.com                        # Дополнительный домен.
    - admin.example.com                      # Еще один дополнительный домен.
    secretName: example-com-tls              # Так будут называться и Certificate, и Secret.

Ошибка: CAA record does not match issuer

Если cert-manager не может заказать сертификаты с ошибкой:

CAA record does not match issuer

то необходимо проверить CAA (Certificate Authority Authorization) DNS-запись у домена, для которого заказывается сертификат. Если вы хотите использовать Let’s Encrypt-сертификаты, у домена должна быть CAA-запись: issue "letsencrypt.org". Подробнее про CAA можно почитать тут и тут.

Интеграция с Vault

Вы можете использовать данную инструкцию для выпуска сертификатов с помощью Vault.

После конфигурации PKI и включения авторизации в Kubernetes, вам нужно:

  • Создать ServiceAccount и скопировать ссылку на его Secret:

    kubectl create serviceaccount issuer
    ISSUER_SECRET_REF=$(kubectl get serviceaccount issuer -o json | jq -r ".secrets[].name")
    
  • Создать Issuer:

    kubectl apply -f - <<EOF
    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: vault-issuer
      namespace: default
    spec:
      vault:
        # Если Vault разворачивался по вышеуказанной инструкции, в этом месте в инструкции опечатка.
        server: http://vault.default.svc.cluster.local:8200
        # Указывается на этапе конфигурации PKI. 
        path: pki/sign/example-dot-com 
        auth:
          kubernetes:
            mountPath: /v1/auth/kubernetes
            role: issuer
            secretRef:
              name: $ISSUER_SECRET_REF
              key: token
    EOF
    
  • Создать ресурс Certificate для получения TLS-сертификата подписанного Vault CA:

    kubectl apply -f - <<EOF
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: example-com
      namespace: default
    spec:
      secretName: example-com-tls
      issuerRef:
        name: vault-issuer
      # Домены указываются на этапе конфигурации PKI в Vault.
      commonName: www.example.com 
      dnsNames:
      - www.example.com
    EOF
    

Как использовать свой или промежуточный CA для заказа сертификатов?

Для использования собственного или промежуточного CA:

  • Сгенерируйте сертификат (при необходимости):

    openssl genrsa -out rootCAKey.pem 2048
    openssl req -x509 -sha256 -new -nodes -key rootCAKey.pem -days 3650 -out rootCACert.pem
    
  • В пространстве имён d8-cert-manager создайте секрет, содержащий данные файлов сертификатов.

    Пример создания секрета с помощью команды kubectl:

    kubectl create secret tls internal-ca-key-pair -n d8-cert-manager --key="rootCAKey.pem" --cert="rootCACert.pem"
    

    Пример создания секрета из YAML-файла (содержимое файлов сертификатов должно быть закодировано в Base64):

    apiVersion: v1
    data:
      tls.crt: <результат команды `cat rootCACert.pem | base64 -w0`>
      tls.key: <результат команды `cat rootCAKey.pem | base64 -w0`>
    kind: Secret
    metadata:
      name: internal-ca-key-pair
      namespace: d8-cert-manager
    type: Opaque
    

    Имя секрета может быть любым.

  • Создайте ClusterIssuer из созданного секрета:

    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: inter-ca
    spec:
      ca:
        secretName: internal-ca-key-pair    # Имя созданного секрета.
    

    Имя ClusterIssuer также может быть любым.

Теперь можно использовать созданный ClusterIssuer для получения сертификатов для всех компонентов Deckhouse или конкретного компонента.

Например, чтобы использовать ClusterIssuer для получения сертификатов для всех компонентов Deckhouse, укажите его имя в глобальном параметре clusterIssuerName (kubectl edit mc global):

  spec:
    settings:
      modules:
        https:
          certManager:
            clusterIssuerName: inter-ca
          mode: CertManager
        publicDomainTemplate: '%s.<public_domain_template>'
    version: 1

Как защитить учетные данные cert-manager?

Если вы не хотите хранить учетные данные конфигурации Deckhouse (например, по соображениям безопасности), можете создать свой собственный ClusterIssuer / Issuer. Например, вы можете создать свой ClusterIssuer для сервиса route53 следующим образом:

  • Создайте Secret с учетными данными:

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Secret
    type: Opaque
    metadata:
      name: route53
      namespace: default
    data:
      secret-access-key: {{ "MY-AWS-ACCESS-KEY-TOKEN" | b64enc | quote }}
    EOF
    
  • Создайте простой ClusterIssuer со ссылкой на этот Secret:

    kubectl apply -f - <<EOF
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: route53
      namespace: default
    spec:
      acme:
        server: https://acme-v02.api.letsencrypt.org/directory
        privateKeySecretRef:
          name: route53-tls-key
        solvers:
        - dns01:
            route53:
              region: us-east-1
              accessKeyID: {{ "MY-AWS-ACCESS-KEY-ID" }}
              secretAccessKeySecretRef:
                name: route53
                key: secret-access-key
    EOF
    
  • Закажите сертификаты как обычно, используя созданный ClusterIssuer:

    kubectl apply -f - <<EOF
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: example-com
      namespace: default
    spec:
      secretName: example-com-tls
      issuerRef:
        name: route53
      commonName: www.example.com 
      dnsNames:
      - www.example.com
    EOF