Kubernetes the hard way kube apiserver
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.