Kubernetes the hard way kube apiserver

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску

Kube-apiserver

Соглашение о расположении файлов

  • bin - /usr/local/bin/
  • конфиги, в том числе сертификаты - /etc/k8s/<имя сервиса> например /etc/k8s/kube-apiserver/
  • конфиги, в том числе сертификаты общие для нескольких сервисов - /etc/k8s/shared/

Загрузка

Общий для всех базовых компонентов скрипт

#!/bin/bash

VERSION="1.25.1"
ARCH="arm64"

mkdir -p k8s_${VERSION}
cd k8s_${VERSION}

wget -q --show-progress --https-only --timestamping \
  "https://storage.googleapis.com/kubernetes-release/release/v${VERSION}/bin/linux/${ARCH}/kube-apiserver" \
  "https://storage.googleapis.com/kubernetes-release/release/v${VERSION}/bin/linux/${ARCH}/kube-controller-manager" \
  "https://storage.googleapis.com/kubernetes-release/release/v${VERSION}/bin/linux/${ARCH}/kube-scheduler" \
  "https://storage.googleapis.com/kubernetes-release/release/v${VERSION}/bin/linux/${ARCH}/kubectl"

Шифрование и encryption-provider-config

encryption-config.yaml https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
encryption-provider-config - конфигурация шифрования в etcd
Файл encryption-config.yaml должен быть одинаковый у всех участников кластера, для того что бы они могли расшифровать данные друг друга

В целом судя по документации может быть более сложная конфигурация чем "один ключ для всего", но особого смысла в этом я не вижу

head -c 32 /dev/urandom | base64
<PRE>
<PRE>
cat configs/encryption-config.yaml
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: sCfG58h5SdUxSFephLyE6M6ppxYfnVFUi+GB2b5+kd4=
      - identity: {}

Сертификаты которые использует kube-apiserver

Сертификат для подключения к etcd: --etcd-certfile , --etcd-keyfile

