Использование Traefik и cert-manager для обслуживания защищенного сайта

Из этого туториала вы узнаете, как установить Traefik поверх нового кластера kubernetes. С помощью простого сервиса nginx будут показаны различные решения о том, как обслуживать страницу с помощью самозаверяющего сертификата, зашифрованного промежуточного и производственного сертификата. Все сертификат вещь сделана с CERT-менеджером .
Вы должны быть знакомы с основами использования kubernetes и иметь под рукой работающий кластер k8s.
Предпосылки
  • Работающий кластер kubernetes (создайте свой собственный с этим руководством )
  • Плавающий IP-адрес, указывающий на ваш кластер или общедоступный IP-адрес одного из ваших узлов ( <10.0.0.1>в этом учебном пособии <>они не являются частью IP-адреса)
  • Знакомство с Linux и работа на оболочке
  • kubectl инструмент командной строки установлен
  • Адрес электронной почты для использования с Let's Encrypt , этот учебник использует:mail@example.com
рекомендуемые
  • Знакомство с понятиями Кубернетеса
  • Действительный домен или поддомен с доступом к настройке DNS.
* Это руководство было протестировано на сервере Ubuntu 18.04 Hetzner Cloud и версиях Kubernetes v1.15.7 и v1.16.4, Ubuntu 18.04 использовалась в качестве локальной машины.
Все шаги могут быть выполнены как есть на машине, где kubectlустановлен. Большинство шагов используют запись heredoc для предотвращения создания файла конфигурации. Используемые сервисы kubernetes будут использовать другой синтаксис, где это возможно. Это покажет вам разные возможности.

Шаг 1 - Разверните Traefik в качестве входного контроллера

Следующая настройка основана на официальных инструкциях по настройке .

Шаг 1.1 - Создайте необходимые роли RBAC

Traefik необходимо настроить некоторые роли kubernetes, прежде чем можно будет выполнить настройку службы.
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/traefik-rbac.yaml

Шаг 1.2 - Настройка Traefik

Как указано в документации, traefik может быть установлен как Controllerили DaemonSet. В этом уроке мы используем DaemonSetразвертывание. Если у вас есть кластер, состоящий из нескольких узлов (мастер или рабочие), на каждом узле будет создан ровно один модуль traefik без дополнительной настройки.
!!! Если у вас есть только один кластер узлов, позаботьтесь о том, чтобы испортить мастер-узел для приема модулей:kubectl taint nodes --all node-role.kubernetes.io/master-
Вы можете определить конфигурацию traefik с помощью флагов cli или файла toml. TOML-файл легче обрабатывать для сложных настроек. Из-за простоты в этом руководстве используются флаги cli.
!!! Конфиг использует type LoadBalancerвместо NodePort. Это должно работать для установок с голым металлом, metallbкак в этом уроке . Если вы используете облачного провайдера с внешним балансировщиком нагрузки, вы можете использовать NodePortвместо этого.
!!! Используемая конфигурация traefik не готова к производству! Вы должны использовать базовую авторизацию http для защиты панели в сочетании с автоматическим перенаправлением https. Для производственной среды также рекомендуется настроить traefik в отдельном пространстве имен.
cat << EOF | kubectl apply -f -
---
apiVersion: v1
kind: ServiceAccount
metadаta:
  name: traefik-ingress-controller
  namespace: kube-system
---
kind: DaemonSet
apiVersion: extensions/v1beta1
metadаta:
  name: traefik-ingress-controller
  namespace: kube-system
  labels:
    k8s-app: traefik-ingress-lb
spec:
  template:
    metadаta:
      labels:
        k8s-app: traefik-ingress-lb
        name: traefik-ingress-lb
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      containers:
      - image: traefik:v1.7
        name: traefik-ingress-lb
        ports:
        - name: http
          containerPort: 80
          hostPort: 80
        - name: https
          containerPort: 443
          hostPort: 443
        - name: dashboard
          containerPort: 8080
          hostPort: 8080
        securityContext:
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
        args:
        - --api
        - --accesslog
        - --logLevel=INFO
        - --kubernetes
        - --defaultentrypoints=http,https
        - --entrypoints=Name:https Address::443 TLS
        - --entrypoints=Name:http Address::80
