Kubernetes集群的身份验证

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

access-control-overview

Kubernetes中的用户有两种类型:service accountsnormal usersservice accountsKubernetes管理,它是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机构签发的,这样一直往上追溯,最终会到某个“根证书”。如果“根证书”是被我们信任的,那么整条“链”上的证书都可信。

certificates in a chain

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

zhihu crt

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

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
    • etcd
    • kubelet
  • 访问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作为客户端需要访问etcdkubeletaggregated 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 

注意,最后四个*.confkubeconfig file,内容包含了集群、用户、namespace等信息,还有用来认证的CA证书、客户端证书和私钥。例如admin.conf就是kubectl访问集群用到的kubeconfig file,缺省情况下kubectl会使用$HOME/.kube/config,你也可以通过KUBECONFIG环境变量,或kubectl--kubeconfig 参数进行设置。

kubelet运行在每个工作节点,无法提前预知 nodeIP 信息,所以 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)作为用户所属的groupAPI 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 filekubectl的请求就有了操控和管理整个集群的权限。

# 使用JWT Tokens进行身份验证

运行在Pod中的进程需要访问API server时,同样需要进行身份验证和授权检查。如何让Pod具有用户身份呢?通过给Pod指定service account来实现。service accountKubernetes内置的一种 “服务账户”,它为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会发现这个新对象,然后为它生成tokentoken实际上是secret对象,内部包含了用来身份验证的tokenservice 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对象的typeservice-account-token,包含了三部分数据:

  • ca.crtAPI ServerCA证书,用于Pod中的进程访问API Server时对服务端证书进行校验
  • namespacesecret所在namespace,使用了base64编码
  • tokenJWT Tokens

JWT Tokenscontroller-managerservice account私钥sa.key签发的,其中包含了用户的身份信息,API Server可以用sa.pub验证token,拿到用户身份信息,从而完成身份验证。

如果是使用kubeadm安装的Kubernetes,我们可以在/etc/kubernetes/manifests/目录中的配置文件确认sa.keysa.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中会携带tokenAPI serverheader中拿到token,进行身份验证:

1
Authorization: Bearer [token]

JWT Tokens的是由用.分割的三部分组成:

  • Header
  • Payload
  • Signature

因此,一个JWT Tokens看起来是这样的:

1
xxxxxxx.yyyyyyyy.zzzzzzz

HeaderPayload都是base64编码的JSON,以上面nginx-example关联的token为例,看看HeaderPayload的内容:

 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属于的namespacedefault,名称为nginx-example

第三部分Signature的构造方式如下,如果加密算法选择了PKCS SHA:

1
2
3
4
PKCSSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

controller-managersa.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-examplePod中的进程可以通过访问文件/var/run/secrets/kubernetes.io/serviceaccount/token拿到token

如何为service account授权?通过定义service accountrole的绑定完成。本文简单演示一下,详细的说明参加官方文档

创建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-roledefault Namesapce内的Podsgetwatchlist操作权限。

下一步就是将service accountrole进行绑定:

 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 TokensKubernetes会将token文件mountPod/var/run/secrets/kubernetes.io/serviceaccount/tokenPod内的进程在向API server发起的HTTP时,就可以在请求头中携带这个token

comments powered by Disqus