Kubernetes10-深入分析集群安全机制
- Kubernetes通过一系列机制来实现集群的安全控制,其中包括API Server的认证授权、准入控制机制及保护敏感信息的Secret机制等。
- 集群的安全性必须考虑如下几个目标。
- (1)保证容器与其所在宿主机的隔离。
- (2)限制容器给基础设施或其他容器带来的干扰。
- (3)最小权限原则——合理限制所有组件的权限,确保组件只执行它被授权的行为,通过限制单个组件的能力来限制它的权限范围。
- (4)明确组件间边界的划分。
- (5)划分普通用户和管理员的角色。
- (6)在必要时允许将管理员权限赋给普通用户。
- (7)允许拥有Secret数据(Keys、Certs、Passwords)的应用在集群中运行。
- 下面分别从Authentication、Authorization、Admission Control、Secret和Service Account等方面来说明集群的安全机制。
1、API Server认证管理
- Kubernetes集群中所有资源的访问和变更都是通过Kubernetes API Server的REST API来实现的,所以集群安全的关键点就在于如何识别并认证客户端身份(Authentication),以及随后访问权限的授权(Authorization)这两个关键问题。
- Kubernetes集群提供了3种级别的客户端身份认证方式:
- 最严格的HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式。
- HTTP Token认证:通过一个Token来识别合法用户。
- HTTP Base认证:通过用户名+密码的方式认证。
1.1、HTTPS证书认证的原理
- HTTPS证书认证需要有一个CA证书,CA是PKI系统中通信双方都信任的实体,被称为可信第三方(Trusted Third Party,TTP)。CA作为可信第三方的重要条件之一就是CA的行为具有非否认性。作为第三方而不是简单的上级,就必须能让信任者有追究自己责任的能力。
- CA通过证书证实他人的公钥信息,证书上有CA的签名。用户如果因为信任证书而有了损失,则证书可以作为有效的证据用于追究CA的法律责任。正是因为CA承担责任的承诺,所以CA也被称为可信第三方。
- 在很多情况下,CA与用户是相互独立的实体,CA作为服务提供方,有可能因为服务质量问题(例如,发布的公钥数据有错误)而给用户带来损失。在证书中绑定了公钥数据和相应私钥拥有者的身份信息,并带有CA的数字签名;在证书中也包含了CA的名称,以便于依赖方找到CA的公钥,验证证书上的数字签名。
- CA认证涉及诸多概念,比如根证书、自签名证书、密钥、私钥、加密算法及HTTPS等,这里大致讲述SSL协议的流程,有助于理解CA认证和Kubernetes CA认证的配置过程。
- 如图6.1所示,CA认证大概包含下面几个步骤。
- (1)HTTPS通信双方的服务器端向CA机构申请证书,CA机构是可信的第三方机构,它可以是一个公认的权威企业,也可以是企业自身。企业内部系统一般都用企业自身的认证系统。CA机构下发根证书、服务端证书及私钥给申请者。
- (2)HTTPS通信双方的客户端向CA机构申请证书,CA机构下发根证书、客户端证书及私钥给申请者。
- (3)客户端向服务器端发起请求,服务端下发服务端证书给客户端。客户端接收到证书后,通过私钥解密证书,并利用服务器端证书中的公钥认证证书信息比较证书里的消息,例如,比较域名和公钥与服务器刚刚发送的相关消息是否一致,如果一致,则客户端认可这个服务器的合法身份。
- (4)客户端发送客户端证书给服务器端,服务端在接收到证书后,通过私钥解密证书,获得客户端证书公钥,并用该公钥认证证书信息,确认客户端是否合法。
- (5)客户端通过随机密钥加密信息,并发送加密后的信息给服务端。在服务器端和客户端协商好加密方案后,客户端会产生一个随机的密钥,客户端通过协商好的加密方案加密该随机密钥,并发送该随机密钥到服务器端。服务器端接收这个密钥后,双方通信的所有内容都通过该随机密钥加密。
- 上述是双向认证SSL协议的具体通信过程,这种情况要求服务器和用户双方都有证书。单向认证SSL协议则不需要客户端拥有CA证书,对于上面的步骤,只需将服务器端验证客户证书的过程去掉,之后协商对称密码方案和对称通话密钥时,服务器发送给客户的密码没被加密即可。
1.2、HTTP Token的认证原理
- HTTP Token的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串—Token来表明客户身份的一种方式。在通常情况下,Token是一个很复杂的字符串,比如我们用私钥签名一个字符串后的数据就可以被当作一个Token。此外,每个Token对应一个用户名,存储在APIServer能访问的一个文件中。当客户端发起API调用请求时,需要在HTTP Header里放入Token,这样一来,API Server就能识别合法用户和非法用户了。
1.3、HTTP Base认证
- HTTP是无状态的,浏览器和Web服务器之间可以通过Cookie来进行身份识别。桌面应用程序(比如新浪桌面客户端、SkyDrive客户端、命令行程序)一般不会使用Cookie,那么它们与Web服务器之间是如何进行身份识别的呢?这就用到了HTTP Base认证,这种认证方式是把“用户名+冒号+密码”用BASE64算法进行编码后的字符串放在HTTP Request中的Header Authorization域里发送给服务端,服务端在收到后进行解码,获取用户名及密码,然后进行用户身份鉴权。
2、API Server授权管理
- 当客户端发起API Server调用时,API Server内部要先进行用户认证,然后执行用户授权流程,即通过授权策略来决定一个API调用是否合法。对合法用户进行授权并且随后在用户访问时进行鉴权,是权限与安全系统的重要一环。简单地说,授权就是授予不同的用户不同的访问权限。
- API Server目前支持以下几种授权策略(通过API Server的启动参数“--authorization-mode”设置)。
- AlwaysDeny:表示拒绝所有请求,一般用于测试。
- AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略,这也是Kubernetes的默认配置。
- ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制。
- Webhook:通过调用外部REST服务对用户进行授权。
- RBAC:Role-Based Access Control,基于角色的访问控制。
- Node:是一种专用模式,用于对kubelet发出的请求进行访问控制。
- API Server在接收到请求后,会读取该请求中的数据,生成一个访问策略对象,如果在该请求中不带某些属性(如Namespace),则这些属性的值将根据属性类型的不同,设置不同的默认值(例如,为字符串类型的属性设置一个空字符串;为布尔类型的属性设置false;为数值类型的属性设置0)。然后将这个访问策略对象和授权策略文件中的所有访问策略对象逐条匹配,如果至少有一个策略对象被匹配,则该请求被鉴权通过,否则终止API调用流程,并返回客户端的错误调用码。
2.1、ABAC授权模式详解
- 注意:该ABAC授权功能在Kubernetes 1.6版本开始已被弃用!
- 在API Server启用ABAC模式时,需要指定授权策略文件的路径和名称(--authorization-policy-file=SOME_FILENAME),授权策略文件里的每一行都以一个Map类型的JSON对象进行设置,这被称为“访问策略对象”。
- 通过设置访问策略对象中的apiVersion、kind、spec属性来确定具体的授权策略:
- apiVersion当前版本为abac.authorization.kubernetes.io/v1beta1;
- kind被设置为Policy;
- spec指详细的策略设置,包括主题属性、资源属性、非资源属性这三个字段。
- (1)主体属性
- user(用户名):字符串类型,该字符串类型的用户名来源于Token文件(--token-auth-file参数设置的文件)或基本认证文件中用户名称段的值。
- group(用户组):在被设置为“system:authenticated”时表示匹配所有已认证的请求,在被设置为“system:unauthenticated”时表示匹配所有未认证的请求。
- (2)资源属性
- apiGroup(API组):字符串类型,表明匹配哪些API Group,例如extensions或*(表示匹配所有API Group)。
- namespace(命名空间):字符串类型,表明该策略允许访问某个Namespace的资源,例如kube-system或*(表示匹配所有Namespace)。
- resource(资源):字符串类型,API资源对象,例如pods或*(表示匹配所有资源对象)。
- (3)非资源属性
- nonResourcePath(非资源对象类路径):非资源对象类的URL路径,例如/version或/apis,*表示匹配所有非资源对象类的请求路径,也可以设置为子路径,/foo/*表示匹配所有/foo路径下的所有子路径。
- readonly(只读标识):布尔类型,当它的值为true时,表明仅允许GET请求通过。
- (1)主体属性
- 下面对ABAC授权算法、使用kubectl时的授权机制、常见ABAC授权示例、以及如何对Service Account进行授权进行说明。
2.1.1、ABAC授权算法
- API Server进行ABAC授权的算法为:在API Server收到请求之后,首先识别出请求携带的策略对象的属性,然后根据在策略文件中定义的策略对这些属性进行逐条匹配,以判定是否允许授权。如果有至少一条匹配成功,那么这个请求就通过了授权(不过还是可能在后续其他授权校验中失败)。
- 常见的策略配置如下:
- 要允许所有认证用户做某件事,可以写一个策略,将group属性设置为system:authenticated。
- 要允许所有未认证用户做某件事,可以把策略的group属性设置为system:unauthenticated。
- 要允许一个用户做任何事,将策略的apiGroup、namespace、resource和nonResourcePath属性设置为“*”即可。
2.1.2、使用kubectl时的授权机制
- kubectl使用API Server的/api和/apis端点来获取版本信息。要验证kubectl create/update命令发送给服务器的对象,kubectl需要向OpenAPI进行查询,对应的URL路径为/openapi/v2。
- 当使用ABAC授权模式时,下列特殊资源必须显式地通过nonResourcePath属性进行设置。
- API版本协商过程中的/api、/api/*、/apis、和/apis/*。
- 使用kubectl version命令从服务器获取版本时的/version。
- create/update操作过程中的/swaggerapi/*。
- 在使用kubectl操作时,如果需要查看发送到API Server的HTTP请求,则可以将日志级别设置为8,例如:
]# kubectl --v=8 version
2.1.3、常见的ABAC授权示例
- 下面通过几个授权策略文件(JSON格式)示例说明ABAC的访问控制用法。
- (1)允许用户alice对所有资源做任何操作:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "alice", "namespace": "*", "resource": "*", "apiGroup": "*"}}
- (2)kubelet可以读取任意Pod:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "pods", "readonly": true}}
- (3)kubelet可以读写Event对象:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kubelet", "namespace": "*", "resource": "events"}}
- (4)用户bob只能读取projectCaribou中的Pod:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "bob", "namespace": "projectCaribou", "resource": "pods", "readonly": true}}
- (5)任何用户都可以对非资源类路径进行只读请求:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:authenticated", "readonly": true, "nonResourcePath": "*"}} {"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:unauthenticated", "readonly": true, "nonResourcePath": "*"}}
- 如果添加了新的ABAC策略,则需要重启API Server以使其生效。
2.1.4、对Service Account进行授权
- Service Account会自动生成一个ABAC用户名(username),用户名按照以下命名规则生成:
system:serviceaccount:<namespace>:<serviceaccountname>
- 创建新的命名空间时,会产生一个如下名称的Service Account:
system:serviceaccount:<namespace>:default
- 如果希望kube-system命名空间中的Service Account“default”具有全部权限,就要在策略文件中加入如下内容:
{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"system:serviceaccount:kube-system:default","namespace":"*","resource":"*","apiGroup":"*"}}
2.2、Webhook授权模式详解
- Webhook定义了一个HTTP回调接口,实现Webhook的应用会在指定事件发生时,向一个URL地址发送(POST)通知信息。启用Webhook授权模式后,Kubernetes会调用外部REST服务对用户进行授权。
- Webhook模式用参数--authorization-webhook-configfile=SOME_FILENAME来设置远端授权服务的信息。
- 配置文件使用的是kubeconfig文件的格式。"users"代表着API Server的webhook,而"cluster"代表着远程服务。
- 下面的例子为设置一个使用HTTPS客户端认证的配置:
apiVersion: v1 kind: Config clusters: #clusters代表远程服务。 - name: name-of-remote-authz-service cluster: certificate-authority: /path/to/ca.pem #验证远端服务的CA。 server: https://authz.example.com/authorize #远程服务的URL,必须使用'https'。 users: #users代表API服务器的webhook配置 - name: name-of-api-server user: client-certificate: /path/to/cert.pem #webhook插件使用证书 client-key: /path/to/key.pem #证书的密钥 current-context: webhook #kubeconfig文件必须有context。需要提供一个给API服务器。 contexts: - context: cluster: name-of-remote-authz-service user: name-of-api-server name: webhook
- 在授权开始时,API Server会生成一个api.authorization.v1beta1.SubjectAccessReview对象,用于描述操作信息,在进行JSON序列化之后POST出来。在这个对象中包含用户尝试访问资源的请求动作的描述,以及被访问资源的属性。
- Webhook API对象和其他API对象一样,遵循同样的版本兼容性规则,在实现时要注意apiVersion字段的版本,以实现正确的反序列化操作。另外,API Server必须启用authorization.k8s.io/v1beta1 API扩展(--runtime-config=authorization.k8s.io/v1beta1=true)。
- 下面是一个希望获取Pod列表的请求报文示例:
{ "apiVersion": "authorization.k8s.io/v1beta1", "kind": "SubjectAccessReview", "spec": { "resourceAttributes": { "namespace": "kittensandponies", "verb": "get", "group": "unicorn.example.org", "resource": "pods" }, "user": "jane", "group": [ "group1", "group2" ] } }
- 远端服务需要填充请求中的SubjectAccessReviewStatus字段,并返回允许或不允许访问的结果。应答报文中的spec字段是无效的,也可以省略。
- 一个返回“运行访问”的应答报文示例如下:
{ "apiVersion": "authorization.k8s.io/v1beta1", "kind": "SubjectAccessReview", "status": { "allowed": true } }
- 一个返回“不允许访问”的应答报文示例如下:
{ "apiVersion": "authorization.k8s.io/v1beta1", "kind": "SubjectAccessReview", "status": { "allowed": false, "reason": "user does not have read access to the namespace" } }
- 非资源的访问请求路径包括/api、/apis、/metrics、/resetMetrics、/logs、/debug、/healthz、/swaggerui/、/swaggerapi/、/ui和/version。通常可以对/api、/api/*、/apis、/apis/*和/version对于客户端发现服务器提供的资源和版本信息给予“允许”授权,对于其他非资源的访问一般可以禁止,以限制客户端对API Server进行没有必要的查询。
- 查询/debug的请求报文示例如下:
{ "apiVersion": "authorization.k8s.io/v1beta1", "kind": "SubjectAccessReview", "spec": { "nonResourceAttributes": { "path": "/debug", "verb": "get" }, "user": "jane", "group": [ "group1", "group2" ] } }
2.3、RBAC授权模式详解
- RBAC授权模式是目前常用的,因此在下面详细讲解。
3、RBAC授权模式详解
- RBAC(Role-Based Access Control,基于角色的访问控制)在Kubernetes的1.5版本中引入,在1.6版本时升级为Beta版本,在1.8版本时升级为GA。作为kubeadm安装方式的默认选项,足见其重要程度。
- 相对于其他访问控制方式,新的RBAC具有如下优势。
- 对集群中的资源和非资源权限均有完整的覆盖。
- 整个RBAC完全由几个API对象完成,同其他API对象一样,可以用kubectl或API进行操作。
- 可以在运行时进行调整,无须重新启动API Server。
- 要使用RBAC授权模式,需要在API Server的启动参数中加上--authorization-mode=RBAC。
3.1、RBAC的API资源对象说明
- RBAC引入了4个新的顶级资源对象:Role、ClusterRole、RoleBinding和ClusterRoleBinding。同其他API资源对象一样,用户可以使用kubectl或者API调用等方式操作这些资源对象。
3.1.1、角色(Role)
- 一个角色就是一组权限的集合,这里的权限都是许可形式的,不存在拒绝的规则。
- 角色(Role)的授权范围是名称空间(namespace)。
- 下面定义的角色可以让用户有权访问default名称空间中Pod的权限:
]# kubectl create role pods_role --verb=get,list,watch --resource=pods -n default --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pods_role namespace: default rules: - apiGroups: - "" resources: - pods verbs: - get - list - watch
- 角色(Role)中的参数说明如下。
- apiGroups:支持的API组列表,例如“apiVersion:batch/v1”“apiVersion: extensions:v1beta1”“apiVersion: apps/v1beta1”等,详细的API组说明参见第9章的说明。
- resources:支持的资源对象列表,例如pods、deployments、jobs等。
- verbs:对资源对象的操作方法列表,例如get、watch、list、delete、replace、patch等,详细的操作方法说明参见第9章的说明。
3.1.2、集群角色(ClusterRole)
- 集群角色除了具有和角色一致的命名空间内资源的管理能力,因其集群级别的范围,还可以用于以下特殊元素的授权。
- 集群范围的资源,例如Node。
- 非资源型的路径,例如“/healthz”。
- 包含全部命名空间的资源,例如pods(用于kubectl get pods --all-namespaces这样的操作授权)。
- 集群角色(ClusterRole)授权范围是整个kubernetes集群(即所有的名称空间)。
- 下面定义的集群角色可以让用户有权访问任意一个或所有命名空间的secrets(视其绑定方式而定):
]# kubectl create clusterrole secrets_clusterrole --verb=get,list,watch --resource=secrets --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: #"namespace"被忽略,因为ClusterRoles不受名字空间限制 name: secrets_clusterrole rules: - apiGroups: - "" resources: #在HTTP层面,用来访问Secret对象的资源的名称为"secrets" - secrets verbs: - get - list - watch
3.1.3、角色绑定(RoleBinding)和集群角色绑定(ClusterRoleBinding)
- 角色绑定(RoleBinding)或集群角色绑定(ClusterRoleBinding)用来把一个角色绑定到一个目标上,绑定目标可以是User(用户)、Group(组)或Service Account。
- 使用RoleBinding为某个命名空间授权。
- 使用ClusterRoleBinding为集群范围内授权。
- User(用户)和Group(组)是对kubenetes使用者(即是对人的)进行授权,Service Account是对pod进行授权。
kubectl create rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] #--group=证书签署请求中O的值 #--user=证书签署请求中CN的值(如果多个user属于同一个group,绑定(rolebinding)group的时候,其组内的所有user都会被授权)
- RoleBinding可以引用Role,只对一个名称空间生效。
- RoleBinding也可以引用ClusterRole,对属于同一命名空间内ClusterRole定义的资源主体进行授权。一种常见的做法是集群管理员为集群范围预先定义好一组ClusterRole,然后在多个命名空间中重复使用这些ClusterRole。
- ClusterRoleBinding只能引用ClusterRole,用于进行集群级别或者对所有命名空间都生效的授权。
示例1:RoleBinding引用Role进行授权
- 下面的例子中的RoleBinding将在default命名空间中把pods_role角色授予用户user_name,这一操作可以让user_name读取default命名空间中的Pod:
]# kubectl create rolebinding rolebinding-pods_role --role=pods_role --user=user_name -n default --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: rolebinding-pods_role namespace: default roleRef: #"roleRef"指定与某Role或ClusterRole的绑定关系 apiGroup: rbac.authorization.k8s.io kind: Role #此字段必须是Role或ClusterRole name: pods_role #此字段必须与你要绑定的Role或ClusterRole的名称匹配 subjects: #你可以指定不止一个“subject(主体)” - apiGroup: rbac.authorization.k8s.io kind: User name: user_name #"user_name"是区分大小写的
示例2:RoleBinding引用ClusterRole进行授权
- 例如,在下面的例子中,虽然secrets_clusterrole是一个集群角色,但是因为使用了RoleBinding,所以user_name只能读取default命名空间中的secret:
]# kubectl create rolebinding rolebinding-secrets_clusterrole --clusterrole=secrets_clusterrole --user=user_name -n default --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 #此角色绑定使得用户"user_name"能够读取"default"名字空间中的Secrets kind: RoleBinding metadata: name: rolebinding-secrets_clusterrole #RoleBinding的名称空间决定了访问权限的授予范围。 #这里隐含授权仅在"default"名字空间内的访问权限。 namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: secrets_clusterrole subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: user_name #'user_name'是区分大小写的
示例3:ClusterRoleBinding引用ClusterRole进行授权
- 下面的例子允许用户user_name读取任意Namespace中的secret:
]# kubectl create clusterrolebinding clusterrolebinding-secrets_clusterrole --clusterrole=secrets_clusterrole --user=user_name -n default --dry-run=client -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: clusterrolebinding-secrets_clusterrole roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: secrets_clusterrole subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: user_name
- 图6.2展示了上述对Pod的get/watch/list操作进行授权的Role和RoleBinding的逻辑关系。
3.2、对资源的引用方式
3.2.1、resources对一类资源进行授权
- 在Kubernetes API中,大多数资源都是使用对象名称的字符串表示来呈现与访问的,例如,对于Pod应使用"pods"。但有一些Kubernetes API涉及子资源(subresource),例如Pod的log,对Pod日志的请求url是GET /api/v1/namespaces/{namespace}/pods/{name}/log。
- 在RBAC角色表达资源时,使用对应API端点的URL中的名字来引用资源。
- 在RBAC角色表达子资源时,使用斜线(/)来分隔资源和子资源("资源/子资源")。
示例:
- pods对应名称空间的Pod资源,而log是pods的子资源。允许某主体能同时够读取Pod和Pod log。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: pod-and-pod-logs-reader rules: - apiGroups: [""] resources: ["pods", "pods/log"] #! verbs: ["get", "list"]
3.2.2、ResourceName对某个资源实例进行授权
- 资源还可以通过资源名字(ResourceName)进行引用。在指定ResourceName后,使用get、delete、update和patch动词的请求,就会被限制在这个资源实例范围内。
- 注意:
- 不能使用资源名字(ResourceName)来限制create或者deletecollection请求。对于create请求而言,这是因为在鉴权时可能还不知道新对象的名字。
- 如果使用资源名字(ResourceName)来限制list或者watch请求,客户端必须在它们的list或者watch请求里包含一个与指定的resourceName匹配的metadata.name字段选择器。例如,kubectl get configmaps --field-selector=metadata.name=my-configmap
示例:
- 某主体只能对一个ConFigmap进行get和update操作。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: configmap-updater rules: - apiGroups: [""] # 在HTT 层面,用来访问ConfigMap的资源的名称为"configmaps" resources: ["configmaps"] resourceNames: ["my-configmap"] #! verbs: ["update", "get"]
3.2.3、查看集群角色(clusterrole)admin
- 包含所有的资源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
]# kubectl get clusterrole admin -o yaml ...... rules: - apiGroups: - "" resources: - pods/attach - pods/exec - pods/portforward - pods/proxy - secrets - services/proxy verbs: - get - list - watch - apiGroups: - "" resources: - serviceaccounts verbs: - impersonate - apiGroups: - "" resources: - pods - pods/attach - pods/exec - pods/portforward - pods/proxy verbs: - create - delete - deletecollection - patch - update - apiGroups: - "" resources: - configmaps - endpoints - persistentvolumeclaims - replicationcontrollers - replicationcontrollers/scale - secrets - serviceaccounts - services - services/proxy verbs: - create - delete - deletecollection - patch - update - apiGroups: - apps resources: - daemonsets - deployments - deployments/rollback - deployments/scale - replicasets - replicasets/scale - statefulsets - statefulsets/scale verbs: - create - delete - deletecollection - patch - update - apiGroups: - autoscaling resources: - horizontalpodautoscalers verbs: - create - delete - deletecollection - patch - update - apiGroups: - batch resources: - cronjobs - jobs verbs: - create - delete - deletecollection - patch - update - apiGroups: - extensions resources: - daemonsets - deployments - deployments/rollback - deployments/scale - ingresses - networkpolicies - replicasets - replicasets/scale - replicationcontrollers/scale verbs: - create - delete - deletecollection - patch - update - apiGroups: - policy resources: - poddisruptionbudgets verbs: - create - delete - deletecollection - patch - update - apiGroups: - networking.k8s.io resources: - ingresses - networkpolicies verbs: - create - delete - deletecollection - patch - update - apiGroups: - "" resources: - configmaps - endpoints - persistentvolumeclaims - persistentvolumeclaims/status - pods - replicationcontrollers - replicationcontrollers/scale - serviceaccounts - services - services/status verbs: - get - list - watch - apiGroups: - "" resources: - bindings - events - limitranges - namespaces/status - pods/log - pods/status - replicationcontrollers/status - resourcequotas - resourcequotas/status verbs: - get - list - watch - apiGroups: - "" resources: - namespaces verbs: - get - list - watch - apiGroups: - apps resources: - controllerrevisions - daemonsets - daemonsets/status - deployments - deployments/scale - deployments/status - replicasets - replicasets/scale - replicasets/status - statefulsets - statefulsets/scale - statefulsets/status verbs: - get - list - watch - apiGroups: - autoscaling resources: - horizontalpodautoscalers - horizontalpodautoscalers/status verbs: - get - list - watch - apiGroups: - batch resources: - cronjobs - cronjobs/status - jobs - jobs/status verbs: - get - list - watch - apiGroups: - extensions resources: - daemonsets - daemonsets/status - deployments - deployments/scale - deployments/status - ingresses - ingresses/status - networkpolicies - replicasets - replicasets/scale - replicasets/status - replicationcontrollers/scale verbs: - get - list - watch - apiGroups: - policy resources: - poddisruptionbudgets - poddisruptionbudgets/status verbs: - get - list - watch - apiGroups: - networking.k8s.io resources: - ingresses - ingresses/status - networkpolicies verbs: - get - list - watch - apiGroups: - authorization.k8s.io resources: - localsubjectaccessreviews verbs: - create - apiGroups: - rbac.authorization.k8s.io resources: - rolebindings - roles verbs: - create - delete - deletecollection - get - list - patch - update - watch
3.3、常用的角色(role)示例
- 以下示例均为从Role或ClusterRole对象中截取出来,仅展示其rules部分。
1、允许读取在核心API组下的 "Pods"。
rules: - apiGroups: [""] #在HTTP层面,用来访问Pod的资源的名称为"pods" resources: ["pods"] verbs: ["get", "list", "watch"]
2、允许读/写在"apps" API组中的Deployment(在HTTP层面,对应URL中资源部分为"deployments")。
rules: - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
3、允许读取核心API组中的"pods"和读/写"batch" API组中的"jobs"。
rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] - apiGroups: ["batch"] resources: ["jobs"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
4、允许读取名称为"my-config"的ConfigMap(需要通过RoleBinding绑定以限制为某名字空间中特定的ConfigMap)。
rules: - apiGroups: [""] resources: ["configmaps"] resourceNames: ["my-config"] verbs: ["get"]
5、允许读取在核心组中的"nodes"资源(因为Node是集群作用域的,所以需要ClusterRole绑定到ClusterRoleBinding才生效)。
rules: - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list", "watch"]
6、允许针对非资源端点/healthz和其子路径上发起GET和POST请求 (必须在ClusterRole绑定ClusterRoleBinding才生效)。
rules: - nonResourceURLs: ["/healthz", "/healthz/*"] #nonResourceURL中的'*'是一个全局通配符 verbs: ["get", "post"]
3.4、常见的角色绑定示例
- RoleBinding或者ClusterRoleBinding可以将角色绑定到某“主体(Subject)”上。 主体可以是User(用户)、Group(组)或Service Account(服务账户)。
- Kubernetes用字符串来表示用户名。用户名可以是普通的用户名,像"alice";或者是邮件风格的名称,如"bob@example.com",或者是以字符串形式表达的数字ID。
- 前缀"system:"是Kubernetes系统保留的,所以自己配置的用户名或者组名不能有"system:"前缀。
- 除了对前缀的限制之外,RBAC鉴权系统不对用户名格式作任何要求。
- 在Kubernetes中,用户组名与用户名一样,用户组名也用字符串来表示,而且对该字符串没有格式要求,只是不能使用保留的前缀"system:"。
- Service Account(服务账户)的用户名和用户组名:
- "system:serviceaccount:"(单数)是用于服务账户用户名的前缀;
- "system:serviceaccounts:"(复数)是用于服务账户组名的前缀。
- 下面示例是RoleBinding中的片段,仅展示其subjects的部分。
1、用户名是"alice@example.com"的Subject。
subjects: - kind: User name: "alice@example.com" apiGroup: rbac.authorization.k8s.io
2、用户组名是"frontend-admins"的Subject。
subjects: - kind: Group name: "frontend-admins" apiGroup: rbac.authorization.k8s.io
3、服务账户名是"default"的Subject,只用于kube-system名称空间。
subjects: - kind: ServiceAccount name: default namespace: kube-system
4、服务账号组名为"qa"的Subject。
subjects: - kind: Group name: system:serviceaccounts:qa apiGroup: rbac.authorization.k8s.io
5、该Subject授权给任意服务账户。
subjects: - kind: Group name: system:serviceaccounts apiGroup: rbac.authorization.k8s.io
6、该Subject授权给所有已经认证的用户。
subjects: - kind: Group name: system:authenticated apiGroup: rbac.authorization.k8s.io
7、该Subject授权给所有未认证用户。
subjects: - kind: Group name: system:unauthenticated apiGroup: rbac.authorization.k8s.io
8、该Subject授权给任意用户。
subjects: - kind: Group name: system:authenticated apiGroup: rbac.authorization.k8s.io - kind: Group name: system:unauthenticated apiGroup: rbac.authorization.k8s.io
3.5、默认的角色和角色绑定
- API Server会创建一组默认的ClusterRole和ClusterRoleBinding对象,其中很多是以"system:"为前缀的,以表明这些资源属于基础架构,对这些对象的改动可能造成集群故障。举例来说,system:node这个ClusterRole为kubelet定义了权限,如果这个集群角色被改动了,kubelet就会停止工作。
- 所有默认的ClusterRole和RoleBinding都会用标签kubernetes.io/bootstrapping: rbac-defaults进行标记。
- 表6.1 系统角色
- 表6.2 面向用户的角色
- 有些默认角色不是以"system:"为前缀的,这部分角色是面向用户的。其中包含超级用户角色(cluster-admin),有的用于集群一级的角色(cluster-status),还有针对名称空间的角色(admin、edit、view)。
- 表6.3 核心组件角色
- 表6.4 其他组件角色
- 表6.5 Controller角色
- Kubernetes Controller Manager负责的是核心控制流。当使用--use-service-account-credentials参数启动时,kube-controller-manager使用单独的服务账户来启动每个控制器。每个内置控制器都有相应的、前缀为"system:controller:"的角色。如果控制管理器启动时未设置--use-service-account-credentials,它使用自己的身份凭据来运行所有的控制器,该身份必须被授予所有相关的角色。这些角色包括:
3.6、初始化与预防权限提升
- RBAC API会阻止用户通过编辑角色或者角色绑定来提升权限。由于这一点是在API级别实现的,所以在RBAC鉴权组件未启用的状态下依然有效。
3.6.1、对Role创建或更新的限制
- 用户要对角色进行创建或更新操作,需要满足下列至少一个条件:
- (1)拥有一个角色的所有权限,且与该角色的生效范围一致(如果是集群角色,则是集群范围;如果是普通角色,则可能是同一个命名空间或者整个集群);
- (2)为用户显式授予针对该角色或集群角色的提权(escalate)操作的权限(要求Kubernetes 1.12及以上版本)。
- 例如,用户user-1没有列出集群中所有secret的权限,就不能创建具有这一权限的集群角色。要让一个用户能够创建或更新角色,需要:
- (1)为其授予一个允许创建或更新Role或ClusterRole资源对象的角色;
- (2)为其授予绑定某一角色的权限,有隐式或显式两种方法。
- 隐式:为用户授予权限,要覆盖该用户所能控制的所有权限范围。用户如果尝试创建超出其自身权限的角色或者集群角色,则该API调用会被禁止。
- 显式:为用户显式授予针对该Role或ClusterRole的提权(Escalate)操作的权限(要求Kubernetes 1.12及以上版本)。
3.6.2、对RoleBinding创建或更新的限制
- 如果一个用户的权限包含一个角色的所有权限,就可以为其创建和更新角色绑定(要求同样的作用范围);或者如果被授予了针对某个角色的绑定授权,则也有权完成此操作。例如,如果用户user-1没有列出集群内所有secret的权限,就无法为一个具有这样权限的角色创建集群角色绑定。要使用户能够创建、更新这一角色绑定,则需要这样做,如下所述。
- (1)为其授予一个允许创建和更新RoleBinding或ClusterRoleBinding的角色。
- (2)为其授予绑定某一角色的权限,有隐式或显式两种方法。
- 隐式:让其具有该角色的所有权限。
- 显式:为用户授予针对该角色(或集群角色)的绑定操作的权限。
- 例如,下面的集群角色和角色绑定能让user-1为其他用户在user-1-namespace命名空间中授予admin、edit及view角色:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: role-grantor rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["rolebindings"] verbs: ["create"] - apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterroles"] verbs: ["bind"] # 忽略 resourceNames 意味着允许绑定任何 ClusterRole resourceNames: ["admin","edit","view"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: role-grantor-binding namespace: user-1-namespace roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: role-grantor subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: user-1
- 在进行第1个角色和角色绑定时,必须让初始用户具备其尚未被授予的权限。要进行初始的角色和角色绑定设置,有以下两种办法。
- (1)使用属于system:masters组的身份,这一群组默认具有clusteradmin这一超级角色的绑定。
- (2)如果API Server以--insecure-port参数运行,则客户端通过非安全端口进行接口调用,该端口没有认证鉴权的限制。
3.7、对Service Account的授权管理
- 默认的RBAC策略为控制平台组件、节点和控制器授予有限范围的权限。但是不会对kube-system名字空间之外的服务账户授予权限。(除了所有认证用户都具有的discovery权限)。
- 这就要求用户根据需要向特定服务账户授予特定权限。细粒度的角色分配能够提高安全性,但也会提高管理成本。粗放的授权方式可能会给Service Account多余的权限,但更易于管理。
- 按从最安全到最不安全的顺序,存在以下五种方法。
1、为特定应用的Service Account赋权(最佳实践)。
- 这要求应用在其Pod的Spec中指定一个serviceAccountName,并创建Service Account。
- 例如,在名字空间"my-namespace"中授予服务账户"my-sa"只读权限:
kubectl create rolebinding my-sa-view \ --clusterrole=view \ --serviceaccount=my-namespace:my-sa \ --namespace=my-namespace
2、为一个命名空间中名为default的Service Account授权。
- 如果某个应用没有指定serviceAccountName,则会使用名为default的Service Account。
- 注意,赋给服务账户“default”的权限会让所有没有指定serviceAccountName的Pod都具有这些权限。
kubectl create rolebinding default-view \ --clusterrole=view \ --serviceaccount=my-namespace:default \ --namespace=my-namespace
- 许多插件组件在kube-system名字空间以"default"服务账户运行。要允许这些插件组件以超级用户权限运行,需要将集群的cluster-admin权限授予kube-system名字空间中的"default"服务账户。
- 启用这一配置意味着在kube-system名字空间中包含以超级用户账号来访问API的Secrets。
kubectl create clusterrolebinding add-on-cluster-admin \ --clusterrole=cluster-admin \ --serviceaccount=kube-system:default
3、为命名空间中的所有Service Account都授予一个角色。
- 如果希望在一个命名空间中,任何Service Account的应用都具有一个角色,则可以为这一命名空间的Service Account群组进行授权。
- 例如,在名字空间"my-namespace"中将只读权限授予该名字空间中的所有服务账户。
kubectl create rolebinding serviceaccounts-view \ --clusterrole=view \ --group=system:serviceaccounts:my-namespace \ --namespace=my-namespace
4、为集群范围内的所有Service Account都授予一个低权限角色(不推荐)。
- 如果不想为每个命名空间管理授权,则可以把一个集群级别的角色赋给所有Service Account。
- 例如,为所有命名空间中的所有Service Account授予只读权限:
kubectl create clusterrolebinding serviceaccounts-view \ --clusterrole=view \ --group=system:serviceaccounts
5、为所有Service Account授予超级用户权限(强烈不建议这样设置)。
- 如果完全不在意权限,则可以把超级用户权限分配给每个ServiceAccount。
- Warning: 这样做会允许所有应用都对你的集群拥有完全的访问权限,并将允许所有能够读取 Secret(或创建 Pod)的用户对你的集群有完全的访问权限。
kubectl create clusterrolebinding serviceaccounts-cluster-admin \ --clusterrole=cluster-admin \ --group=system:serviceaccounts
3.8、命令行工具
3.8.1、创建Role
- 创建Role对象,定义在某一名字空间中的权限。
Usage: kubectl create role NAME --verb=verb --resource=resource.group/subresource [--resource-name=resourcename] [--dry-run=server|client|none] [options]
1、创建名称为"pod-reader"的Role对象,允许用户对Pods执行get、watch和list操作。
kubectl create role pod-reader --verb=get,list,watch --resource=pods --dry-run=client -o yaml
2、创建名称为"pod-reader"的Role对象并指定resourceNames。
kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod --dry-run=client -o yaml
3、创建名为"foo"的Role对象并指定apiGroups。
kubectl create role foo --verb=get,list,watch --resource=replicasets.apps --dry-run=client -o yaml
4、创建名为"foo"的 Role对象,并指定子资源权限。
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status --dry-run=client -o yaml
5、创建名为"my-component-lease-holder"的Role对象,使其具有对特定名称的资源执行get/update的权限。
kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component --dry-run=client -o yaml
3.8.2、创建ClusterRole
Usage: kubectl create clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run=server|client|none] [options]
1、创建名称为"pod-reader"的ClusterRole对象,允许用户对Pods对象执行get、watch和list操作。
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods --dry-run=client -o yaml
2、创建名为"pod-reader"的ClusterRole对象并指定resourceNames。
kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod --dry-run=client -o yaml
3、创建名为"foo"的ClusterRole对象并指定apiGroups。
kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps --dry-run=client -o yaml
4、创建名为"foo"的ClusterRole对象并指定子资源。
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status --dry-run=client -o yaml
5、创建名为"foo"的ClusterRole对象并指定nonResourceURL。
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/* --dry-run=client -o yaml
6、创建名为"monitoring"的ClusterRole对象并指定aggregationRule。
kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true" --dry-run=client -o yaml
3.8.3、创建Rolebinding
- 在特定的名字空间中对Role或ClusterRole授权。
Usage: kubectl create rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none] [options]
1、在名字空间"acme"中,将名为admin的ClusterRole中的权限授予名称"bob"的用户。
kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme --dry-run=client -o yaml
2、在名字空间"acme"中,将名为view的ClusterRole中的权限授予名字空间"acme"中名为myapp的服务账户。
kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme --dry-run=client -o yaml
3、在名字空间"acme"中,将名为view的ClusterRole对象中的权限授予名字空间"myappnamespace"中名称为myapp的服务账户。
kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole=view --serviceaccount=myappnamespace:myapp --namespace=acme --dry-run=client -o yaml
3.8.4、创建ClusterRolebinding
- 在整个集群(所有名字空间)中用 ClusterRole 授权。
Usage: kubectl create clusterrolebinding NAME --clusterrole=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none] [options]
1、在整个集群范围,将名为cluster-admin的ClusterRole中定义的权限授予名为"root"用户。
kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root --dry-run=client -o yaml
2、在整个集群范围内,将名为system:node-proxier的ClusterRole的权限授予名为"system:kube-proxy"的用户。
kubectl create clusterrolebinding kube-proxy-binding --clusterrole=system:node-proxier --user=system:kube-proxy --dry-run=client -o yaml
3、在整个集群范围内,将名为view的ClusterRole中定义的权限授予"acme"名字空间中名为"myapp"的服务账户。
kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --dry-run=client -o yaml
3.9、RBAC授权详细过程
1、创建客户端使用CA证书
- 创建客户端使用的CA证书,要使用kubenetes集群的CA进行创建。
#复制ca文件 cp /etc/kubernetes/pki/ca* ./ #创建用户hengha的私钥 (umask 077;openssl genrsa -out user_hengha.key 2048) #创建user_hengha的证书签署请求(O=组织信息,CN=用户名)。 openssl req -new -key user_hengha.key -out user_hengha.csr -subj "/O=mai/CN=user_hengha" #签署证书 openssl x509 -req -in user_hengha.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out user_hengha.crt -days 365
- 注解
kubectl create rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] #--group=证书签署请求中O的值 #--user=证书签署请求中CN的值(如果多个user属于同一个group,绑定(rolebinding)group的时候,其组内的所有user都会被授权)
2、创建并配置.kube/config文件
- 创建kubectl工具用于与api-server通信使用的.kube/config文件。
#设置clusters(集群)参数 kubectl config set-cluster cluster_hengha \ --certificate-authority=ca.crt \ --embed-certs=true \ --server=https://10.1.1.11:6443 \ --kubeconfig=./user_hengha.conf #设置users(客户端认证)参数(注意,这里的"user_hengha"可以是任意值,作用是用于contexts将哪个cluster和哪个user关联在一起) kubectl config set-credentials user_hengha \ --client-certificate=user_hengha.crt \ --client-key=user_hengha.key \ --embed-certs=true \ --kubeconfig=./user_hengha.conf #设置contexts(上下文)参数(注意,这里的"--user=user_hengha"需要与上面的保持一致) kubectl config set-context context_hengha \ --cluster=cluster_hengha \ --user=user_hengha \ --kubeconfig=./user_hengha.conf #设置current-context(当前上下文) kubectl config use-context context_hengha --kubeconfig=./user_hengha.conf
3、验证.kube/config文件
- 只要将.kube/config文件放在系统用户的家目录即可。
useradd hengha mkdir -p /home/hengha/.kube cp ./user_hengha.conf /home/hengha/.kube/config chown hengha.hengha -R /home/hengha/ su - hengha #这时候不能正常查看pod,是因为没有进行权限绑定 ]$ kubectl get pods -n default Error from server (Forbidden): pods is forbidden: User "user_hengha" cannot list resource "pods" in API group "" in the namespace "default"
4、权限绑定
- 创建Role的yaml文件
cat > read_pods_role.yaml << EOF apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pods_role namespace: default rules: - apiGroups: - "" resources: - pods verbs: - get - list - watch EOF
- 创建Rolebinding的yaml文件
cat > read_pods_rolebinding.yaml << EOF apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: rolebinding-pods_role namespace: default roleRef: #"roleRef"指定与某Role或ClusterRole的绑定关系 apiGroup: rbac.authorization.k8s.io kind: Role #此字段必须是Role或ClusterRole name: pods_role #此字段必须与你要绑定的Role或ClusterRole的名称匹配 subjects: #你可以指定不止一个“subject(主体)” - apiGroup: rbac.authorization.k8s.io kind: User name: user_hengha #注意,这里的“user_hengha”要与证书签署请求中的CN的值保持值 EOF
- 创建Role和Rolebinding(root用户下执行)
kubectl apply -f read_pods_role.yaml kubectl apply -f read_pods_rolebinding.yaml
4、验证.kube/config文件
- 可以获取default名称空间下的pods,但kube-system名称空间下的pods没有权限查看,这是和授权绑定有关。
su - hengha ]$ kubectl get pods -n default NAME READY STATUS RESTARTS AGE ...... ]$ kubectl get pods -n kube-system Error from server (Forbidden): pods is forbidden: User "user_hengha" cannot list resource "pods" in API group "" in the namespace "kube-system"
3.10、查看使用的k8s用户及其权限
1、获取.kube/config
//一般在系统用户的家目录下 ]# cat .kube/config #或kubectl config view
2、获取当前上下文
//使用的哪个上下文用户和哪个k8s集群进行通信 ]# kubectl config current-context
3、获取k8s的用户和组
//(1)反解.kube/config文件中的client-certificate-data的值 ]# echo "client-certificate-data_VALUE" | base64 -d > 123.pem //(2)解析123.pem文件中的值(获取O=system:masters, CN=kubernetes-admin) ]# openssl x509 -in 123.pem -noout -text
4、查看Binding
//获取有哪些RoleBinding或ClusterRoleBinding使用了system:masters组或kubernetes-admin用户 ]# kubectl get RoleBinding -A -o yaml | grep 'system:masters' ]# kubectl get ClusterRoleBinding -A -o yaml | grep 'system:masters' name: system:masters ]# kubectl get RoleBinding -A -o yaml | grep 'kubernetes-admin' ]# kubectl get ClusterRoleBinding -A -o yaml | grep 'kubernetes-admin'
5、根据Binding使用的哪个角色获取当前k8s用户的权限
//查看role和clusterrole ]# kubectl get role -A ]# kubectl get clusterrole
4、Admission Control
- 客户端的请求通过api server的认证和鉴权后,还需要通过Admission Control(准入控制)所控制的一个准入控制链的层层考验,才能获得成功的响应。
- Kubernetes官方标准的准入控制链“关卡”有30多个,还允许用户自定义扩展。
- Admission Control配备了一个准入控制器的插件列表,发送给API Server的任何请求都需要通过列表中每个准入控制器的检查,检查不通过,则API Server就会拒绝该客户端的请求。此外,准入控制器插件能够修改请求参数以完成一些自动化任务,比如ServiceAccount这个控制器插件。
4.1、准入控制器插件
- AlwaysPullImages:在启动容器之前总是尝试重新下载镜像。
- 这对于多租户共享一个集群的场景非常有用,系统在启动容器之前可以保证总是使用租户的密钥去下载镜像。
- 如果不设置这个控制器,则在Node上下载的镜像的安全性将被削弱,只要知道该镜像的名称,任何人便都可以使用它们了。
- DefaultStorageClass:会关注PersistentVolumeClaim资源对象的创建(仅关注PVC的创建过程,对更新过程无效)。
- 如果请求中没有包含任何针对特定Storage class的请求,则为其指派指定的Storage class。在这种情况下,用户无须在PVC中设置任何特定的Storage class就能完成PVC的创建了。
- 如果没有设置默认的Storage class,该控制器就不会进行任何操作;
- 如果设置了超过一个的默认Storage class,该控制器就会拒绝所有PVC对象的创建申请,并返回错误信息。
- 管理员必须检查Storage Class对象的配置,确保只有一个默认值。
- 如果请求中没有包含任何针对特定Storage class的请求,则为其指派指定的Storage class。在这种情况下,用户无须在PVC中设置任何特定的Storage class就能完成PVC的创建了。
- DefaultTolerationSeconds:针对没有设置容忍node.kubernetes.io/not-ready:NoExecute或者node.alpha.kubernetes.io/unreachable:NoExecute的Pod,设置5min的默认容忍时间。
- DenyEscalatingExec:拦截所有exec和attach到具有特权的Pod上的请求。
- 如果你的集群支持运行有escalated privilege权限的容器,又希望限制用户在这些容器内执行命令,那么强烈推荐使用它。
- EventReateLimit:Alpha版本,用于应对事件密集情况下对APIServer造成的洪水攻击。
- ExtendedResourceToleration:如果运维人员要创建带有特定资源(例如GPU、FPGA等)的独立节点,则可能会对节点进行Taint处理来进行特别配置。该控制器能够自动为申请这些特别资源的Pod加入Toleration定义,无须人工干预。
- ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。
- ImagePolicyWebhook需要使用一个配置文件(通过kube-apiserver的启动参数--admission-control-config-file设置)定义后端Webhook的参数。
- 目前是Alpha版本的功能。
- Initializers:Alpha。用于为动态准入控制提供支持,通过修改待创建资源的元数据来完成对该资源的修改。
- LimitPodHardAntiAffinityTopology:该插件启用了Pod的反亲和性调度策略设置,在设置亲和性策略参数requiredDuringSchedulingRequiredDuringExecution时要求将topologyKey的值设置为“kubernetes.io/hostname”,否则Pod会被拒绝创建。
- LimitRanger:这个插件会监控进入的请求,确保请求的内容符合在Namespace中定义的LimitRange对象里的资源限制。
- 如果要在Kubernetes集群中使用LimitRange对象,则必须启用该插件才能实施这一限制。
- LimitRanger还能用于为没有设置资源请求的Pod自动设置默认的资源请求,该插件会为default命名空间中的所有Pod设置0.1CPU的资源请求。
- MutatingAdmissionWebhook:Beta。这一插件会变更符合要求的请求的内容,Webhook以串行的方式顺序执行。
- NamespaceAutoProvision:这一插件会检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就会自动创建命名空间。
- NamespaceExists:这一插件会检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就会拒绝这一创建过程。
- NamespaceLifecycle:如果尝试在一个不存在的Namespace中创建资源对象,则该创建请求将被拒绝。当删除一个Namespace时,系统将会删除该Namespace中的所有对象,包括Pod、Service等,并阻止删除default、kube-system和kube-public这三个命名空间。
- NodeRestriction:该插件会限制kubelet对Node和Pod的修改行为。
- 为了实现这一限制,kubelet必须使用system:nodes组中用户名为system:node:<nodeName>的Token来运行。符合条件的kubelet只能修改自己的Node对象,也只能修改分配到各自Node上的Pod对象。
- 在Kubernetes 1.11以后的版本中,kubelet无法修改或者更新自身Node的taint属性。
- 在Kubernetes 1.13以后,这一插件还会阻止kubelet删除自己的Node资源,并限制对有kubernetes.io/或k8s.io/前缀的标签的修改。
- OnwerReferencesPermissionEnforcement:在该插件启用后,一个用户要想修改对象的metadata.ownerReferences,就必须具备delete权限。该插件还会保护对象的metadata.ownerReferences[x].blockOwnerDeletion字段,用户只有在对finalizers子资源拥有update权限的时候才能进行修改。
- PodNodeSelector:该插件会读取命名空间的annotation字段及全局配置,来对一个命名空间中对象的节点选择器设置默认值或限制其取值。
- PersistentVolumeClaimResize:该插件实现了对PersistentVolumeClaim发起的resize请求的额外校验。
- PodPreset:该插件会使用PodSelector选择Pod,为符合条件的Pod进行注入。
- PodSecurityPolicy:在创建或修改Pod时决定是否根据Pod的security context和可用的PodSecurityPolicy对Pod的安全策略进行控制。
- PodTolerationRestriction:该插件首先会在Pod和其命名空间的Toleration中进行冲突检测,如果其中存在冲突,则拒绝该Pod的创建。它会把命名空间和Pod的Toleration进行合并,然后将合并的结果与命名空间中的白名单进行比较,如果合并的结果不在白名单内,则拒绝创建。如果不存在命名空间级的默认Toleration和白名单,则会采用集群级别的默认Toleration和白名单。
- Priority:这一插件使用priorityClassName字段来确定优先级,如果没有找到对应的Priority Class,该Pod就会被拒绝。
- ResourceQuota:用于资源配额管理目的,作用于Namespace。该插件拦截所有请求,以确保在Namespace上的资源配额使用不会超标。推荐在Admission Control参数列表中将这个插件排最后一个,以免可能被其他插件拒绝的Pod被过早分配资源。
- SecurityContextDeny:这个插件将在Pod中定义的SecurityContext选项全部失效。
- SecurityContext在Container中定义了操作系统级别的安全设定(uid、gid、capabilities、SELinux等)。
- 在未设置PodSecurityPolicy的集群中建议启用该插件,以禁用容器设置的非安全访问权限。
- ServiceAccount:这个插件将ServiceAccount实现了自动化,如果想使用ServiceAccount对象,那么强烈推荐使用它。
- StorageObjectInUseProtection:这一插件会在新创建的PVC或PV中加入kubernetes.io/pvc-protection或kubernetes.io/pv-protection的finalizer。如果想要删除PVC或者PV,则直到所有finalizer的工作都完成,删除动作才会执行。
- ValidatingAdmissionWebhook:该插件会针对符合其选择要求的请求调用校验Webhook。
- 目标Webhook会以并行方式运行;如果其中任何一个Webhook拒绝了该请求,该请求就会失败。
- 在Kubernetes 1.8中为Alpha版本,在Kubernetes 1.9中为Beta版本。
- AlwaysAdmit:已弃用,允许所有请求。
- AlwaysDeny:已弃用,禁止所有请求,用于测试。
- DenyExecOnPrivileged:已弃用,拦截所有想在PrivilegedContainer上执行命令的请求。
- 如果你的集群支持Privileged Container,又希望限制用户在这些Privileged Container上执行命令,那么强烈推荐使用它。
- 其功能已被合并到DenyEscalatingExec中。
- PersistentVolumeLabel:已弃用。这一插件自动根据云供应商(例如GCE或AWS)的定义,为PersistentVolume对象加入region或zone标签,以此来保障PersistentVolume和Pod同处一区。如果插件不为PV自动设置标签,则需要用户手动保证Pod和其加载卷的相对位置。该插件正在被Cloud controller manager替换,从Kubernetes 1.11版本开始默认被禁止。
4.2、使用控制器插件
- 设置API Server的启动参数即可使用需要的准入控制链,如果启用多种准入控制选项。
- 在Kubernetes 1.9及之前的版本中使用的参数是--admission-control,并且其中的内容是顺序相关的;
- 在Kubernetes 1.10及之后的版本中,该参数为--enable-admission-plugins,并且与顺序无关。
- 对Kubernetes 1.10及以上版本设置如下:
- 对Kubernetes 1.9及以下版本设置如下:
5、Service Account
- Service Account也是一种账号,但它并不是给Kubernetes集群的用户(系统管理员、运维人员、租户用户等)用的,而是给运行在Pod里的进程用的,它为Pod里的进程提供了必要的身份证明。
- Kubernetes之所以要创建用户账号(User)和服务账号(Service Account)两套独立的账号系统,原因如下:
- User账号是给人用的,Service Account是给Pod里的进程使用的,面向的对象不同。
- User账号是全局性的,Service Account则属于某个具体的Namespace。
- 通常来说,User账号是与后端的用户数据库同步的,创建一个新用户通常要走一套复杂的业务流程才能实现,Service Account的创建则需要极轻量级的实现方式,集群管理员可以很容易地为某些特定任务创建一个Service Account。
- 对于这两种不同的账号,其审计要求通常不同。
- 对于一个复杂的系统来说,多个组件通常拥有各种账号的配置信息,Service Account是Namespace隔离的,可以针对组件进行一对一的定义,同时具备很好的“便携性”。
- 在正常情况下,为了确保Kubernetes集群的安全,API Server都会对客户端进行身份认证,认证失败的客户端无法进行API调用。
- 在Pod中访问Kubernetes API Server服务时,是以Service方式访问名为Kubernetes的这个服务的,而Kubernetes服务又只在HTTPS安全端口443上提供,那么如何进行身份认证呢?这的确是个谜,因为Kubernetes的官方文档并没有清楚说明这个问题。
- 通过查看官方源码,我们发现这是在用一种类似HTTP Token的新认证方式——Service Account Auth,Pod中的客户端调用Kubernetes API时,在HTTP Header中传递了一个Token字符串,这类似于之前提到的HTTP Token认证方式,但有以下几个不同之处:
- 这个Token的内容来自Pod里指定路径下的一个文件(/run/secrets/kubernetes.io/serviceaccount/token),这种Token是动态生成的,确切地说,是由Kubernetes Controller进程用API Server的私钥(--service-account-private-key-file指定的私钥)签名生成的一个JWT Secret。
- 在官方提供的客户端REST框架代码里,通过HTTPS方式与API Server建立连接后,会用Pod里指定路径下的一个CA证书(/run/secrets/kubernetes.io/serviceaccount/ca.crt)验证API Server发来的证书,验证是否为CA证书签名的合法证书。
- API Server在收到这个Token以后,采用自己的私钥(实际上是使用service-accountkey-file参数指定的私钥,如果没有设置此参数,则默认采用tls-private-key-file指定的参数,即自己的私钥)对Token进行合法性验证。
- 明白了认证原理,接下来继续分析在上面的认证过程中所涉及的Pod中的三个文件:
- /var/run/secrets/kubernetes.io/serviceaccount/token。
- /var/run/secrets/kubernetes.io/serviceaccount/ca.crt。
- /var/run/secrets/kubernetes.io/serviceaccount/namespace(客户端采用这里指定的namespace作为参数调用Kubernetes API)。
- 这三个文件由于参与到Pod进程与API Server认证的过程中,起到了类似secret(私密凭据)的作用,所以它们被称为Kubernetes Secret对象。Secret从属于Service Account资源对象,属于Service Account的一部分,在一个Service Account对象里面可以包括多个不同的Secret对象,分别用于不同目的的认证活动。
- 下面通过运行一些命令来加深我们对Service Account与Secret的直观认识。
- (1)查看系统中的Service Account对象,可以看到有一个名为default的Service Account对象。
]# kubectl get serviceaccounts NAME SECRETS AGE default 1 78d
-
- (2)查看名为default的Service Account对象,可以看到有一个名为default-token-6qqf8的Secret,这个Secret同时是Mountable secrets,表明它是需要被挂载到Pod上的。
]# kubectl describe serviceaccounts default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-6qqf8 Tokens: default-token-6qqf8 Events: <none>
-
- (3)接下来看看default-token-6qqf8都有什么内容。
]# kubectl describe secrets default-token-6qqf8 Name: default-token-6qqf8 Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name: default kubernetes.io/service-account.uid: c633316c-6d28-443a-a2a2-23bca6db9255 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1066 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkVQYTVtYi1HSS1VWmdQa051VHlBX1dlQ3UwbFFxUlVTYmJIUmJiUDBlRG8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tNnFxZjgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImM2MzMzMTZjLTZkMjgtNDQzYS1hMmEyLTIzYmNhNmRiOTI1NSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.UUU4OaJJ1OsctZKO84TQGLSN-AtSnF2Fyo2HJ4fMOmgFL7PsBG_wHomis7j_7TwbNvDTjxzuof2bC94kU0jai8zh2Nkl8XNriLybLOmNl-zSDBCqGG4gClU3IDd0U-4M79TFfF2C2I-6CETwJdxx9IPAhTq6iGtgoVpGtB9SIyxzlyI7VwSxaheEjc6hFnYkLs2s1HyCzWh54JcLB8wG1W0MCkA3TOCXsJeLfIWiJ7BuDU8esxiNFJKs91DGVuA014NoHyWxACSxaOvI0TQTh7L6J59-DOTffdVKDUQrEShF7kSYFTMPIMKWHO9B7MPpcgbKr0Irau7j6noNLNrRkg
- 从上面的输出信息中可以看到,default-token-6qqf8包含三个数据项,分别是token、ca.crt、namespace。联想到Mountable secrets的标记,以及之前看到的Pod中的三个文件的文件名,我们恍然大悟:在每个Namespace下都有一个名为default的默认Service Account对象,在这个Service Account里面有一个名为Tokens的可以当作Volume被挂载到Pod里的Secret,当Pod启动时,这个Secret会自动被挂载到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权。
- 如图6.3所示,一个Service Account可以包括多个Secret对象。
- (1)名为Tokens的Secret用于访问API Server的Secret,也被称为Service Account Secret。
- (2)名为imagePullSecrets的Secret用于下载容器镜像时的认证过程,通常镜像库运行在Insecure模式下,所以这个Secret为空。
- (3)用户自定义的其他Secret,用于用户的进程。
- 如果一个Pod在定义时没有指定spec.serviceAccountName属性,则系统会自动为其赋值为default,即大家都使用同一个Namespace下的默认Service Account。如果某个Pod需要使用非default的Service Account,则需要在定义时指定:
apiVersion: apps/v1 kind: Deployment metadata: name: busybox-deployment namespace: default spec: selector: matchLabels: app: busybox-pod replicas: 2 template: metadata: labels: app: busybox-pod spec: containers: - name: busybox-container image: busybox:1.28 imagePullPolicy: IfNotPresent command: ["/bin/sleep", "10000000000"] serviceAccountName: myserviceaccount #!
- 接下来深入分析Service Account与Secret相关的一些运行机制。
- Controller manager创建了ServiceAccount Controller与Token Controller这两个安全相关的控制器。其中ServiceAccount Controller一直监听Service Account和Namespace的事件,如果在一个Namespace中没有default Service Account,那么ServiceAccount Controller会为该Namespace创建一个默认(default)的为default的Service Account的原因。
- 如果Controller manager进程在启动时指定了API Server私钥(service-accountprivate-key-file参数),那么Controller manager会创建Token Controller。Token Controller也监听Service Account的事件,如果发现在新创建的Service Account里没有对应的Service Account Secret,则会用API Server私钥创建一个Token(JWT Token),并用该Token、CA证书及Namespace名称等三个信息产生一个新的Secret对象,然后放入刚才的Service Account中;如果监听到的事件是删除Service Account事件,则自动删除与该Service Account相关的所有Secret。此外,Token Controller对象同时监听Secret的创建、修改和删除事件,并根据事件做不同的处理。
- 当我们在API Server的鉴权过程中启用了Service Account类型的准入控制器,即在kube-apiserver启动参数中包括下面的内容时:
--admission_control=ServiceAccount
- 则针对Pod新增或修改的请求,Service Account准入控制器会验证Pod里的Service Account是否合法。
- (1)如果spec.serviceAccount域没有被设置,则Kubernetes默认为其指定名称为default的Service accout。
- (2)如果spec.serviceAccount域指定了default以外的ServiceAccount,而该Service Account没有被事先创建,则该Pod操作失败。
- (3)如果在Pod中没有指定ImagePullSecrets,那么这个spec.serviceAccount域指定的Service Account的ImagePullSecrets会被加入该Pod中。
- (4)给Pod添加一个特殊的Volume,在该Volume中包含ServiceAccount Secret中的Token,并将Volume挂载到Pod中所有容器的指定目录下(/var/run/secrets/kubernetes.io/serviceaccount)。
- 综上所述,Service Account的正常工作离不开以下几个控制器。
- (1)Admission Controller。
- (2)Token Controller。
- (3)ServiceAccount Controller。
6、Secret私密凭据
- Secret的主要作用是保管私密数据,比如密码、OAuth Tokens、SSH Keys等信息。将这些私密信息放在Secret对象中比直接放在Pod或Docker Image中更安全,也更便于使用和分发。
- Secret的类型:
- (1)Service Account:Kubernetes自动创建包含访问 API 凭据的 secret,并自动修改pod以使用此类型的 secret。
- (2)Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。
- (3)kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息。
- Secret使用方式:
- (1)在创建Pod时,通过为Pod指定Service Account来自动使用该Secret。
- (2)通过挂载该Secret到Pod来使用它。作为volume中的文件被挂载到pod中的一个或者多个容器里。
- (3)当kubelet为pod拉取镜像时使用。通过指定Pod的spc.ImagePullSecrets来引用它。
- 每个单独的Secret大小不能超过1MB,Kubernetes不鼓励创建大的Secret,因为如果使用大的Secret,则将大量占用API Server和kubelet的内存。当然,创建许多小的Secret也能耗尽API Server和kubelet的内存。
- 可以通过Secret保管其他系统的敏感信息(比如数据库的用户名和密码),并以Mount的方式将Secret挂载到Container中,然后通过访问目录中文件的方式获取该敏感信息。
- 当Pod被API Server创建时,API Server不会校验该Pod引用的Secret是否存在。一旦这个Pod被调度,则kubelet将试着获取Secret的值。
- 如果Secret不存在或暂时无法连接到API Server,则kubelet按一定的时间间隔定期重试获取该Secret,并发送一个Event来解释Pod没有启动的原因。
- 一旦Secret被Pod获取,则kubelet将创建并挂载包含Secret的Volume。只有所有Volume都挂载成功,Pod中的Container才会被启动。
- 在kubelet启动Pod中的Container后,Container中和Secret相关的Volume将不会被改变,即使Secret本身被修改。为了使用更新后的Secret,必须删除旧Pod,并重新创建一个新Pod。
6.1、Service Account使用Secret
- service accout创建时Kubernetes会默认创建对应的secret。对应的secret会自动挂载到Pod的/var/run/secrets/kubernetes.io/serviceaccount目录中。
]# kubectl run busyboxpod --image=busybox:1.28 --command -- sleep 3600 ]# kubectl exec -it busyboxpod -- /bin/sh / # ls -l /var/run/secrets/kubernetes.io/serviceaccount/ total 0 lrwxrwxrwx 1 root root 13 Jun 4 09:42 ca.crt -> ..data/ca.crt lrwxrwxrwx 1 root root 16 Jun 4 09:42 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 Jun 4 09:42 token -> ..data/token
- 每个namespace下有一个名为default的默认的ServiceAccount对象
]# kubectl get secrets NAME TYPE DATA AGE default-token-6qqf8 kubernetes.io/service-account-token 3 87d ]# kubectl get pods busyboxpod -o yaml apiVersion: v1 kind: Pod metadata: name: busyboxpod namespace: default spec: containers: - command: - sleep - "3600" image: busybox:1.28 imagePullPolicy: IfNotPresent name: busyboxpod volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-6qqf8 #! readOnly: true volumes: - name: default-token-6qqf8 #! secret: secretName: default-token-6qqf8 #!
6.2、挂载Secret到Pod
1、创建Secret
]# kubectl create secret generic mysecret --from-literal=user=myuser --from-literal=passwd=mypasswd --dry-run=client -o yaml apiVersion: v1 data: passwd: bXlwYXNzd2Q= user: bXl1c2Vy kind: Secret metadata: name: mysecret
2、在Pod中挂载Secret
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mycontainer image: busybox:1.28 command: ["sleep", "3600"] volumeMounts: - name: mysecret mountPath: /mysecret #pod挂载点 readOnly: true #只读 volumes: - name: mysecret secret: secretName: mysecret #加载名为mysecret的secret为存储卷
3、进入pod查看secret
]# kubectl exec -it mypod -- sh / # ls -l /mysecret total 0 lrwxrwxrwx 1 root root 13 Jun 4 10:11 passwd -> ..data/passwd lrwxrwxrwx 1 root root 11 Jun 4 10:11 user -> ..data/user / # cat /mysecret/user myuser/ # / # cat /mysecret/passwd mypasswd/ #
6.3、pod拉取镜像时使用secret
1、创建Secret
]# kubectl create secret docker-registry myregistrykey --docker-server=www.harbor.com --docker-username=admin --docker-password=password --dry-run=client -o yaml apiVersion: v1 data: .dockerconfigjson: eyJhdXRocyI6eyJ3d3cuaGFyYm9yLmNvbSI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJwYXNzd29yZCIsImF1dGgiOiJZV1J0YVc0NmNHRnpjM2R2Y21RPSJ9fX0= kind: Secret metadata: name: myregistrykey type: kubernetes.io/dockerconfigjson
2、在Pod中使用Secret
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: mycontainer image: www.harbor.com/test/busyboy:1.28 imagePullSecrets: - name: myregistrykey #!