In my last article, I showed how to develop the KubeLogExporter, a tool that collects log data from a set of pods. So far the exporter relies on full access to the Cluster by using the local .kubeconfig
file. If we want the exporter to run as a cron job inside the cluster, it needs to have suitable access rights. Originally I wanted to just write about the cron job implementation, but found that investigating how Kubernetes access rights work to be very educational. That’s why it became the article that you are reading now.
在上一篇文章中,我展示了如何开发KubeLogExporter,该工具可以从一组容器中收集日志数据。 到目前为止,导出器依赖于使用本地.kubeconfig
文件对群集的完全访问权限。 如果我们希望导出器在集群中作为cron作业运行,则它需要具有适当的访问权限。 最初,我只想写有关cron作业的实现,但是发现调查Kubernetes访问权限的工作方式非常有教育意义。 这就是为什么它成为您现在正在阅读的文章。
This article originally appeared at my blog.
本文最初出现在 我的博客中 。
Kubernetes基本概念 (Essential Kubernetes Concepts)
When you run a Pod inside a Kubernetes cluster, several default configurations and security aspects are already made by default. To determine the access right to the rich Kubernetes API, the essential resources are ServiceAccount
, Role
and RoleBindings
.
在Kubernetes集群中运行Pod时,默认情况下已经进行了一些默认配置和安全方面的操作。 为了确定对丰富Kubernetes API的访问权,必不可少的资源是ServiceAccount
, Role
和RoleBindings
。
Let’s understand these concepts by considering how the cron job to read pod logs works. When the job runs, it needs read access to namespaces and pods. This access is defined in a Role
or ClusterRole
. The Role
is limited to one namespace only, so we will use the ClusterRole
. When a pod is created, it is given the default system account and the default system account token to access the K8S API. However, this account does not have the required access rights, so we need to define a custom ServiceAccount
. The final piece is the RoleBinding
or ClusterRoleBinding
: It connects the ClusterRole
with the ServiceAccount
.
让我们通过考虑cron作业读取pod日志的方式来理解这些概念。 作业运行时,它需要对名称空间和Pod的读取权限。 此访问权限在Role
或ClusterRole
定义。 该Role
仅限于一个名称空间,因此我们将使用ClusterRole
。 创建Pod时,会为它提供默认的系统帐户和默认的系统帐户令牌,以访问K8S API。 但是,此帐户没有必需的访问权限,因此我们需要定义一个自定义ServiceAccount
。 最后一块是RoleBinding
或ClusterRoleBinding
:它将ClusterRole
与ServiceAccount
连接起来。
K8S API:直接访问 (K8S API: Direct Access)
To see how those concepts are applied when working with Kubernetes, I followed this excellent article in which the API is accessed directly with curl
.
为了了解在使用Kubernetes时如何应用这些概念,我关注了这篇出色的文章 ,其中使用curl
直接访问API。
Let’s start simple by creating the api-explorer
pod that by writing the api-explorer-pod.yaml
file with the following content.
让我们从创建api-explorer
容器开始,从创建具有以下内容的api-explorer-pod.yaml
文件开始。
apiVersion: v1
kind: Pod
metadata:
name: api-explorer
spec:
containers:
- name: alpine
image: alpine
args: ['sleep', '3600']
Then we create the container and wait until it is started.
然后,我们创建容器并等待其启动。
> kubectl create -f api-explorer-pod.yamlpod/api-explorer created
Then we jump into the container, and install the curl
package.
然后,我们跳入容器,并安装curl
包装。
> kubectl api-explorer -it sh
> apk add curl
To access the Kubernetes API, we always need to send a valid security token. This token is stored inside the pod at the location /run/secrets/kubernetes.io/serviceaccount/token
. With this token, we can make the API request.
要访问Kubernetes API,我们始终需要发送一个有效的安全令牌。 该令牌存储在pod内部的/run/secrets/kubernetes.io/serviceaccount/token
位置。 使用此令牌,我们可以发出API请求。
> TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
> curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods/ --insecure
{
"kind": "Status",
"apiVersion": "v1",
"metadata": { },
"status": "Failure",
"message": "pods is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",
"reason": "Forbidden",
"details": {
"kind": "pods"
},
"code": 403
}
But as we see, we cannot access the pod at all because the default service account does not have the correct access rights.
但是,正如我们看到的那样,因为默认服务帐户没有正确的访问权限,所以我们根本无法访问Pod。
定义自定义服务帐户 (Defining a Custom Service Account)
So we need to have a properly configured ServiceAccount
that grants us a token with which the Kubernetes API can be accessed.
因此,我们需要具有正确配置的ServiceAccount
,该ServiceAccount
会授予我们可以访问Kubernetes API的令牌。
Create the file pod-read-access-service-account.yaml
and put the ServiceAccount
definition on top. This resource is basically only metadata.
创建文件pod-read-access-service-account.yaml
,并将ServiceAccount
定义放在顶部。 该资源基本上只是元数据。
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: log-exporter-sa
namespace: default
labels:
app: log-exporter
---
The next thing is the ClusterRole
definition. Inside its spec
block, we define which apiGroups
and resources
we want to access. The core API group is denoted by ""
, and under resources
we list pods
. Finally, the verbs
determine which action we want to apply to the resources: In our case, its reading and listing.
接下来是ClusterRole
定义。 在其spec
块内,我们定义要访问的apiGroups
和resources
。 核心API组用""
表示,在resources
下方列出了pods
。 最后, verbs
确定我们要对资源执行的操作:在我们的案例中,是其阅读和列出。
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: log-exporter-cr
labels:
app: log-exporter
rules:
- apiGroups:
- ''
resources:
- pods
- pods/log
- namespaces
verbs:
- get
- list
---
Finally we create the RoleBinding
resource to combine the SeviceAccount
and the ClusterRole
.
最后,我们创建RoleBinding
资源以结合SeviceAccount
和ClusterRole
。
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: log-exporter-rb
roleRef:
kind: ClusterRole
name: log-exporter-cr
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: log-exporter-sa
namespace: default
---
Now we create all resources.
现在我们创建所有资源。
> kubectl create -f pod-read-access-service-account.yamlserviceaccount/log-exporter-sa created
clusterrole.rbac.authorization.k8s.io/log-exporter-cr created
rolebinding.rbac.authorization.k8s.io/log-exporter-rb created
Some more details: As you see, the ServiceAccount
is explicitly created in the default
namespace. Be careful about the ClusterRoleBinding
as it needs to reference this ServiceAccount
in its defined namespace as well, or it will not work correctly.
更多详细信息:如您所见, ServiceAccount
是在default
名称空间中显式创建的。 注意ClusterRoleBinding
因为它也需要在其定义的名称空间中引用此ServiceAccount
,否则它将无法正常工作。
K8S API:使用自定义服务帐户访问 (K8S API: Access with Custom Service Account)
To use the newly created ServiceAccount
, we define that the pod uses the new role. Going back to the api-explorer-pod.yaml
file, we add the new configuration item spec.serviceAccountName
.
要使用新创建的ServiceAccount
,我们定义吊舱使用新角色。 回到api-explorer-pod.yaml
文件,我们添加了新的配置项目spec.serviceAccountName
。
apiVersion: v1
kind: Pod
metadata:
name: api-explorer
spec:
serviceAccountName: log-exporter-sa
containers:
- name: alpine
image: alpine
args: ['sleep', '3600']
Back in the container, we grab the token to make the request — and it works!
回到容器中,我们抓取令牌以发出请求-它起作用了!
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods/ --insecure
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/default/pods/",
"resourceVersion": "320995"
},
"items": [
{
"metadata": {
"name": "api-explorer2",
"namespace": "default",
"selfLink": "/api/v1/namespaces/default/pods/api-explorer2",
"uid": "343aaf7e-1be5-45da-aadb-e83ee329a7fd",
"resourceVersion": "320976",
"creationTimestamp": "2020-05-24T10:16:58Z"
},
...
Now as the final proof of concepts, let’s try to read logs from a different pod in a different namespace. We grab the coredns
pod from the kube-system
namespace.
现在,作为概念的最终证明,让我们尝试从不同名称空间中的其他容器读取日志。 我们从kube-system
名称空间中获取coredns
pod。
kb get pods -n kube-systemNAME READY STATUS RESTARTS AGE
metrics-server-6d684c7b5-6ww29 1/1 Running 7 8d
coredns-d798c9dd-pdswq 1/1 Running 7 8d
The url to access this pod is composed as this: /api/v1/namespaces/{namespace}/pods/{name}/log
. So, we need the exact namespace and the exact pod name for this request to work. Get back into the api-explorer
pod and access the log files.
用于访问此pod的url是这样组成的: /api/v1/namespaces/{namespace}/pods/{name}/log
。 因此,我们需要确切的名称空间和确切的pod名称,此请求才能正常工作。 返回api-explorer
窗格并访问日志文件。
> curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/kube-system/pods/coredns-d798c9dd-pdswq/log --insecure[INFO] plugin/reload: Running configuration MD5 = 4665410bf21c8b272fcfd562c482cb82
______ ____ _ _______
/ ____/___ ________ / __ \/ | / / ___/ ~ CoreDNS-1.6.3
/ / / __ \/ ___/ _ \/ / / / |/ /\__ \ ~ linux/arm, go1.12.9
/ /___/ /_/ / / / __/ /_/ / /| /___/ /
\____/\____/_/ \___/_____/_/ |_//____/
We are happy to see that it works as intended.
我们很高兴看到它按预期工作。
结论 (Conclusion)
In this article, we learned about the essential Kubernetes resources for enabling to access pods and their log files in any namespace. A ClusterRole
defines which resources and which actions on those resources should be given. These access rights are bound with a ClusterRoleBinding
to a ServiceAccount
. Then we use this ServiceAccount
to provide the access rights to a Pod
. We showed how the Kubernetes API can be access from within a pod by using the curl
command. In the next article we will see how to implement a cron job that uses this service account to export the log files.
在本文中,我们了解了必要的Kubernetes资源,这些资源可用于访问任何命名空间中的Pod及其日志文件。 ClusterRole
定义应指定哪些资源以及ClusterRole
资源应执行的操作。 这些访问权限通过ClusterRoleBinding
绑定到ServiceAccount
。 然后,我们使用此ServiceAccount
提供对Pod
的访问权限。 我们展示了如何使用curl
命令从pod内访问Kubernetes API。 在下一篇文章中,我们将看到如何实现使用该服务帐户导出日志文件的cron作业。
翻译自: https://medium.com/@admantium/kubernetes-api-how-custom-service-accounts-work-936709f18740