cert-manager выпускает сертификаты через Issuer или ClusterIssuer типа vault, который обращается к механизму секретов PKI по пути <MOUNT>/sign/<ROLE>. Stronghold совместим с API Vault, поэтому такой Issuer можно направить на Stronghold. Эндпоинт <MOUNT>/sign/<ROLE> принимает поля, которые отправляет cert-manager (csr, common_name, alt_names, ip_sans, uri_sans, other_sans, ttl).

В Deckhouse Platform cert-manager устанавливается модулем cert-manager.

Цель

После настройки:

  • cert-manager в кластере выпускает и перевыпускает TLS-сертификаты для поддоменов example.com через промежуточный CA в Stronghold.
  • cert-manager входит в Stronghold через метод аутентификации Kubernetes, поэтому долгоживущие учётные данные Stronghold в кластере не хранятся.

Предварительные требования

Перед началом подготовьте:

  • Механизм секретов PKI по пути pki_int с промежуточным CA и ролью example-com для домена example.com, как в разделе «Внутренний PKI на Stronghold».
  • Метод аутентификации Kubernetes, включённый в Stronghold для кластера.
  • cert-manager, установленный в кластере.
  • Токен Stronghold с правами на запись политик и ролей метода Kubernetes.
  • Сертификат CA, которым подписан TLS-сертификат Stronghold.

Шаг 1. Настройте Stronghold

Разрешите cert-manager подписывать запросы на сертификаты по роли example-com.

  1. Создайте политику, разрешающую подписывать запросы на сертификат по роли:

    d8 stronghold policy write cert-manager-pki - <<'POLICY'
    path "pki_int/sign/example-com" {
      capabilities = ["create", "update"]
    }
    POLICY
  2. Создайте роль метода Kubernetes для ServiceAccount stronghold-issuer в неймспейсе myapp. Параметр audience должен совпадать с аудиторией токена, которую запрашивает cert-manager: для Issuer это vault://<NAMESPACE>/<ISSUER_NAME>, для ClusterIssuer — vault://<CLUSTER_ISSUER_NAME>:

    d8 stronghold write auth/kubernetes/role/cert-manager-myapp bound_service_account_names=stronghold-issuer bound_service_account_namespaces=myapp audience=vault://myapp/stronghold policies=cert-manager-pki ttl=20m

    В DP в режиме Automatic метод аутентификации Kubernetes для текущего кластера создаётся модулем по пути kubernetes_local: в этом случае используйте auth/kubernetes_local вместо auth/kubernetes в командах и в mountPath Issuer. Путь auth/kubernetes относится к методу, включённому вручную.

Шаг 2. Настройте cert-manager

Создайте Issuer, который обращается к Stronghold, и запросите сертификат.

  1. Создайте ServiceAccount и разрешите cert-manager запрашивать для него токены:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: stronghold-issuer
      namespace: myapp
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: stronghold-issuer-token
      namespace: myapp
    rules:
      - apiGroups: [""]
        resources: ["serviceaccounts/token"]
        resourceNames: ["stronghold-issuer"]
        verbs: ["create"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: stronghold-issuer-token
      namespace: myapp
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: stronghold-issuer-token
    subjects:
      - kind: ServiceAccount
        name: cert-manager
        namespace: d8-cert-manager
  2. Создайте Issuer:

    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: stronghold
      namespace: myapp
    spec:
      vault:
        server: https://stronghold.example.com
        path: pki_int/sign/example-com
        caBundle: <CA_BUNDLE_BASE64>
        auth:
          kubernetes:
            role: cert-manager-myapp
            mountPath: /v1/auth/kubernetes
            serviceAccountRef:
              name: stronghold-issuer

    <CA_BUNDLE_BASE64> — сертификат CA, которым подписан TLS-сертификат Stronghold, в кодировке base64.

  3. Запросите сертификат:

    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: www
      namespace: myapp
    spec:
      secretName: www-tls
      commonName: www.example.com
      dnsNames:
        - www.example.com
      duration: 72h
      renewBefore: 24h
      issuerRef:
        name: stronghold
        kind: Issuer

cert-manager сохранит сертификат и закрытый ключ в Secret www-tls и будет перевыпускать сертификат до истечения срока. Значение duration не должно превышать max_ttl роли example-com.

Другие способы аутентификации

Issuer типа vault также поддерживает аутентификацию через AppRole (auth.appRole с roleId и ссылкой на Secret с secret_id) и через токен (auth.tokenSecretRef). При этих способах secret_id или токен хранится в Secret в кластере. Аутентификация Kubernetes не требует хранить долгоживущие учётные данные в кластере.

Проверка

Проверьте результат:

  1. Проверьте, что Issuer готов:

    d8 k -n myapp get issuer stronghold

    В колонке READY должно быть значение True.

  2. Проверьте, что сертификат выпущен:

    d8 k -n myapp get certificate www

    В колонке READY должно быть значение True.

  3. Проверьте издателя и срок действия сертификата в Secret:

    d8 k -n myapp get secret www-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -issuer -enddate

    В поле issuer должно быть имя промежуточного CA из pki_int.

Очистка

Удалите объекты, созданные в этом руководстве.

  1. Удалите Certificate, его Secret, Issuer и ServiceAccount с Role и RoleBinding:

    d8 k -n myapp delete certificate www
    d8 k -n myapp delete secret www-tls
    d8 k -n myapp delete issuer stronghold
    d8 k -n myapp delete rolebinding stronghold-issuer-token
    d8 k -n myapp delete role stronghold-issuer-token
    d8 k -n myapp delete serviceaccount stronghold-issuer
  2. Удалите роль метода Kubernetes и политику в Stronghold:

    d8 stronghold delete auth/kubernetes/role/cert-manager-myapp
    d8 stronghold policy delete cert-manager-pki

    Уже выпущенные сертификаты действуют до истечения срока. Чтобы отозвать их, используйте pki_int/revoke, как в разделе «Внутренний PKI на Stronghold».

Дополнительные ресурсы