company_banner

Что делать, если протухли сертификаты и кластер превратился в тыкву?

    Если в ответ на команду kubectl get pod вы получаете:

    Unable to connect to the server: x509: certificate has expired or is not yet valid
    

    то, скорее всего, прошел год, у сертификатов вашего kubernetes закончился срок действия, компоненты кластера перестали их использовать, взаимодействие между ними прекратилось и ваш кластер превратился в тыкву.

    image

    Что же делать и как восстановить кластер?

    Для начала нам надо понимать, где находятся сертификаты, которые необходимо обновить.

    В зависимости от способа, которым устанавливался кластер, расположение и название файлов с сертификатами может меняться. Так, например, Kubeadm при создании кластера раскладывает файлы с сертификатами согласно best-practices. Таким образом, все сертификаты находятся в каталоге /etc/kuberenetes/pki, в файлах с расширением .crt, приватные ключи, соответственно, — в файлах .key. Плюс в /etc/kubernetes/ лежат .conf файлы с конфигурацией доступа для user accounts администратора, контроллер менеджера, шедулера и kubelet с мастер-узла. Сертификаты в .conf файлах лежат в поле user.client-certificate-data в base64-кодированном виде.

    Посмотреть на срок действия, кому выписан и кем подписан сертификат, можно с помощью вот этого небольшого скрипта shcert

    shcert
    #!/bin/bash
    
    [ -f "$1" ] || exit
    
    if [[ $1 =~ \.(crt|pem)$ ]]; then
     openssl x509 -in "$1" -text -noout
    fi
    
    if [[ $1 =~ \.conf$ ]]; then
     certfile=$(mktemp)
     grep 'client-certificate-data:' "$1"| awk '{ print $2}' | base64 -d > "$certfile"
     openssl x509 -in "$certfile" -text -noout
     rm -f "$certfile"
    fi
    


    Еще есть сертификаты, которые используют kubelet на рабочих узлах для аутентификации в API. Если для добавления узлов в кластер вы использовали kubeadm join, то, скорее всего, узел был подключен по процедуре TLS bootstrapping и в таком случае kubelet умеет обновлять свой сертификат автоматически, если ему задана опция --rotate-certificates. В последних версиях kubernetes эта опция уже включена по умолчанию.
    Проверить, что узел подключен по процедуре TLS bootstrap достаточно просто — в этом случае в файле /etc/kubernetes/kubelet.conf в поле client-certificate обычно указан файл /var/lib/kubelet/pki/kubelet-client-current.pem, который является симлинком на текущий сертификат.

    Посмотреть сроки действия этого сертификата можно так же, с помощью скрипта shcert

    Возвращаемся к проблеме обновления сертификатов.

    Если вы ставили кластер с помощью kubeadm, то у меня для вас хорошие новости. Начиная с версии 1.15 kubeadm умеет обновлять почти все сертификаты control plane одной командой

    kubeadm alpha certs renew all

    Эта команда обновит все сертификаты в каталоге /etc/kubernetes, даже если они уже закончились и все сломалось.

    Не будет обновлен только сертификат kubelet — это тот, который лежит в файле /etc/kubernetes/kubelet.conf!
    Для обновления этого сертификата надо воспользоваться командой создания user account

    kubeadm alpha kubeconfig user --client-name system:node:kube.slurm.io --org system:nodes > /etc/kubernetes/kubelet.conf
    

    При наличии в системе user account эта команда обновляет сертификат для этого аккаунта. Не забываем указывать правильное название узла в опции --client-name, подсмотреть название узла можно в поле Subject существующего сертификата:

    shcert /etc/kubernetes/kubelet.conf

    И конечно же, после обновления сертификатов надо перезапустить все компоненты control plane, перезагрузив узел целиком или остановив контейнеры с etcd, api, controller-manager и scheduler командой docker stop, и затем отрестартить kubelet systemctl restart kubelet.

    Если ваш кластер старой версии: 1.13 или меньше — просто обновить kubeadm до 1.15 не получится, так как он тянет за собой по зависимостями kubelet и kubernetes-cni, что может вызывать проблемы, так как работоспособность компонентов кластера, различающихся по версиям более чем на одну ступень, не гарантируется. Самый простой выход из данной ситуации — это установить kubeadm на какой-нибудь другой машине, взять бинарный файл /usr/bin/kubeadm, скопировать на мастер-узлы умершего кластера и использовать его только для обновления сертификатов. А уже после оживления кластера обновить его пошагово штатными способами, устанавливая каждый раз kubeadm на одну версию новее.

    И наконец, с версии 1.15 kubeadm научился-таки продлевать все-все сертификаты при обновлении кластера командой kubeadm upgrade. Так что если вы регулярно обновляете свой кластер, не реже одного раза в год, ваши сертификаты всегда будут действительны.

    А вот если кластер установлен не с помощью kubeadm, то тогда придется взять в руки openssl и обновлять все сертификаты в индивидуальном порядке.

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

    Я приведу пример конфигурации для openssl, в отдельных секциях которого описываются расширенные атрибуты, свои для каждого типа сертификата. На соответствующую секцию будем ссылаться при создании и подписывании csr. Эта конфигурация использовалась для оживления кластера, установленного год назад ранчером.

    openssl.cnf
    [req]
    distinguished_name      = req_distinguished_name
    req_extensions  = v3_req
    
    [v3_req]
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
    
    [client]
    keyUsage = critical,digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
    
    [apiproxyclient]
    keyUsage = critical,digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth, serverAuth
    
    [etcd]
    keyUsage = critical,digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth, serverAuth
    subjectAltName = @alt_names
    
    [api]
    keyUsage = critical,digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth, serverAuth
    subjectAltName = @alt_names
    
    [alt_names]
    DNS.1 = ec2-us-east-1-1a-c1-master-2
    DNS.2 = ec2-us-east-1-1a-c1-master-3
    DNS.3 = ec2-us-east-1-1a-c1-master-1
    DNS.4 = localhost
    DNS.5 = kubernetes
    DNS.6 = kubernetes.default
    DNS.7 = kubernetes.default.svc
    DNS.8 = kubernetes.default.svc.cluster.local
    IP.1 = 10.0.0.109
    IP.2 = 10.0.0.159
    IP.3 = 10.0.0.236
    IP.4 = 127.0.0.1
    IP.5 = 10.43.0.1


    Актуальные атрибуты и дополнительные имена в сертификате можно посмотреть с помощью команды

    openssl x509 -in cert.crt -text
    

    При продлении сертификата для API сервера у меня возникала проблема: обновленный сертификат не работал. Решением стала выписка сертификата, который был действителен на 1 год в прошлом.

    В openssl нельзя простой командой выпустить сертификат, действительный в прошлом, в коде жестко указано, что сертификат действует только с текущего момента. Но можно локально переместиться в прошлое с помощью библиотеки libfaketime

    yum install libfaketime
    LD_PRELOAD=/usr/lib64/faketime/libfaketime.so.1 FAKETIME="-365d" openssl x509 -req ...

    Выпускаем продленные сертификаты по следующему алгоритму:

    Создаем CSR по существующему сертификату, указываем нужный раздел со списком расширенных аттрибутов в файле конфигурации:

    openssl x509 -x509toreq -in "node.cert" -out "node.csr" -signkey "node.key" -extfile "openssl.cnf" -extensions client

    Подписываем его соответствующим корневым сертификатом, смещая время на 1 год назад и указывая нужный раздел со списком расширенных аттрибутов в файле конфигурации

    LD_PRELOAD=/usr/lib64/faketime/libfaketime.so.1 FAKETIME="-365d" openssl x509 -req -days 36500 -in "node.csr" -CA "kube-ca.pem" -CAkey "kube-ca-key.pem" -CAcreateserial -out "node.new.cert" -extfile "openssl.cnf" -extensions client

    Проверяем атрибуты и перезапускаем компоненты control plane.

    Сергей Бондарев,
    преподаватель Слёрма
    slurm.io
    Southbridge
    721,13
    Обеспечиваем стабильную работу серверов
    Поделиться публикацией

    Комментарии 11

      +4

      Я считаю, что даже одноразовые вещи надо делать хорошо — пригодится :)


      #!/usr/bin/env bash
      
      showcert() {
       openssl x509 -text -noout
      }
      
      [ -f "$1" ] || exit 1
      
      case "$1" in
        *.crt|*.pem)
          showcert < "$1"
          ;;
        *.conf)
          awk '/client-certificate-data:/ { print $2 }' "$1" | base64 -d | showcert
          ;;
      esac
        +1
        в старых кластерах (на 1.9 отлично работает) можно не извращаться с протаскиванием в них нового kubeadm или играми с Openssl:
        kubeadm alpha phase certs front-proxy-client
        kubeadm alpha phase certs apiserver-kubelet-client
        kubeadm alpha phase certs apiserver
        kubeadm alpha phase kubeconfig all
          +1
          а он обновит срок сертификата?
          Просто в доке написано: If both files already exist, kubeadm skips the generation step and existing files will be used.
            0
            нужно будет сначала удалить то, что нужно перегенерить, или в отдельной папке это делать. там, в принципе, всё интуитивно. kubeadm будет ругаться на всё, что ему не нравится, нужно это устранять и запускать заново :)
            если нужно восстановить работу кластера здесь и сейчас, то путь через kubeadm займёт буквально пару минут.
          0
          Что делать, если протухли сертификаты и кластер превратился в тыкву?

          Положить новую мину, чтобы сработала на следующий год.
            +1
            позвольте процитировать документацию. вольный перевод
            с версии 1.15 kubeadm научился-таки продлевать все-все сертификаты при обновлении кластера командой kubeadm upgrade
            –3
            Не проще положить сертификат с лайфтаймом на лет 20-25 (за это время либо уволиться можно, либо пойти выше и головняк будет у других)? У меня такие самоподписанные используются.
              +2
              Надеюсь с таким подходом вас всё же уволят, а не повысят
                0
                проще. кубспрей так раньше и делал — генерировал сертификаты на 100 лет.
                в Kubeadm 1 год захардкожен — поэтому живем с тем, что есть.

                Насколько это влияет на безопасность в случае утечки ключей — вопрос холиварный и к статье отношения не имеет.
                0
                Не подскажете, как в этом плане обстоят дела с kops?
                  0
                  я с kops дела не имел, так что не знаю. Коллега давно уже с kops работал, уточню у него

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое