12.1 了解认证机制
在前面的章节中,我们讲到API服务器可以配置一到多个认证的插件(授权插件同样也可以)。API服务器接收到的请求会经过一个认证插件的列表,列表中的每个插件都可以检查这个请求和尝试确定谁在发送这个请求。列表中的第一个插件可以提取请求中客户端的用户名、用户ID和组信息,并返回给API服务器。API服务器会停止调用剩余的认证插件并继续进入授权阶段。
目前有几个认证插件是直接可用的。它们使用下列方法获取客户端的身份认证:
- 客户端证书
- 传入在HTTP头中的认证token
- 基础的HTTP认证
- 其他
启动API服务器时,通过命令行选项可以开启认证插件。
12.1.1 用户和组
认证插件会返回已经认证过用户的用户名和组(多个组)。Kubernetes不会在任何地方存储这些信息,这些信息被用来验证用户是否被授权执行某个操作。
了解用户
Kubernetes区分了两种连接到API服务器的客户端。
- 真实的人(用户)
- pod(更准确地说是运行在pod中的应用)
这两种类型的客户端都使用了上述的认证插件进行认证。用户应该被管理在外部系统中,例如单点登录系统(SSO),但是pod使用一种称为service accounts的机制,该机制被创建和存储在集群中作为ServiceAccount资源。相反,没有资源代表用户账户,这也就意味着不能通过API服务器来创建、更新或删除用户。
我们不会详细讨论如何管理用户,但是会具体地探讨ServiceAccount,因为它们对于运行中的pod很重要。关于如何配置集群来供用户身份认证的更多信息,集群管理员应该参考集群管理 中的Kubernetes集群管理员指南。
了解组
正常用户和ServiceAccount都可以属于一个或多个组。我们已经讲过认证插件会连同用户名和用户ID返回组。组可以一次给多个用户赋予权限,而不是必须单独给用户赋予权限。
由插件返回的组仅仅是表示组名称的字符串,但是系统内置的组会有一些特殊的含义。
- system:unauthenticated 组用于所有认证插件都不会认证客户端身份的请求。
- system:authenticated 组会自动分配给一个成功通过认证的用户。
- system:serviceaccounts 组包含所有在系统中的ServiceAccount。
- system:serviceaccounts: 组包含了所有在特定命名空间中的ServiceAccount。
12.1.2 ServiceAccount介绍
接下来我们更详细地探讨ServiceAccount。你已经了解API服务器要求客户端在服务器上执行操作之前对自己进行身份认证,并且你已经了解了pod是怎么通过发送 /var/run/secrets/kubernetes.io/serviceaccount/token
文件内容来进行身份认证的。这个文件通过加密卷挂载进每个容器的文件系统中。
但是那个文件具体表示了什么呢?每个pod都与一个ServiceAccount相关联,它代表了运行在pod中应用程序的身份证明。token文件持有ServiceAccount的认证token。应用程序使用这个token连接API服务器时,身份认证插件会对ServiceAccount进行身份认证,并将ServiceAccount的用户名传回API服务器内部。ServiceAccount用户名的格式像下面这样:
system.serviceaccount:<namespace>:<service account name>
API服务器将这个用户名传给已配置好的授权插件,这决定该应用程序所尝试执行的操作是否被ServiceAccount允许执行。
ServiceAccount只不过是一种运行在pod中的应用程序和API服务器身份认证的一种方式。如前所述,应用程序通过在请求中传递ServiceAccount token来实现这一点。
了解ServiceAccount资源
ServiceAccount就像Pod、Secret、ConfigMap等一样都是资源,它们作用在单独的命名空间,为每个命名空间自动创建一个默认的ServiceAccount(你的pod会一直使用)。
可以像其他资源那样查看ServiceAccount列表:
$ kubectl get sa
注意 serviceaccount 的缩写是sa。
如你所见,当前命名空间只包含default ServiceAccount,其他额外的ServiceAccount可以在需要时添加。每个pod都与一个ServiceAccount相关联,但是多个pod可以使用同一个ServiceAccount。通过图12.1我们可以了解,pod只能使用同一个命名空间中的ServiceAccount。
imgimage
图12.1 每个pod会分配一个在这个pod命名空间中的单一ServiceAccount
ServiceAccount如何和授权进行绑定
在pod的manifest文件中,可以用指定账户名称的方式将一个ServiceAccount赋值给一个pod。如果不显式地指定ServiceAccount的账户名称,pod会使用在这个命名空间中的默认ServiceAccount。
可以通过将不同的ServiceAccount赋值给pod来控制每个pod可以访问的资源。当API服务器接收到一个带有认证token的请求时,服务器会用这个token来验证发送请求的客户端所关联的ServiceAccount是否允许执行请求的操作。API服务器通过管理员配置好的系统级别认证插件来获取这些信息。其中一个现成的授权插件是基于角色控制的插件(RBAC),这个插件会在本章后续进行讨论。从Kubernetes 1.6版本开始,RBAC插件是绝大多数集群应该使用的授权插件。
12.1.3 创建ServiceAccount
我们已经讲过每个命名空间都拥有一个默认的ServiceAccount,也可以在需要时创建额外的ServiceAccount。但是为什么应该费力去创建新的ServiceAccount而不是对所有的pod都使用默认的ServiceAccount?
显而易见的原因是集群安全性。不需要读取任何集群元数据的pod应该运行在一个受限制的账户下,这个账户不允许它们检索或修改部署在集群中的任何资源。需要检索资源元数据的pod应该运行在只允许读取这些对象元数据的ServiceAccount下。反之,需要修改这些对象的pod应该在它们自己的ServiceAccount下运行,这些ServiceAccount允许修改API对象。
下面让我们了解一下如何创建其他的ServiceAccount,它们如何与密钥进行关联,以及如何将它们分配给pod。
创建ServiceAccount
得益于 kubectl create serviceaccount 命令,创建ServiceAccount非常容易。让我们新创建一个名为foo的 ServiceAccount:
$ kubectl create serviceaccount foo
然后如下面的代码清单所示的那样,可以使用describe命令来查看ServiceAccount。
代码清单12.1 使用 kubectl describe
命令查看ServiceAccount
$ kubectl describe serviceaccount foo
Name: foo
Namespace: custom
Labels: <none>
Annotations: <none>
Image pull secrets: <none> #
Mountable secrets: foo-token-shg6r
Tokens: foo-token-shg6r #认证tonken
Events: <none>
可以看到,我们已经创建了自定义的token密钥,并将它和ServiceAccount相关联。如果通过 kubectl describe secret foo-token-qzq7j
查看密钥里面的数据,如下面的代码清单所示,你会发现它包含了和默认的ServiceAccount相同的条目(CA证书、命名空间和token),当然这两个token本身显然是不相同的。
代码清单12.2 查看自定义的ServiceAccount密钥
$ kubectl describe secret foo-token-shg6r
注意 你可能已经了解过JSON Web Token(JWT)。ServiceAccount中使用的身份认证token就是JWT token。
了解ServiceAccount上的可挂载密钥
通过使用 kubectl describe
命令查看ServiceAccount时,token会显示在可挂载密钥列表中。下面让我们来解释一下这个列表代表什么。在第7章中,你学会了如何创建密钥并且把它们挂载进一个pod里。在默认情况下,pod可以挂载任何它需要的密钥。但是我们可以通过对ServiceAccount进行配置,让pod只允许挂载ServiceAccount中列出的可挂载密钥。为了开启这个功能,ServiceAccount必须包含以下注解:kubernetes.io/enforce-mountable-secrets=″true″
。
如果ServiceAccount被加上了这个注解,任何使用这个ServiceAccount的pod只能挂载进ServiceAccount的可挂载密钥——这些pod不能使用其他的密钥。
了解ServiceAccount的镜像拉取密钥
ServiceAccount也可以包含镜像拉取密钥的list。这个list曾经在第7章中查看过;如果你已经不记得了,镜像拉取密钥持有从私有镜像仓库拉取容器镜像的凭证。
下面的代码清单中显示了ServiceAccount定义的一个例子,它包含了我们在第7章中创建的镜像拉取密钥。
代码清单12.3 带有镜像拉取密钥的ServiceAccount:sa-image-pull-secrets.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
imagePullSecrets:
- name: my-dockerhub-secret
ServiceAccount的镜像拉取密钥和它的可挂载密钥表现有些轻微不同。和可挂载密钥不同的是,ServiceAccount中的镜像拉取密钥不是用来确定一个pod可以使用哪些镜像拉取密钥的。添加到ServiceAccount中的镜像拉取密钥会自动添加到所有使用这个ServiceAccount的pod中。向ServiceAccount中添加镜像拉取密钥可以不必对每个pod都单独进行镜像拉取密钥的添加操作。
12.1.4 将ServiceAccount分配给pod
在创建另外的ServiceAccount之后,需要将它们赋值给pod。通过在pod定义文件中的 spec.serviceAccountName 字段上设置ServiceAccount的名称来进行分配。
注意 pod的ServiceAccount必须在pod创建时进行设置,后续不能被修改。
创建使用自定义ServiceAccount的pod
在第8章中部署了一个运行在基于 tutum/curl 镜像的容器上的pad,并在其旁边放置了一个ambassador容器。我们使用它来查看API服务器的REST接口。这个ambassador容器会运行 kubectl proxy
进程,这个进程会使用pod的ServiceAccount 的token和API服务器进行身份认证。
现在可以修改pod,让pod使用我们几分钟前创建的foo ServiceAccount。接下来的代码清单展示了这个pod的定义。
代码清单12.4 使用一个非默认ServiceAccount的pod:curl-custom-sa.yaml
无法找到了tutum/curl 镜像
apiVersion: v1
kind: Pod
metadata:
name: curl-custom-sa
spec:
serviceAccountName: foo # 这个pod使用的ServiceAccount是foo
containers:
- name: main
image: curlimages/curl
command: ["sleep", "9999999"]
- name: ambassador
image: luksa/kubectl-proxy:latest
为了确认自定义的ServiceAccount token已经挂载进这两个容器中,如下面的代码清单所示的那样,可以打印出这个token的内容。
代码清单12.5 查看挂载进pod容器内的token
$ kubectl exec -it curl-custom-sa -c main -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
通过对比代码清单12.5和 12.2中token的字符串,你会发现这个token来自foo ServiceAccount的token。
使用自定义的ServiceAccount token和API服务器进行通信
让我们看看是否可以使用这个token和API服务器进行通信。前面提到过,ambassador容器在使用这个token和服务器进行通信,因此可以通过ambassador来测试这个token,这个ambassador监听在 localhost:8001 上,如下面的代码清单所示。
**代码清单12.6 使用自定义的ServiceAccount和API服务器进行通信 **
新版这里也有变化: 访问之后是403
$ kubectl exec -it curl-custom-sa -c main -- curl localhost:8001/api/v1/pods{ "kind": "PodList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/pods", "resourceVersion": "433895" }, "item": [ ......
好的,我们从服务器得到了正确的响应,也就意味着自定义的ServiceAccount可以允许列出pod。这可能是因为集群没有使用RBAC授权插件,或者按照第8章所讲的那样,你给了所有的ServiceAccount全部的权限。
如果集群没有使用合适的授权,创建和使用额外的ServiceAccount并没有多大意义,因为即使默认的ServiceAccount也允许执行任何操作。在这种情况下,使用ServiceAccount的唯一原因就是前面讲过的加强可挂载密钥,或者通过ServiceAccount提供镜像拉取密钥。
如果使用RBAC授权插件,创建额外的ServiceAccount实际上是必要的,我们会在后面讨论RBAC授权插件的使用。
网友评论