Имеются у нас Github runners, которые запущены в виде подов в Kubernetes-кластере, см. Github: обзор Github Actions и деплой с ArgoCD.
На них выполняеттся сборка docker-образов и их пуш в Docker Hub, а затем деплой приложения с Helm или ArgoCD.
При первом запуске helm install
в поде получаем ошибку “x509: certificate signed by unknown authority“:
[simterm]
# 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
[/simterm]
А без указания API-сервера – ошибка прав доступа:
[simterm]
# 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"
[/simterm]
Собственно, проблема ясна: Helm в поде пытается достучаться к API-серверу, используя default ServiceAccount, который был создан при деплое Github runner.
Содержание
Проверка доступа ServiceAccount
Как уже писалось в Kubernetes: ServiceAccounts, JWT-токены, аутентификация и RBAC-авторизация, для аутентификации на API-сервере нам нужны токен и ключ Certificate Authority.
Подключаемся в под:
[simterm]
$ 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#
[/simterm]
Создаём переменные:
[simterm]
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)
[/simterm]
И пробуем получить доступ к API сейчас:
[simterm]
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 }
[/simterm]
Аналогичную ошибку увидим, если запустим kubectl get pod
сейчас из этого пода:
[simterm]
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"
[/simterm]
Хорошо, и что нам делать?
Создание Kubernetes ServiceAccount для Kubernetes Pod
Собственно, вместо того, что бы монтировать к создаваемым подам дефолтный ServiceAccount с его токеном – нам надо создать свой, которому мы дадим доступ к ресурсам API-сервера через Kubernetes RBAC Role и Kubernetes RBAC RoleBinding.
Создание RBAC Role
Роль можно взять дефолтную, из списка User-facing roles, например cluster-admin:
[simterm]
$ kubectl get clusterrole cluster-admin NAME CREATED AT cluster-admin 2020-11-27T14:44:52Z
[/simterm]
Или написать свою, с чётким ограничем доступов. К примеру, дадим доступ к 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"]
Создаём эту роль в нужном нам неймспейсе:
[simterm]
$ 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
[/simterm]
Проверяем:
[simterm]
$ kk -n dev-1-18-backend-github-runners-helm-ns get role NAME CREATED AT github-runner-deployer-role 2021-09-28T08:05:20Z
[/simterm]
Создание ServiceAccount
Далее, создадим сервис-аккаунт в этом неймспейсе:
apiVersion: v1 kind: ServiceAccount metadata: name: "github-runner-deployer-sa"
Применяем:
[simterm]
$ kubectl -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-sa.yaml
[/simterm]
Создание 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
Применяем её:
[simterm]
$ 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
[/simterm]
Подключение 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"
Создаём под:
[simterm]
$ kubectl -n dev-1-18-backend-github-runners-helm-ns apply -f github-runner-deployer-pod.yaml pod/github-runners-deployer-pod created
[/simterm]
Подключаемся в него:
[simterm]
$ kubectl -n dev-1-18-backend-github-runners-helm-ns exec -ti github-runners-deployer-pod bash root@github-runners-deployer-pod:/#
[/simterm]
И проверяем доступ к API:
[simterm]
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", ...
[/simterm]
Теперь доступ к get
и list
подов есть. А вот к Secrets – нет, так как не задано в нашей роли:
[simterm]
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\"", ...
[/simterm]
Helm ServiceAccount
Вернёмся к Helm и нашим Github Runners.
Собственно, Helm из подов Github runners будет деплоить всё и везде, так что можно выдать ему админ-права на весь кластер, подключив ClusterRole cluster-admin.
Удаляем созданный выше биндинг:
[simterm]
$ 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
[/simterm]
Создаём новый, но теперь с типом 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
Подключаем сервис-аккаунт к этой кластер-роли:
[simterm]
$ 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
[/simterm]
Возвращаемся в под, и пробуем получить доступ к секретам теперь:
[simterm]
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", ...
[/simterm]
Всё – теперь наш Helm получил полный доступ к кластеру.
Осталось обновить Deployment для Github runners, и задать там новый ServiceAccount.
Редактируем его:
[simterm]
$ kubectl -n dev-1-18-backend-github-runners-helm-ns edit deploy actions-runner-deployment
[/simterm]
Задаём новый SA – указываем serviceAccount
:
Ждём, пока поды пересоздадутся (см. Kubernetes: ConfigMap и Secrets — auto-reload данных в подах), подключаемся, и проверяем:
[simterm]
root@actions-runner-deployment-6dfc9b457f-mc7rt:/actions-runner# kubectl auth can-i list pods yes
[/simterm]
Доступ к другим неймспейсам – ведь мы создавали ClusterRoleBinding, который применяется ко всему кластеру, а не отдельному namespace, как в случае с обычным RoleBinding:
[simterm]
root@actions-runner-deployment-6dfc9b457f-mc7rt:/actions-runner# kubectl auth can-i list pods --namespace istio-system yes
[/simterm]
Отлично – в неймспейс istio-system тоже полный доступ есть.
И доступ самого Helm:
[simterm]
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
[/simterm]
Готово.