Kubernetes: Helm — «x509: certificate signed by unknown authority» и ServiceAccount для Pod

Автор: | 09/28/2021
 

Имеются у нас Github runners, которые запущены в виде подов в Kubernetes-кластере, см. Github: обзор Github Actions и деплой с ArgoCD.

На них выполняеттся сборка docker-образов и их пуш в Docker Hub, а затем деплой приложения с Helm или ArgoCD.

При первом запуске helm install в поде получаем ошибку «x509: certificate signed by unknown authority«:

helm --kube-apiserver=https://kubernetes.default.svc.cluster.local list
Error: Kubernetes cluster unreachable: Get "https://kubernetes.default.svc.cluster.local/version?timeout=32s": x509: certificate signed by unknown authority

А без указания API-сервера — ошибка прав доступа:

helm list
Error: list: failed to list: secrets is forbidden: User "system:serviceaccount:dev-1-18-backend-github-runners-helm-ns:default" cannot list resource "secrets" in API group "" in the namespace "dev-1-18-backend-github-runners-helm-ns"

Собственно, проблема ясна: Helm в поде пытается достучаться к API-серверу, используя default ServiceAccount, который был создан при деплое Github runner.

Проверка доступа ServiceAccount

Как уже писалось в Kubernetes: ServiceAccounts, JWT-токены, аутентификация и RBAC-авторизация, для аутентификации на API-сервере нам нужны токен и ключ Certificate Authority.

Подключаемся в под:

kk -n dev-1-18-backend-github-runners-helm-ns exec -ti actions-runner-deployment-7f78968949-tmrtt bash
Defaulting container name to runner.
Use 'kubectl describe pod/actions-runner-deployment-7f78968949-tmrtt -n dev-1-18-backend-github-runners-helm-ns' to see all of the containers in this pod.
root@actions-runner-deployment-7f78968949-tmrtt:/actions-runner#

Создаём переменные:

root@actions-runner-deployment-7f78968949-tmrtt:/actions-runner# CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
root@actions-runner-deployment-7f78968949-tmrtt:/actions-runner# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
root@actions-runner-deployment-7f78968949-tmrtt:/actions-runner# NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

И пробуем получить доступ к API сейчас:

root@actions-runner-deployment-7f78968949-tmrtt:/actions-runner# curl -s --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc.cluster.local/api/v1/namespaces/$NAMESPACE/pods" | jq '{message, code}'
{
"message": "pods is forbidden: User \"system:serviceaccount:dev-1-18-backend-github-runners-helm-ns:default\" cannot list resource \"pods\" in API group \"\" in the namespace \"dev-1-18-backend-github-runners-helm-ns\"",
"code": 403
}

Аналогичную ошибку увидим, если запустим kubectl get pod сейчас из этого пода:

root@actions-runner-deployment-7f78968949-tmrtt:/actions-runner# kubectl get pod
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:dev-1-18-backend-github-runners-helm-ns:default" cannot list resource "pods" in API group "" in the namespace "dev-1-18-backend-github-runners-helm-ns"

Хорошо, и что нам делать?

Создание Kubernetes ServiceAccount для Kubernetes Pod

Собственно, вместо того, что бы монтировать к создаваемым подам дефолтный ServiceAccount с его токеном — нам надо создать свой, которому мы дадим доступ к ресурсам API-сервера через Kubernetes RBAC Role и Kubernetes RBAC  RoleBinding.

Создание RBAC Role

Роль можно взять дефолтную, из списка User-facing roles, например cluster-admin:

kubectl get clusterrole cluster-admin
NAME            CREATED AT
cluster-admin   2020-11-27T14:44:52Z

Или написать свою, с чётким ограничем доступов. К примеру, дадим доступ к verb (действия) get и list на resources pod:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
   name: "github-runner-deployer-role"
rules:
 - apiGroups: [""]
   resources: ["pods"]
   verbs: ["get", "list"]

Создаём эту роль в нужном нам неймспейсе:

kubectl -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-role.yaml
role.rbac.authorization.k8s.io/github-runner-deployer-role created

Проверяем:

kk -n dev-1-18-backend-github-runners-helm-ns get role
NAME                          CREATED AT
github-runner-deployer-role   2021-09-28T08:05:20Z

Создание ServiceAccount

Далее, создадим сервис-аккаунт в этом неймспейсе:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: "github-runner-deployer-sa"

Применяем:

kubectl -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-sa.yaml

Создание RoleBinding

Теперь, надо создать binding — связь между этим ServiceAccount и созданной ранее ролью.

Создаём RoleBinding:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
   name: "github-runner-deployer-rolebinding"
subjects:
 - kind: ServiceAccount
   name: "github-runner-deployer-sa"
   namespace: "dev-1-18-backend-github-runners-helm-ns"
roleRef:
   kind: Role
   name: "github-runner-deployer-role"
   apiGroup: rbac.authorization.k8s.io

Применяем её:

kk -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/github-runner-deployer-rolebinding created

Подключение ServiceAccount к Kubernetes Pod

И последним осталось подключить созданный ServiceAccount к нашим подам.

