用户在访问Kubernetes集群的API server时,访问请求需要经过身份验证、授权和准入控制这三个阶段的检查,才能真正到达API服务,如下图所示:

Kubernetes中的用户有两种类型:service accounts 和 normal users。service accounts 由 Kubernetes管理,它是Pod中的进程用于访问API服务的account,为Pod中的进程提供了一种身份标识。normal users是由外部系统管理,在Kubernetes中并没有对应的 user 对象,它为人类用户使用kubectl之类的工具访问API服务时提供身份标识。所有用户,不管是使用 kubectl、客户端lib、还是直接发起REST请求访问API server,都需要经过上述三个步骤的检查。
本文将介绍Kubernetes集群的身份验证,即Kubernetes如何确认来访者的身份。
Kubernetes支持多种方式的身份验证:客户端证书,Password, Plain Tokens,JWT(JSON Web Token),HTTP basic auth等。你可以同时启用多种认证,一般建议至少使用两种:
- 为验证
normal users身份的客户端证书方式
- 为验证
Service accounts身份的 JWT Tokens方式
使用客户端证书进行身份验证
理解数字证书
非对称加密算法是证书的基础。数字签名、数字证书等一系列概念有点绕,但只要记住:公钥用来加密,私钥用来签名 就可以了。
怎么理解呢?公钥可以随意分发,谁都可以持有,如果你用私钥加密,任何持有对应公钥的人都可以解密,这样做和没加密一样,没什么意义。因此,我们需要用公钥加密,只有持有私钥的那个人才能解密。私钥之所以称为私钥,一定会私密保存,不会向其他人泄漏。同时,用私钥加密虽然没有意义,但如果别人用公钥解开了私钥加密的信息,就能够证明信息是由私钥持有者发出的,验证了信息发送者的身份,这就是数字签名。
每个人制作好自己的公钥和私钥,然后把公钥发布出去。两个人如果都有对方的公钥,就可以用对方的公钥给对方发送加密信息,同时附上用私钥加密的信息摘要作为数字签名,证明消息发送者的身份。
通过加密防止了窃听风险,通过数字签名防止了冒充风险,数字签名内的消息摘要防止了篡改风险,一起看似很完美。
等等,这里有个很重要的问题被忽略了:如何安全的将公钥发布出去?如果双方希望安全通信,最好当面交换公钥,以免被别人冒充,并且要保护好自己的电脑,避免公钥被别有用心的人替换。现实中不可能这样分发公钥,效率太低,几乎无法大规模实行。于是出现了CA(certificate authority),为公钥做认证。CA用自己的私钥,对申请用户的公钥和一些身份信息加密,生成"数字证书"(Digital Certificate)。你现在可以用任何方式将内含公钥的数字证书发布出去,例如有客户发起请求,希望以HTTPS的方式访问你的WEB服务,你可以在第一次回复客户的响应中带上数字证书。客户拿到你的数字证书,用CA的公钥解开数字证书,安全的获得你的公钥。有了CA为你的数字证书背书,客户可以确定你的身份,不是有人在冒充你。
那么CA的公钥如何安全的分发呢?首先,证书的签发是“链”式结构,给你签发证书的CA,它的证书可能还是由上一级CA机构签发的,这样一直往上追溯,最终会到某个“根证书”。如果“根证书”是被我们信任的,那么整条“链”上的证书都可信。

其次,操作系统都内置了“受信任的根证书”。我们拿到某个证书,如果它的根证书在系统的“受信任的根证书”列表中,那么这个证书就是可信的。例如知乎的证书:

可以看到,它的根证书是DigiCert Global Root CA,在操作系统的“受信任的根证书”列表中能找到它:

