Получение сертификатов в k3s (k8s) через cert-manager

Nikolay Gushcharin февраля 12, 2025 #kubernetes #k3s #k8s #certmanager #letsencrypt

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

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

SSL-сертификаты и HTTPS-соединение

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

SSL (Secure Sockets Layer) — технология, которая обеспечивает безопасное соединение. В последнее время чаще используется аббревиатура TLS (Transport Layer Security). Этот протокол защищает информацию, которую пользователь отправляет на сервер или получает от него. Например, логины, пароли, данные банковских карт. Если не использовать шифрование, то обычный HTTP-трафик очень легко перехватить и прочитать содержимое пакета.

HTTPS (HyperText Transfer Protocol Secure) — это защищенная версия протокола HTTP, использующая сертификаты и технологии SSL/TLS для шифрования данных.

Доверенные центры сертификации (CA)

Центры сертификации (Certificate Authorities, CA) — это организации, которые занимаются выдачей SSL-сертификатов. Их задача — проверить, что пользователь, запросивший сертификат, является владельцем домена, на который запрашивается сертификат, и подтвердить его подлинность. Другими словами, эти организации выступают своего рода гарантами взаимосвязи домена и владельца, а в подтверждение выдают подписанный документ — сертификат. Приведу список известных центров сертификации:

Как происходит обмен данными:

  1. Клиент (браузер) запрашивает соединение с сервером (например, при переходе на сайт).
  2. Сервер отправляет клиенту свой SSL-сертификат.
  3. Клиент проверяет сертификат (может проверить самостоятельно или обратиться к центру сертификации, который выдал документ):
    • Доверен ли центр сертификации?
    • Не истек ли срок действия сертификата?
    • Соответствует ли доменное имя сертификата запрашиваемому сайту?
  4. Если проверка прошла успешно, клиент и сервер устанавливают безопасное соединение с использованием шифрования (например, с помощью алгоритмов RSA или ECDHE).
  5. После этого данные передаются в зашифрованном виде. Например, если вы вводите пароль на сайте, он будет зашифрован и расшифрован только на сервере.

Как получить SSL-сертификат

Есть несколько способов получения сертификата. Кратко рассмотрим их.

Создание самоподписных сертификатов

Это делается достаточно легко с помощью утилиты openssl (Linux). Используя всего несколько команд, мы получаем два файла: сам сертификат и ключ. Их уже можно привязывать к веб-серверу. Весь процесс выдачи описывать не вижу смысла, так как по этому поводу есть множество статей, например эта.

Какие минусы у этого подхода:

Использование утилиты certbot

Эта утилита позволяет в автоматическом режиме получить сертификат Let's Encrypt для указанного домена. Можно получать сертификаты как на конкретные домены, так и по схеме wildcard (когда можно получить один сертификат на все возможные поддомены). Например, если запросить SSL для example.com, то он будет доступен только для него, а если запросить wildcard *.example.com, то его можно использовать как для one.example.com, так и для two.example.com и даже для one.two.example.com.

Процесс получения сертификата достаточно прост. Необходимо установить утилиту, а затем использовать команду certbot certonly {...params...}. Для более подробной информации о процессе получения можно почитать эту статью.

Следует упомянуть о двух способах получения (ACME challenges):

Использование certbot может решить огромное количество проблем, но все же остаются минусы:

Использование сервиса cert-manager

И вот мы добрались до решения, которое сейчас используется в моем кластере. Сервис cert-manager берет на себя всю работу по выдаче, отслеживанию, обновлению и присвоению сертификатов. От пользователя требуется только настроить процесс выдачи и просто прописывать в Ingress новых сервисов указание использования TLS-соединения. И cert-manager увидит создание нового Ingress с TLS, сразу получит валидный сертификат и присвоит его сервису.

Рассмотрим процесс установки и настройки. Для начала необходимо наличие кластера k3s или k8s (или другого Kubernetes). Установленная утилита helm для управления Helm-чартами.

Для удобства я создал на машине с кластером папку /home/ms/helms, где хранятся все values.yaml и другие настройки Helm-чартов. Переходим в эту папку и начинаем выполнять команды:

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm pull jetstack/cert-manager

Команды выше добавляют репозиторий jetstack, в котором расположен необходимый нам чарт. Далее загружаем чарт cert-manager с помощью команды pull.

Проверяем, что архив был загружен, и разархивируем его. Необходимо обратить внимание на версию чарта: на момент написания статьи это версия 1.16.3.

ls -la
tar zxf cert-manager-v1.16.3.tgz
rm cert-manager-v1.16.3.tgz
cp cert-manager/values.yaml cert-manager-values.yaml

