Блог Лаборатории

Настройка cert-manager, nginx-ingress и Let's Encrypt

Развертывание веб приложений в Kubernetes является одной из стандартных задач для бизнеса. В современной инфраструктуре использование сертификатов TLS и HTTPs протокола не является опциональным (например, с 2018 года браузер Chrome помечает все HTTP сайты как небезопасные). В этой статье мы рассмотрим как с помощью приложений с открытым исходным кодом абсолютно бесплатно получить сертификат для вашего приложения, автоматизировать весь процесс и обновление.
В нашем примере мы будем рассматривать следующие компоненты:
  1. Kubernetes кластер - подойдет любой кластер с доступом в Интернет и публичным IP-адресом
  2. ingress-nginx - их много вариантов, но мы возьмем тот, что активно поддерживается сообществом и Cloud Native Computing Foundation
  3. cert-manager - это оператор, который автоматизирует создание и управление сертификатами в k8s
  4. Let’s Encrypt - бесплатный, автоматизированный центр сертификации - выпускают TLS сертификаты

Установка компонентов

Веб сервис

Для начала задеплоим простейший Под с nginx и создадим для него Service объект:
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: my-test-app
spec:
  containers:
  - name: nginx
    image: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: my-test-app
  ports:
    - protocol: TCP
      port: 80
Это будет наше приложение, которое спрятано за сертификатами.

Ingress

Установим ingress-nginx. Проще всего делать это с helm:
helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace
Это установит ingress контроллер и выставит его во внешний мир через LoadBalancer сервис. Если у ваш кластер не поддерживает LoadBalancer, то есть другие варианты, которые вы можете посмотреть в документации.

cert-manager

Проще всего тоже взять команду из документации, так как версии могут отличаться. На момент написания статьи, стабильная версия 1.14.3:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.3/cert-manager.yaml

HTTP

Все готово к первым шагам. Начнем с того, что наше приложение будет доступно по HTTP через ingress. Для этого, создадим Ingress объект:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.allow-http: "true"
spec:
  ingressClassName: nginx
  rules:
  - host: mytest.l3k.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
Из основных параметров:
  1. ingressClassName: nginx - указывает, что мы хотим использовать именно ingress-nginx контроллер. Это нужно, если у вас в кластере есть и другие контроллеры. Например, так часто бывает в публичных облаках.
  2. - host: mytest.l3k.io - открывает секцию, которая указывает на Домен, который мы хотим выставить наружу.
  3. Секция backend: - говорит Ingress объекту, куда нужно отправлять запросы для этого сайта. Это наш сервис, который смотрит на Под nginx.
Посмотреть состояние Ingress можно следующим образом:

% kubectl get ingress
NAME           CLASS   HOSTS           ADDRESS          PORTS   AGE
test-ingress   nginx   mytest.l3k.io   12.123.123.123   80      5m1s
Можно проверить, все ли работает и увидеть стандартное приглашение nginx:
% curl -H "Host: mytest.l3k.io" http://12.123.123.123
…
<h1>Welcome to nginx!</h1>
…
Чтобы двигаться дальше, нужно добавить в DNS запись, которая направит нужный домен на IP-адрес ингресса. В моем случае, я добавлю A-запись:
mytest.l3k.io A 12.123.123.123
Теперь сайт откроется и в браузере.

HTTPs

Staging

cert-manager будет заниматься тем, что запросит для нас сертификат у Let’s Encrypt и создаст секрет в кубернетес кластере, который ingress будет использовать. У Let’s Encrypt очень жесткие лимиты по количеству подключений в минуту, поэтому для начала обкатываем все на staging.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-staging
  namespace: default
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: <YOUR_EMAIL> # поменять на ваш имейл
    privateKeySecretRef:
      name: letsencrypt-staging
    solvers:
    - http01:
        ingress:
          ingressClassName: nginx
