Применение Let's Encrypt для автоматизации HTTPS в кластерах Kubernetes на Raspberry Pi

Автоматическая генерация сертификатов  —  на первый взгляд, нечто прямиком из мира крупных корпораций, и потому вне досягаемости обычного разработчика, который размещает контент в кластере Kubernetes, запущенном на Raspberry Pi. К счастью, автоматическая генерация сертификатов стала на редкость доступной благодаря Let’s Encrypt. И что лучше всего  —  любой владелец доменного имени может получить сертификат совершенно бесплатно.

В этом руководстве мы воспользуемся Let’s Encrypt для автоматического предоставления сертификатов ресурсам Ingress так, чтобы приложения были доступны по сети через HTTPS.

Предварительные требования

1. Кластер Raspberry Pi.

2. К3, запущенный в кластере Raspberry Pi.

3. Traefik в качестве провайдера для Kubernetes Ingress.

4. Доменное имя. Вы можете создать домен, к примеру, на godaddy.com.

Как работает Let’s Encrypt

Let’s Encrypt  —  это источник сертификатов (certificate authority, CA), который внедряет протокол ACME и позволяет конфигурировать HTTPS-сервер без вмешательства человека. Здесь мы настроим HTTPS-сервер для домена https://cloud-tack.com, а вы замените его на свой домен. Говоря точнее, HTTPS-сервер в этом руководстве  —  это Traefik Ingress Controller, который запущен как контейнер внутри K3 на Raspberry Pi.

Выдача сертификата проводится в два этапа. На первом этапе агент, который намерен запросить сертификат, должен доказать источнику сертификатов, что владеет доменом, для которого он выдается. Этот процесс называется валидация домена. Только после того, как агент предоставит доказательства, можно запрашивать, обновлять и отзывать сертификат. Агент в контексте этого руководства  —  приложение-менеджер сертификатов (cert-manager), развернутое внутри пространства имен менеджера сертификатов (подробнее об этом далее в статье).

Агент инициирует процесс, запрашивая у источника сертификатов, как именно ему доказать владение доменом. Источник сертификатов отвечает набором вызовов, и агент должен исполнить один из вызовов в качестве доказательства. В этом гайде агент исполнит вызов HTTP01.

