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

Важно! Чтобы воспользоваться этим способом выпуска SSL-сертификатов, у вас должны быть лишний кластер Kubernetes и FreeIPA, между которыми настроена сетевая связность. Если это так, поздравляю, летим за сертификатами.

Коротко о задаче

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

Также можно использовать бесплатные способы, в том числе основанные на алгоритмах протокола Automated Certificate Management Environment (ACME). Благодаря им можно заказать SSL-сертификат типа domain-validated. Это самый простой сертификат, который можно получить бесплатно на 90 дней. Дальше требуется обновление. 

Упростить работу с сертификатами можно с помощью Kubernetes. А точнее — благодаря плагину cert-manager. Его задача как раз сводится к тому, чтобы автоматизировать выпуск и обновление SSL-сертификатов. 

Сделать процесс еще проще, как выяснилось, позволяет сетевая связность Kubernetes с решениями IdM (от identity management, если вдруг забыли). В моем случае это FreeIPA. 

Плюсы способа:

  • выпуск SSL-сертификата занимает максимум 5 минут (если делать все вручную, то можно зависнуть с задачей на полдня),

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

  • все обновления происходят автоматически, не нужно просматривать сертификаты, отслеживать статусы по ним и т.д.

Переходим к делу

1. Для начала необходимо включить поддержку ACME во FreeIPA:

(idm)# ipa-acme-manage enable

2. Чтобы cert-manager добавил DNS-запись _acme-challenge во FreeIPA, используем провайдера cert-manager (в моем случае RFC-2136). Для этого мы должны создать новый ключ TSIG на нашем IPA-сервере:

tsig-keygen -a hmac-sha512 acme-update >> /etc/named/ipa-ext.conf

3. Далее необходимо подключить cert-manager к Kubernetes. Выполняется это действие следующей командой:

helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.14.4 \

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

kubectl -n cert-manager create secret generic ipa-tsig-secret --from-literal=tsig-secret-key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

На этом этапе заканчиваются основные настройки Kubernetes. При выпуске следующего сертификата начинаем с п. 5. 

5. Формируем ряд манифестов.

Переходим к ресурсу ClusterIssuer, который предоставляет сертификаты TLS. В нем указываем наш сервер IdM и зону, внутри которой мы будем выпускать сертификаты:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: myservice.testzone.nubes.ru
spec:
  acme:
    email: test@testzone.nubes.ru
    server: https://idm1.testzone.nubes.ru/acme/directory
    privateKeySecretRef:
      name: ipa-issuer-account-key
    solvers:
      - dns01:
          rfc2136:
            nameserver: idm.testzone.nubes.ru
            tsigKeyName: acme-update
            tsigAlgorithm: HMACSHA512
            tsigSecretSecretRef:
              name: ipa-tsig-secret
              key: tsig-secret-key
        selector:
          dnsZones:
            - 'testzone.nubes.ru'

7. Далее создаем NetworkPolicy.

NetworkPolicy представляет собой механизм управления сетевыми политиками. Он предназначен для определения правил доступа для сетевого трафика между подами в кластере. 

С помощью NetworkPolicy мы указываем нужные хуки в белые списки:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  annotations:
  name: myservice-acme-http-solver
  namespace: testzone
spec:
  ingress:
  - {}
  podSelector:
    matchExpressions:
    - key: acme.cert-manager.io/http-domain
      operator: Exists
    - key: acme.cert-manager.io/http-token
      operator: Exists
  policyTypes:
  - Ingress

8. В сертификате указываем, к какому ClusterIssuer будем обращаться и на какой адрес хотим выпустить сертификат. Также прописываем secretName (это нам понадобится для последнего манифеста):

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: myservice-cert
  namespace: testzone
spec:
  commonName: 'myservice.testzone.nubes.ru'
  dnsNames:
    - myservice.testzone.nubes.ru
  issuerRef:
    name: myservice.testzone.nubes.ru
    kind: ClusterIssuer
  privateKey:
    algorithm: RSA
    encoding: PKCS1
    size: 4096
  secretName: myservice-tls

9. И, собственно, последний из манифестов представляет собой Ingress. Указываем аннотацию 'kubernetes.io/tls-acme: "true"' и наш secretName:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: myservice.testzone.nubes.ru
    kubernetes.io/tls-acme: "true"
  name: myservice-ingress
  namespace: testzone
spec:
  ingressClassName: nginx
  rules:
  - host: myservice.testzone.nubes.ru
    http:
      paths:
      - backend:
          service:
            name: myservice-service
            port:
              number: 8080
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - myservice.testzone.nubes.ru
    secretName: myservice-tls

10. На стороне IdM получаем:

А в Kubernetes видим:

kubectl -n testzone describe certificaterequests.cert-manager.io
...
...
...
  Conditions:
    Last Transition Time:  2024-04-17T12:19:01Z
    Message:               Certificate request has been approved by cert-manager.io
    Reason:                cert-manager.io
    Status:                True
    Type:                  Approved
    Last Transition Time:  2024-04-17T12:20:03Z
    Message:               Certificate fetched from issuer successfully
    Reason:                Issued
    Status:                True
    Type:                  Ready

Все, сертификат выпущен. В целом, конечно, можно заморочиться и делать все через Helm, и так даже правильнее. Но это уже совсем другая история…

Комментарии (4)


  1. ALexhha
    25.04.2024 07:35
    +1

    Чтобы воспользоваться этим способом выпуска SSL-сертификатов, у вас
    должны быть лишний кластер Kubernetes и FreeIPA
    , между которыми
    настроена сетевая связность.

    ну так то да, а чем не устроил тот же certbot ?


    1. xahyka Автор
      25.04.2024 07:35
      +1

      Приветствую! Данная статья является одним из кейсов, аля "Как можно зайти в дом".
      Certbot выпускал в основном для LE-сертификатов, не доводилось менять его логику на что-то другое.

      Если так получилось, что в компании хочется выпускать сертификаты со своим CA и приклеивать к своим внутренним сервисам - данный кейс поможет решить проблему (в том числе и обновление). Больше решений - больше выбора =)


  1. xitriy87
    25.04.2024 07:35
    +1

    А зачем у вас у ClusterIssuer указан namespace?


    1. xahyka Автор
      25.04.2024 07:35
      +2

      И правда - очепятка с моей стороны. Спасибо что указал