Если попробовать открыть страницу, то сертификат будет невалидный, так как подписан staging CA Let’s Encrypt:
% curl -v --insecure https://mytest.l3k.io 
…
* Server certificate:
*  subject: CN=mytest.l3k.io
*  start date: Feb 23 11:56:07 2024 GMT
*  expire date: May 23 11:56:06 2024 GMT
*  issuer: C=US; O=(STAGING) Let's Encrypt; CN=(STAGING) Artificial Apricot R3

Production

Если все работает, то можно пробовать Let’s Encrypt production. Для этого нужно создать production Issuer:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-production
  namespace: default
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <YOUR_EMAIL>
    privateKeySecretRef:
      name: letsencrypt-production
    solvers:
    - http01:
        ingress:
          ingressClassName: nginx
Так же меняем Ingress, где указываем, что Issuer будет production:
% diff -u ingress-https-staging.yaml ingress-https-prod.yaml 
--- ingress-https-staging.yaml	2024-02-23 15:53:22
+++ ingress-https-prod.yaml	2024-02-23 18:03:02
@@ -5,7 +5,7 @@
   namespace: default
   annotations:
     kubernetes.io/ingress.allow-http: "true"
-    cert-manager.io/issuer: letsencrypt-staging
+    cert-manager.io/issuer: letsencrypt-production
     acme.cert-manager.io/http01-edit-in-place: "true" 
 spec:
   ingressClassName: nginx
Теперь у нас есть полностью валидный сертификат.

Wildcard

Wildcard дает возможность покрыть TLS сертификатами поддомены. Это удобно, если у вас приложение обслуживает несколько субдоменов сразу. Например:
  • abc.l3k.io
  • def.l3k.io
Для получения wildcard сертификата, небходима проверка DNS01. Можно почитать про это здесь. У cert-manager есть нативная интеграция со многими DNS провайдерами, например AWS Route 53 (список). К тому же есть поддержка вебхуков (webhooks). Вебхуки для всевозможных провайдеров можно найти в Гитхабе в поиске по cert-manager-webhook. Есть официальные проекты, например от Яндекса.
Свой вебхук тоже можно написать, используя пример отсюда. Мы используем name.com для домена l3k.io, который тоже поддерживается только хуками. Берем вот этот проект: https://github.com/imgrant/cert-manager-webhook-namecom/tree/main
Деплоим все по инструкции. В итоге мы создаем следующие ресурсы
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-namecom
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <YOUR_EMAIL>
    privateKeySecretRef:
      name: letsencrypt-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.name.com
          solverName: namedotcom
          config:
            username: <YOUR_LOGIN>
            apitokensecret:
              name: namedotcom-credentials
              key: api-token
  • Certificate - сам сертификат wild card. Это создаст сертификат в wild-cert.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: l3k-test
spec:
  dnsNames:
    - '*.l3k.io'
  issuerRef:
    name: letsencrypt-namecom
    kind: ClusterIssuer
  secretName: wild-cert
Теперь можно и поменять наш Ingress. Мы поменяем домен на *.l3k.io и будем ссылаться на сертификат напрямую:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.allow-http: "true"
spec:
  ingressClassName: nginx
  tls:
    - secretName: wild-cert 
      hosts:
        - '*.l3k.io'
  rules:
  - host: '*.l3k.io'
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
Теперь мы можем направить любой поддомен l3k.io на этот ingress, приложение уже само будет распределять трафик по субдоменам.

Заключение

В заключение, использование сертификатов TLS и протокола HTTPS в Kubernetes является критически важным аспектом для обеспечения безопасности и надежности веб-приложений. В этом посте мы обсудили, как можно легко и бесплатно развернуть сертификаты для вашего приложения, используя открытые и доступные инструменты, такие как ingress-nginx, cert-manager, и Let’s Encrypt.
Следуя этим шагам, вы не только обеспечите безопасность своего веб-приложения, но и автоматизируете процесс обновления сертификатов, снизив тем самым ручные усилия и вероятность возникновения ошибок. Это демонстрирует мощь и гибкость Kubernetes как платформы для развертывания современных веб-приложений и подчеркивает важность внедрения лучших практик безопасности с самого начала разработки.
Поможем с настройкой, проконсультируем и поддержим. Напишите нам.