主要内容
简介
Kubernetes以REST API的形式向外提供接口来管理集群。这就引发了安全问题:假如别人知道你的API地址,那么你的服务器就很容易遭到恶意破坏。当然Kubernetes开发团队不会不了解这些问题,因此,它提供了多种安全手段来保障服务器的安全。
API SERVER
所有的安全问题都是针对API SERVER而言的,因为它提供了管理集群的唯一接口。API SERVER使用https协议工作,使数据传输更加安全,传输内容不容器被窃听
1 2 3 4 |
[k8s@centos74vm:~]$ kubectl cluster-info Kubernetes master is running at https://192.168.0.153:6443 KubeDNS is running at https://192.168.0.153:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy |
Kubernetes安全的三个层次
kubernetes在三个层次上保证了集群的安全,其中之一是上面说的https协议(即TLS加密)。假如我们的集群是通过kubeadm
命令建立的,那么TLS证书也由kubeadm负责创建,它们保存在/etc/kubernetes/pki
目录下
1 2 3 4 5 6 |
[root@centos74vm:~]# cd /etc/kubernetes/pki/ [root@centos74vm:/etc/kubernetes/pki]# ls apiserver.crt apiserver.key ca.crt front-proxy-ca.crt front-proxy-client.key apiserver-etcd-client.crt apiserver-kubelet-client.crt ca.key front-proxy-ca.key sa.key apiserver-etcd-client.key apiserver-kubelet-client.key etcd front-proxy-client.crt sa.pub |
除了TLS加密之外,另外两个是:
- 认证(Authentication)
- 授权(Authorization)
认证是为了识别访问者是谁,是否是一个合法的用户,而授权则决定用户可以干什么,拥有什么权限。
认证
认证过程中涉及的相关概念:
- ServiceAccount
- JWT
- 用户
- 组
ServiceAccount中主要保存的信息就是一个令牌(token),令牌格式就是JWT(json web token)。JWT本身并不是Kubernetes中的概念,详细请参考JWT。
用户、组和JWT之间的关系并不完全独立,JWT保存着用户和组的名称,下面是从Kubernetes默认ServiceAccount中解码的PAYLOAD部分JWT内容
1 2 3 4 5 6 7 8 9 |
{ "iss": "kubernetes/serviceaccount", "kubernetes.io/serviceaccount/namespace": "default", "kubernetes.io/serviceaccount/secret.name": "default-token-7j4cw", "kubernetes.io/serviceaccount/service-account.name": "default", "kubernetes.io/serviceaccount/service-account.uid": "dde1fb85-d593-11e8-b939-e0d55ecb19d6", "sub": "system:serviceaccount:default:default" } |
可以看到,ServiceAccount所属的namespace是default,ServiceAccount名称也是default。
最后的sub由三个部分组成:
- system:serviceaccount (用户所属的组)
- default (namespace名称)
- default (用户名称)
Kubernetes内置了几个特别的组:
- system:unauthenticated 包括未认证的用户
- system:authenticated 包括已经认证的用户
- system:serviceaccounts 系统范围 serviceaccount中的所有用户
- system:serviceaccounts:<namespace> 特定名称空间范围内serviceaccount中的所有用户
ServiceAccount
当kubernetes集群搭建好后,系统中就存在多个ServiceAccount,它们隶属于不同的名称空间,不同名称空间之间的ServiceAccount名称可以相同。通过下面的命令查看当前系统的ServiceAccount
1 2 3 4 |
[k8s@centos74vm:~]$ kubectl get serviceaccounts NAME SECRETS AGE default 1 2d5h |
如果不添加其他参数,默认只返回default名称空间中的ServiceAccount。
查看所有名称空间的account,下面的输出省略部分内容
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[k8s@centos74vm:~]$ kubectl get serviceaccounts --all-namespaces NAMESPACE NAME SECRETS AGE default default 1 2d5h kube-public default 1 2d5h kube-system attachdetach-controller 1 2d5h kube-system bootstrap-signer 1 2d5h ...... kube-system service-controller 1 2d5h kube-system statefulset-controller 1 2d5h kube-system tiller 1 5h kube-system token-cleaner 1 2d5h kube-system ttl-controller 1 2d5h |
创建自己的ServiceAccount
可以创建自己的ServiceAccount,创建方法很简单
1 2 3 4 5 6 7 |
[k8s@centos74vm:~]$ kubectl create serviceaccount myaccount serviceaccount/myaccount created [k8s@centos74vm:~]$ kubectl get serviceaccounts NAME SECRETS AGE default 1 2d6h myaccount 1 6s |
如果没有指定名称空间,默认在default中创建。ServiceAccount创建的同时会自动创建secrets
,名称以account的名称为前缀
1 2 3 4 5 |
[k8s@centos74vm:~]$ kubectl get secrets NAME TYPE DATA AGE default-token-7j4cw kubernetes.io/service-account-token 3 2d6h myaccount-token-2dbww kubernetes.io/service-account-token 3 2m36s |
ServiceAccount中的token
前面我们说过,ServiceAccount中主要用于保存JWT的token(不同ServiceAccount token不一样),那么怎样查看呢?很简单
1 2 3 4 5 6 7 8 9 10 |
[k8s@centos74vm:~]$ kubectl describe serviceaccount default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-7j4cw Tokens: default-token-7j4cw Events: <none> |
输出的内容告诉我们,default
ServiceAccount对应的token名称是default-token-7j4cw,拿到token的名称,就可以查看这个token,token属于Kubernetes中的secrets
资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[k8s@centos74vm:~]$ kubectl describe secrets default-token-7j4cw Name: default-token-7j4cw Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name: default kubernetes.io/service-account.uid: dde1fb85-d593-11e8-b939-e0d55ecb19d6 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tN2o0Y3ciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImRkZTFmYjg1LWQ1OTMtMTFlOC1iOTM5LWUwZDU1ZWNiMTlkNiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.js8BQWKf3r9YgA_o7CNqcLHoMiNmzPQ75YtBlIRF5_ULzxe6esLLY6XFMsRFmSieVFblxysddYFmdA9h89MJg_NLP1gZ_R79DQl9tZk5rQgo4yePuhJWZlWfETYJe0ZeQV5aGpQ3T0v_Sujs738VcLhRr9LUQzHHOrw0sGHDTkeOQWXqOPW2LXdMT4ukxUgfWU3-cjdwdaSfdeL5gUS4K5_Z9ZzUzYVJ-cJaIYOZETnmulLt2VJeXAn4KcJZfejGbrmLjQ3iMdBLyNRPq-Uhwe1KoY3bAUFzpzRtkF8nz1UI33l0TXotd0J9FTNnRlm0Lp-RUWIwDD_Nf1qikNjIGA |
最后一行就是具体token的内容,可以复制这个token到JWT官方解码,地址是JWT Debugger
Pod和ServiceAccount
每一个Pod创建的时候,都会被分配到指定的ServiceAccount下面,通常是default,也可以在编写Pod的manifest文件的时候指定。
例如
1 2 3 4 5 6 7 |
[k8s@centos74vm:~]$ kubectl run nginx --image=nginx [k8s@centos74vm:~]$ kubectl edit pod nginx-dbddb74b8-5pb2m .... serviceAccount: default serviceAccountName: default .... |
其中就可以看到它的ServiceAccount确实是default。
另外,在Pod创建的时候,相关的认证文件会被以volume的形式挂载进Pod,目的是方便在Pod里访问API SERVER(例如kubernetes的dashboard),下面的内容来自已经部署的Pod。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
..... spec: containers: - image: nginx name: nginx volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-7j4cw readOnly: true volumes: - name: default-token-7j4cw secret: defaultMode: 420 secretName: default-token-7j4cw ....... |
测试ServiceAccount中的token
token用于验证用户的合法性,现在我们使用default ServiceAccount的token进行测试。前面已经介绍了如何取得token,这里不再说明。
首先第一次向API SERVER发送请求,这次请求没有递交token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[k8s@centos74vm:~]$ curl https://192.168.0.153:6443 { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"", "reason": "Forbidden", "details": { }, "code": 403 } |
服务器响应的是匿名用户禁止访问
接下来进行第二次请求,这次请求包括token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[k8s@centos74vm:~]$ export CURL_CA_BUNDLE=/etc/kubernetes/pki/ca.crt #TLS证书加密,curl使用这个变量 [k8s@centos74vm:~]$ export TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tN2o0Y3ciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImRkZTFmYjg1LWQ1OTMtMTFlOC1iOTM5LWUwZDU1ZWNiMTlkNiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.js8BQWKf3r9YgA_o7CNqcLHoMiNmzPQ75YtBlIRF5_ULzxe6esLLY6XFMsRFmSieVFblxysddYFmdA9h89MJg_NLP1gZ_R79DQl9tZk5rQgo4yePuhJWZlWfETYJe0ZeQV5aGpQ3T0v_Sujs738VcLhRr9LUQzHHOrw0sGHDTkeOQWXqOPW2LXdMT4ukxUgfWU3-cjdwdaSfdeL5gUS4K5_Z9ZzUzYVJ-cJaIYOZETnmulLt2VJeXAn4KcJZfejGbrmLjQ3iMdBLyNRPq-Uhwe1KoY3bAUFzpzRtkF8nz1UI33l0TXotd0J9FTNnRlm0Lp-RUWIwDD_Nf1qikNjIGA [k8s@centos74vm:~]$ curl -H "Authorization: Bearer $TOKEN" https://192.168.0.153:6443 { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "forbidden: User \"system:serviceaccount:default:default\" cannot get path \"/\"", "reason": "Forbidden", "details": { }, "code": 403 } |
注意curl中使用的请求头:"Authorization: Bearer $TOKEN"
,这是JWT认证的固定格式。
服务器响应的信息虽然还是是403,但是已经正确识别出了用户信息system:serviceaccount:default:default
,证明认证已经通过。至于为什么是403,原因就是接下来要说的授权。
授权
当认证通过,下一步要做的就是授权,授权决定一个用户可以干什么。Kubernetes可以配置多种授权方式,其中目前使用的是RBAC(Role Based Access Control)。RBAC涉及几个重要概念,它们是:
- Role
- RoleBinding
- ClusterRole
- ClusterRoleBinding
首先必须清楚一点:授权离不开认证,授权是基于ServiceAccount的。Role可以理解为权限(例如get,list,update,delete,add),它决定了ServiceAccount可以干什么,RoleBinding用于把Role绑定到指定的ServiceAccount。
ClusterRole和ClusterRoleBinding的作用一样,不同的是,它们的作用域是整个集群,而Role和RoleBinding的作用域是namespace。
下图是Role的绑定
从上图可知,Role不仅可以和ServiceAccount绑定,还可以和用户、组绑定。
创建Role
通过kubectl create role来创建role,由于default ServiceAccount不允许执行任何操作
1 2 3 |
[k8s@centos74vm:~]$ kubectl auth can-i list pods -n default --as=system:serviceaccount:default:default no - no RBAC policy matched |
现在我们尝试开放它的权限,允许它访问Pod的列表。
1 2 3 |
[k8s@centos74vm:~]$ kubectl create role default-read-pod --verb=get,list,watch --resource=pods role.rbac.authorization.k8s.io/default-read-pod created |
创建RoleBinding
RoleBinding的创建和Role的创建差不多
1 2 3 |
[k8s@centos74vm:~]$ kubectl create rolebinding default --role=default-read-pod --serviceaccount=default:default rolebinding.rbac.authorization.k8s.io/default created |
测试效果
现在我们已经为默认的ServiceAccount添加了权限,现在它应该可以获取所在名称空间里的pod列表。
1 2 3 |
[k8s@centos74vm:~]$ kubectl auth can-i list pods -n default --as=system:serviceaccount:default:default yes |
curl测试:
下面使用的token依然是默认的。
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 |
[k8s@centos74vm:~]$ curl -H "Authorization: Bearer $TOKEN" https://192.168.0.153:6443/api/v1/namespaces/default/pods { "kind": "PodList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces/default/pods", "resourceVersion": "272327" }, "items": [ { "metadata": { "name": "curl-755d5f58f4-ljhxz", "generateName": "curl-755d5f58f4-", "namespace": "default", "selfLink": "/api/v1/namespaces/default/pods/curl-755d5f58f4-ljhxz", "uid": "e4e304b4-d6a9-11e8-a981-e0d55ecb19d6", "resourceVersion": "158962", "creationTimestamp": "2018-10-23T09:56:21Z", "labels": { "pod-template-hash": "755d5f58f4", "run": "curl" }, "ownerReferences": [ { "apiVersion": "apps/v1", "kind": "ReplicaSet", "name": "curl-755d5f58f4", "uid": "e4cc6839-d6a9-11e8-a981-e0d55ecb19d6", "controller": true, "blockOwnerDeletion": true } ] } ........ |
转载请注明:Pure nonsense » Kubernetes认证和授权