---
kind: Service
apiVersion: v1
metadаta:
  name: traefik-ingress-service
  namespace: kube-system
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: http
    - protocol: TCP
      port: 443
      name: https
    - protocol: TCP
      port: 8080
      name: dashboard
  type: LoadBalancer
EOF
Приведенная выше конфигурация публикует панель мониторинга Traefik для порта 8080и публикует порты по умолчанию, используемые для трафика HTTP: 80и 443.

Шаг 1.3 - Подтвердите настройку

Проверьте, все ли стручки Running.
kubectl -n kube-system get pod
# Output:
NAME                               READY   STATUS    RESTARTS   AGE
coredns-5d4dd4b4db-frkpb           1/1     Running   0          12m
coredns-5d4dd4b4db-t8z2s           1/1     Running   0          12m
etcd-k1                            1/1     Running   1          11m
kube-apiserver-k1                  1/1     Running   1          6m49s
kube-controller-manager-k1         1/1     Running   1          11m
kube-flannel-ds-amd64-hbk58        1/1     Running   0          5m47s
kube-proxy-dmng9                   1/1     Running   1          12m
kube-scheduler-k1                  1/1     Running   1          12m
traefik-ingress-controller-z8xdn   1/1     Running   0          36s
Получить EXTERNAL-IPназначенный на traefik-ingress-service.
kubectl -n kube-system get service
# Output:
NAME                      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                                                    AGE
kube-dns                  ClusterIP      10.96.0.10     <none>        53/UDP,53/TCP,9153/TCP                                     16m
traefik-ingress-service   LoadBalancer   10.109.23.93   10.0.0.1    80:31681/TCP,443:31856/TCP,8080:32684/TCP                  4m23s
Вы также можете показать журналы для модуля traefik, используя его метку.
  • журналы для конкретного модуля
    kubectl -n kube-system logs -f $(kubectl -n kube-system get pods -l k8s-app=traefik-ingress-lb  -o jsonpath='{.items[0].metadata.name}')
  • журналы для всех модулей, полезно, если у вас есть несколько узлов
    kubectl -n kube-system logs -f -l k8s-app=traefik-ingress-lb

Шаг 1.4 - Открыть панель инструментов Traefik

