Недавно стал использовать новый способ получения 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, и так даже правильнее. Но это уже совсем другая история…