Получение сертификатов в 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-сертификатов. Их задача — проверить, что пользователь, запросивший сертификат, является владельцем домена, на который запрашивается сертификат, и подтвердить его подлинность. Другими словами, эти организации выступают своего рода гарантами взаимосвязи домена и владельца, а в подтверждение выдают подписанный документ — сертификат. Приведу список известных центров сертификации:
- Let's Encrypt (который используется в статье)
- DigiCert
- Comodo
- GlobalSign
Как происходит обмен данными:
- Клиент (браузер) запрашивает соединение с сервером (например, при переходе на сайт).
- Сервер отправляет клиенту свой SSL-сертификат.
- Клиент проверяет сертификат (может проверить самостоятельно или обратиться к центру сертификации, который выдал документ):
- Доверен ли центр сертификации?
- Не истек ли срок действия сертификата?
- Соответствует ли доменное имя сертификата запрашиваемому сайту?
- Если проверка прошла успешно, клиент и сервер устанавливают безопасное соединение с использованием шифрования (например, с помощью алгоритмов RSA или ECDHE).
- После этого данные передаются в зашифрованном виде. Например, если вы вводите пароль на сайте, он будет зашифрован и расшифрован только на сервере.
Как получить SSL-сертификат
Есть несколько способов получения сертификата. Кратко рассмотрим их.
Создание самоподписных сертификатов
Это делается достаточно легко с помощью утилиты openssl
(Linux). Используя всего несколько команд, мы получаем два файла: сам сертификат и ключ. Их уже можно привязывать к веб-серверу. Весь процесс выдачи описывать не вижу смысла, так как по этому поводу есть множество статей, например эта.
Какие минусы у этого подхода:
- Придется прописывать сертификат в каждом браузере на каждом устройстве. Потому что мы будем являться CA, а весь мир не знает о существовании Иванова Ивана, который считает себя доверенным центром сертификации. Поэтому при входе на такой сайт браузер будет ругаться из-за невозможности проверить центр сертификации.
- Следить за валидностью и временем жизни сертификата придется самостоятельно. Можно сделать заметку в календаре, но нередко встречал ситуации в больших компаниях, когда забывали выпустить новые сертификаты, и работа вставала на полдня.
Использование утилиты 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):
- HTTP01 challenge — процесс, при котором на хосте, где запускается утилита, создается endpoint
{requested_domain}/.well-known/acme-challenge/{ID}
, который ссылается на содержимое файла, созданногоcertbot
, в котором хранится секретный ключ.certbot
отправляет callback-запрос на сторону Let's Encrypt. В свою очередь, Let's Encrypt должен вызвать наш домен с указанным адресом, чтобы получить и проверить секретный ключ. Если проверка пройдет успешно, то в ответ отправляется созданный сертификат и ключ. В этом случае необходимо, чтобы сервис, для которого выдается сертификат, был виден из глобальной сети. Либо нужно создать "сервис-заглушку", который не будет настоящим сервисом, а просто будет отдавать по указанному пути данные секретного ключа. Кстати, в статье, приведенной ранее, есть описание процесса, позволяющего сделать такой "сервис-заглушку". - DNS01 challenge — в этой ситуации нет необходимости, чтобы сервис, для которого запрашивается сертификат, был доступен из глобальной сети. Но здесь нужен доступ к PublicAPI сервиса, где был куплен искомый домен. При таком запросе мы должны указать логин, пароль или API-ключ от PublicAPI, чтобы
certbot
смог создать TXT-запись для запрашиваемого домена. После этого сервис Let's Encrypt обращается по указанному домену, чтобы прочитать ключ из созданной TXT-записи. После проверки мы также получаем сертификат и секретный ключ.
Использование certbot
может решить огромное количество проблем, но все же остаются минусы:
- Отслеживать время жизни сертификата все еще приходится самостоятельно. Начиная с января 2025 года, Let's Encrypt закрыла проект оповещения об окончании действия сертификата через email-рассылку, так как для них это стало дорого.
- Каждый раз необходимо вручную запрашивать новые сертификаты. Конечно, можно автоматизировать этот процесс через другие утилиты, но наша цель — интегрироваться в k3s-кластер, чтобы все работало "из коробки". Да, я ленивый.
Использование сервиса cert-manager
И вот мы добрались до решения, которое сейчас используется в моем кластере. Сервис cert-manager
берет на себя всю работу по выдаче, отслеживанию, обновлению и присвоению сертификатов. От пользователя требуется только настроить процесс выдачи и просто прописывать в Ingress новых сервисов указание использования TLS-соединения. И cert-manager
увидит создание нового Ingress с TLS, сразу получит валидный сертификат и присвоит его сервису.
Рассмотрим процесс установки и настройки. Для начала необходимо наличие кластера k3s или k8s (или другого Kubernetes). Установленная утилита helm
для управления Helm-чартами.
Для удобства я создал на машине с кластером папку /home/ms/helms
, где хранятся все values.yaml
и другие настройки Helm-чартов. Переходим в эту папку и начинаем выполнять команды:
Команды выше добавляют репозиторий jetstack
, в котором расположен необходимый нам чарт. Далее загружаем чарт cert-manager
с помощью команды pull
.
Проверяем, что архив был загружен, и разархивируем его. Необходимо обратить внимание на версию чарта: на момент написания статьи это версия 1.16.3
.
Я использовал 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
в кластер:
Для того чтобы использовать 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
Но следует учесть, что для работы этого способа домен должен быть доступен из внешней сети. Либо необходимо использовать отдельный сервис, который сможет подтвердить право на владение ресурсом.
Применяем параметры через команду:
Теперь настраиваем файл 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:
И вишенкой на торте будет файл, который будет перенаправлять все 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
И применяем конфигурацию
На этом все. После применения настроек Ingress cert-manager
автоматически их найдет и начнет процесс выдачи сертификата для указанного домена в файле Ingress в секции tls.hosts
.