Агент разместит HTTP-файл по указанному URI. Далее источник сертификатов проверит, доступен ли этот файл, содержит ли необходимый контент и правильную сигнатуру. Для этого он скачает файл по указанному URI (например, http://cloud-tack.com/8303). Если мы не владеем доменом, то не можем разместить файл в нужном месте, с правильным контентом и сигнатурой. Следовательно, это  —  доказательство владения.

Менеджер сертификатов отвечает за создание этого файла и, вместе с ним, временного Ingress таким образом, чтобы http://cloud-tack.com/8303 перенаправлял трафик к файлу, и источник сертификатов мог скачать его. Только после того, как владение доменом доказано, агент может подавать запросы на выдачу, обновление и отзыв сертификата.

Сертификат, выданный Let’s Encrypt, хранится в качестве секрета Kubernetes. Когда этот секрет применяется к ресурсу Ingress для домена cloud-tack.com, ресурс cloud-tack.com становится доступен через HTTPS.

Сконфигурируйте сеть

Локальный маршрутизатор должен перенаправить порты HTTP (80) и HTTPS (443) по умолчанию на порты узлов Traefik для HTTP и HTTPS соответственно.

Создайте порты узлов Traefik

HTTP-трафик доступен через порт 30179, а HTTPS-трафик  —  через 30180, как сконфигурировано в следующем манифесте:

kubectl create -f traefik-svc-http-https.yaml

Содержимое traefik-svc-http-https.yaml:

apiVersion: v1
kind: Service
metadata:
  name: traefik
  namespace: kube-system
  labels:
    app: traefik
spec:
  type: NodePort
  ports:
    - name: traefik-https
      port: 443
      nodePort: 30180
      targetPort: 443
    - name: traefik-http
      port: 80
      nodePort: 30179
      targetPort: 80
  selector:
    app: traefik

Пробросьте порты HTTP и HTTPS по умолчанию на ваш маршрутизатор

Следующие скриншоты получены с маршрутизатора Verizon Fios. Интерфейс каждого маршрутизатора немного различается, но все они позволяют конфигурировать проброс портов.

После входа в маршрутизатор по адресу 192.168.1.1 перейдите на страницу переброса портов (как указано в инструкции вашего маршрутизатора). На рисунке 1 в красной рамке указан IP-адрес главного узла Kubernetes. В синей рамке выбран HTTP-порт по умолчанию (80). Наконец, зеленая рамка указывает, на какой порт должен по умолчанию направляться трафик HTTP. Это значение соответствует порту узла traefik-http из файла traefik-svc-http-https.yaml. Чтобы указать целевой порт для HTTP и HTTPS по умолчанию, вам, возможно, придется открыть дополнительные параметры. Нажмите кнопку “Add”, чтобы добавить конфигурацию HTTP.

Рисунок 1. Проброс портов по умолчанию для HTTP

Выполните ту же последовательность операций, что и для HTTP, но измените значения для порта HTTPS по умолчанию, как показано на рисунке 2.

Рисунок 2. Проброс портов по умолчанию для HTTPS

Проброс HTTP-порта позволяет cert-manager и Let’s Encrypt завершить задачи проверки домена и выдачи. Трафик для HTTPS-доменов будет проходить через проброшенный порт HTTPS.

Настройка DNS

Домен, для которого вы собираетесь создать сертификат, должен указывать на общедоступный IP-адрес вашего маршрутизатора. Например, cloud-tack.com -> 71.105.198.177. Мы создадим DNS-запись A, которая свяжет наш домен с этим IP. Запись A  —  это простой DNS-ресурс, который работает как псевдоним. Все запросы на cloud-tack.com “под капотом” будут разрешаться на IP-адрес 71.105.198.177.

Определите общедоступный IP-адрес вашего маршрутизатора, перейдя на whatsmyip.org. С помощью этого IP-адреса мы создадим запись A. Я создал домен cloud-tack.com через GoDaddy, поэтому интерфейс для создания записи A будет отличаться от вашего, если у вас другой поставщик. В синей рамке на рисунке 3 видно, что запись типа A указывает на общедоступный IP-адрес моего маршрутизатора 71.105.198.177. Убедитесь, что это единственная запись A в списке и что в вашем домене нет никакой пользовательской переадресации.

Рисунок 3. Запись для cloud-tack.com

Убедитесь, что DNS настроен правильно, запустив поиск по домену. Ответом должен быть общедоступный IP-адрес вашего маршрутизатора:

$ dig +short cloud-tack.com
71.105.198.177

Теперь, когда запись A создана, HTTP-трафик в вашем домене будет перенаправляться на порт 80 маршрутизатора. Аналогично, трафик HTTPS будет перенаправляться на порт 443. Затем ваш маршрутизатор перенаправит трафик с этих портов в Traefik Ingress Controller на порты 30179 и 30180 соответственно.

Развертывание диспетчера сертификатов

Для шифрования требуется агент, который запускает программное обеспечение, способное выполнять задачи проверки домена, а также выдачу, продление и отзыв сертификатов. С небольшими корректировками для работы на Raspberry Pi мы будем следовать руководству Kubernetes по развертыванию агента cert-manager.

Подготовка манифеста

Загрузите манифест, предоставленный Jet stack для cert-manager, и замените каждый образ cert-manager тем, что совместим с ARM. Имейте в виду, что загружаемый вами манифест зависит от версии Kubernetes.

# Kubernetes 1.16+
$ curl -sL https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml | \
sed -r 's/(image:.*quay.io\/jetstack\/cert-manager-.*):(.*)$/\1-arm:\2/g' > cert-manager.yaml

# Kubernetes <1.16
$ curl -sL https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager-legacy.yaml | \
sed -r 's/(image:.*quay.io\/jetstack\/cert-manager-.*):(.*)$/\1-arm:\2/g' > cert-manager.yaml

Настройка менеджера сертификатов

Создайте пространство имен cert-manager:

$ kubectl create namespace cert-manager

Примените cert-manager на кластере:

$ kubectl apply -f cert-manager.yaml

Верифицируйте установку cert-manager:

$ kubectl get pods --namespace cert-manager
NAME                              READY   STATUS    RESTARTS   AGE
cert-manager-84f968b6f9-jm9rd     1/1     Running   0          52s
cert-manager-cainjector-64cdd465  1/1     Running   0          52s
cert-manager-webhook-57b8c5f965   1/1     Running   0          52s

Создайте ClusterIssuer ACME

Издатель ACME представляет собой учетную запись, зарегистрированную на сервере центра сертификации среды автоматического управления сертификатами. Эта учетная запись однозначно идентифицируется парой из открытого и закрытого ключей и необходима для выдачи сертификатов. Как упоминалось выше, для верификации домена агенту необходимо решать задачи. Здесь будет приведен пример манифеста ClusterIssuer, который решает задачу с HTTP01.

Примените ClusterIssuer к своему кластеру:

$ kubectl apply -f staging-issuer.yaml

Обязательно измените адрес электронной почты в строке 7:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: acme-staging
spec:
  acme:
    email: [email protected] # Замените на свой email
    server: https://acme-staging-v02.api.letsencrypt.org/directory # staging
    privateKeySecretRef:
      name: acme-staging
    solvers:
    - http01:
        ingress:
          class: traefik

Проверьте, что ClusterIssuer настроен правильно:

$ kubectl get clusterissuers
NAME           READY   AGE
acme-staging   True    10s

Создайте тестовый сертификат

Кластеризатор, который мы применили, будет ориентирован на непродуктовую среду Let’s Encrypt. Эта среда более скоростная, так что во время отладки вы можете выдавать много сертификатов и не получить блокировку. Как только мы убедимся, что можем создать сертификат в этой среде, то нацелимся на продакшн.

Примените сертификат к своему кластеру:

$ kubectl apply -f test-acme-certificate.yaml

Содержимое test-acme-certificate.yaml:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: cloud-tack-com # Замените на свой домен
  namespace: default
spec:
  secretName: cloud-tack-com-tls # Замените на tls своего домена
  issuerRef:
    name: acme-staging
    kind: ClusterIssuer
  commonName: cloud-tack.com # Замените на свой домен
  dnsNames:
  - cloud-tack.com # Замените на свой домен

Убедитесь, что сертификат создан правильно:

$ kubectl get certificate
NAME             READY   SECRET               AGE
cloud-tack-com   True    cloud-tack-com-tls   13s

Если статус остается False, вам придется выполнить отладку. Например, убедитесь, что ваш домен может успешно перенаправлять интернет-трафик HTTP-запросов приложению, запущенному в вашем кластере. Самый быстрый способ  —  перенаправить трафик в Traefik по неизвестному пути (к примеру, http://your-domain.com/test-http-traffic). При правильном пробросе портов ответ должен быть “404  —  страница не найдена”. Если вы получаете такой ответ и все еще не можете предоставить тестовый сертификат, потребуется дальнейшая отладка.

Удалите промежуточные ресурсы

Тестовые ресурсы были нужны только для проверки настроек. Уже сейчас можно их удалить.

Удалите сертификат:

$ kubectl delete certificate cloud-tack-com
certificate.cert-manager.io "cloud-tack-com" deleted

Удалите секрет:

$ kubectl delete secret cloud-tack-com-tls
secret "cloud-tack-com-tls" deleted

Удалите ClusterIssuer:

$ kubectl delete -f staging-issuer.yaml

Примените ClusterIssuer для продакшена

Теперь, когда мы знаем, что Let’s Encrypt может выдавать сертификаты, а cert-manager способен решать задачи, можно переходить к настройке Let’s Encrypt на продакшене.

$ kubectl apply -f prod-issuer.yaml

Обязательно измените адрес электронной почты в строке 7:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: acme-prod
spec:
  acme:
    email: [email protected] # Замените на свой email
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: acme-prod
    solvers:
    - http01:
        ingress:
          class: traefik

Убедитесь, что издатель готов к обслуживанию:

$ kubectl get clusterissuers
NAME        READY   AGE
acme-prod   True    20s

Отлично! Ваш кластер готов автоматически предоставлять сертификаты ресурсам Ingress. 

Настройте Ingress Resource для HTTPS

Следующий манифест запускает Ingress для приложения fma-ui, которое работает в кластере-примере. Вам нужно будет развернуть свое собственное приложение и настроить для него Ingress.

$ kubectl apply -f ingress-https.yaml

Ресурс Ingress предоставляет дополнительный раздел конфигурации для шифрования. Следующий манифест  —  пример ресурса Ingress, который настраивает сертификат для домена cloud-tack.com. Если задействован Ingress, а секрет сертификата не существует, то для домена, указанного в строке 19, будет автоматически создан сертификат, а секрет получит имя из строки 20. Измените значения в строках 4, 12–16 и 19–20 этого манифеста, чтобы они соответствовали вашим потребностям. Другие строки менять не нужно!

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: fma-ui
  annotations:
    kubernetes.io/ingress.class: traefik
    cert-manager.io/cluster-issuer: acme-prod
spec:
  rules:
  - http:
      paths:
      - path: /fma-ui
        pathType: Prefix
        backend:
          serviceName: fma-ui
          servicePort: 9090
  tls:
  - hosts:
    - cloud-tack.com
    secretName: cloud-tack-com-tls

Убедитесь, что сертификат создан:

$ kubectl get certificates
NAME                 READY   SECRET               AGE
cloud-tack-com-tls   True    cloud-tack-com-tls   20s

Вот и все! Теперь вы можете пользоваться URL-адресом https://cloud-tack.com/fma-ui. Этот сертификат действителен в течение 90 дней, после чего менеджер сертификатов автоматически продлит его действие.

Заключение

Настройка HTTPS значительно улучшит кластер Raspberry Pi. С помощью HTTPS вы сможете безопасно и профессионально разместить сайт в интернете.

Мы автоматизировали HTTPS для ресурсов Kubernetes Ingress, развернув менеджер сертификатов и кластеризатор, которые могут взаимодействовать с Let’s Encrypt и Traefik. Эти сертификаты предоставляются бесплатно и автоматически продляются по истечении срока действия.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи: Jonathan Scott: Use Let’s Encrypt to Automate HTTPS for Your Kubernetes Cluster on Raspberry Pi

Предыдущая статьяПрограмма на C++ для перестановки цифр числа в обратном порядке
Следующая статьяPHP 8.1 уже обещает стать одним из лучших релизов