Можно проверить через создание нового статичного пода, без деплоймента и скейлинга:

apiVersion: v1
kind: Pod
metadata:
  name: "github-runners-deployer-pod"
spec:
  containers:
    - name: "github-runners-deployer"
      image: "nginx"
      ports:
        - name: "web"
          containerPort: 80
          protocol: TCP
  serviceAccountName: "github-runner-deployer-sa"

Создаём под:

kubectl -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-pod.yaml
pod/github-runners-deployer-pod created

Подключаемся в него:

kubectl -n dev-1-18-backend-github-runners-helm-ns exec -ti github-runners-deployer-pod bash
root@github-runners-deployer-pod:/#

И проверяем доступ к API:

root@github-runners-deployer-pod:/# CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
root@github-runners-deployer-pod:/# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
root@github-runners-deployer-pod:/# NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
oot@github-runners-deployer-pod:/# curl -s --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc.cluster.local/api/v1/namespaces/$NAMESPACE/pods"
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/dev-1-18-backend-github-runners-helm-ns/pods",
"resourceVersion": "251020450"
},
"items": [
{
"metadata": {
"name": "actions-runner-deployment-7f78968949-jsh6l",
"generateName": "actions-runner-deployment-7f78968949-",
"namespace": "dev-1-18-backend-github-runners-helm-ns",
...

Теперь доступ к get и list подов есть. А вот к Secrets — нет, так как не задано в нашей роли:

root@github-runners-deployer-pod:/# curl -s --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc.cluster.local/api/v1/namespaces/$NAMESPACE/secrets"
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "secrets is forbidden: User \"system:serviceaccount:dev-1-18-backend-github-runners-helm-ns:github-runner-deployer-sa\" cannot list resource \"secrets\" in API group \"\" in the namespace \"dev-1-18-backend-github-runners-helm-ns\"",
...

Helm ServiceAccount

Вернёмся к Helm и нашим Github Runners.

Собственно, Helm из подов Github runners будет деплоить всё и везде, так что можно выдать ему админ-права на весь кластер, подключив ClusterRole cluster-admin.

Удаляем созданный выше биндинг:

kubectl -n dev-1-18-backend-github-runners-helm-ns delete rolebinding github-runner-deployer-rolebinding
rolebinding.rbac.authorization.k8s.io "github-runner-deployer-rolebinding" deleted

Создаём новый, но теперь с типом ClusterRoleBinding, что бы дать права на все неймспейсы кластера:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
   name: "github-runner-deployer-cluster-rolebinding"
subjects:
 - kind: ServiceAccount
   name: "github-runner-deployer-sa"
   namespace: "dev-1-18-backend-github-runners-helm-ns"
roleRef:
   kind: ClusterRole
   name: "cluster-admin"
   apiGroup: rbac.authorization.k8s.io

Подключаем сервис-аккаунт к этой кластер-роли:

kubectl -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-clusterrolebinding.yaml
clusterrolebinding.rbac.authorization.k8s.io/github-runner-deployer-rolebinding created

Возвращаемся в под, и пробуем получить доступ к секретам теперь:

root@github-runners-deployer-pod:/# curl -s --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc.cluster.local/api/v1/namespaces/$NAMESPACE/secrets"
{
"kind": "SecretList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/dev-1-18-backend-github-runners-helm-ns/secrets",
"resourceVersion": "251027845"
},
"items": [
{
"metadata": {
"name": "bttrm-docker-secret",
"namespace": "dev-1-18-backend-github-runners-helm-ns",
"selfLink": "/api/v1/namespaces/dev-1-18-backend-github-runners-helm-ns/secrets/bttrm-docker-secret",
...

Всё — теперь наш Helm получил полный доступ к кластеру.

Осталось обновить Deployment для Github runners, и задать там новый ServiceAccount.

Редактируем его:

kubectl -n dev-1-18-backend-github-runners-helm-ns edit deploy actions-runner-deployment

Задаём новый SA — указываем serviceAccount:

Ждём, пока поды пересоздадутся (см. Kubernetes: ConfigMap и Secrets — auto-reload данных в подах), подключаемся, и проверяем:

root@actions-runner-deployment-6dfc9b457f-mc7rt:/actions-runner# kubectl auth can-i list pods
yes

Доступ к другим неймспейсам — ведь мы создавали ClusterRoleBinding, который применяется ко всему кластеру, а не отдельному namespace, как в случае с обычным RoleBinding:

root@actions-runner-deployment-6dfc9b457f-mc7rt:/actions-runner# kubectl auth can-i list pods --namespace istio-system
yes

Отлично — в неймспейс istio-system тоже полный доступ есть.

И доступ самого Helm:

root@actions-runner-deployment-6dfc9b457f-mc7rt:/actions-runner# helm list
NAME            NAMESPACE                               REVISION        UPDATED                                         STATUS          CHART                           APP VERSION
github-runners  dev-1-18-backend-github-runners-helm-ns 2               2021-09-22 19:50:18.828686642 +0300 +0300       deployed        github-runners-1632329415       v1.0.0

Готово.