根证书是通过预装的方式完成的分发,因此安装来源不明的操作系统有风险,可能潜伏了非法的根证书。一旦被植入了非法的根证书,一整套的安全体系瞬间土崩瓦解。同时,不能随意向系统中添加可信任的根证书,你很难验证根证书的真伪,它已经是root,没人能为它做背书了。12306网站早期的根证书就不在操作系统的“受信任根证书”列表中,需要用户手工安装,在网上引起轩然大波。最终12306在17年底的时候换成了Digicert的证书。
简单总结一下基于非对称加密算法的公钥/私钥体系,公钥用来加密,私钥用来签名,引入CA保证公钥的安全分发。你可以找CA签发数字证书,那么你的客户就可以根据本地“受信任的根证书”验证你的数字证书,从而确认你的身份,然后用证书内包含的公钥给你发加密的信息。同样,你也可以要求对方的数字证书,以便确认对方的身份,并给他回加密的信息。
理解了数字证书的基本原理,我们再看看Kubernetes中如何使用客户端证书进行身份验证。
数字证书在Kubernetes中的应用
Kubernetes各组件之间的通信都是基于TLS,实现服务的加密访问,同时支持基于证书的双向认证。
我们在搭建私有Kubernetes集群时,一般是自建root CA,因为参与认证的所有集群节点,包括远程访问集群的客户端桌面都完全由自己控制,我们可以安全的将根证书分发到所有节点。有了CA,我们再用CA的私钥/公钥为各个组件签发所需的证书。
CA的创建,以及一系列客户端、服务端证书的签发,实际上是建立了Kubernetes集群的PKI(Public key infrastructure)。
Kubernetes中的组件比较多,所以需要的证书会非常多,这篇文档做了介绍。我按证书的用途归类总结一下:
CA证书
- 服务端证书
- 访问
API server时进行身份验证的客户端证书
kubelet -> API server
controller-manager -> API server
kube-scheduler -> API server
admin用户 -> API server
API server 访问其他组件时进行身份验证的客户端证书
API server -> etcd
API server -> kubelet
API server -> aggregated API server
etcd 相关功能
etcd 集群中节点互相通信使用的客户端证书
- 如果
etcd是以Pod方式运行,针对etcd的 Liveness 需要的客户端证书
Service accounts 私钥/公钥对,用于生成Service accounts身份验证的 JWT Tokens
最后一个不是证书,不过也在Kubernetes PKI的管理范围。关于Service accounts 私钥/公钥对的作用,后面会讲到。
理论上CA根证书可以只使用一个,不过为了安全和方便管理,官方强调在不同的上下文最好使用不同的CA:
Warning: Do not reuse a CA that is used in a different context unless you understand the risks and the mechanisms to protect the CA’s usage.
可以看出,API server是核心组件,其他组件、包括admin用户对它的访问都需要TLS双向认证,所以会有API server的服务端证书和各个组件的客户端证书。API server作为客户端需要访问etcd、kubelet和aggregated API server,所以也会有相应的服务端、客户端证书。
当我们使用kubeadm安装Kubernetes时,kubeadm会为我们生成上述的一系列私钥和证书,放在/etc/kubernetes/目录下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# tree --dirsfirst /etc/kubernetes/
/etc/kubernetes/
├── manifests 组件的配置文件,以Pod方式运行在集群中
│ ├── etcd.yaml
│ ├── kube-apiserver.yaml
│ ├── kube-controller-manager.yaml
│ └── kube-scheduler.yaml
├── pki
│ ├── etcd
│ │ ├── ca.crt etcd 集群CA证书
│ │ ├── ca.key etcd 集群CA私钥
│ │ ├── healthcheck-client.crt Liveness 健康检查使用的客户端证书
│ │ ├── healthcheck-client.key
│ │ ├── peer.crt etcd节点间通信使用的客户端证书
│ │ ├── peer.key
│ │ ├── server.crt etcd服务端证书
│ │ └── server.key
│ ├── apiserver.crt API Server 服务端证书
│ ├── apiserver.key
│ ├── apiserver-etcd-client.crt API server -> etcd
│ ├── apiserver-etcd-client.key
│ ├── apiserver-kubelet-client.crt API server -> kubelet
│ ├── apiserver-kubelet-client.key
│ ├── ca.crt CA证书
│ ├── ca.key CA私钥
│ ├── front-proxy-ca.crt aggregation 相关功能CA证书
│ ├── front-proxy-ca.key aggregation 相关功能CA私钥
│ ├── front-proxy-client.crt API server -> aggregated API server
│ ├── front-proxy-client.key
│ ├── sa.key Service accounts 私钥
│ └── sa.pub Service accounts 公钥
├── admin.conf admin -> API server
├── controller-manager.conf controller-manager -> API server
├── kubelet.conf kubelet -> API server
└── scheduler.conf kube-scheduler -> API server
|
注意,最后四个*.conf是kubeconfig file,内容包含了集群、用户、namespace等信息,还有用来认证的CA证书、客户端证书和私钥。例如admin.conf就是kubectl访问集群用到的kubeconfig file,缺省情况下kubectl会使用$HOME/.kube/config,你也可以通过KUBECONFIG环境变量,或kubectl 的 --kubeconfig 参数进行设置。
kubelet运行在每个工作节点,无法提前预知 node 的 IP 信息,所以 kubelet 一般不会明确指定服务端证书, 而是只指定 CA 根证书, 让 kubelet 根据本机信息自动生成服务端证书,保存到配置参数指定的–cert-dir目录中。cert-dir的缺省值是/var/lib/kubelet/pki。
1
2
3
4
5
6
7
|
# tree /var/lib/kubelet/pki
/var/lib/kubelet/pki
├── kubelet-client-2019-04-28-10-48-13.pem
├── kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2019-04-28-10-48-13.pem
├── kubelet.crt kubelet服务端证书
└── kubelet.key kubelet服务端私钥
|
另外,API server有很多认证相关的启动参数,参数名称让人容易混淆,有人还专门提了issue,这个回答根据用途对这些参数进行了分类,说明的非常清晰。
API server 如何用客户端证书进行身份验证
前面提到,当用户使用kubectl访问API server时,需要以某种方式进行身份验证,最常用的方式就是使用客户端证书。Kubernetes是没有 user 这种 API 对象,kubectl的用户身份信息就包含在客户端证书中。API server验证了客户端证书,也就可以从证书中获得用户名和所属的group。
我们以/etc/kubernetes/admin.conf 为例,看看客户端证书中提供了那些信息。
先查看admin.conf文件的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
$ kubectl --kubeconfig /etc/kubernetes/admin.conf config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://172.18.100.90:6443
name: test
contexts:
- context:
cluster: test
user: kubernetes-admin
name: kubernetes-admin@test
current-context: kubernetes-admin@test
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
|
这个文件提供了API server的地址,以及身份验证用到的证书:
certificate-authority-data :CA证书
client-certificate-data :客户端证书
client-key-data: 客户端私钥
客户端证书是以base64编码的方式保存在client-certificate-data字段中,我们将证书提取出来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# cat /etc/kubernetes/admin.conf | grep client-certificate-data | cut -d " " -f 6 | base64 -d > admin.crt
# cat admin.crt
-----BEGIN CERTIFICATE-----
MIIC8jCCAdqgAwIBAgIIaBuxevPYGaswDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0xOTA0MjgwMjQwMDBaFw0yMDA0MjcwMjQwMDNaMDQx
FzAVBgNVBAoTDnN5c3RlbTptYXN0ZXJzMRkwFwYDVQQDExBrdWJlcm5ldGVzLWFk
bWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvRTIQ4YEMh0mUWKP
17pLdUocgZMLVCK6tYmj0DJIihRk+wKvNzYSStfxsug9nnEqVVzmbW5/UxER776H
844y/1NGk/8LsDIkFGspf3cEmQ8OE8TlLNW7h9gWIGymLQ/K1qhYfNOPDYoJXPix
eUWTJgn0+neJNbJ3JoJk2WRlDFwbE0uXgYYczuDcJablSdbb8Oc+E4qJ1U7u9YMN
Bo/JBY68wYtdjXHl6Mg28aCioVZrs5eZWkNzNpXMVjQwFdZAdWbnS3OJGN1b6IrV
gWk9PMoCE2TtFv5NdlHSYFtEAaBEwfl3/D3rGHKb4ZH/fgKWsepy8ffxxibM6pND
pLnmAwIDAQABoycwJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH
AwIwDQYJKoZIhvcNAQELBQADggEBALPYorv2mlXyu6jpX/6gE1kTvpPGK4vylfSY
9jl4PtQZgRaXvmVsUKpyIdtVhdMZp9EFaYNC4AYkqaVEOoAbU96SYhdO/6h7Rn8T
0Ae+f1Vwt+8GxErEN3xp4noHfXM0eSEuFLPXt43BBJInYRyx1J0urAjYtNCvc9wX
uQFVmNKsqgmjvHQsRkvKcb8HEzcaD1TqqnTpq3usGjNggVZFTChB58R909yGPEXL
n7VsilmN86gom3fgqwCn2C00iKcuzCOwYN2T+Mi8KI2DraDDoVeRMSaYQNUfKNIX
Ngeod/C4piq+OAdyrPPFEINdLi404EYHyod0CgiD6uhoX5W06O4=
-----END CERTIFICATE-----
|
使用openssl查看证书内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# openssl x509 -in ./admin.crt -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 7501784745950845355 (0x681bb17af3d819ab)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=kubernetes
Validity
Not Before: Apr 28 02:40:00 2019 GMT
Not After : Apr 27 02:40:03 2020 GMT
Subject: O=system:masters, CN=kubernetes-admin
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
......
|
注意这一行:
1
|
Subject: O=system:masters, CN=kubernetes-admin
|
API server会将证书中CN(Common Name)作为用户名,O(Organization)作为用户所属的group。API server从这个证书得到的信息是:admin用户所属的group是system:masters。至此,身份验证阶段完成。
下一步是授权检查,也就是检查用户有没有权限执行这个操作。这是另外一个话题,本文不做详细讨论,只是简单介绍一下。根据官方文档,Kubernetes提供了缺省的 ClusterRole - group 绑定关系,已经将system:masters group 和 角色 cluster-admin绑定到了一起:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# kubectl get clusterrolebindings cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2019-04-28T02:40:17Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
resourceVersion: "98"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/cluster-admin
uid: f58a87a7-695e-11e9-91ca-005056ac1c1c
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:masters
|
这个绑定关系的意思是,属于system:mastersgroup的用户,都拥有cluster-admin角色包含的权限。我们再看看角色cluster-admin的具体权限信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# kubectl get clusterrole cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2019-04-28T02:40:17Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
resourceVersion: "44"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/cluster-admin
uid: f5523c21-695e-11e9-91ca-005056ac1c1c
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
|
从rules列表可以看出,cluster-admin这个角色对所有资源都有无限制的操作权限。因此,使用了这个kubeconfig file的kubectl的请求就有了操控和管理整个集群的权限。
使用JWT Tokens进行身份验证
运行在Pod中的进程需要访问API server时,同样需要进行身份验证和授权检查。如何让Pod具有用户身份呢?通过给Pod指定service account来实现。service account是Kubernetes内置的一种 “服务账户”,它为Pod中的进程提供了一种身份标识。如果Pod没有显式的指定service account,系统会自动为其关联default service account。
我们自己创建service account对象非常简单:
1
2
3
4
5
6
7
8
|
//serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-example
# kubectl apply -f serviceaccount.yaml
serviceaccount/nginx-example created
|
查看刚刚创建的service account:
1
2
3
4
5
6
7
8
9
10
11
|
# kubectl describe serviceaccounts nginx-example
Name: nginx-example
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"nginx-example","namespace":"default"}}
Image pull secrets: <none>
Mountable secrets: nginx-example-token-r2cv6
Tokens: nginx-example-token-r2cv6
Events: <none>
|
当service account对象创建成功,controller-manager会发现这个新对象,然后为它生成token。token实际上是secret对象,内部包含了用来身份验证的token。service account对象的Tokens列引用的就是controller-manager为它创建的token。
我们来看看token的内容:
1
2
3
4
5
6
7
8
9
10
11
|
# kubectl get secrets nginx-example-token-r2cv6 -o yaml
apiVersion: v1
data:
ca.crt: (APISERVER'S CA BASE64 ENCODED)
namespace: ZGVmYXVsdA==
token: (BEARER TOKEN BASE64 ENCODED)
kind: Secret
metadata:
......
type: kubernetes.io/service-account-token
|
可以看到,这个secret对象的type是service-account-token,包含了三部分数据:
ca.crt: API Server的CA证书,用于Pod中的进程访问API Server时对服务端证书进行校验
namespace: secret所在namespace,使用了base64编码
token:JWT Tokens
JWT Tokens 是 controller-manager 用 service account私钥sa.key签发的,其中包含了用户的身份信息,API Server可以用sa.pub验证token,拿到用户身份信息,从而完成身份验证。
如果是使用kubeadm安装的Kubernetes,我们可以在/etc/kubernetes/manifests/目录中的配置文件确认sa.key和sa.pub的作用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# cat /etc/kubernetes/manifests/kube-controller-manager.yaml
...
spec:
containers:
- command:
- kube-controller-manager
...
- --service-account-private-key-file=/etc/kubernetes/pki/sa.key
...
# cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
- kube-apiserver
...
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
...
|
controller-manager用私钥sa.key签名,API Server用公钥sa.pub验签。
运行在Pod中的进程在向API server发起HTTP请求时,HTTP header中会携带token,API server从header中拿到token,进行身份验证:
1
|
Authorization: Bearer [token]
|
JWT Tokens的是由用.分割的三部分组成:
因此,一个JWT Tokens看起来是这样的:
1
|
xxxxxxx.yyyyyyyy.zzzzzzz
|
Header和Payload都是base64编码的JSON,以上面nginx-example关联的token为例,看看Header和Payload的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
$ kubectl describe secrets nginx-example-token-r2cv6 | grep token: | cut -d " " -f 7
eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im5naW54LWV4YW1wbGUtdG9rZW4tcjJjdjYiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibmdpbngtZXhhbXBsZSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjBmZWRlOWM1LTc2YjUtMTFlOS05MWNhLTAwNTA1NmFjMWMxYyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0Om5naW54LWV4YW1wbGUifQ.VOjVQBr5PKg1WyZYtIW0Fos5fxFN4cYE3Mz9p1eWbQP6rQRQGDEiGX-LBuM6ECI9cpSL-F4nYQAL9vmIlA4vbAgS4OFgC4nwu8SzLu2FVeE7RDpguvsdAsj-4T_LxEGX1RPljGTpvlt8HRjTnp9K8W4dy7PyJQEB5XvCf-IVNAs3zESgmuJ7wJwO7mXQe5WdeqhI5vXjcZiXP97oH0VRYT1vTKVP-GooC5YfaNhU7rHoJ0gmR10xNqZjwKGsHKkq5maC5BOrXFLlHRqVRwm9-hRn-ZLgAoCwujCIpLvPaFUR8HaatzX4GQ_HWev2soJnk1qcav0smxfjC-fu540vZA
$ kubectl describe secrets nginx-example-token-r2cv6 | grep token: | cut -d " " -f 7 | cut -d "." -f 1 | base64 -d | python -mjson.tool
{
"alg": "RS256",
"kid": ""
}
$ kubectl describe secrets nginx-example-token-r2cv6 | grep token: | cut -d " " -f 7 | cut -d "." -f 2 | base64 -d | python -mjson.tool
{
"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "default",
"kubernetes.io/serviceaccount/secret.name": "nginx-example-token-r2cv6",
"kubernetes.io/serviceaccount/service-account.name": "nginx-example",
"kubernetes.io/serviceaccount/service-account.uid": "0fede9c5-76b5-11e9-91ca-005056ac1c1c",
"sub": "system:serviceaccount:default:nginx-example"
}
|
Header中的alg指明了签名用到的加密算法,Payload 中包含了用户的身份信息,可以知道这个service account属于的namespace为default,名称为nginx-example。
第三部分Signature的构造方式如下,如果加密算法选择了PKCS SHA:
1
2
3
4
|
PKCSSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
|
controller-manager用sa.key签名,API Server用公钥sa.pub验签,进行身份验证。
如果先深入了解JWT(JSON Web Token),建议阅读这篇文档<JWT: The Complete Guide to JSON Web Tokens>
Pod中的进程如何获得这个token呢?Kubernetes在创建Pod时,会将service account token映射到Pod的/var/run/secrets/kubernetes.io/serviceaccount 目录下。我们通过一个例子演示一下。
创建Pod:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// simple.yaml
apiVersion: v1
kind: Pod
metadata:
name: firstpod
spec:
serviceAccountName: nginx-example
containers:
- image: nginx
name: stan
$ kubectl apply -f simple.yaml
pod/firstpod created
|
查看Pod内/var/run/secrets/kubernetes.io/serviceaccount目录的内容:
1
2
3
4
|
$ kubectl exec firstpod -- ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token
|
查看Pod内文件/var/run/secrets/kubernetes.io/serviceaccount/token的内容:
1
2
3
4
5
6
7
8
9
|
$ kubectl exec firstpod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d "." -f 2 | base64 -d | python -mjson.tool
{
"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "default",
"kubernetes.io/serviceaccount/secret.name": "nginx-example-token-r2cv6",
"kubernetes.io/serviceaccount/service-account.name": "nginx-example",
"kubernetes.io/serviceaccount/service-account.uid": "0fede9c5-76b5-11e9-91ca-005056ac1c1c",
"sub": "system:serviceaccount:default:nginx-example"
}
|
可以看到,映射进Pod中的token,正是我们在配置中通过serviceAccountName指定的nginx-example。Pod中的进程可以通过访问文件/var/run/secrets/kubernetes.io/serviceaccount/token拿到token。
如何为service account授权?通过定义service account和role的绑定完成。本文简单演示一下,详细的说明参加官方文档。
创建role:
1
2
3
4
5
6
7
8
9
10
11
12
|
// example-role.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
$ kubectl apply -f example-role.yaml
role.rbac.authorization.k8s.io/example-role created
|
role可以理解为一组权限的集合,例如上面创建的example-role对default Namesapce内的Pods有get、watch和list操作权限。
下一步就是将service account和role进行绑定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//example-rolebinding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: nginx-example
namespace: default
roleRef:
kind: Role
name: example-role
apiGroup: rbac.authorization.k8s.io
$ kubectl apply -f example-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/example-rolebinding created
|
通过绑定的创建,service account就拥有了role定义的权限。
总结
用户对API server的访问需要通过身份验证、授权和准入控制这三个阶段的检查。
一般集群外部用户访问API Server使用客户端证书进行身份验证。Kubernetes各组件之间的通信都使用了TLS加密传输,同时支持基于证书的双向认证。因此Kubernetes的安装过程涉及很多证书的创建,本文分类介绍了这些证书的作用。
集群内Pod中的进程访问API server时,使用service account关联的token进行身份验证。
每个Pod都会关联一个service account,没有明确指定时使用default。当我们创建service account对象,controller-manager会为这个service account生成Secret,内部包含了用来身份验证的JWT Tokens。Kubernetes会将token文件mount到Pod的/var/run/secrets/kubernetes.io/serviceaccount/token,Pod内的进程在向API server发起的HTTP时,就可以在请求头中携带这个token。