Созданный Serviceвами, должен был получить EXTERNAL-IPсервер. Это должен совпадать с нашим плавающим IP-адресом или общедоступным IP-адресом сервера, в зависимости от настроек. А LoadBalancerсервис связывается с каким - то случайным портом (ы) , как вы видите в колонке PORT(S). Знание порта и IP - все, что вам нужно, чтобы открыть панель инструментов ( http://<10.0.0.1>:32684).

Шаг 2 - хост простой сервис nginx

На следующем этапе мы развернем простой сервис nginx и опубликуем его в вашем домене.

Шаг 2.1 - Подготовьте домен

Если у вас есть собственный домен (в этом руководстве:) <example.com>, позаботьтесь о том, чтобы A-Recordв настройках DNS создать плавающий IP-адрес вашего кластера ( <10.0.0.1>) или публичный IP-адрес сервера. В зависимости от TTL вашего провайдера (Время жизни в секундах), распространение DNS может занять некоторое время. Поскольку DNS вашей локальной системы не будет обновляться так часто, как настройки сервера, рекомендуется также выполнить проверку на сервере.
Вы можете проверить, указывает ли ваш домен правильный IP с помощью:
host <example.com>
# Outputs:
example.com has address 10.0.0.1
Сохраните ваш домен как переменную среды для дальнейшего использования:
DOMAIN=<example.com>

Шаг 2.2 - Развертывание службы nginx

Теперь мы будем использовать настроенные DOMAINпрямо внутри конфига. Как видите, сервис будет создан в отдельном пространстве имен. Это рекомендуемый способ.
cat << EOF | kubectl apply -f -
---
apiVersion: v1
kind: Namespace
metadаta:
  name: testing
spec:
  finalizers:
  - kubernetes
status:
  phase: Active
---
apiVersion: apps/v1beta2
kind: Deployment
metadаta:
  name: nginx
  namespace: testing
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadаta:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - name: http
          containerPort: 80

---
apiVersion: v1
kind: Service
metadаta:
  name: nginx
  namespace: testing
spec:
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    targetPort: 80
  type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadаta:
  annotations:
    kubernetes.io/ingress.class: traefik
    traefik.ingress.kubernetes.io/rule-type: "PathPrefixStrip"
  name: nginx
  namespace: testing
spec:
  rules:
  - host: ${DOMAIN}
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80
EOF
Убедитесь, что модуль nginx создан и Running.
kubectl -n testing get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-74f6bc9c7c-bvjh5   1/1     Running   0          9s
После применения изменений, откройте <example.com>в вашем браузере. Вы должны увидеть очень простую HTML-страницу сейчас.
Если вы откроете журнал pods и перезагрузите домен в браузере, вы увидите запросы в журналах nginx.
kubectl -n testing logs -f $(kubectl -n testing get pods -l app=nginx -o jsonpath='{.items[0].metadata.name}')

Let's Encrypt это очень простая задача. Нет более сложной обработки сертификатов.
Traefik может использовать Let's Encrypt самостоятельно, но это не рекомендуемый способ, и в некоторых случаях может произойти сбой. Гораздо лучшим способом (в смысле do-one-thing-and-to-it-right) является использование cert-manager.

Шаг 3.1 - Настройка cert-менеджера

  • Официальные документы: https://cert-manager.io/docs/usage/ingress/
kubectl create namespace cert-manager
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml
Через несколько секунд все капсулы должны быть в RUNNINGсостоянии.
!!! Команда watchне является специальной командой kubernetes. Это просто стандартная команда Unix, которая выполняет следующую команду каждую секунду.
watch kubectl -n cert-manager get pods
#Outputs:
NAME                                      READY   STATUS    RESTARTS   AGE
cert-manager-66c8bc8b67-5qcb4             1/1     Running   0          24s
cert-manager-cainjector-df4dc78cd-s56zv   1/1     Running   0          24s
cert-manager-webhook-5f78ff89bc-hr9ck     1/1     Running   0          24s
cert-managerтеперь готов к использованию. На следующих этапах мы опробуем различные способы создания готовых к тестированию и производству сертификатов.

Шаг 3.2 - Создание самозаверяющего сертификата

В некоторых случаях, например, для внутреннего или тестового использования, вы можете легко создать самозаверяющий сертификат cert-manager. Чтобы настроить сертификат с cert-managerвсегда нужно IssuerClusterIssuerи CertificateRessource.
cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadаta:
  name: selfsigned
  namespace: testing
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadаta:
  name: ${DOMAIN}-cert
  namespace: testing
spec:
  commonName: ${DOMAIN}
  secretName: ${DOMAIN}-cert
  issuerRef:
    name: selfsigned
EOF
Вы можете проверить статус Certificateочень легко.
  • короткий статус
    kubectl -n testing get certificate
    # Outputs:
    10.0.0.1.example.com-cert   True    10.0.0.1.example.com-cert   47s
  • полная информация и статус
    kubectl -n testing describe certificate <10.0.0.1>.example.com-cert
    # Outputs:
    ...
    Events:
    Type    Reason        Age   From          Message
    ----    ------        ----  ----          -------
    Normal  GeneratedKey  94s   cert-manager  Generated a new private key
    Normal  Requested     94s   cert-manager  Created new CertificateRequest resource "10.0.0.1.example.com-cert-2241091300"
    Normal  Issued        94s   cert-manager  Certificate issued successfully
Наш сервис nginx сейчас не знает о сертификате. Переконфигурируйте, Ingressчтобы изменить это.
cat << EOF | kubectl apply -f -
---
apiVersion: extensions/v1beta1
kind: Ingress
metadаta:
  annotations:
    kubernetes.io/ingress.class: traefik
    traefik.ingress.kubernetes.io/rule-type: "PathPrefixStrip"
  name: nginx
  namespace: testing
spec:
  rules:
  - host: ${DOMAIN}
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80
  tls:
  - hosts:
    - ${DOMAIN}
    secretName: ${DOMAIN}-cert
EOF
Если вы открываете https://<example.com>в своем браузере, вы должны сначала принять сертификат. Это связано с тем, что ваш сертификат не подписан официальным доверенным центром сертификации (CA). После добавления исключения вы должны увидеть страницу nginx по умолчанию, но защищенную https.

Викимедиа )
Процесс создания Let`s Encrypt очень прост:

  • проверьте, указывает ли запрашиваемый домен на тот же IP-адрес, с которого поступил запрос
  • проверьте наличие некоторых специальных файлов на указывающем сервере
  • если одно из условий не выполнено, отклонить запрос
Промежуточный сертификат предназначен только для тестирования и должен приниматься вручную, как самоподписанный сертификат. Убедитесь, что ваш домен настроен правильно! Пожалуйста, замените электронную почту на правильный почтовый адрес!

эмитент

Чтобы получить сертификат от Let's Encrypt, вам нужно настроить Issuerили ClusterIssuer. A Issuerявляется действительным для текущего пространства имен, a ClusterIssuerне зависит от пространства имен.
# Please replace mail with your mail
YOUR_MAIL_ADDRESS=mail@example.com

cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadаta:
  name: letsencrypt-staging
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: ${YOUR_MAIL_ADDRESS}
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource used to store the account's private key.
      name: staging-issuer-account-key
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
    - http01:
        ingress:
          class: traefik
EOF
Получить статус с describeили простой статус с get.
# check status
kubectl describe clusterissuer letsencrypt-staging
# Outputs:
Status:
  Acme:
    Last Registered Email:  mail@example.com
    Uri:                    https://acme-staging-v02.api.letsencrypt.org/acme/acct/12082973
  Conditions:
    Last Transition Time:  2020-01-13T15:40:13Z
    Message:               The ACME account was registered with the ACME server
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>

сертификат

Позаботьтесь о правильной настройке своего домена, как описано в разделе Step 2.
Содержание сертификатов хранится в секрете. Чтобы создать новый сертификат, мы должны удалить этот секрет. Также удалите сертификат, иначе cert-managerвы мгновенно создадите новый. Вы должны добавить метку к своему входу nginx cert-manager.io/cluster-issuer: letsencrypt-staging. В противном случае cert-managerне знает, какой эмитент использовать.
kubectl -n testing delete certificate "${DOMAIN}-cert"
kubectl -n testing delete secret "${DOMAIN}-cert"
cat << EOF | kubectl apply -f -
---
apiVersion: extensions/v1beta1
kind: Ingress
metadаta:
  annotations:
    kubernetes.io/ingress.class: traefik
    traefik.ingress.kubernetes.io/rule-type: "PathPrefixStrip"
    cert-manager.io/cluster-issuer: letsencrypt-staging
  name: nginx
  namespace: testing
spec:
  rules:
  - host: ${DOMAIN}
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80
  tls:
  - hosts:
    - ${DOMAIN}
    secretName: ${DOMAIN}-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadаta:
  name: ${DOMAIN}-cert
  namespace: testing
spec:
  commonName: ${DOMAIN}
  secretName: ${DOMAIN}-cert
  issuerRef:
    name: letsencrypt-staging
EOF
Запрос занимает около 30 секунд. Вы можете постоянно проверять статус с помощью команды bash watch.
watch kubectl -n testing describe certificate "${DOMAIN}-cert"
# Output
Status:
  Conditions:
    Last Transition Time:  2020-01-13T16:06:33Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2020-04-12T15:06:33Z
Events:
  Type    Reason        Age    From          Message
  ----    ------        ----   ----          -------
  Normal  GeneratedKey  2m26s  cert-manager  Generated a new private key
  Normal  Requested     2m26s  cert-manager  Created new CertificateRequest resource "10.0.0.1.xip.io-cert-4183194560"
  Normal  Issued        2m     cert-manager  Certificate issued successfully
cert-managerвыводит много материала во время этой операции. Если что-то идет не так (например, нет сертификата через минуту или две), вы можете проверить журналы с помощью:
  • статический журнал
    kubectl -n cert-manager logs $(kubectl -n cert-manager get pod -l app=cert-manager -o jsonpath='{.items[0].metadata.name}')
  • живой журнал
    kubectl -n cert-manager logs -f --tail 20 $(kubectl -n cert-manager get pod -l app=cert-manager -o jsonpath='{.items[0].metadata.name}')

Открыть в браузере

Если вы откроете или перезагрузите https://example.comили https://$DOMAINв своем браузере, сертификат не будет доверенным, сопоставимым с самозаверяющим сертификатом. Имя отличается, теперь имя эмитента Fake LE Intermediate X1вместо cert-manager.

Шаг 3.4 - Создание производственного сертификата

Если на последних этапах все работало хорошо, вы можете продолжить получать готовый сертификат Let's Encrypt. Производственный сертификат подписан официальным центром сертификации и будет надежно использоваться всеми распространенными браузерами (например, Chrome / Chromium, Firefox, IE, Edge, Safari, Opera). Полученный сертификат действителен в течение 90 дней. cert-manager обновляет сертификаты каждые 30 дней самостоятельно, поэтому срок действия сертификата никогда не истекает.
!!! Для того, чтобы заставить обновления, просто удалите секрет , содержащий сертификат: kubectl -n testing delete secret "${DOMAIN}-cert".
Если вы используете свой собственный домен, процесс должен работать.

эмитент

YOUR_MAIL_ADDRESS=mail@example.com

cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadаta:
  name: letsencrypt
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: ${YOUR_MAIL_ADDRESS}
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource used to store the account's private key.
      name: letsencrypt-issuer-account-key
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
    - http01:
        ingress:
          class: traefik
EOF
Проверь состояние
kubectl describe clusterissuer letsencrypt
# Outputs:
Status:
  Acme:
    Last Registered Email:  mail@example.com
    Uri:                    https://acme-v02.api.letsencrypt.org/acme/acct/75756861
  Conditions:
    Last Transition Time:  2020-01-13T17:08:07Z
    Message:               The ACME account was registered with the ACME server
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>

сертификат

Опять сбросьте Certificateи соответствующий Secret.
kubectl -n testing delete certificate "${DOMAIN}-cert"
kubectl -n testing delete secret "${DOMAIN}-cert"
DOMAIN=example.com

cat << EOF | kubectl apply -f -
---
apiVersion: extensions/v1beta1
kind: Ingress
metadаta:
  annotations:
    kubernetes.io/ingress.class: traefik
    traefik.ingress.kubernetes.io/rule-type: "PathPrefixStrip"
    cert-manager.io/cluster-issuer: letsencrypt
  name: nginx
  namespace: testing
spec:
  rules:
  - host: ${DOMAIN}
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80
  tls:
  - hosts:
    - ${DOMAIN}
    secretName: ${DOMAIN}-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadаta:
  name: ${DOMAIN}-cert
  namespace: testing
spec:
  commonName: ${DOMAIN}
  secretName: ${DOMAIN}-cert
  issuerRef:
    name: letsencrypt
EOF
Опять же, проверьте статус запроса и проверьте журналы cert-manager через минуту, если сертификат не отображается Status: True.
watch kubectl -n testing describe certificate "${DOMAIN}-cert"
# Output
Status:
  Conditions:
    Last Transition Time:  2020-01-13T16:06:33Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2020-04-12T15:06:33Z
Events:
  Type    Reason        Age    From          Message
  ----    ------        ----   ----          -------
  Normal  GeneratedKey  2m26s  cert-manager  Generated a new private key
  Normal  Requested     2m26s  cert-manager  Created new CertificateRequest resource "example.com-cert-4183194560"
  Normal  Issued        2m     cert-manager  Certificate issued successfully
kubectl -n cert-manager logs -f --tail 10 $(kubectl -n cert-manager get pod -l app=cert-manager -o jsonpath='{.items[0].metadata.name}')

Вывод

Из этого туториала вы узнаете, как настроить traefik в качестве входного контроллера в простом кластере kubernetes. Кроме того, вы должны иметь возможность защитить развернутый веб-сервис с помощью самозаверяющего сертификата TLS или сертификата Let's Encrypt.
!!! Используемая конфигурация traefik не готова к производству! Вы должны использовать (как минимум) базовую авторизацию http для защиты панели в сочетании с автоматическим перенаправлением https.

Добавить комментарий

Автору будет очень приятно узнать обратную связь о своем руководстве.

Комментариев 0