《深刻浅出 Kubernetes 安全机制》最先发布在 blog.hdls.me/15432142284…html
在 Kubernetes 中,全部资源的访问和变动都是围绕 APIServer 展开的。好比说 kubectl 命令、客户端 HTTP RESTFUL 请求,都是去 call APIServer 的 API 进行的,本文就重点解读 k8s 为了集群安全,都作了些什么。node
首先,Kubernetes 官方文档给出了上面这张图。描述了用户在访问或变动资源的以前,须要通过 APIServer 的认证机制、受权机制以及准入控制机制。这三个机制能够这样理解,先检查是否合法用户,再检查该请求的行为是否有权限,最后作进一步的验证或添加默认参数。web
Kubernetes 中有两种用户,一种是内置“用户” ServiceAccount,另外一种我称之为天然人。json
所谓天然人就是指区别于 pod 等资源概念的“人”,能够理解成实际操做 "kubectl" 命令的人。admin 能够分发私钥,但天然人能够储存相似 KeyStone 甚至包含帐号密码的文件,因此 k8s 中没有对天然人以 API 对象描述之。bootstrap
在典型的 Kubernetes 集群中,API 一般服务在 443 端口,APIServer 提供自签名证书。当你使用 kube-up.sh
建立集群用户时,证书会自动在 $USER/.kube/config
中建立出来,然后续用 kubectl
命令访问 APIServer 时,都是用这个证书。api
与之相反,k8s 中以 API 对象的形式描述和管理 ServiceAccount。它们被绑定在某个具体的 namespace 中,能够由 APIServer 自动建立出来或手动 call k8s API。安全
k8s 中的认证机制,是在用户访问 APIServer 的第一步。一般是一个完整的 HTTP 请求打过来,可是这一步每每只检测请求头或客户端证书。bash
认证机制目前有客户端证书、bearer tokens、authenticating proxy、HTTP basic auth 这几种模式。使用方式一般有如下几种:ide
X509 Client Certs: 客户端证书模式须要在 kubectl
命令中加入 --client-ca-file=<SOMEFILE>
参数,指明证书所在位置。ui
Static Token File: --token-auth-file=<SOMEFILE>
参数指明 bearer tokens
所在位置。
bearer tokens: 在 HTTP 请求头中加入 Authorization: Bearer <TOKEN>
。
Bootstrap Tokens: 与 bearer tokens
一致,但 TOKEN 格式为 [a-z0-9]{6}.[a-z0-9]{16}
。该方式称为 dynamically-managed Bearer token,以 secret
的方式保存在 kube-system namespace
中,能够被动态的建立和管理。同时,启用这种方式还须要在 APIServer 中打开 --enable-bootstrap-token-auth
,这种方式还处于 alpha 阶段。
Static Password File: 以参数 --basic-auth-file=<SOMEFILE>
指明 basic auth file 的位置。这个 basic auth file 以 csv 文件的形式存在,里面至少包含三个信息:password、username、user id,同时该模式在使用时须要在请求头中加入 Authorization: Basic BASE64ENCODED(USER:PASSWORD)
。
Service Account Tokens: 该方式一般被 pod 所使用,在 PodSpec
中指明 ServiceAccount 来访问 ApiServer。
除了以上列出来的几种方式外,还有一些比较特殊的访问方式,这里再也不详细解读。
当用户经过认证后,k8s 的受权机制将对用户的行为等进行受权检查。换句话说,就是对这个请求自己,是否对某资源、某 namespace、某操做有权限限制。
受权机制目前有 4 种模式:RBAC、ABAC、Node、Webhook。下面对这 4 种模式分别作分析。
Role-based access control (RBAC) 是基于角色的权限访问控制,一般是对于“内置用户”而言的。该模式是在 k8s v1.6 开发出来的。若要开启该模式,须要在 APIServer 启动时,设置参数 --authorization-mode=RBAC
。
RBAC 所使用的 API Group 是 rbac.authorization.k8s.io/v1beta1
,直到 Kubernetes v1.8 后,RBAC 模块达到稳定水平,所使用的 API Group 为 rbac.authorization.k8s.io/v1
。
所谓基于角色的权限访问控制,就是对某个用户赋予某个角色,而这个角色一般决定了对哪些资源拥有怎样的权限。
首先来看看这个 “内置用户”,在大多时候咱们都不使用 “天然人” 这个功能,而是使用 ServiceAccount,再对其余资源授予某个 ServiceAccount,就使得其可以以 “内置用户” 的身份去访问 APIServer。
建立一个 ServiceAccount 很简单,只须要指定其所在 namespace 和 name 便可。举个例子:
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: hdls
name: hdls-sa
复制代码
RBAC 中最重要的概念就是 Role
和 RoleBinding
。Role
定义了一组对 Kubernetes API 对象的操做权限,而 RoleBinding
则定义的是具体的 ServiceAccount 和 Role 的对应关系。
举个 Role 的例子以下:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: hdls
name: hdls-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
复制代码
其中: namespace: 在这里仅限于逻辑上的“隔离”,并不会提供任何实际的隔离或者多租户能力; rules:定义的是权限规则,容许“被做用者”,对 hdls 下面的 Pod 对象,进行 GET 和 LIST 操做; apiGroups:为 "" 表明 core API Group; resources:指的是资源类型,对此还能够进行详细的划分,指定能够操做的资源的名字,好比:
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-config"]
verbs: ["get"]
复制代码
verbs: 指的是具体的操做,当前 Kubernetes(v1.11)里可以对 API 对象进行的全部操做有 "get", "list", "watch", "create", "update", "patch", "delete"。
再看 RoleBinding 的例子:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: hdls-rolebinding
namespace: hdls
subjects:
- kind: ServiceAccount
name: hdls-sa
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: hdls-role
apiGroup: rbac.authorization.k8s.io
复制代码
能够看到,这个 RoleBinding 对象里定义了一个 subjects 字段,即“被做用者”。它的类型是 ServiceAccount,就是上面建立的 sa。这个 subjects 还能够是 User 和 Group,User 是指 k8s 里的用户,而 Group 是指 ServiceAccounts。
roleRef
字段是用来直接经过名字,引用咱们前面定义的 Role 对象(hdls-role),从而定义了 Subject 和 Role 之间的绑定关系。
此时,咱们再用 kubectl get sa -n hdls -o yaml
命令查看以前的 ServiceAccount
,就能够看到 ServiceAccount.secret
,这是由于 k8s 会为一个 ServiceAccount
自动建立并分配一个 Secret
对象,而这个 Secret
就是用来跟 APIServer 进行交互的受权文件: Token
。Token
文件的内容通常是证书或者密码,以一个 Secret
对象的方式保存在 etcd 当中。
这个时候,咱们在咱们的 Pod 的 YAML 文件中定义字段 .spec.serviceAccountName
为上面的 ServiceAccount name
便可声明使用。
若是一个 Pod 没有声明 serviceAccountName
,Kubernetes 会自动在它的 Namespace 下建立一个名叫 default
的默认 ServiceAccount
,而后分配给这个 Pod。然而这个默认 ServiceAccount 并无关联任何 Role。也就是说,此时它有访问 APIServer 的绝大多数权限。
须要注意的是 Role 和 RoleBinding 对象都是 Namespaced 对象,它们只对本身的 Namespace 内的资源有效。
而某个 Role 须要对于非 Namespaced 对象(好比:Node),或者想要做用于全部的 Namespace 的时候,咱们须要使用 ClusterRole 和 ClusterRoleBinding 去作受权。
这两个 API 对象的用法跟 Role 和 RoleBinding 彻底同样。只不过,它们的定义里,没有了 Namespace 字段。
值得一提的是,Kubernetes 已经内置了不少个为系统保留的 ClusterRole
,它们的名字都以 system:
开头。通常来讲,这些系统级别的 ClusterRole
,是绑定给 Kubernetes 系统组件对应的 ServiceAccount
使用的。
除此以外,Kubernetes 还提供了四个内置的 ClusterRole
来供用户直接使用:
cluster-admin:整个集群的最高权限。若是在 ClusterRoleBinding
中使用,意味着在这个集群中的全部 namespace 中的全部资源都拥有最高权限,随心所欲;若是在 RoleBinding
中使用,即在某个 namespace 中随心所欲。
admin:管理员权限。若是在 RoleBinding
中使用,意味着在某个 namespace 中,对大部分资源拥有读写权限,包括建立 Role 和 RoleBinding 的权限,但没有对资源 quota 和 namespace 自己的写权限。
edit:写权限。在某个 namespace 中,拥有对大部分资源的读写权限,但没有对 Role 和 RoleBinding 的读写权限。
view:读权限。在某个 namespace 中,仅拥有对大部分资源的读权限,没有对 Role 和 RoleBinding 的读权限,也没有对 seccrets 的读权限。
在 Kubernetes v1.9 以后,ClusterRole
有一种新的定义方法,就是使用 aggregationRule
将多个 ClusterRole
合成一个新的 ClusterRole
。
首先看个 k8s 官网的例子:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: monitoring
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-monitoring: "true"
rules: []
复制代码
其中 rules
字段没必要定义,会被 controller manager
自动填充。
能够看出 aggregationRule
就是将全部知足 label
条件的 ClusterRole
的合成一个 ClusterRole
,而这个新的 ClusterRole
权限为其余总和。
相对于 User 而言,k8s 还拥有“用户组”(Group)的概念,也就是一组“用户”的意思。而对于“内置用户” ServiceAccount 来讲,“用户组”的概念也一样适用。
实际上,一个 ServiceAccount,在 Kubernetes 里对应的“用户”的名字是: system:serviceaccount:<ServiceAccount 名字 >
;而它对应的内置“用户组”的名字,就是 system:serviceaccounts:<Namespace 名字 >
。
对于 Group 的运用,咱们举个例子,在 RoleBinding 里这样定义 subjects:
subjects:
- kind: Group
name: system:serviceaccounts:hdls
apiGroup: rbac.authorization.k8s.io
复制代码
这就意味着这个 Role 的权限规则,做用于 hdls 里的全部 ServiceAccount。
而若是 Group 不指定 Namespace,即直接定义为 system:serviceaccounts
,意味着做用于整个系统里的全部 ServiceAccount。
Attribute-based access control (ABAC) 是基于属性的权限访问控制。若要开启该模式,须要在 APIServer 启动时,开启 --authorization-policy-file=<SOME_FILENAME>
和 --authorization-mode=ABAC
两个参数。
其 policy 文件用来指定权限规则,必须知足每行都是一个 json 对象的格式。能够指定 user 或 group 为某个特定的对象,并描述其拥有的权限。
与 Yaml 文件一致,必须描述的属性有 apiVersion、kind、spec,而 spec 里描述了具体的用户、资源和行为。看个例子:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "bob", "namespace": "projectCaribou", "resource": "pods", "readonly": true}}
复制代码
这就描述了用户 bob 只有在 namespace projectCaribou 下对 pod 的读权限。相似的,这个 User 能够是某我的,也能够是 kubelet 或者某个 ServiceAccount,这里 ServiceAccount 须要写全,好比:system:serviceaccount:kube-system:default
。
若是是描述某个 namespace 下的全部人,须要用到 group,好比:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:serviceaccounts:default", "readonly": true, "resource": "pods"}}
复制代码
Node 受权机制是一种特殊的模式,是 kubelet
发起的请求受权。开启该模式,须要开启参数 --authorization-mode=Node
。
经过启动 --enable-admission-plugins=...,NodeRestriction,...
,来限制 kubelet 访问 node,endpoint、pod、service以及secret、configmap、PV 和 PVC 等相关的资源。
Webhook 模式是一种 HTTP 回调模式,是一种经过 HTTP POST
方式实现的简单事件通知。该模式须要 APIServer 配置参数 –authorization-webhook-config-file=<SOME_FILENAME>
,HTTP 配置文件的格式跟 kubeconfig 的格式相似。
# Kubernetes API version
apiVersion: v1
# kind of the API object
kind: Config
# clusters refers to the remote service.
clusters:
- name: name-of-remote-authz-service
cluster:
# CA for verifying the remote service.
certificate-authority: /path/to/ca.pem
# URL of remote service to query. Must use 'https'. May not include parameters.
server: https://authz.example.com/authorize
# users refers to the API Server's webhook configuration.
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # cert for the webhook plugin to use
client-key: /path/to/key.pem # key matching the cert
# kubeconfig files require a context. Provide one for the API Server.
current-context: webhook
contexts:
- context:
cluster: name-of-remote-authz-service
user: name-of-api-server
name: webhook
复制代码
其中,Cluster 指须要回调的地方的客户端,指定其访问证书和 URL;user 指回调处访问的身份,指明其所需证书和 key;contexts 指回调的内容。
在一个请求经过了认证机制和受权认证后,须要通过最后一层筛查,即准入控制。这个准入控制模块的代码一般在 APIServer 中,并被编译到二进制文件中被执行。这一层安全检查的意义在于,检查该请求是否达到系统的门槛,便是否知足系统的默认设置,并添加默认参数。
准入控制以插件的形式存在,开启的方式为: kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...
关闭的方式为: kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
经常使用的准入控制插件有:
PersistentVolumeClaim
建立默认的 PV;node.kubernetes.io/not-ready:NoExecute
和 node.alpha.kubernetes.io/unreachable:NoExecute
没有容忍,为其建立默认的 5 分钟容忍 notready:NoExecute
和unreachable:NoExecute
;priorityClassName
来决定优先级;以上只列举了部分,详情请移步 Kubernetes 官方文档。
官方建议:
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
复制代码
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
复制代码
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
复制代码
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota
复制代码