Самая простая часть - это сертификат и ключ с помощью которых можно авторизоваться у etcd сервера и писать/читать данные.
Этот вопрос уже обсуждался при настройке etcd
TODO: вставить ссылку!!!
Для каждого kube-apiserver можно получить от CA настроенного для etcd отдельный сертификат и ключ, с отдельным пользователем и ролью: (имя пользователя и пароль для VAULT были созданы при работе с etcd CA (TODO: ССЫЛКА!)

  • PKI - тот же что использовался для ETCD
  • Для каждого AZ свой пользователь

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

#!/bin/bash

PKI_NAME="k8s_pki_intermediate_ca_for_service_etcd"
CERTS_PATH="/etc/k8s/kube-apiserver/certs/etcd"
mkdir -p ${CERTS_PATH}

AZ=1
DOMAIN="etcd.master.az${AZ}.k8s.cluster.home"
NAME="${DOMAIN}-client"

vault \
    login \
    -method=userpass \
    username="${NAME}-user" \
    password="${NAME}-password"


USERNAME_PREFIX="kube-apiserver"
DATE=$(date +%Y%m%dT%H%M)
USERNAME="${USERNAME_PREFIX}-${AZ}"

echo "========"
vault \
    write \
    -format=json \
    ${PKI_NAME}/issue/${NAME}-role \
    common_name="${USERNAME}" \
    ca=false \
    ttl="43800h" \
    > ${CERTS_PATH}/${USERNAME}.crt.json

cat \
    ${CERTS_PATH}/${USERNAME}.crt.json \
    | jq -r '.data.private_key' > ${CERTS_PATH}/${USERNAME}-${DATE}.key

cat \
    ${CERTS_PATH}/${USERNAME}.crt.json \
    | jq -r '.data.certificate' > ${CERTS_PATH}/${USERNAME}-${DATE}.pem

cat \
    ${CERTS_PATH}/${USERNAME}.crt.json \
    | jq -r '.data.ca_chain[]' >> ${CERTS_PATH}/${USERNAME}-${DATE}.pem

ln -sf  ${CERTS_PATH}/${USERNAME}-${DATE}.key  ${CERTS_PATH}/${USERNAME_PREFIX}.key
ln -sf  ${CERTS_PATH}/${USERNAME}-${DATE}.pem  ${CERTS_PATH}/${USERNAME_PREFIX}.pem

Проверить что пользователь верный: kube-apiserver-1

openssl x509 -noout -text -in kube-apiserver.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            56:ce:57:67:7d:9b:54:76:6d:60:51:4a:0c:09:c7:11:ac:f1:95:3b
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = Ukraine, L = Kharkov, street = Lui Pastera st. 322 app. 131, postalCode = 61172, O = K8s The Hardest Way Labs, OU = IT, CN = Intermediate CA for service ETCd
        Validity
            Not Before: Oct 17 15:23:37 2022 GMT
            Not After : Oct 16 15:24:04 2027 GMT
        Subject: C = Ukraine, L = Kharkov, street = Lui Pastera st 322 app. 311, postalCode = 61172, O = Home Network, OU = IT, CN = kube-apiserver-1


Назначение прав в кластере etcd

  • имя пользователя совпадает с паролем но для каждого kube-apiserver свой отдельный пользователь kube-apiserver-1 kube-apiserver-3 kube-apiserver-3
  • роль k8s создана на из одном предыдущих шагов
#!/bin/bash


export ETCDCTL_API=3

P="/etc/etcd/certs/client"
ENDPOINT="https://etcd.master.az1.k8s.cluster.home:2379"

ETCDCTL="etcdctl \
    --endpoints="${ENDPOINT}" \
    --cert=${P}/root.pem \
    --key=${P}/root.key "




echo kube-apiserver-1 | ${ETCDCTL} --interactive=false  user add kube-apiserver-1
echo kube-apiserver-2 | ${ETCDCTL} --interactive=false  user add kube-apiserver-2
echo kube-apiserver-3 | ${ETCDCTL} --interactive=false  user add kube-apiserver-3

echo "---ROLES---"
{ETCDCTL} role list
    user list
    member list


#${ETCDCTL} user add root
echo "---USERS---"
${ETCDCTL} user list



${ETCDCTL} user grant-role kube-apiserver-1  k8s
${ETCDCTL} user grant-role kube-apiserver-2  k8s
${ETCDCTL} user grant-role kube-apiserver-3  k8s

Проверка прав

на всякий случай - проверить доступы с полученными сертификатами

#!/bin/bash


export ETCDCTL_API=3

P="/etc/k8s/kube-apiserver/certs/etcd"
ENDPOINT="https://etcd.master.az1.k8s.cluster.home:2379"

ETCDCTL="etcdctl \
    --endpoints="${ENDPOINT}" \
    --cert=${P}/kube-apiserver.pem \
    --key=${P}/kube-apiserver.key "


${ETCDCTL}  get  /k8s/a

${ETCDCTL} put /k8s/a c

${ETCDCTL}  get  /k8s/a

--tls-cert-file --tls-private-key-file

Эти два параметра описывают сертификат и ключ которые используются для HTTP API
По сути это обычный серверный сертефика

  • Важно использовать верные доменные имена
  • Нужны ли SAN IP? не уверен
  • Для этого сертификата настраиваю совершенно отдельный PKI со своим промежуточным CA и со своими ролями и пользователями
  • Настройка аналогична СA для ETCd но тем не менее вынесена в отдельный документ
--tls-cert-file string             
    File containing the default x509 Certificate for HTTPS. (CA cert, if any,
    concatenated after server cert). If HTTPS serving is enabled, and
    --tls-cert-file and --tls-private-key-file are not provided, a self-signed
    certificate and key are generated for the public address and saved to
    /var/run/kubernetes.
 --tls-private-key-file string      
    File containing the default x509 private key matching --tls-cert-file.
You’re probably using TLS to connect to your Kubernetes API server. These two options (to the API server) let you pick what certificate the API server should use.

Once you set a TLS cert, you’ll need to set up a kubeconfig file for the components (like the kubelet and kubectl) that want to talk to the API server.

The kubeconfig file will look something like this:

current-context: my-context
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /path/to/my/ca.crt # CERTIFICATE AUTHORITY THAT ISSUED YOUR TLS CERT
    server: https://horse.org:4443 # this name needs to be on the certificate in --tls-cert-file
  name: my-cluster
kind: Config
users:
- name: green-user
  user:
    client-certificate: path/to/my/client/cert # we'll get to this later
    client-key: path/to/my/client/key # we'll get to this later
One thing I found surprising about this is – almost everything else in the universe that uses TLS will look in /etc/ssl to find a list of certificate authorities the computer trusts by default. But Kubernetes doesn’t do that, instead it mandates “no, you have to say exactly which CA issued the API server’s TLS cert”.

You can pass this kubeconfig file into any kubernetes component with --kubeconfig /path/to/kubeconfig.yaml

So! We’ve met our first certificate authority: the CA that issues the API server’s TLS cert. This CA doesn’t need to be the same as any of the other certificate authorities we’re going to discuss.

Для этой пары серетфикатов используется свой СА Vault_PKI_Intermediate_ca_kube_apiserver_Kubernetes_the_hard_way_v2

После того как настроен этот CA и выделены права можно получить сертификаты для кажого экземпляра kube-apiserver
Обратить внимание что используется переменная AZ которая определена в .bashrc

#!/bin/bash

set -eu${DEBUG:+x}

PKI_NAME="k8s_pki_intermediate_ca_for_service_kube_apiserver_tls"
CERTS_PATH="/etc/k8s/kube-apiserver/certs/tls"
mkdir -p ${CERTS_PATH}

N='kube-apiserver'
#AZ=1
DOMAIN="${N}.az${AZ}.k8s.cluster.home"
NAME_SERVER="${DOMAIN}-server"
NAME_CLIENT="${DOMAIN}-client"
BALANCER_DOMAIN="${N}.k8s.cluster.home"
IP="10.0.${AZ}2.1"
vault \
    login \
    -method=userpass \
    username="${NAME_SERVER}-user" \
    password="${NAME_SERVER}-password"


    DATE=$(date +%Y%m%dT%H%M)
    USERNAME="${N}-${AZ}-tls"

echo "========"
vault \
    write \
    -format=json \
    ${PKI_NAME}/issue/${NAME_SERVER}-role \
    common_name="${DOMAIN}" \
    alt_names="${DOMAIN},${BALANCER_DOMAIN}" \
    ip_sans="${IP}" \
    ca=false \
    ttl="43800h" \
    > ${CERTS_PATH}/${USERNAME}.crt.json

cat \
    ${CERTS_PATH}/${USERNAME}.crt.json \
    | jq -r '.data.private_key' > ${CERTS_PATH}/${USERNAME}-${DATE}.key

cat \
    ${CERTS_PATH}/${USERNAME}.crt.json \
    | jq -r '.data.certificate' > ${CERTS_PATH}/${USERNAME}-${DATE}.pem

cat \
    ${CERTS_PATH}/${USERNAME}.crt.json \
    | jq -r '.data.ca_chain[]' >> ${CERTS_PATH}/${USERNAME}-${DATE}.pem

ln -sf  ${CERTS_PATH}/${USERNAME}-${DATE}.key  ${CERTS_PATH}/${N}-tls.key
ln -sf  ${CERTS_PATH}/${USERNAME}-${DATE}.pem  ${CERTS_PATH}/${N}-tls.pem

--service-account-key-file --service-account-signing-key-file

Достаточно сумбурная часть - все что тут написано собрано по частям из разных источников и кое-как скомпоновано

  • пара КЛЮЧЕЙ а не сертификатов, приватная и публичная части
  • НИКАК не связаны ни с каким СА!

Вот цитата из документации, которая лично мне мало что поясняет

# API server argument
--service-account-key-file stringArray
    File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to
    verify ServiceAccount tokens. If unspecified, --tls-private-key-file is used.
    The specified file can contain multiple keys, and the flag can be specified
    multiple times with different files.
# controller manager argument
--service-account-private-key-file string
    Filename containing a PEM-encoded private RSA or ECDSA key used to sign service
    account tokens.

После гугления удалось понять что kube-controller-manager подписывает токены сервисного аккаунта закрытым ключом, другими словами этот ключ нужен не только kube-apiserver
При этом kube-apiserverнужна как приватная так и публичная часть ключа, в то же время kube-controller-manager нужна только приватная (почему так мне выяснить не удалось)
Это означает что нужно иметь один и тот же закрытый ключ на всех запущенных kube-controller-manage и kube-apiserver

Вот цитата из документации:

kube-controller-manager это компонент Control Plane запускает процессы контроллера.

Вполне логично, что каждый контроллер в свою очередь представляет собой отдельный процесс, 
и для упрощения все такие процессы скомпилированы в один двоичный файл и выполняются в одном процессе.
Эти контроллеры включают:

Контроллер узла (Node Controller): уведомляет и реагирует на сбои узла.
Контроллер репликации (Replication Controller): поддерживает правильное количество подов для каждого объекта контроллера репликации в системе.
Контроллер конечных точек (Endpoints Controller): заполняет объект конечных точек (Endpoints), то есть связывает сервисы (Services) и поды (Pods).
Контроллеры учетных записей и токенов (Account & Token Controllers): создают стандартные учетные записи и токены доступа API для новых пространств имен.

Тут, как и обычно яснее не стало


Нужна пара ключей, причем эта пара одна в пределах кластера:

  • Для API сервера нужна публичная часть ключа, которая передается параметром --service-account-key-file и используется для проверки токенов и приватная часть которая передается в параметре --service-account-private-key-file и используется для подписания токенов сервисных аккаунтов
  • Для controller manager нужна приватная часть ключа которая используется для подписания токенов сервисных аккаунтов, параметр--service-account-private-key-file

Ключ создается командой:

openssl genrsa -out service-account-private.key 4096
openssl rsa -in service-account-private.key  -pubout > service-account-public.key

Вот что пишет Джулия:

Using --tls-private-key-file for this seems generally fine to me though,<BR>
as long as you give every API server the same TLS key (which I think you usually would?).<BR>

Это может быть не так и в моем сетапе это не так - по тому что каждый kube-apiserver выписывает себе сам сертификат у волта, со своим ключем

(I’m assuming here that you have a HA setup where you run more than one API server and more than one controller manager)

If you give 2 different controller managers 2 different keys, they’ll sign serviceaccount tokens with different keys and you’ll end up with invalid serviceaccount tokens (see this issue: https://github.com/kubernetes/kubernetes/issues/22351).<BR>
I think this isn’t ideal (Kubernetes should probably support these keys being issued from a CA like it does for ~every other private key).


From reading the source code I think the reason it’s set up this way is that jwt-go doesn’t support using a CA to check signatures.

--client-ca-file

TL;DR: Это СА которым проверяются сертификаты клиентов которые подключаются к kube-apiserver, как сервисов так и kubectl

Документация говорит: Если эта опция установлена, любой запрос, представляющий сертификат клиента, подписанный одним из CA из файле client-ca, аутентифицируется с именем СommonName

(что будет если эта опция не выставлена и кто такие клиенты - из документации не ясно)

В качестве клиентов выступают как компоненты кластера так и "пользователи" запускающие kubectl

Один из возможных способов для компонентов Kubernetes авторизоваться (в оригинале в to authenticate что правильно но я не знаю русского аналога) у API это использовать клиентские сертификаты. Все эти клиентские сертификаты должны быть выпущены одним и тем же СA, который, повторюсь может отличаться от того СА который выпустил сертификат для HTTPS сервера API

Когда используется файл kubeconfig клиентские сертификаты можно указать примерно так:

kind: Config
users:
- name: green-user
  user:
    client-certificate: path/to/my/client/cert
    client-key: path/to/my/client/key

Kubernetes ожидает что клиентские сертификаты будут созданы определенным образом.

Common Name - содержит username Organization - содержит group

Если это не то что нужно то вместо клиентских сертификатов нужно использовать аутентифиуирующий прокси (use an authenticating proxy) (но я не знаю как) Соответственно для авторизации клиентов (пока идет настройка это только компоненты k8s) используется отдельный СА - Настройка СА для клиенских сертефикатов

--kubelet-certificate-authority --kubelet-client-certificate --kubelet-client-key

Тут есть два различных CA

  • CA которым kube-apiserverпроверяет сертификаты kubelet и который описан в параметре --kubelet-certificate-authority
    • сертификат и ключ kubeletописан в параметрах --tls-cert-file и --tls-private-key-file
  • CA которым kubelet проверяет пользовательские сертификаты с которыми kube-apiserver подключается
    • эти сертификаты описаны в параметрах --kubelet-client-certificate --kubelet-client-key и это клиентские сертефикаты
# API server arguments
--kubelet-certificate-authority string    Path to a cert file for the certificate authority.
--kubelet-client-certificate string       Path to a client cert file for TLS.
--kubelet-client-key string               Path to a client key file for TLS.
# kubelet arguments
--client-ca-file string                   If set, any request presenting a client certificate signed by one of the authorities in the client-ca-file is authenticated with an identity corresponding to the CommonName of the client certificate.
--tls-cert-file string                    File containing x509 Certificate used for serving HTTPS (with intermediate certs, if any, concatenated after server cert). If --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to the directory passed to --cert-dir.
--tls-private-key-file string             File containing x509 private key matching --tls-cert-file.