Я использовал DNS01 challenge для получения сертификатов, так как кластер Kubernetes находится в локальной сети, и сервисы не выходят в глобальную сеть. Заморачиваться с отдельным "сервисом-заглушкой", который бы помог использовать HTTP01, не стал. Мне показалось, что через DNS будет проще. Но столкнулся с проблемой, когда при запросе сертификата cert-manager бесконечно висел с ошибкой:

DNS record for xxx not yet propagated

Потратил на решение этой проблемы около трех дней, пока не нашел Issue на GitHub с решением. Ссылка на Issue и еще одна ссылка с решением.

Необходимо подправить файл values.yaml, найти следующие строки и исправить их следующим образом:

crds:
  enabled: true

extraArgs:
  - --enable-certificate-owner-ref=true
  - --dns01-recursive-nameservers-only
  - --dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53

prometheus:
  enabled: true
  servicemonitor:
    enabled: true

Далее устанавливаем сам сервис cert-manager в кластер:

helm upgrade --install cert-manager -f cert-manager-values.yaml cert-manager/

Для того чтобы использовать DNS challenge, нам нужно получить API-ключ провайдера, где был приобретен домен. Настройка для многих зарубежных провайдеров схожа, но я использую reg.ru. Для него нет стандартной реализации webhook для cert-manager. Поэтому воспользуемся еще одним сервисом. В этом репозитории лежит реализация webhook для использования DNS challenge через reg.ru. В README репозитория есть инструкция по использованию, оттуда можно взять все, кроме процесса создания файла сертификата. Я предпочитаю использовать wildcard-сертификат, а не выдавать для каждого домена отдельно.

Теперь нужно настроить объект ClusterIssuer, который отвечает за настройки процесса выдачи сертификатов. Для этого создадим отдельную папку /home/ms/ci и в этой папке создадим файл cert-manager-issuer.yml:

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod-issuer
spec:
  acme:
    email: <YOUR_EMAIL>
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: <SUPER_SECRET_KEY>
    solvers:
      - dns01:
          webhook:
            config:
              # здесь могут быть настройки для другого webhook-провайдера.
              regruPasswordSecretRef:
                name: regru-password
                key: REGRU_PASSWORD
            # groupName должен совпадать с `groupName.name` в файле `values.yaml`.
            groupName: acme.regru.ru
            solverName: regru-dns
---

Пример solver DNS01 для Cloudflare:

- dns01:
  cloudflare:
    email: xxx
    apiTokenSecretRef:
      name: cloudflare-token-secret
      key: cloudflare-token

Если не хочется заморачиваться с получением сертификата через DNS challenge, то можно использовать HTTP challenge. Для этого в секции solvers необходимо прописать следующее:

- http01:
    # The ingressClass used to create the necessary ingress routes
    ingress:
      serviceType: ClusterIP
      ingressClassName: traefik

Но следует учесть, что для работы этого способа домен должен быть доступен из внешней сети. Либо необходимо использовать отдельный сервис, который сможет подтвердить право на владение ресурсом.

Применяем параметры через команду:

kubectl apply -f cert-manager-issuer.yml

Теперь настраиваем файл Ingress на примере сервиса Portainer. YAML-файлы для Ingress удобно хранить в отдельной папке, например /home/ms/ingress. В качестве ReverseProxy используется Traefik, так как он был установлен по умолчанию при развертывании k3s.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: portainer-ingress
  namespace: default
  labels:
    app: portainer
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    # указываем название созданного ранее ClusterIssuer
    cert-manager.io/cluster-issuer: letsencrypt-prod-issuer
spec:
  rules:
    - host: portainer.EXAMPLE.COM
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: portainer
                port:
                  number: 9000
  ingressClassName: traefik
  tls:
    - hosts:
        - portainer.EXAMPLE.COM
      secretName: portainer-ssl

Применяем параметры Ingress:

kubectl apply -f portainer-ingress.yml

И вишенкой на торте будет файл, который будет перенаправлять все http запросы к ingress на https. Для этого создадим middleware файл в папке home/ms/middlewares и назовем его traefik-https-redirect-middleware.yaml. Теперь наполним содержимое файла следующими строками:

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: redirect-https
spec:
  redirectScheme:
    scheme: https
    permanent: true

И применяем конфигурацию

kubectl apply -f traefik-https-redirect-middleware.yaml

На этом все. После применения настроек Ingress cert-manager автоматически их найдет и начнет процесс выдачи сертификата для указанного домена в файле Ingress в секции tls.hosts.