十五、Kubernetes之安全配置
在任何将资源或服务提供给有限使用者的系统上,认证和授权是两个必不可少的功能,前者用于身份鉴别,负责验证“来者是谁”,而后者则实现权限分派,负责鉴证“他有权做哪些事”。Kubernetes系统完全分离了身份验证和授权功能,将二者分别以多种不同的插件实现,而且,特有的准入控制机制,还能在“写”请求上辅助完成更为精细的操作验证及变异功能。
Kubernetes集群中所有资源的访问和变更都是通过Kubernetes API Server的REST API来实现的,所以集群安全的关键点就在于如何识别并认证客户端身份(Authentication),以及随后访问权限的授权(Authorization)这两个关键问题。
一、Kubernetes的访问控制介绍
1、apiserver的认证
API Server作为Kubernetes集群系统的网关,是访问及管理资源对象的唯一入口,它默认监听TCP的6443端口,通过HTTPS协议暴露了一个RESTful风格的接口。所有需要访问集群资源的集群组件或客户端,包括kube-controller-manager、kube-scheduler、kubelet和kube-proxy等集群基础组件,CoreDNS等集群的附加组件,以及此前使用的kubectl命令等都必须要经此网关请求与集群进行通信。所有客户端均要经由API Server访问或改变集群状态以及完成数据存储,并且API Server会对每一次的访问请求进行合法性检验,包括用户身份鉴别、操作权限验证以及操作是否符合全局规范的约束等。所有检查均正常完成且对象配置信息合法性检验无误之后才能访问或存入数据到后端存储系统etcd中。
客户端认证操作由API Server配置的一到多个认证插件完成。收到请求后,API Server依次调用配置的认证插件来校验客户端身份,直到其中一个插件可以识别出请求者的身份为止。授权操作则由一到多个授权插件完成,这些插件负责确定通过认证的用户是否有权限执行发出的资源操作请求,该类操作包括创建、读取、删除或修改指定的对象等。随后,通过授权检测的用户请求修改相关的操作还要经由一到多个准入控制插件的遍历式检测,例如使用默认值补足要创建的目标资源对象中未定义的各字段、检查目标Namespace资源对象是否存在、检查请求创建的Pod对象是否违反系统资源限制等,而其中任何的检查失败都可能会导致写入操作失败。
1).认证(Authenticating)是对客户端的认证,通俗点就是用户名密码验证。认证通过,则客户端可以与Kubernetes集群进行交互。
2).授权(Authorization)是对资源的授权,Kubernetes中的资源无非是容器,最终其实就是容器的计算,网络,存储资源,当一个请求经过认证后,需要访问某一个资源(比如创建一个pod),授权检查都会通过访问策略比较该请求上下文的属性,(比如用户,资源和Namespace),根据授权规则判定该资源(比如某namespace下的pod)是否是该客户可访问的。授权通过,则客户端可以对集群中授权的资源做增、删、改、查的操作。
3).准入(Admission Control)机制是一种在改变资源的持久化之前(比如某些资源的创建或删除,修改等之前)的机制。不会拦截查询操作。
2、Kubernetes的用户账户及用户组
Kubernetes系统上的用户账户及用户组的实现机制与常规应用略有不同。Kubernetes集群将那些通过命令行工具kubectl、客户端库或者直接使用RESTful接口向API Server发起请求的客户端上的请求主体分为两个不同的类别:现实中的“人”和Pod对象,它们的用户身份分别对应用户账户(User Account,也称为普通用户)和服务账户(Service Account,简称SA)。
1)用户账户:其使用主体往往是“人”,一般由外部的用户管理系统存储和管理,Kubernetes本身并不维护这一类的任何用户账户信息,它们不会存储到API Server之上,仅仅用于检验用户是否有权限执行其所请求的操作。
2)服务账户:其使用主体是“应用程序”,专用于为Pod资源中的服务进程提供访问Kubernetes API时的身份标识(identity);ServiceAccount资源通常要绑定到特定的名称空间,它们由API Server自动创建或通过API调用,由管理员手动创建,通常附带着一组访问API Server的认证凭据——Secret,可由同一名称的Pod应用访问API Server时使用。
3)用户账户通常用于复杂的业务逻辑管控,作用于系统全局,因而名称必须全局唯一。Kubernetes并不会存储由认证插件从客户端请求中提取的用户及所属的组信息,因而也就没有办法对普通用户进行身份认证,它们仅仅用于检验该操作主体是否有权限执行其所请求的操作。
4)服务账户则隶属于名称空间级别,仅用于实现某些特定操作任务,因此功能上要轻量得多。这两类账户都可以隶属于一个或多个用户组。
举例说明:
sa账号:访问pod资源中提供的服务的账号。如:登陆dashboard使用的账号。
user account:这个是登陆Kubernetes集群物理机器的账号。如:配置一个账号让其只能访问集群中namespace=dev下的资源。
用户组只是用户账户的逻辑集合,它本身没有执行系统操作的能力,但附加于组上的权限可由其内部的所有用户继承,以实现高效的授权管理机制。Kubernetes有以下几个内置用于特殊目的的组。
▪system:unauthenticated:未能通过任何一个授权插件检验的账户的、所有未通过认证测试的用户统一隶属的用户组。
▪system:authenticated:认证成功后的用户自动加入的一个专用组,用于快捷引用所有正常通过认证的用户账户。
▪system:serviceaccounts:所有名称空间中的所有ServiceAccount对象。
▪system:serviceaccounts:<namespace>:特定名称空间内所有的ServiceAccount对象。
3、认证、授权与准入控制基础知识
Kubernetes使用身份验证插件对API请求进行身份验证,它允许管理员自定义服务账户和用户账户要启用或禁用的插件,并支持各自同时启用多种认证机制。具体设定时,至少应该为服务账户和用户账户各自启用一个认证插件。 如果启用了多种认证机制,账号认证过程由认证插件以串行方式进行,直到其中一种认证机制成功完成即结束。若认证失败,服务器则响应以401状态码,反之,请求者就会被Kubernetes识别为某个具体的用户(以其用户名进行标识),并且该连接上随后的操作都会以此用户身份进行。API Server对于接收到的每个访问请求会调用认证插件,尝试将以下属性与访问请求相关联。
▪Username:用户名,例如kubernetes-admin等。
▪UID:用户的数字标签符,用于确保用户身份的唯一性。
▪Groups:用户所属的组,用于权限指派和继承。
▪Extra:键值数据类型的字符串,用于提供认证需要用到的额外信息。
3.1 Kubernetes的认证方式
1)静态密码文件认证:将用户名和密码等信息以明文形式存储在CSV格式的文件中,由kube-apiserver在启动时通过--basic-auth-file选项予以加载,添加或删除用户都需要重启API Server;客户端通过在HTTP Basic认证(Authorization: Basic base64-encoded-username:password标头)方式中将用户名和密码编码后对该文件进行认证;不建议生产环境中使用。
说明:静态密码文件认证插件自Kubernetes v1.20版本中预以弃用,该插件的测试操作部分在v1.20及之后的版本上不可用,但在v1.19及之前的版本中,仍然可用。
2)静态令牌文件认证:即保存用于认证的令牌信息的静态文件,由kube-apiserver的命令行选项--token-auth-file加载,且API Sever进程启动后不可更改;HTTP协议的客户端能基于承载令牌(Authorization: Bearer <token>标头)对静态令牌文件进行身份验证,它将令牌编码后通过请求报文中的Authorization头部承载并传递给API Server即可;不建议生产环境中使用。
3)X509客户端证书认证:客户端在请求报文中携带X.509格式的数字证书用于认证,其认证过程类似于HTTPS协议通信模型;认证通过后,证书中的主体标识(Subject)将被识别为用户标识,其中的字段CN(Common Name)的值是用户名,字段O(Organization)的值是用户所属的组。例如/CN=ilinux/O=opmasters/O=admin中,用户名为ilinux,它属于opmasters和admin两个组;该认证方式可通过--client-ca-file=SOMEFILE选项启用。
4)引导令牌(Bootstrap Token)认证:一种动态管理承载令牌进行身份认证的方式,常用于简化组建新Kubernetes集群时将节点加入集群的认证过程,需要由kube-apiserver通过--experimental-bootstrap-token-auth选项启用;新的工作节点首次加入时,Master使用引导令牌确认节点身份的合法性之后自动为其签署数字证书以用于后续的安全通信,kubeadm初始化的集群也是这种认证方式;这些令牌作为Secrets存储在kube-system命名空间中,可以动态管理和创建它们,并由TokenCleaner控制器负责删除过期的引导令牌。 5)ServiceAccount令牌认证:该认证方式会由kube-apiserver程序自动启用,它同样使用签名的承载令牌来验证请求;该认证方式还支持通过可选项--service-account-key-file加载签署承载令牌的密钥文件,未指定时将使用API Server自己的TLS私钥;ServiceAccount通常由API Server自动创建,并通过ServiceAccount准入控制器将其注入Pod对象,包括ServiceAccount上的承载令牌,容器中的应用程序请求API Server的服务时以此完成身份认证。 6)OpenID Connect令牌认证:简称为OIDC,是OAuth 2协议的一种扩展,由Azure AD、Salesforce和Google Accounts等OAuth 2服务商所支持,协议的主要扩展是返回的附加字段,其中的访问令牌也称为ID令牌;它属于JSON Web令牌(JWT)类型,有服务器签名过的常用字段,例如email等;kube-apiserver启用这种认证功能的相关选项较多。
7)Webhook令牌认证:Webhook身份认证是用于验证承载令牌的钩子;HTTP协议的身份验证允许将服务器的URL注册为Webhook,并接收带有承载令牌的POST请求进行身份认证;客户端使用kubeconfig格式的配置文件,在文件中,users指的是API Server的Webhook,而clusters则指的是API Server。 8)代理认证:API Server支持从请求头部的值中识别用户,例如常用的X-Remote-User、X-Remote-Group和几个以X-Remote-Extra-开头的头部,它旨在与身份验证代理服务相结合,由该代理设置相应的请求头部;为了防止头欺骗,在检查请求标头之前,需要身份认证代理服务向API Server提供有效的客户端证书,以验证指定CA(由选项--requestheader-client-ca-file等进行指定)的代理服务是否合法。
9)那些未能被任何验证插件明确拒绝的请求中的用户即为匿名用户,该类用户会被冠以system:anonymous用户名,隶属于system:unauthenticated用户组。若API Server启用了除AlwaysAllow以外的认证机制,则匿名用户处于启用状态。但是,出于安全因素的考虑,建议管理员通过--anonymous-auth=false选项将其禁用。
注意:
API Server还允许用户通过模拟头部冒充另一个用户,这些请求可以以手动方式覆盖请求中用于身份验证的用户信息。例如,管理员可以使用此功能临时模拟其他用户来查看请求是否被拒绝,以进行授权策略调试。
除了身份信息,请求报文还需要提供操作方法及其目标对象,例如针对某Pod资源对象进行的创建、查看、修改或删除操作等:
▪API:用于定义请求的目标是否为一个API资源。 ▪Request path:请求的非资源型路径,例如/api或/healthz。 ▪API group:要访问的API组,仅对资源型请求有效;默认为core API group。 ▪Namespace:目标资源所属的名称空间,仅对隶属于名称空间类型的资源有效。 ▪API request verb:API请求类的操作,即资源型请求(对资源执行的操作),包括get、list、create、update、patch、watch、proxy、redirect、delete和deletecollection等。 ▪HTTP request verb:HTTP请求类的操作,即非资源型请求要执行的操作,如get、post、put和delete。 ▪Resource:请求的目标资源的ID或名称。 ▪Subresource:请求的子资源。
3.2 Kubernetes的授权
▪Node:基于Pod资源的目标调度节点来实现对kubelet的访问控制。 ▪ABAC:Attribute-based access control,基于属性的访问控制。 ▪RBAC:Role-based access control,基于角色的访问控制。 ▪Webhook:基于HTTP回调机制实现外部REST服务检查,确认用户授权的访问控制。 另外,还有AlwaysDeny和AlwaysAllow两个特殊的授权插件,其中AlwaysDeny(总是拒绝)仅用于测试,而AlwaysAllow(总是允许)则用于不期望进行授权检查时直接在授权检查阶段放行所有的操作请求。--authorization-mode选项用于定义API Server要启用的授权机制,多个选项值彼此间以逗号进行分隔。
3.3 Kubernetes的准入控制
1)AlwaysAdmit和AlwaysDeny:前者允许所有请求,后者则拒绝所有请求。 2)AlwaysPullImages:总是下载镜像,即每次创建Pod对象之前都要去下载镜像,常用于多租户环境中,以确保私有镜像仅能够由拥有权限的用户使用。 3)NamespaceLifecycle:拒绝在不存在的名称空间中创建资源,而删除名称空间则会级联删除其下的所有其他资源。 4)LimitRanger:可用资源范围界定,用于对设置了LimitRange的对象所发出的所有请求进行监控,以确保其资源请求不会超限。 5)ServiceAccount:用于实现服务账户管控机制的自动化,实现创建Pod对象时自动为其附加相关的Service Account对象。 6)PersistentVolumeLabel:为那些由云计算服务商提供的PV自动附加region或zone标签,以确保这些存储卷能正确关联且仅能关联到所属的region或zone。 7)DefaultStorageClass:监控所有创建PVC对象的请求,以保证那些没有附加任何专用StorageClass的请求会被自动设定一个默认值。 8)ResourceQuota:用于为名称空间设置可用资源上限,并确保当其中创建的任何设置了资源限额的对象时,不会超出名称空间的资源配额。 9)DefaultTolerationSeconds:如果Pod对象上不存在污点宽容期限,则为它们设置默认的宽容期,以宽容notready:NoExecute和unreachable:NoExecute类的污点5分钟时间。 10)ValidatingAdmissionWebhook:并行调用匹配当前请求的所有验证类的Webhook,任何一个校验失败,请求即失败。 11)MutatingAdmissionWebhook:串行调用匹配当前请求的所有变异类的Webhook,每个调用都可能会更改对象。
二、ServiceAccount及认证
Kubernetes原生的应用程序意味着专为运行于Kubernetes系统之上而开发的应用程序,这些程序托管运行在Kubernetes之上,能够直接与API Server进行交互,并进行资源状态的查询或更新,例如Flannel和CoreDNS等。API Server同样需要对这类来自Pod资源中的客户端程序进行身份验证,服务账户也是专用于这类场景的账号。ServiceAccount资源一般由用户身份信息及保存了认证信息的Secret对象组成。
1、ServiceAccount自动化
[root@k8s-master01 k8s-yaml]# kubectl describe pod test-pod ...... Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-btlhc (ro) ...... Volumes: default-token-btlhc: Type: Secret (a volume populated by a Secret) SecretName: default-token-btlhc Optional: false ......
容器的该挂载点目录中通常存在3个文件:ca.crt、namespace和token,其中,token文件保存了ServiceAccount的认证令牌,容器中的进程使用该账户认证到API Server,进而由认证插件完成用户认证并将其用户名传递给授权插件。
[root@k8s-master01 k8s-yaml]# kubectl exec -it test-pod -- bash [root@test-pod /]# ls /var/run/secrets/kubernetes.io/serviceaccount/ ca.crt namespace token
每个Pod对象只有一个服务账户,若创建Pod资源时未予明确指定,则ServiceAccount准入控制器会为其自动附加当前名称空间中默认的服务账户,其名称通常为default。下面的命令显示了default这个服务账户的详细信息。
[root@k8s-master01 k8s-yaml]# kubectl describe serviceaccounts default -n default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-btlhc Tokens: default-token-btlhc Events: <none>
Kubernetes系统通过3个独立的组件间相互协作实现了上面描述的Pod对象服务账户的自动化过程:ServiceAccount准入控制器、令牌控制器和ServiceAccount控制器。ServiceAccount控制器负责为名称空间管理相应的资源对象,它需要确保每个名称空间中都存在一个名为default的服务账户对象。ServiceAccount准入控制器内置在API Server中,负责在创建或更新Pod时按需进行ServiceAccount资源对象相关信息的修改。
▪若Pod没有显式定义使用的ServiceAccount对象,则将其设置为default。
▪若Pod显式引用了ServiceAccount,则负责检查被引用的对象是否存在,不存在时将拒绝Pod资源的创建请求。
▪若Pod中不包含ImagePullSecerts,则把ServiceAccount的ImagePullSecrets附加其上。
▪为带有访问API的令牌的Pod对象添加一个存储卷。
▪为Pod对象中的每个容器添加一个volumeMounts,将ServiceAccount的存储卷挂至/var/run/secrets/kubernetes.io/serviceaccount。
令牌控制器是控制平面组件Controller Manager中的一个专用控制器,它工作于异步模式,负责完成如下任务:
▪监控ServiceAccount的创建操作,并为其添加用于访问API的Secret对象;
▪监控ServiceAccount的删除操作,并删除其相关的所有ServiceAccount令牌密钥;
▪监控Secret对象的添加操作,确保其引用的ServiceAccount存在,并在必要时为Secret对象添加认证令牌;
▪监控Secret对象的删除操作,以确保删除每个ServiceAccount对此Secret的引用。
2、ServiceAccount基础应用
ServiceAccount是Kubernetes API上的一种资源类型,它属于名称空间级别,用于让Pod对象内部的应用程序在与API Server通信时完成身份认证。同样名为ServiceAccount的准入控制器实现了服务账户自动化,该准入控制器为每个名称空间都自动生成了一个名为default的默认资源对象。 每个Pod对象可附加其所属名称空间中的一个ServiceAccount资源,且只能附加一个。不过,一个ServiceAccount资源可由其所属名称空间中的多个Pod对象共享使用。创建Pod资源时,用户可使用spec.serviceAccountName属性直接指定要使用的ServiceAccount对象,或者省略此字段而由准入控制器自动附加当前名称空间中默认的ServiceAccount,以确保每个Pod对象至少基于该服务账户有权限读取当前名称空间中其他资源对象的元数据信息。 Kubernetes也支持用户按需创建ServiceAccount资源并将其指定到特定应用的Pod对象之上,结合集群启用的授权机制为该ServiceAccount资源赋予所需要的更多权限,从而构建出更加灵活的权限委派模型。
2.1 命令式ServiceAccount资源创建
kubectl create serviceaccount命令能够快速创建自定义的ServiceAccount资源,我们仅需要在命令后给出目标ServiceAccount资源的名称。
[root@k8s-master01 ~]# kubectl create serviceaccount my-sa serviceaccount/my-sa created [root@k8s-master01 ~]# kubectl get sa my-sa NAME SECRETS AGE my-sa 1 48s
Kubernetes会为创建的ServiceAccount资源自动生成并附加一个Secret对象,该对象以ServiceAccount资源名称为前缀,后面加token-随机数的方式。
[root@k8s-master01 ~]# kubectl get secrets NAME TYPE DATA AGE default-token-btlhc kubernetes.io/service-account-token 3 15d my-sa-token-5kw7c kubernetes.io/service-account-token 3 95s
该Secret对象属于特殊的kubernetes.io/service-account-token类型,它包含ca.crt、namespace和token这3个数据项,它们分别包含Kubernetes Root CA证书、Secret对象所属的名称空间和访问API Server的token令牌。由Pod对象以Secret存储卷的方式将该类型的Secret对象挂载至/var/run/secrets/kubernetes.io/serviceaccount目录后,这3个数据项映射为同名的3个文件。
[root@k8s-master01 ~]# kubectl get secrets my-sa-token-5kw7c -o yaml apiVersion: v1 data: ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0...... namespace: ZGVmYXVsdA== token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklqVkdSV...... kind: Secret metadata: annotations: kubernetes.io/service-account.name: my-sa kubernetes.io/service-account.uid: 07ce8903-29d5-456f-a0d4-5a7cf50c2527 ...... name: my-sa-token-5kw7c namespace: default resourceVersion: "2650574" uid: 9adfd7d5-1a3f-4cef-b8d5-2229f8cc3d8f type: kubernetes.io/service-account-token
2.2 ServiceAccount资源清单
完善地创建ServiceAccount资源的方式是使用资源规范,该规范比较简单,它没有spec字段,而是将几个关键定义直接通过一级字段给出。
apiVersion: v1 # ServiceAccount所属的API群组及版本 kind: ServiceAccount # 资源类型标识 metadata: name <string> # 资源名称 namespace <string> # ServiceAccount是名称空间级别的资源 automountServiceAccountToken <boolean> # 是否让Pod自动挂载API令牌 secrets <[]Object> # 以该SA运行的Pod要使用的Secret对象所组成的列表 apiVersion <string> # 引用的Secret对象所属的API群组及版本,可省略 kind <string> # 引用的资源类型,这里是指Secret,可省略 name <string> # 引用的Secret对象的名称,通常仅给出该字段即可 namespace <string> # 引用的Secret对象所属的名称空间 uid <string> # 引用的Secret对象的标识符 imagePullSecrets <[]Object> # 引用的用于下载Pod中容器镜像的Secret对象列表 name <string> # docker-registry类型的Secret资源名称
serviceacceount实例
[root@k8s-master01 k8s-yaml]# vim sa-demo.yaml apiVersion: v1 kind: ServiceAccount metadata: name: namespace-admin namespace: default automountServiceAccountToken: true
将配置清单中定义的default-ns-admin资源创建到集群上,ServiceAccount控制器会自动为其附加以该资源名称为前缀的Secret对象。随后,用户便可以在创建的Pod对象上引用该ServiceAccount对象,以借助权限管理机制实现自主控制Pod对象资源访问权限。
[root@k8s-master01 k8s-yaml]# kubectl apply -f sa-demo.yaml serviceaccount/namespace-admin created [root@k8s-master01 k8s-yaml]# kubectl get sa NAME SECRETS AGE default 1 15d my-sa 1 15m namespace-admin 1 74s [root@k8s-master01 k8s-yaml]# kubectl get secrets NAME TYPE DATA AGE default-token-btlhc kubernetes.io/service-account-token 3 15d my-sa-token-5kw7c kubernetes.io/service-account-token 3 16m namespace-admin-token-gqmrb kubernetes.io/service-account-token 3 91s
另外,ServiceAccount资源还可以基于spec.imagePullSecret字段附带一个由下载镜像专用的Secret资源组成的列表,让Pod对象在创建容器时且从私有镜像仓库下载镜像文件之前完成身份认证。下面的示例定义了一个从本地私有镜像仓库Harbor下载镜像文件时的Secret对象信息的ServiceAccount。
--- #定义harbor的账号密码在secret中 apiVersion: v1 kind: Secret metadata: name: local-harbor-secret type: Opaque data: username: YWRtaW4= #harbor的账号,使用base64加密 password: MTIzNDU2 #harbor的密码,使用base64加密 --- apiVersion: v1 kind: ServiceAccount metadata: name: eshop-sa namespace: eshop imagePullSecrets: - name: local-harbor-secret #调用secret
2.3 Pod资源上的服务账户
借助权限分配模型,按需应用“最小权限法则”将不同的资源操作权限配置给不同的账户,是有效降低安全风险的法则之一。有相当一部分Kubernetes原生应用程序依赖的权限都会大于从Pod默认ServiceAccount继承到的权限,且彼此间各有不同,为这类应用定制一个专用的ServiceAccount并授予所需的全部权限是主流的解决方案。
[root@k8s-master01 k8s-yaml]# vim pod-with-sa.yml apiVersion: v1 kind: Pod metadata: name: pod-with-sa namespace: default spec: containers: - name: adminbox image: ikubernetes/admin-toolbox:v1.0 imagePullPolicy: IfNotPresent serviceAccountName: namespace-admin #调用前面创建的sa
该Pod资源创建完成后会以Secret存储卷的形式自动挂载serviceaccounts/default-ns-admin的Secret对象
[root@k8s-master01 k8s-yaml]# kubectl apply -f pod-with-sa.yml pod/pod-with-sa created [root@k8s-master01 k8s-yaml]# kubectl get pod NAME READY STATUS RESTARTS AGE pod-with-sa 1/1 Running 0 29s [root@k8s-master01 k8s-yaml]# kubectl describe pod pod-with-sa Name: pod-with-sa Namespace: default ...... Containers: ...... Mounts: /var/run/secrets/kubernetes.io/serviceaccount from namespace-admin-token-gqmrb (ro) ...... Volumes: namespace-admin-token-gqmrb: Type: Secret (a volume populated by a Secret) SecretName: namespace-admin-token-gqmrb ......
Secret对象默认的挂载路径是/var/run/secrets/kubernetes.io/serviceaccount。与API Server交互时,工作负载进程会使用该目录下的ca.crt证书文件验证API Server的服务器证书是否为自己信任的证书颁发机构(所在集群的kubernetes-ca)所签发;验证服务器身份成功通过后,工作负载向API Server请求操作namespace文件指定的名称空间中的资源时,会将token文件中的令牌以承载令牌的认证方式提交给API Server进行验证,权限校验则由授权插件完成。
1)切换到pods/pod-with-sa的adminbox容器的Secret对象的挂载点为工作目录以便于加载所需要的文件:
[root@k8s-master01 k8s-yaml]# kubectl exec -it pod-with-sa -- bash [root@pod-with-sa /]$ ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
2)在容器中使用curl命令向API Server发起访问请求,--cacert选项用于指定验证服务器端的CA证书,而-H选项用于自定义头部,它指定了使用的承载令牌;下面的命令使用了“命令引用”机制来加载token和namespace文件的内容,其结果显示容器进程使用指定的ServiceAccount进行身份认证成功。因为没有授权所有该sa账户没有该namespace下所有资源的操作权限。
[root@pod-with-sa /]$ cd /var/run/secrets/kubernetes.io/serviceaccount/ [root@pod-with-sa /var/run/secrets/kubernetes.io/serviceaccount]$ curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v1/namespaces/$(cat ./namespace)/ { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "namespaces \"default\" is forbidden: User \"system:serviceaccount:default:namespace-admin\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"default\"", "reason": "Forbidden", "details": { "name": "default", "kind": "namespaces" }, "code": 403
1)切换至kubectl管理终端运行如下资源创建命令:
[root@k8s-master01 k8s-yaml]# kubectl create rolebinding namespace-admin-binding-admin --clusterrole=admin \ > --serviceaccount=default:namespace-admin -n default rolebinding.rbac.authorization.k8s.io/namespace-admin-binding-admin created
2)回到pods/pod-with-sa的adminbox容器中再次运行访问测试命令即可验证授权结果,如下命令表示namespace-admin用户已然有权限访问default名称空间。事实上,它拥有该名称空间中所有资源的CRUD权限。
[root@k8s-master01 k8s-yaml]# kubectl exec -it pod-with-sa -- bash [root@pod-with-sa /]$ [root@pod-with-sa /]$ cd /var/run/secrets/kubernetes.io/serviceaccount/ [root@pod-with-sa /var/run/secrets/kubernetes.io/serviceaccount]$ curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v1/namespaces/$(cat ./namespace)/ { "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "default", "uid": "84b5b410-0c48-4163-a0cb-0af27a941b32", "resourceVersion": "195", "creationTimestamp": "2021-04-23T02:20:54Z", "managedFields": [ { "manager": "kube-apiserver", "operation": "Update", "apiVersion": "v1", "time": "2021-04-23T02:20:54Z", "fieldsType": "FieldsV1", "fieldsV1": {"f:status":{"f:phase":{}}} } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } } #授权后可以正常访问该namespace下的资源
三、 X509数字证书认证
X509数字证书认证常用的方式有“单向认证”和“双向认证”。SSL / TLS最常见的应用场景是将X.509数字证书与服务器端关联,但客户端不使用证书。
单向认证是客户端能够验证服务端的身份,但服务端无法验证客户端的身份,至少不能通过SSL / TLS协议进行。之所以如此,是因为SSL / TLS安全性最初是为互联网应用开发,保护客户端是高优先级的需求,它可以让客户端确保目标服务器不会被冒名顶替。
双向认证的场景中,服务端与客户端需各自配备一套数字证书,并拥有信任的签证机构的证书列表。使用私有签证机构颁发的数字证书时,除了证书管理和分发,通常还要依赖用户手动将此私有签证机构的证书添加到信任的签证机构列表中。X509数字证书认证是Kubernetes默认使用的认证机制,采用双向认证模式。
1、Kubernetes的X509数字证书认证体系
构建安全基础通信环境的Kubernetes集群时,需要用到PKI基础设施以完成独立HTTPS安全通信及X509数字证书认证的场景有多种。API Server是整个Kubernetes集群的通信网关,controller-manager、scheduler、kubelet及kube-proxy等API Server的客户端均需要经由API Server与etcd通信,完成资源状态信息获取及更新等。同样出于安全通信的目的,Master的各组件(API Server、controller-manager和scheduler)需要基于SSL/TLS向外提供服务,而且与集群内部组件间通信时(主要是各节点上的kubelet和kube-proxy)还需要进行双向身份验证。
1.1 Kubernetes集群中的PKI设施与X509数字证书认证体系
kubernetes集群中的CA:
(2)Kubernetes集群CA及相关的数字证书
Kubernetes集群的其他各组件均需要通过kube-apiserver访问集群资源,同样出于安全性等目的,API Server也要借助HTTPS协议与其客户端通信,而X509双向数字证书认证仅是API Server支持的认证方式中的一种,客户端也可能会使用HTTP Basic或Bearer Token认证方式接入到API Server。
kubelet也通过HTTPS端点暴露了一组API,这些API提供了多个不同级别的敏感数据接口,并支持来自客户端的请求在节点和容器上执行不同级别的操作。默认情况下,匿名请求将自动隶属于system:unauthenticated用户组,其用户名为system:anonymous。不过,kubelet可使用--anonymous-auth=false选项拒绝匿名访问,并通过--client-ca-file选项指定CA方式验证客户端身份。kubelet可直接使用kubernetes-ca,同时应该为kube-apiserver使用--kubelet-client-certificate和--kubelet-client-key选项指定认证到kubelet的客户端证书与私钥。
(3)认证代理服务体系CA及相关的数字证书
API Server支持将认证功能交由外部的其他认证服务代为完成,这些服务通过特定的响应头部返回身份验证的结果状态,API Server扩展服务就是认证代理的最常见应用场景之一。
除了API Server提供的核心API,Kubernetes还支持通过聚合层(aggregation layer)对其进行扩展。简单来说,聚合层允许管理员在群集中部署使用其他Kubernetes风格的API,例如service catalog或用户自定义的API Server等。聚合层本身打包在kube-apiserver程序中,并作为进程的一部分运行,但仅在管理员通过指定的APIService对象注册扩展资源之后,它才会代理转发相应的请求。而APIService则会由运行在Kubernetes集群上的Pod中的extention-apiserver实现。
创建一个APIService资源时,作为注册和发现过程的一部分,kube-aggregator控制器(位于kube-apiserver内部)将与extention-apiserver的HTTP2连接[1],而后将经过身份验证的用户请求经由此连接代理到extention-apiserver上,于是,kube-aggregator被设置为执行RequestHeader客户端认证。 不过,只有kube-apiserver在启动时使用了如下选项时,才能启用其内置的聚合层:
▪--requestheader-client-ca-file=<path to aggregator CA cert> ▪--requestheader-allowed-names=front-proxy-client ▪--requestheader-extra-headers-prefix=X-Remote-Extra- ▪--requestheader-group-headers=X-Remote-Group ▪--requestheader-username-headers=X-Remote-User ▪--proxy-client-cert-file=<path to aggregator proxy cert> ▪--proxy-client-key-file=<path to aggregator proxy key>
1.2 Kubernetes集群需要的数字证书
完整运行的Kubernetes系统需要为etcd、API Server及前端代理(front proxy)生成多个数字证书
普通用户使用这种认证方式的前提是,它们各自拥有自己的数字证书,证书中的CN和O属性分别提供了准确的用户标识和用户组。API Server可接受或拒绝这些证书,评估标准在于证书是否由API Server信任的客户端证书CA(由选项--client-ca-file指定,默认为kubernetes-ca)所签发,但API Server自身并不了解这些证书,因此也不了解各个用户,它仅知道负责为各个客户端颁发证书的CA。因此,相较于静态密码文件认证和静态令牌文件认证来说,X509数字证书认证实现了用户管理与Kubernetes集群的分离,且有着更好的安全性。
X509数字证书认证因其可不依赖第三方服务、有着更好的安全性以及与API Server相分离等优势,成为Kubernetes系统内部默认使用的认证方式。但是,将X509数字证书用于普通用户认证的缺陷也是显而易见的,它主要表现在如下两个方面。
▪证书的到期时间在颁发时设定,其生命周期往往很长(数月甚至数年),且事实上的身份验证功能也是在颁发时完成,若撤销用户的可用身份只能使用证书吊销功能完成。
▪现实使用中,证书通常由一些通用的签证机构签发,而API Server需要信任该CA;显然,获得该CA使用权限的用户便能够授予自己可认证到的Kubernetes的任意凭据或身份,因而集群管理员必须自行集中管理证书,管理员的工作量比较大。
2、TLS Bootstrapping机制
TLS Bootstrapping机制有什么用途呢?
新的工作节点接入Kubernetes集群时需要事先配置好相关的证书和私钥等以进行安全通信,管理员可以手动管理这些证书,也可以选择由kubelet自行生成私钥和自签证书。集群略具规模后,第一种方式无疑会为管理员带来不小的负担,但对于保障集群安全运行却又必不可少。第二种方式降低了管理员的工作量,却也损失了PKI本身具有的诸多优势。取而代之,Kubernetes采用了由kubelet自行生成私钥和证书签署请求,而后发送给集群上的证书签署进程(CA),由管理员审核后予以签署或直接由控制器进程自动统一签署。这种方式就是kubelet TLS Bootstrapping机制,它实现了前述第一种方式的功能,却基本不增加管理员工作量。
一旦开启TLS Bootstrapping功能,任何kubelet进程都可以向API Server发起验证请求并加入到集群中,包括那些非计划或非授权主机,这必将增大管理验证操作时的审核工作量。为此,API Server设计了可经由--enable-bootstrap-token-auth选项启用的Bootstrap Token(引导令牌)认证插件。该插件用于加强TLS Bootstrapping机制,仅那些通过Bootstrap Token认证的请求才可以使用TLS Bootstrapping发送证书签署请求给控制平面,并由相应的审批控制器(approval controller)完成证书签署和分发。
说明:
kubeadm启用了节点加入集群时的证书自动签署功能,因此加入过程在kubeadm join命令成功后即完成。
~# kubeadm alpha certs check-expiration
~# kubeadm alpha certs renew all
四、kubeconfig配置文件
基于无状态协议HTTP/HTTPS的API Server需要验证每次连接请求中的用户身份,因而kube-controller-manager、kube-scheduler和kube-proxy等各类客户端组件必须能自动完成身份认证信息的提交,但通过程序选项来提供这些信息会导致敏感信息泄露。另外,管理员还面临着使用kubectl工具分别接入不同集群时的认证及认证信息映射难题。为此,Kubernetes设计了一种称为kubeconfig的配置文件,它保存有接入一到多个Kubernetes集群的相关配置信息,并允许管理员按需在各配置间灵活切换。
1、kubeconfig文件格式
kubeconfig文件中,各集群的接入端点以列表形式定义在clusters配置段中,每个列表项代表一个Kubernetes集群,并拥有名称标识;各身份认证信息(credentials)定义在users配置段中,每个列表项代表一个能够认证到某Kubernetes集群的凭据。将身份凭据与集群分开定义以便复用,具体使用时还要以context(上下文)在二者之间按需建立映射关系,各context以列表形式定义在contexts配置段中,而当前使用的映射关系则定义在current-context配置段中。
kubectl config view命令能打印kubeconfig文件的内容,下面的命令结果显示了默认路径下的文件配置,包括集群列表、用户列表、上下文列表以及当前使用的上下文(current-context)等。
[root@k8s-master01 ~]# kubectl config view apiVersion: v1 #k8s的集群列表 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://192.168.32.201:6443 name: cluster1 #上下文列表 contexts: - context: cluster: cluster1 user: admin name: context-cluster1-admin #当前上下文列表 current-context: context-cluster1-admin #用户列表 kind: Config preferences: {} users: - name: admin user: client-certificate-data: REDACTED client-key-data: REDACTED
kubectl config的常用子命令有如下几项:
▪view:打印kubeconfig文件内容。 ▪set-cluster:设定新的集群信息,以单独的列表项保存于clusters配置段。 ▪set-credentials:设置认证凭据,保存为users配置段的一个列表项。 ▪set-context:设置新的上下文信息,保存为contexts配置段的一个列表项。 ▪use-context:设定current-context配置段,确定当前以哪个用户的身份接入到哪个集群之中。 ▪delete-cluster:删除clusters中指定的列表项。 ▪delete-context:删除contexts中指定的列表项。 ▪get-clusters:获取clusters中定义的集群列表。 ▪get-contexts:获取contexts中定义的上下文列表。
2、自定义kubeconfig文件
一个完整kubeconfig配置的定义至少应该包括集群、身份凭据、上下文及当前上下文4项,但在保存有集群和身份凭据的现有kubeconfig文件基础上添加新的上下文时,可能只需要提供身份凭据而复用已有的集群定义,具体的操作步骤要按实际情况进行判定。
2.1 基于静态密码文件认证
例如,我们下面尝试创建一个新的kubeconfig文件,设定它使用此前定义的基于静态密码文件认证的ilinux用户接入到现有的Kubernetes集群,该集群API Server的网络端点为https://192.168.32.248:6443,相关的CA证书保存在Master节点上的/etc/kubernetes/pki/ca.crt文件中,而配置结果则使用--kubeconfig选项保存在当前用户主目录下的.kube/kube-dev.config文件中。
步骤1:添加集群配置,包括集群名称、API Server URL和信任的CA的证书;clusters配置段中的各列表项名称需要唯一。
kubeadm部署的k8s集群
[root@k8s-master01 ~]# kubectl config set-cluster kube-dev --embed-certs=true \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --server="https://192.168.32.248:6443" \ --kubeconfig=$HOME/.kube/kube-dev.config
步骤2:添加身份凭据,使用静态密码文件认证的客户端提供用户名和密码即可。
[root@k8s-master01 ~]# kubectl config set-credentials ilinux \ --username=ilinux --password=ilinux@123 \ --kubeconfig=$HOME/.kube/kube-dev.config User "ilinux" set.
步骤3:以用户ilinux的身份凭据与kube-dev集群建立映射关系。
[root@k8s-master01 ~]# kubectl config set-context ilinux@kube-dev \ --cluster=kube-dev --user=ilinux \ --kubeconfig=$HOME/.kube/kube-dev.config Context "ilinux@kube-dev" created.
步骤4:设置当前上下文为ilinux@kube-dev
[root@k8s-master01 ~]# kubectl config use-context ilinux@kube-dev --kubeconfig=$HOME/.kube/kube-dev.config Switched to context "ilinux@cluster1".
步骤5:预览kube-dev.config文件,确认其配置信息。
[root@k8s-master01 ~]# kubectl config view --kubeconfig=$HOME/.kube/kube-dev.config apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://192.168.32.248:6443 name: kube-dev contexts: - context: cluster: kube-dev user: ilinux name: ilinux@kube-dev current-context: ilinux@kube-dev kind: Config preferences: {} users: - name: ilinux user: password: ilinux@123 username: ilinux
步骤6:使用该kubeconfig中的当前上下文进行测试访问;该用户仅被授权了default名称空间的所有权限,因而不具有列出集群级别资源的权限,但能查看default名称空间的状态。
[root@k8s-master01 ~]#kubectl get namespaces --kubeconfig=$HOME/.kube/kube-dev.config Error from server (Forbidden): namespaces is forbidden: User "ilinux" cannot list resource "namespaces" in API group "" at the cluster scope ~$ kubectl get namespaces/default --kubeconfig=$HOME/.kube/kube-dev.config NAME STATUS AGE default Active 3d #有时需要重新拷贝admin.conf文件 #[root@k8s-master01 ~]#cp /etc/kubernetes/admin.conf /root/.kube/config
步骤7:添加身份凭据,使用静态令牌文件认证的客户端认证时只需要提供静态令牌信息
~$ TOKEN=$(sudo awk -F "," '$2=="ik8s"{print $1}' /etc/kubernetes/authfiles/token.csv) ~$ kubectl config set-credentials ik8s --token="$TOKEN" \ --kubeconfig=$HOME/.kube/kube-dev.config User "ik8s" set.
步骤8:为用户ik8s的身份凭据与kube-dev集群建立映射关系。
~$ kubectl config set-context ik8s@kube-dev \ --cluster=kube-dev --user=ik8s \ --kubeconfig=$HOME/.kube/kube-dev.config Context "ik8s@kube-dev" created.
步骤9:将当前上下文切换为ik8s@kube-dev。
~$ kubectl config use-context ik8s@kube-dev --kubeconfig=$HOME/.kube/kube-dev.config Switched to context "ik8s@kube-dev".
步骤10:预览kube-dev.config文件,确认ik8s用户相关的各项配置信息。
$ kubectl config view --kubeconfig=$HOME/.kube/kube-dev.config
步骤11:依旧使用当前上下文发起集群访问测试,ik8s用户未获得任何授权,但它能够被系统识别为ik8s用户,这表示身份认证请求成功返回;我们这次使用kubectl的whoami插件进行测试:
~ $ kubectl whoami --kubeconfig=$HOME/.kube/kube-dev.config ik8s
说明:
事实上除了静态令牌,客户端认证到API Server的各种令牌都可以使用前面的这种方式添加到kubeconfig文件中,包括ServiceAccount令牌、OpenID Connnect令牌和Bootstrap令牌等。
当kubectl引用了拥有两个及以上context的kubeconfig文件时,可随时通过kubectl config use-context命令在不同上下文之间切换,它们可能使用不同的身份凭据接入相同的集群或不同的集群之上。如下命令结果表示当前加载的配置文件中共有两个context,而拥有星号标识的是当前使用的context,即current-context。
~ kubectl config get-contexts --kubeconfig=HOME/.kube/kube-dev.config CURRENT NAME CLUSTER AUTHINFO NAMESPACE - ik8s@kube-dev kube-dev ik8s ilinux@kube-dev kube-dev ilinux
2.2 X509数字证书身份凭据
kubeadm部署Kubernetes集群的过程中会自动生成多个kubeconfig文件,它们是默认位于/etc/kubernetes目录下以.conf为后缀名的文件,前缀名称代表了它的适用场景,其中的admin.conf中保存了以X509数字证书格式提供身份凭据的kubernetes-admin用户,该用户能够以管理员的身份对当前集群发起资源操作请求。
由kubeadm初始化的Kubernetes集群上,kube-apiserver默认信任的CA就是集群自己的kubernetes-ca,该CA的数字证书是Master节点之上的/etc/kubernetes/pki/ca.crt文件。于是,客户端按需生成证书签署请求,再由管理员通过kubernetes-ca为客户端签署证书,便可让客户端以其证书中的CN为用户名认证到API Server上。为了便于说明问题,下面将客户端生成私钥和证书签署请求,服务器签署该请求,以及客户端将证书配置为kubeconfig文件的步骤统一进行说明,所有操作都在Master节点上运行。
步骤1:以客户端的身份,生成目标用户账号mason的私钥及证书签署请求,保存在用户主目录下的.certs目录中。
① 生成私钥文件,注意其权限应该为600以阻止其他用户读取。
[root@k8s-master01 ~]# mkdir $HOME/.certs [root@k8s-master01 ~]# (umask 077; openssl genrsa -out $HOME/.certs/mason.key 2048) Generating RSA private key, 2048 bit long modulus .............................................+++ ...............................+++ e is 65537 (0x10001)
② 创建证书签署请求,-subj选项中CN的值将被API Server识别为用户名,O的值将被识别为用户组。
[root@k8s-master01 ~]# openssl req -new -key $HOME/.certs/mason.key \ -out $HOME/.certs/mason.csr \ -subj "/CN=mason/O=developers"
① 基于kubernetes-ca签署证书,并为其设置合理的生效时长,例如365天。
[root@k8s-master01 ~]# openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \ -in $HOME/.certs/mason.csr -out $HOME/.certs/mason.crt Signature ok subject=/CN=mason/O=developers Getting CA Private Key
② 必要时,还可以验证生成的数字证书的相关信息(可选)。
[root@k8s-master01 ~]# openssl x509 -in $HOME/.certs/mason.crt -text -noout
① 根据X509数字证书及私钥创建身份凭据,列表项名称同目标用户名。
[root@k8s-master01 ~]# kubectl config set-credentials mason --embed-certs=true \ --client-certificate=$HOME/.certs/mason.crt \ --client-key=$HOME/.certs/mason.key User "mason" set.
② 配置context,以mason的身份凭据访问已定义的Kubernetes集群,该context的名称为mason@kubernetes。
[root@k8s-master01 ~]# kubectl config set-context mason@kubernetes --cluster=kubernetes --user=mason Context "mason@kubernetes" created.
③ 将当前上下文切换为mason@kubernetes,或直接在kubectl命令上使用“--context= 'mason@kubernetes'”以完成该用户的认证测试,下面的命令选择了以第二种方式进行认证,虽然提示权限错误,但mason用户已被API Server正确识别;
[root@k8s-master01 ~]# kubectl get namespaces/default --context='mason@kubernetes' Error from server (Forbidden): namespaces "default" is forbidden: User "mason" cannot get resource "namespaces" in API group "" in the namespace "default"
通过创建自定义的数字证书,实现了将mason用户认证到API Server,并将该用户的身份凭据保存至kubeconfig文件中。还没有给该用户授权。
kubectl config一次仅能使用单个kubeconfig文件。事实上,若将两个文件路径以冒号分隔并赋值给KUBECONFIG环境变量,也能够让kubectl config一次加载多个文件信息,优先级由高到低为各文件自左而右的次序。下面两条命令的结果显示,左侧文件拥有较高的优先级,因而其配置的current-context成为默认使用的context。
[root@k8s-master01 ~]# export KUBECONFIG="$HOME/.kube/config:$HOME/.kube/kube-dev.config" [root@k8s-master01 ~]# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE ilinux@kube-dev kube-dev ilinux * kubernetes-admin@kubernetes kubernetes kubernetes-admin mason@kubernetes kubernetes mason
联合使用多个kubeconfig时,同样可以按需调整当前使用的context,其实现方式同使用单个kubeconfig文件并没有不同之处。
[root@k8s-master01 ~]# kubectl config use-context mason@kubernetes Switched to context "mason@kubernetes". [root@k8s-master01 ~]# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE ilinux@kube-dev kube-dev ilinux kubernetes-admin@kubernetes kubernetes kubernetes-admin * mason@kubernetes kubernetes mason
kubectl config view命令会将多个配置文件的内容按给定的次序连接并输出,其风格类似于Linux系统的cat命令。但我们也能够在view命令中将加载的多个配置文件展平为单个配置文件的格式予以输出,并将结果保存在指定路径下便能将多个kubeconfig文件合并为一。例如,下面的命令便将KUBECONFIG环境变量中指定的两个kubeconfig文件合并成了单个配置文件。
[root@k8s-master01 ~]#kubectl config view --merge --flatten > $HOME/.kube/kube.config
此时,切换kubectl config加载新生成的kubeconfig配置文件,它便直接拥有了此前两个文件中定义的所有信息,且current-context亦遵循此前命令中的设定,即mason@kubernetes。
[root@k8s-master01 ~]# KUBECONFIG="$HOME/.kube/kube.config" [root@k8s-master01 ~]# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE ilinux@kube-dev kube-dev ilinux kubernetes-admin@kubernetes kubernetes kubernetes-admin * mason@kubernetes kubernetes mason
4.1 生成私钥和证书签署请求
① 生成私钥文件,注意其权限应该为600以阻止其他用户读取。
[root@k8s-master01 ~]# mkdir $HOME/.certs [root@k8s-master01 ~]# (umask 077; openssl genrsa -out $HOME/.certs/mason.key 2048) Generating RSA private key, 2048 bit long modulus .............................................+++ ...............................+++ e is 65537 (0x10001)
② 创建证书签署请求,-subj选项中CN的值将被API Server识别为用户名,O的值将被识别为用户组。
[root@k8s-master01 ~]# openssl req -new -key $HOME/.certs/mason.key \ -out $HOME/.certs/mason.csr \ -subj "/CN=mason/O=developers"
基于kubernetes-ca签署证书,并为其设置合理的生效时长,例如365天。
[root@k8s-master01 ~]# openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \ -in $HOME/.certs/mason.csr -out $HOME/.certs/mason.crt Signature ok subject=/CN=mason/O=developers Getting CA Private Key
4.3 生成mason-kube.config
① 生成kubeconfig授权文件:
[root@k8s-master01 ~]#kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=https://192.168.32.248:6443 \ --kubeconfig=$HOME/.kube/mason-kube.config
② 设置客户端认证
[root@k8s-master01 ~]#kubectl config set-credentials mason \ --client-key=$HOME/.certs/mason.key \ --client-certificate=$HOME/.certs/mason.crt \ --embed-certs=true \ --kubeconfig=$HOME/.kube/mason-kube.config
③配置context,以mason的身份凭据访问已定义的Kubernetes集群,该context的名称为mason@kubernetes。
[root@k8s-master01 ~]#kubectl config set-context mason@kubernetes \ --cluster=kubernetes \ --user=mason \ --kubeconfig=$HOME/.kube/mason-kube.config
④切换当前上下文
[root@k8s-master01 ~]# kubectl config use-context mason@kubernetes --kubeconfig=$HOME/.kube/mason-kube.config
⑤直接在kubectl命令上使用“--context= 'mason@kubernetes'”以完成该用户的认证测试,下面的命令选择了以第二种方式进行认证,虽然提示权限错误,但mason用户已被API Server正确识别。
[root@k8s-master01 .kube]# kubectl get ns --kubeconfig=$HOME/.kube/mason-kube.config Error from server (Forbidden): namespaces is forbidden: User "mason" cannot list resource "namespaces" in API group "" at the cluster scope
DAC(自主访问控制)、MAC(强制访问控制)、RBAC(基于角色的访问控制)和ABAC(基于属性的访问控制)这4种主流的权限管理模型中,Kubernetes支持使用后两种完成普通账户和服务账户的权限管理,另外支持的权限管理模型还有Node和Webhook两种。
RBAC是一种新型、灵活且使用广泛的访问控制机制,它将权限授予角色,通过让“用户”扮演一到多个“角色”完成灵活的权限管理,这有别于传统访问控制机制中将权限直接赋予使用者的方式。相对于Kubernetes支持的ABAC和Webhook等授权机制,RBAC具有如下优势:
▪对集群中的资源和非资源型URL的权限实现了完整覆盖。
▪整个RBAC完全由少数几个API对象实现,而且与其他API对象一样可以用kubectl或API调用进行操作。
▪支持权限的运行时调整,无须重新启动API Server。
1、RBAC授权模型
RBAC中用户、角色与权限之间的关系:
简单来说,RBAC就是一种访问控制模型,它以角色为中心界定“谁”(subject)能够“操作”(verb)哪个或哪类“对象”(object)。动作的发出者即“主体”,通常以“账号”为载体,在Kubernetes系统上,它可以是普通账户,也可以是服务账户。“动作”用于表明要执行的具体操作,包括创建、删除、修改和查看等行为,对于API Server来说,即PUT、POST、DELETE和GET等请求方法。而“对象”则是指管理操作能够施加的目标实体,对Kubernetes API来说主要指各类资源对象以及非资源型URL。 API Server是RESTful风格的API,各类客户端由认证插件完成身份验证,而后通过HTTP协议的请求方法指定对目标对象的操作请求,并由授权插件进行授权检查,而操作的对象则是URL路径指定的REST资源。
Kubernetes系统上的普通账户或服务账户向API Server发起资源操作请求,并以相应HTTP方法承载,如下图所示,由运行在API Server之上的授权插件RBAC进行鉴权。
Kubernetes系统的RBAC授权插件将角色分为Role和ClusterRole两类,它们都是Kubernetes内置支持的API资源类型,其中Role作用于名称空间级别,用于承载名称空间内的资源权限集合,而ClusterRole则能够同时承载名称空间和集群级别的资源权限集合。Role无法承载集群级别的资源类型的操作权限,这类的资源包括集群级别的资源(例如Nodes)、非资源类型的端点(例如/healthz),以及作用于所有名称空间的资源(例如跨名称空间获取任何资源的权限)等。 利用Role和ClusterRole两类角色进行赋权时,需要用到另外两种资源RoleBinding和ClusterRoleBinding,它们同样是由API Server内置支持的资源类型。RoleBinding用于将Role绑定到一个或一组用户之上,它隶属于且仅能作用于其所在的单个名称空间。RoleBinding可以引用同一名称中的Role,也可以引用集群级别的ClusterRole,但引用ClusterRole的许可权限会降级到仅能在RoleBinding所在的名称空间生效。而ClusterRoleBinding则用于将ClusterRole绑定到用户或组,它作用于集群全局,且仅能够引用ClusterRole。
Kubernetes集群用户大体规划为集群管理员、名称空间管理员和用户(通常为开发人员)3类。
▪集群管理员可以创建、读取、更新和删除任何策略对象,能够创建命名空间并将其分配给名称空间管理员;此角色适用于在整个集群中管理所有租户或项目的管理员。
▪名称空间管理员可以管理其名称空间中的用户,此角色适用于特定单一租户或项目的管理员。
▪开发者用户可以创建、读取、更新和删除名称空间内的非策略对象,如Pod、Job和Ingress等,但只在它们有权访问的名称空间中拥有这些权限。
2.1 、Role与ClusterRole
Role和ClusterRole是API Server内置的两种资源类型,它们在本质上都只是一组许可权限的集合。Role和ClusterRole的资源规范完全相同,该规范没有使用spec字段,而是直接使用rules字段嵌套授权规则列表。规则的基本要素是动作(verb)和相关的目标资源,后者支持指定一个或多个资源类型、特定资源类型下的单个或多个具体的资源,以及非资源类型的URL等。在Role和ClusterRole资源上定义的rules也称为PolicyRule,即策略规则,它可以内嵌的字段有如下几个。
1)apiGroups <[]string>:目标资源的API群组名称,支持列表格式指定多个组,空值("")表示核心群组。 2)resources <[]string>:规则应用的目标资源类型,例如pods、services、deployments和daemonsets等,未同时使用resourceNames字段时,表示指定类型下的所有资源。ResourceAll表示所有资源。 3)resourceNames <[]string>:可选字段,指定操作适用的具体目标资源名称。 4)nonResourceURLs <[]string>:用于定义用户有权限访问的网址列表,它并非名称空间级别的资源,因此只能应用于ClusterRole,Role支持此字段仅是为了格式上的兼容;该字段在一条规则中与resources和resourceNames互斥。 5)verbs <[]string>:可应用在此规则匹配到的所有资源类型的操作列表,可用选项有get、list、create、update、patch、watch、proxy、redirect、delete和deletecollection;此为必选字段。
下面的配置清单示例(pods-reader-rbac.yaml)在default名称空间中定义了一个名称为Role的资源,它设定了读取、列出及监视pods和services资源,以及pods/log子资源的许可权限。
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: pods-reader rules: - apiGroups: [""] # "" 表示核心API群组 resources: ["pods", "services", "pods/log"] #资源对象 verbs: ["get", "list", "watch"] #授权
绝大多数资源可通过其资源类型的名称引用,例如pods或services等,这些名称与它们在API endpoint中的形式相同。另外,有些资源类型支持子资源,例如Pod对象的/log,Node对象的/status等,它们在API Server上的URL形如下面的表示格式。
/api/v1/namespaces/{namespace}/pods/{name}/log
ClusterRole资源隶属于集群级别,它引用名称空间级别的资源意味着相关的操作权限能够在所有名称空间生效,同时,它也能够引用Role所不支持的集群级别的资源类型,例如nodes和persistentvolumes等。下面的清单示例(nodes-admin-rbac.yaml)定义了ClusterRole资源nodes-admin,它拥有管理集群节点信息的权限。ClusterRole不属于名称空间,所以其配置不能够使用metadata.namespace字段。
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nodes-admin rules: - apiGroups: [""] resources: ["nodes"] verbs: ["*"]
将上面两个清单中分别定义的Role和ClusterRole资源创建到集群上,以便按需调用并验证其权限。
[root@k8s-master01 apps]# kubectl apply -f pods-reader-rbac.yaml -f nodes-admin-rbac.yaml role.rbac.authorization.k8s.io/pods-reader created clusterrole.rbac.authorization.k8s.io/nodes-admin created
Role或ClusterRole资源的详细描述能够以比较直观的方式打印相关的规则定义,由kubectl describe roles/pods-reader clusterroles/nodes-admin命令输出的规则定义。
[root@k8s-master01 apps]# kubectl describe roles pods-reader Name: pods-reader Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- pods/log [] [] [get list watch] pods [] [] [get list watch] service [] [] [get list watch] [root@k8s-master01 apps]# kubectl describe clusterroles nodes-admin Name: nodes-admin Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- nodes [] [] [*]
kubectl命令也分别提供了创建Role和ClusterRole资源的命令式命令,create role和create clusterrole,它们支持如下几个关键选项。
▪--verb:指定可施加于目标资源的动作,支持以逗号分隔的列表值,也支持重复使用该选项分别指定不同的动作,例如--verb=get,list,watch,或者--verb=get --verb=list --verb=watch。 ▪--resource:指定目标资源类型,使用格式类似于--verb选项。 ▪--resource-name:指定目标资源,使用格式类似于--verb选项。 ▪--non-resource-url:指定非资源类型的URL,使用格式类似于--verb选项,但仅适用于clusterrole资源。
例如,下面的第一条命令创建了dev名称空间,第二条命令在该名称空间创建了一个具有所有资源管理权限的roles/admin资源,第三条命令则创建了一个有PVC和PV资源管理权限的clusterroles/pv-admin资源。
[root@k8s-master01 ~]# kubectl create namespace dev namespace/dev created [root@k8s-master01 ~]# kubectl create role admin -n dev --resource="*.*" \ --verb="get,list,watch,create,delete,deletecollection,patch,update" role.rbac.authorization.k8s.io/admin created [root@k8s-master01 ~]# kubectl create clusterrole pv-admin --verb="*" \ --resource="persistentvolumeclaims,persistentvolumes" clusterrole.rbac.authorization.k8s.io/pv-admin created
2.2 RoleBinding与ClusterRoleBinding
RoleBinding负责在名称空间级别向普通账户、服务账户或组分配Role或ClusterRole,而ClusterRoleBinding则只能用于在集群级别分配ClusterRole。但二者的配置规范格式完全相同,它们没有spec字段,直接使用subjects和roleRef两个嵌套的字段。其中,subjects的值是一个对象列表,用于给出要绑定的主体,而roleRef的值是单个对象,用于指定要绑定的Role或ClusterRole资源。
subjects字段的可嵌套字段如下:
▪apiGroup <string>:要引用的主体所属的API群组,对于ServiceAccount类的主体来说默认为"",而User和Group类主体的默认值为"rbac.authorization.k8s.io"。 ▪kind <string>:要引用的资源对象(主体)所属的类别,可用值为User、Group和ServiceAccount,必选字段。 ▪name <string>:引用的主体的名称,必选字段。 ▪namespace <string>:引用的主体所属的名称空间,对于非名称空间类型的主体,例如User和Group,其值必须为空,否则授权插件将返回错误信息。
roleRef的可嵌套字段如下:
▪apiGroup <string>:引用的资源(Role或ClusterRole)所属的API群组,必选字段。 ▪kind <string>:引用的资源所属的类别,可用值为Role或ClusterRole,必选字段。 ▪name <string>:引用的资源(Role或ClusterRole)的名称。
例如下面配置清单中的RoleBindings在dev名称空间中把admin角色分配给前面配置的用户mason,从而mason拥有了此角色之上的所有许可授权。
配置role角色admin
[root@k8s-master01 ~]# kubectl create namespace dev namespace/dev created [root@k8s-master01 ~]# kubectl create role admin -n dev --resource="*.*" \ --verb="get,list,watch,create,delete,deletecollection,patch,update" role.rbac.authorization.k8s.io/admin created [root@k8s-master01 apps]# kubectl describe roles admin -n dev Name: admin Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- *.* [] [] [get list watch create delete deletecollection patch update]
将role角色admin与普通用户mason绑定
[root@k8s-master01 apps]# vim mason-rolebindings.yaml kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: mason-admin namespace: dev subjects: - kind: User name: mason apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: admin apiGroup: rbac.authorization.k8s.io
在绑定role角色之前,mason用户无法在dev命名空间下创建pod
[root@k8s-master01 apps]# kubectl run demoapp --image="ikubernetes/demoapp:v1.0" -n dev --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): pods is forbidden: User "mason" cannot create resource "pods" in API group "" in the namespace "dev"
示例中的RoleBinding资源mason-admin创建到集群上,便能够以该用户的身份测试其继承而来的权限是否已然生效。下面以--config选项临时将用户切换为mason进行资源管理,测试命令及结果显示,mason已然具有dev名称空间下的资源操作权限。
[root@k8s-master01 apps]# kubectl apply -f mason-rolebindings.yaml rolebinding.rbac.authorization.k8s.io/mason-admin created [root@k8s-master01 apps]# kubectl run demoapp --image="ikubernetes/demoapp:v1.0" -n dev --kubeconfig=/root/.kube/mason-kube.config pod/demoapp created [root@k8s-master01 apps]# kubectl get pods -n dev --kubeconfig=/root/.kube/mason-kube.config NAME READY STATUS RESTARTS AGE demoapp 1/1 Running 0 11s [root@k8s-master01 apps]# kubectl delete pods demoapp -n dev --kubeconfig=/root/.kube/mason-kube.config pod "demoapp" deleted
一种高效分配权限的做法是,由集群管理员在集群范围预先定义好一组具有名称空间级别资源权限的ClusterRole资源,而后由RoleBinding分别在不同名称空间中引用它们,从而在多个名称空间向不同用户授予RoleBinding所有名称空间下的相同权限。
Role和RoleBinding是名称空间级别的资源,它们仅能用于完成单个名称空间内的访问控制,需要赋予某主体多个名称空间中的访问权限时就不得不在各名称空间分别进行。若需要完成集群全局的资源管理授权,或者希望资源操作能够针对Nodes、Namespaces和PersistentVolumes等集群级别的资源进行,或者针对/api、/apis、/healthz或/version等非资源型URL路径进行,就需要使用ClusterRoleBinding。
说明;nonResourceURLs资源仅支持get访问权限。
下面的配置清单示例(rolebinding-and-clusterrolebinding-rbac.yaml)中,rolebinding/mason-pvc-admin资源位于dev名称空间,它使用RoleBinding为用户mason分配了pv-admin这一集群角色,而clusterrolebinding/ik8s-pv-admin隶属集群级别,它使用ClusterRoleBinding为ik8s分配了pv-admin这一集群。
[root@k8s-master01 apps]#vim rolebinding-and-clusterrolebinding-rbac.yaml kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: mason-pvc-admin namespace: dev subjects: - kind: User name: mason apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: pv-admin apiGroup: rbac.authorization.k8s.io --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: ik8s-pv-admin subjects: - kind: User name: ik8s apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: pv-admin apiGroup: rbac.authorization.k8s.io
运行rolebinding-and-clusterrolebinding-rbac.yaml
[root@k8s-master01 apps]# kubectl apply -f rolebinding-and-clusterrolebinding-rbac.yaml rolebinding.rbac.authorization.k8s.io/mason-pvc-admin created clusterrolebinding.rbac.authorization.k8s.io/ik8s-pv-admin created
我们使用mason用户进行测试,它仅能访问dev名称空间下的名称空间级别的PVC资源,且无法通过RoleBinding从clusterroles/pv-admin继承指定名称空间之外的任何权限,如下面的命令及结果所示。
[root@k8s-master01 apps]# kubectl get pvc -n dev --kubeconfig=/root/.kube/mason-kube.config No resources found in dev namespace. [root@k8s-master01 apps]# kubectl get pv -n dev --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): persistentvolumes is forbidden: User "mason" cannot list resource "persistentvolumes" in API group "" at the cluster scope [root@k8s-master01 apps]# kubectl get pv -n default --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): persistentvolumes is forbidden: User "mason" cannot list resource "persistentvolumes" in API group "" at the cluster scope [root@k8s-master01 apps]# kubectl get pvc -n default --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): persistentvolumeclaims is forbidden: User "mason" cannot list resource "persistentvolumeclaims" in API group "" in the namespace "default"
使用ik8s用户进行测试,它通过ClusterRoleBinding从clusterroles/pv-admin继承了该集群角色的所有授权,如下面的命令及结果所示。
[root@k8s-master01 apps]# kubectl get pvc -n default --context="ik8s@kube-dev" No resources found in default namespace. [root@k8s-master01 apps]# kubectl get pvc -n dev --context="ik8s@kube-dev" No resources found in dev namespace. [root@k8s-master01 apps]# kubectl get pv --context="ik8s@kube-dev" No resources found in default namespace.
kubectl也提供了分别创建RoleBinding和ClusterRoleBinding资源的命令式命令:create rolebinding和create clusterrolebinding,它们使用的选项基本相同,常用的选项如下。
▪--role="":绑定的角色,仅RoleBinding支持。 ▪--clusterrole="":绑定的集群角色,RoleBinding和ClusterRoleBinding均支持。 ▪--group=[]:绑定的组,支持逗号分隔的列表格式。 ▪--user=[]:绑定的普通账户,支持逗号分隔的列表格式。 ▪--serviceaccount=[]:绑定的服务账户,支持逗号分隔的列表格式。
下面的命令为用户组kubeusers分配了集群角色nodes-admin,从而该组的所有用户均自动继承该角色上的所有许可权限。
[root@k8s-master01 apps]#kubectl create clusterrolebinding kubeusers-nodes-admin \ --clusterrole='nodes-admin' --group='kubeusers' clusterrolebinding.rbac.authorization.k8s.io/kubeusers-nodes-admin created
Kubernetes自1.9版本开始支持在ClusterRole的rules字段中嵌套aggregationRule字段来整合其他ClusterRole资源的规则,这种类型的ClusterRole对象的实际可用权限受控于控制器,具体许可授权由所有被标签选择器匹配到的ClusterRole的聚合授权规则合并生成。 下面的配置清单中首先定义了两个拥有标签的集群角色global-resources-view和global-resources-edit,而后在第三个集群角色资源global-resources-admin上使用聚合规则的标签选择器来匹配前两个资源的标签,因此,集群角色global-resources-admin的权限将由匹配到的其他ClusterRole资源的规则列表自动聚合而成。
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: global-resources-view labels: rbac.ilinux.io/aggregate-to-global-admin: "true" rules: - apiGroups: [""] resources: ["nodes", "namespaces", "persistentvolumes", "clusterroles"] verbs: ["get", "list", "watch"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: global-resources-edit labels: rbac.ilinux.io/aggregate-to-global-admin: "true" rules: - apiGroups: [""] resources: ["nodes", "namespaces", "persistentvolumes"] verbs: ["create", "delete", "deletecollection", "patch", "update"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: global-resources-admin aggregationRule: clusterRoleSelectors: - matchLabels: rbac.ilinux.io/aggregate-to-global-admin: "true" rules: [] # 该规则列表为空,它将由控制器自动聚合生成
事实上,Kubernetes系统上面向用户的内置ClusterRole admin和edit也是聚合型的ClusterRole对象,因为这可以使得默认角色中包含自定义资源的相关规则,例如由CustomResourceDefinitions或Aggregated API服务器提供的规则等。
4、面向用户的内置ClusterRole
API Server内置了一组默认的ClusterRole和ClusterRoleBinding资源预留给系统使用,其中大多数都以system:为前缀。另外有一些不以system:为前缀的默认的ClusterRole资源是为面向用户的需求而设计,包括集群管理员角色cluster-admin,以及专用于授予特定名称空间级别权限的集群角色admin、edit和view,如下图所示。掌握这些默认的内置ClusterRole资源有助于按需创建用户并分配相应权限。
内置的clusterroles/cluster-admin资源拥有管理集群所有资源的权限,而内置的clusterrolebindings/cluster-admn将该角色分配给了system:masters用户组,这意味着所有隶属于该组的用户都将自动具有集群的超级管理权限。kubeadm安装设置集群时,自动创建的配置文件/etc/kubernetes/admin.conf中定义的用户kubernetes-admin使用证书文件/etc/kubernetes/pki/apiserver-kubelet-client.crt向API Server进行验证。而该数字证书的Subject属性值为/O=system:masters,API Server会在成功验证该用户的身份之后将其识别为system: master用户组的成员。
[root@k8s-master01 apps]# openssl x509 -in /etc/kubernetes/pki/apiserver-kubelet-client.crt -noout -subject subject= /O=system:masters/CN=kube-apiserver-kubelet-client
为Kubernetes集群自定义超级管理员的方法至少有两种:一是将用户归入system:masters组,二是通过ClusterRoleBinding直接将用户绑定至内置的集群角色cluster-admin上。
[root@k8s-master01 apps]#kubectl create rolebinding ik8s-admin --clusterrole=admin --user=ik8s -n dev
若仅需要授予编辑或只读权限,在创建RoleBinding时引用ClusterRole的edit或view便能实现。
每次启动时,API Server都会自动为所有默认的ClusterRole重新赋予缺失的权限,同时为默认的ClusterRoleBinding绑定缺失的主体。这种机制给了集群从意外修改中自动恢复的能力,以及升级版本后自动将ClusterRole和ClusterRoleBinding升级到满足新版本需求的能力。
说明:
必要时,在默认的ClusterRole或ClusterRoleBinding上设置annnotation中的rbac.authorization.kubernetes.io/autoupdate属性的值为false,即可禁止这种自动恢复功能。
启用RBAC后,Kubernetes系统的各核心组件、附加组件,以及由controller-manager运行的核心控制器等,几乎都要依赖于合理的授权才能正常运行。因而,RBAC权限模型为这些组件内置了可获得最小化的资源访问授权的ClusterRole和ClusterRoleBinding,例如system:kube-sheduler、system:kube-controller-manager、system:node、system:node-proxier和system:kube-dns等,其中大多数组件都可以做到见名知义。
1、dashboard的介绍及部署
https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml下载dashboard清单文件,手动修改service暴露nodeport的端口为30001
# Copyright 2017 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apiVersion: v1 kind: Namespace metadata: name: kubernetes-dashboard --- apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard --- kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: NodePort ports: - port: 443 targetPort: 8443 nodePort: 30001 selector: k8s-app: kubernetes-dashboard --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-certs namespace: kubernetes-dashboard type: Opaque --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-csrf namespace: kubernetes-dashboard type: Opaque data: csrf: "" --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-key-holder namespace: kubernetes-dashboard type: Opaque --- kind: ConfigMap apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-settings namespace: kubernetes-dashboard --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard rules: # Allow Dashboard to get, update and delete Dashboard exclusive secrets. - apiGroups: [""] resources: ["secrets"] resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"] verbs: ["get", "update", "delete"] # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map. - apiGroups: [""] resources: ["configmaps"] resourceNames: ["kubernetes-dashboard-settings"] verbs: ["get", "update"] # Allow Dashboard to get metrics. - apiGroups: [""] resources: ["services"] resourceNames: ["heapster", "dashboard-metrics-scraper"] verbs: ["proxy"] - apiGroups: [""] resources: ["services/proxy"] resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"] verbs: ["get"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard rules: # Allow Metrics Scraper to get metrics from the Metrics server - apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: kubernetes-dashboard template: metadata: labels: k8s-app: kubernetes-dashboard spec: containers: - name: kubernetes-dashboard image: kubernetesui/dashboard:v2.2.0 imagePullPolicy: Always ports: - containerPort: 8443 protocol: TCP args: - --auto-generate-certificates - --namespace=kubernetes-dashboard # Uncomment the following line to manually specify Kubernetes API server Host # If not specified, Dashboard will attempt to auto discover the API server and connect # to it. Uncomment only if the default does not work. # - --apiserver-host=http://my-address:port volumeMounts: - name: kubernetes-dashboard-certs mountPath: /certs # Create on-disk volume to store exec logs - mountPath: /tmp name: tmp-volume livenessProbe: httpGet: scheme: HTTPS path: / port: 8443 initialDelaySeconds: 30 timeoutSeconds: 30 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 volumes: - name: kubernetes-dashboard-certs secret: secretName: kubernetes-dashboard-certs - name: tmp-volume emptyDir: {} serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule --- kind: Service apiVersion: v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: ports: - port: 8000 targetPort: 8000 selector: k8s-app: dashboard-metrics-scraper --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: dashboard-metrics-scraper template: metadata: labels: k8s-app: dashboard-metrics-scraper annotations: seccomp.security.alpha.kubernetes.io/pod: 'runtime/default' spec: containers: - name: dashboard-metrics-scraper image: kubernetesui/metrics-scraper:v1.0.6 ports: - containerPort: 8000 protocol: TCP livenessProbe: httpGet: scheme: HTTP path: / port: 8000 initialDelaySeconds: 30 timeoutSeconds: 30 volumeMounts: - mountPath: /tmp name: tmp-volume securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule volumes: - name: tmp-volume emptyDir: {}
[root@k8s-master01 apps]# kubectl apply -f dashboard.yaml namespace/kubernetes-dashboard created serviceaccount/kubernetes-dashboard created service/kubernetes-dashboard created secret/kubernetes-dashboard-certs created secret/kubernetes-dashboard-csrf created secret/kubernetes-dashboard-key-holder created configmap/kubernetes-dashboard-settings created role.rbac.authorization.k8s.io/kubernetes-dashboard created clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created deployment.apps/kubernetes-dashboard created service/dashboard-metrics-scraper created deployment.apps/dashboard-metrics-scraper created [root@k8s-master01 apps]# kubectl get svc -A NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 172.26.0.1 <none> 443/TCP 2d2h kube-system kube-dns ClusterIP 172.26.0.10 <none> 53/UDP,53/TCP,9153/TCP 2d2h kubernetes-dashboard dashboard-metrics-scraper ClusterIP 172.26.186.123 <none> 8000/TCP 12s kubernetes-dashboard kubernetes-dashboard NodePort 172.26.237.12 <none> 443:30001/TCP 12s
Dashboard的默认登录页面,它支持直接通过目标Service Account的令牌加载身份凭据,或者以该令牌为身份凭据生成专用的kubeconfig文件,并通过指定的文件路径向Dashboard提交认证信息。访问https://192.168.32.204:30001(https://nodeIP:端口)
2、dashboard的认证与授权
2.1 使用token登录dashboard
Kubernetes Dashboard自身并不进行任何形式的身份验证和鉴权,它仅是把用户提交的身份凭据转发至后端的API Server完成验证,资源操作请求及权限检查同样会提交至后端的API Server进行。从某种意义上讲,Dashboard更像是用户访问Kubernetes的代理程序,发送给API Server的身份认证及资源操作请求都是由Dashboard应用程序完成,因而用户提交的身份凭据需要关联至某个Service Account。
集群全局的资源管理操作依赖于集群管理员权限,因而需要为专用于访问Dashboard的Service Account分配内置的cluster-admin集群角色。随后,将相应Service Account的令牌信息提交给Dashboard并认证到API Server,便可使得Dashboard继承了该账户的所有管理权限。
例如,下面在kubernetes-dashboard名称空间创建一个名为dashboard-admin的Service Account完成该目标。
[root@k8s-master01 apps]# kubectl create serviceaccount admin-dashboard -n kubernetes-dashboard serviceaccount/admin-dashboard created [root@k8s-master01 apps]# kubectl create clusterrolebinding admin-dashboard --clusterrole=cluster-admin \ --serviceaccount=kubernetes-dashboard:admin-dashboard clusterrolebinding.rbac.authorization.k8s.io/admin-dashboard-clusterrolebinding created
配置清单
--- #配置访问dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: admin-dashboard namespace: kubernetes-dashboard --- #把访问dashboaed的sa与clusterrole的cluster-admin绑定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-dashboard subjects: - kind: ServiceAccount name: admin-dashboard namespace: kubernetes-dashboard apiGroup: "" #sa default "" ; user and group default rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
获取到服务账户kubernetes-dashboard:admin-dashboard关联的Secret对象中的令牌信息,提交给Dashboard即可完成认证。下面第一个命令检索到该服务账户的Secret对象名称并保存到变量中,第二个命令则从该Secret中获取经过Base64编码后的令牌信息,并打印出解码后的令牌信息。
[root@k8s-master01 apps]# kubectl get secrets -n kubernetes-dashboard NAME TYPE DATA AGE admin-dashboard-token-jldx4 kubernetes.io/service-account-token 3 10m default-token-gp58x kubernetes.io/service-account-token 3 23m kubernetes-dashboard-certs Opaque 0 23m kubernetes-dashboard-csrf Opaque 1 23m kubernetes-dashboard-key-holder Opaque 2 23m kubernetes-dashboard-token-w9lgr kubernetes.io/service-account-token 3 23m [root@k8s-master01 apps]# kubectl describe secrets admin-dashboard-token-jldx4 -n kubernetes-dashboard Name: admin-dashboard-token-jldx4 Namespace: kubernetes-dashboard Labels: <none> Annotations: kubernetes.io/service-account.name: admin-dashboard kubernetes.io/service-account.uid: 0fa14043-bae1-400b-b373-437b0b2155aa Type: kubernetes.io/service-account-token Data ==== namespace: 20 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2cXNJdXJONzB2MmY1UDdadlFQZkxGSjFwUEI2TW0wVnIwdkhZWi1vakkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi1kYXNoYm9hcmQtdG9rZW4tamxkeDQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiYWRtaW4tZGFzaGJvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMGZhMTQwNDMtYmFlMS00MDBiLWIzNzMtNDM3YjBiMjE1NWFhIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmFkbWluLWRhc2hib2FyZCJ9.GLRbOy-sWiFhd6fuBxvHRH2Zd3nykDoBDCJAz1IIm98-swb9IdgYCcYv5T67lKPZDYZC28ZXj0XM8vKSg5GipjBmxoD_TQkDgHjDB38z2HNYjOaT_3oB7VnDVGGbSdPx9OvojPgPcmqYN0VStVnXm6jx09qg6MiE3BWhIe9krAE7yRFmBALuY33FDL0FOxXU0g0cWBzLAaJJblk8zIJBl_eggydYgYXNU0FDS6rd1nq4WNXqEX3QB_w6l3VhJiNX1ocvTYPnL07p_dKbjM317JIJKgu3VFL-23685N6rComDAYh-H9__CuS_gmx6jGkTZdAuZqlIxhgARmiFuWnPtg ca.crt: 1066 bytes
将令牌信息复制到Dashboard的登录界面便可完成认证。
每次访问Dashboard之前都要先通过如上命令获取相应的令牌是件相当烦琐的事情,更简便的办法是依该身份凭据创建出一个专用的kubeconfig文件并存储到客户端,随后登录时在浏览器中通过本地路径加载该kubeconfig文件即可完成认证,更加安全和便捷。
下面仅给出相关步骤,实现为服务账户kubernetes-dashboard:admin-user创建相关的配置文件
1)创建admin-user私钥及证书签发请求
[root@k8s-master01 ~]#mkdir $HOME/.certs [root@k8s-master01 ~]#(umask 077; openssl genrsa -out $HOME/.certs/admin-user.key 2048) [root@k8s-master01 ~]# openssl req -new -key $HOME/.certs/admin-user.key \ -out $HOME/.certs/admin-user.csr \ -subj "/CN=admin-user/O=dashboard"
2) kubernetes-ca证书签发admin-user证书
[root@k8s-master01 ~]#openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \ -in $HOME/.certs/admin-user.csr -out $HOME/.certs/admin-user.crt
3)添加集群配置,包括集群名称、API Server URL和信任的CA的证书;clusters配置段中的各列表项名称需要唯一。
[root@k8s-master01 ~]#kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=https://192.168.32.248:6443 \ --kubeconfig=$HOME/.kube/admin-user.config
4) 设置客户端认证
[root@k8s-master01 ~]#kubectl config set-credentials admin-user \ --client-key=$HOME/.certs/admin-user.key \ --client-certificate=$HOME/.certs/admin-user.crt \ --embed-certs=true \ --kubeconfig=$HOME/.kube/admin-user.config
5) 以用户admin-user的身份凭据与Kubernetes集群建立映射关系。配置上下文
[root@k8s-master01 ~]#kubectl config set-context admin-user@kubernetes \ --cluster=kubernetes \ --user=admin-user \ --kubeconfig=$HOME/.kube/admin-user.config
6) 设置当前上下文为admin-user@kubernetes。
[root@k8s-master01 ~]#kubectl config use-context admin-user@kubernetes --kubeconfig=$HOME/.kube/admin-user.config
清单配置
[root@k8s-master01 ~]#vim admin-dashboard.yaml --- #配置访问dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard --- #把访问dashboaed的sa与clusterrole的cluster-admin绑定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard apiGroup: "" roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
[root@k8s-master01 apps]# kubectl apply -f admin-dashboard.yaml serviceaccount/admin-user created clusterrolebinding.rbac.authorization.k8s.io/admin-user created
8) 获取token名称及token信息
[root@k8s-master01 apps]# kubectl get secrets -n kubernetes-dashboard|grep admin-user admin-user-token-v52gz kubernetes.io/service-account-token 3 7m9s [root@k8s-master01 apps]# kubectl describe secrets admin-user-token-v52gz -n kubernetes-dashboard Name: admin-user-token-v52gz Namespace: kubernetes-dashboard Labels: <none> Annotations: kubernetes.io/service-account.name: admin-user kubernetes.io/service-account.uid: b2dde30c-b6a7-479a-ba25-8503291ce91e Type: kubernetes.io/service-account-token Data ==== namespace: 20 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2cXNJdXJONzB2MmY1UDdadlFQZkxGSjFwUEI2TW0wVnIwdkhZWi1vakkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXY1Mmd6Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMmRkZTMwYy1iNmE3LTQ3OWEtYmEyNS04NTAzMjkxY2U5MWUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.B0RPQ9mUue3GLt7xAfYD9nwKidGVEmaITNHVe-9o_IGkMZJHF3P0JFBSc6JagaTtqlzJCYzebqwqqYrI7o3XuQXRJ_LCQyQ9BuWkGOe27Wh95gaEgqR7oWH50iLT-ClWxOQOjPrchHfEeZV4XlCbk8NhphibZECKG7V6ExK4X0pBu0sc8Gb8bgVz-rvM69ipWncJgP7CmNcHh_-U4VYLEtMh1NkgMKOmf0nB61UF7lBLxLav1cKDNSJAowLSwJgKttNyOBS1Z5OQJDCwZbS2lN2A4PapfRisVGsOuxLt-ZePfr-RlhVWPvd02HXZjnw7VGzrc-yC8nAHzChQ3CQ1cw ca.crt: 1066 bytes
9) 将token信息写入/root/.kube/admin-user.config的最后面
[root@k8s-master01 apps]# vim /root/.kube/admin-user.config contexts: - context: cluster: kubernetes user: admin-user name: admin-user@kubernetes current-context: admin-user@kubernetes kind: Config preferences: {} users: - name: admin-user user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN1akND...... client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFB...... token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2cXNJdXJONzB2MmY1UDdadlFQZkxGSjFwUE......
将admin-user.config拷贝到其它电脑上,即可使用token登录dashboard
说明:
dashboard是使用serviceaccount账号。
若需要设定的用户仅具有某个名称空间的管理权限,或者仅拥有集群或名称空间级别的资源读取权限,都能够通过RBAC权限管理模型来实现。这类用户的设定过程与前述步骤中的关键不同之处仅在于角色分配步骤。例如,在名称空间kubernetes-dashboard中创建服务账户monitor-user,并通过ClusterRoleBinding为其分配默认的集群角色view,便可创建一个仅具有全局读取权限的Dashboard用户,所需步骤如下。
[root@k8s-master01 apps]# kubectl create serviceaccount monitor -n kubernetes-dashboard [root@k8s-master01 apps]# kubectl create clusterrolebinding monitor --clusterrole=view \ --serviceaccount=kubernetes-dashboard:monitor
配置清单
[root@k8s-master01 ~]#vim monitor-dashboard.yaml --- #配置访问dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: monitor namespace: kubernetes-dashboard --- #把访问dashboaed的sa与clusterrole的cluster-admin绑定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: monitor subjects: - kind: ServiceAccount name: monitor namespace: kubernetes-dashboard apiGroup: "" roleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.io
或者在名称空间dev中创建一个服务账户dev-ns-admin,并通过RoleBinding为其分配默认的集群角色admin,就能创建一个仅具有dev名称空间管理权限的Dashboard用户,所需要的步骤如下。
[root@k8s-master01 apps]# kubectl create serviceaccount ns-admin -n dev
[root@k8s-master01 apps]# kubectl create rolebinding ns-admin --clusterrole=admin --serviceaccount=dev:ns-admin
配置清单
[root@k8s-master01 ~]#vim monitor-dashboard.yaml --- #配置访问dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: ns-admin namespace: dev --- #把访问dashboaed的sa与clusterrole的cluster-admin绑定 apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: ns-admin subjects: - kind: ServiceAccount name: ns-adminr namespace: dev apiGroup: "" roleRef: kind: ClusterRole name: admin apiGroup: rbac.authorization.k8s.io
最后找出token并写入kubeconfig文件的最后即可。
七、准入控制器
1、准入控制器概述
准入控制器的相关代码必须要由管理员编译进kube-apiserver中才能使用,实现方式缺乏灵活性。于是,Kubernetes自1.7版本引入了Initializers和External Admission Webhooks来尝试突破此限制,而且自1.9版本起,External Admission Webhooks又被分为MutatingAdmissionWebhook和ValidatingAdmissionWebhook两种类型,分别用于在API中执行对象配置的“变异”和“验证”操作,前一种类型的控制器会“改动”和“验证”资源规范,而后一种类型仅“验证”资源规范是否合规。
在具体的代码实现上,一个准入控制器可以是验证型、变异型或兼具此两项功能。例如,LimitRanger准入控制器可以使用默认资源请求和限制(变异阶段)来扩展Pod,也能够校验有着显式资源需求定义的Pod是否超出LimitRange对象(验证阶段)的定义。而在具体运行时,准入控制也会根据准入控制器类型分阶段运行,第一个阶段串行运行各变异型控制器,第二阶段则串行运行各验证型控制器,如图所示。在此过程中,任何控制器拒绝请求都将导致整个请求被即刻拒绝,并将错误信息返回给客户端。
Kubernetes内置支持的所有准入控制器及其功能说明请参考官方文档中的说明,具体地址为https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/。
2、LimitRange
尽管用户可以为容器或Pod资源指定资源需求及资源限制,但这并非强制性要求,那些未明确定义资源限制的容器应用很可能会因程序Bug或真实需求而吞掉本地工作节点上的所有可用计算资源。因此妥当的做法是,使用LimitRange资源在每个名称空间中限制每个容器的最小及最大计算资源用量,以及为未显式定义计算资源使用区间的容器设置默认的计算资源需求和计算资源限制。一旦在名称空间上定义了LimitRange对象,客户端创建或修改资源对象的操作必将受到LimitRange控制器的“验证”,任何违反LimitRange对象定义的资源最大用量的请求都将被直接拒绝。 LimitRange支持在Pod级别与容器级别分别设置CPU和内存两种计算资源的可用范围,它们对应的资源范围限制类型分别为Pod和Container。一旦在名称空间上启用LimitRange,该名称空间中的Pod或容器的requests和limits的各项属性值必须在对应的可用资源范围内,否则将会被拒绝,这是验证型准入控制器的功能。以Pod级别的CPU资源为例,若某个LimitRange资源为其设定了[0.5,4]的区间,则相应名称空间下任何Pod资源的requests.cpu的属性值必须要大于等于500m,同时,limits.cpu的属性值也必须要小于等于4。而未显式指定request和limit属性的容器,将会从LimitRange资源上分别自动继承相应的默认设置,这是变异型准入控制器的功能。
LimitRange也支持在PersistentVolumeClaim资源级别设定存储空间的范围限制,它用于限制相应名称空间中创建的PVC对象请求使用的存储空间不能逾越指定的范围。未指定requests和limits属性的PVC规范,将在创建时自动继承LimitRange上配置的默认值。
[root@k8s-master01 apps]# vim limitrange-demo.yaml apiVersion: v1 kind: LimitRange metadata: name: resource-limits namespace: dev spec: limits: - type: Pod max: cpu: "4" memory: "4Gi" min: cpu: "500m" memory: "16Mi" - type: Container max: cpu: "4" memory: "1Gi" min: cpu: "100m" memory: "4Mi" default: cpu: "2" memory: "512Mi" defaultRequest: cpu: "500m" memory: "64Mi" maxLimitRequestRatio: cpu: "4" - type: PersistentVolumeClaim max: storage: "10Gi" min: storage: "1Gi" default: storage: "5Gi" defaultRequest: storage: "1Gi" maxLimitRequestRatio: storage: "5"
LimitRange资源的详细描述会以非常直观、清晰的方式输出相关的资源限制及默认值的定义,将如上配置清单中的LimitRange资源resource-limits创建到集群上,而后便可使用describe命令查看:
[root@k8s-master01 apps]# kubectl create ns dev namespace/dev created [root@k8s-master01 apps]# kubectl apply -f limitrange-demo.yaml limitrange/resource-limits created [root@k8s-master01 apps]# kubectl get limitrange No resources found in default namespace. [root@k8s-master01 apps]# kubectl get limitrange -n dev NAME CREATED AT resource-limits 2021-05-12T06:17:14Z [root@k8s-master01 apps]# kubectl describe limitrange resource-limits -n dev Name: resource-limits Namespace: dev Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio ---- -------- --- --- --------------- ------------- ----------------------- Pod cpu 500m 4 - - - Pod memory 16Mi 4Gi - - - Container cpu 100m 4 500m 2 4 Container memory 4Mi 1Gi 64Mi 512Mi - PersistentVolumeClaim storage 1Gi 10Gi 1Gi 5Gi 5
通过在dev名称空间中创建Pod对象与PVC对象对各限制的边界和默认值的效果进行多维度测试。先创建一个仅包含一个容器且没有默认系统资源需求和限制的Pod对象:
[root@k8s-master01 apps]# kubectl run testpod-1 --image="ikubernetes/demoapp:v1.0" -n dev pod/testpod-1 created
Pod对象testpod-1资源规范中被自动添加了CPU和内存资源的requests和limits属性,它的值来自limitranges/resource-limits中的定义,如下面的命令及截取的结果片段所示。
[root@k8s-master01 apps]# kubectl get pod testpod-1 -n dev -o yaml apiVersion: v1 kind: Pod metadata: annotations: kubernetes.io/limit-ranger: 'LimitRanger plugin set: cpu, memory request for container testpod-1; cpu, memory limit for container testpod-1' ...... spec: containers: - image: ikubernetes/demoapp:v1.0 imagePullPolicy: IfNotPresent name: testpod-1 resources: limits: cpu: "2" memory: 512Mi requests: cpu: 500m memory: 64Mi ......
没有给containers配置资源限制,则使用定义好的默认资源限制mincpu=0.5,minmen=64m;maxcpu=2,maxmem=512m。
若Pod对象设定的CPU或内存的requests属性值小于LimitRange中相应资源的下限,或limits属性值大于设定的相应资源的上限,就会触发LimitRanger准入控制器拒绝相关的请求。例如下面创建Pod的命令中,仅requests.memory一个属性值违反了limitrange/resource-limits中的定义,但请求同样会被拒绝。
[root@k8s-master01 apps]# kubectl run testpod-2 --image="ikubernetes/demoapp:v1.0" -n dev \ > --limits='cpu=2,memory=1Gi' --requests='cpu=1,memory=8Mi' Error from server (Forbidden): pods "testpod-2" is forbidden: minimum memory usage per Pod is 16Mi, but request is 8388608 #配置pod的内存最少为8m,我们设置的pod资源位16m~4G,小于最下值,pod创建失败。
在dev名称空间中创建的PVC对象的可用存储空间也将受到LimitRange资源中定义的限制。
3、ResourceQuota
尽管LimitRange资源能在名称空间上限制单个容器、Pod或PVC相关的系统资源用量,但用户依然可以创建出无数的资源对象,进而侵占集群上所有的可用系统资源。ResourceQuota资源能够定义名称空间级别的资源配额,从而在名称空间上限制聚合资源消耗的边界,它支持以资源类型来限制用户可在本地名称空间中创建的相关资源的对象数量,以及这些对象可消耗的计算资源总量等。 而同名的ResourceQuota准入控制器负责观察传入的请求,并确保它没有违反相应名称空间中ResourceQuota资源定义的任何约束。ResourceQuota准入控制器属于“验证”类型的控制器,用户创建或更新资源的操作违反配额约束时将会被拒绝,API Server会响应以HTTP状态代码403 FORBIDDEN,并显示一条消息以提示违反的约束条件。 ResourceQuota资源可限制名称空间中处于非终止状态的所有Pod对象的计算资源需求及计算资源限制总量。
▪cpu或requests.cpu:CPU资源相关请求的总量限额。
▪memory或requests.cpu:内存资源相关请求的总量限额。
▪limits.cpu:CPU资源相关限制的总量限额。
▪limits.memory:内存资源相关限制的总量限额。
ResourceQuota资源还支持为本地名称空间中的PVC存储资源的需求总量和限制总量设置限额,它能够分别从名称空间中的全部PVC、隶属于特定存储类的PVC以及基于本地临时存储的PVC分别进行定义。
▪requests.storage:所有PVC存储需求的总量限额。 ▪persistentvolumeclaims:可以创建的PVC总数限额。 ▪<storage-class-name>.storageclass.storage.k8s.io/requests.storage:特定存储类上可使用的所有PVC存储需求的总量限额。 ▪<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims:特定存储类上可使用的PVC总数限额。 ▪requests.ephemeral-storage:所有Pod可以使用的本地临时存储资源的requets总量。 ▪limits.ephemeral-storage:所有Pod可用的本地临时存储资源的limits总量。
下面的资源清单(resourcequota-demo.yaml)在dev名称空间中定义了一个ResourceQuota资源对象,它定义了计算资源与存储资源分别在requests和limits维度的限额,也定义了部署资源类型中的可用对象数量。
[root@k8s-master01 apps]# vim resourcequota-demo.yaml apiVersion: v1 kind: ResourceQuota metadata: name: resourcequota-demo namespace: dev spec: hard: pods: "5" count/services: "5" count/configmaps: "5" count/secrets: "5" count/cronjobs.batch: "2" requests.cpu: "2" requests.memory: "4Gi" limits.cpu: "4" limits.memory: "8Gi" count/deployments.apps: "2" count/statefulsets.apps: "2" persistentvolumeclaims: "6" requests.storage: "20Gi" fast-rbd.storageclass.storage.k8s.io/requests.storage: "20Gi" fast-rbd.storageclass.storage.k8s.io/persistentvolumeclaims: "6"
与LimitRange不同的是,ResourceQuota会计入指定范围内,先前的资源对象对系统资源和资源对象的限额占用情况,因此将resourceqouta-demo创建到集群上之后,dev名称空间中现有的资源会立即分去限额内的一部分可用空间,这在ResourceQuota资源的详细描述中会有直观展示。
[root@k8s-master01 apps]# kubectl apply -f resourcequota-demo.yaml resourcequota/resourcequota-demo created [root@k8s-master01 apps]# kubectl describe resourcequotas/resourcequota-demo -n dev Name: resourcequota-demo Namespace: dev Resource Used Hard -------- ---- ---- count/configmaps 1 5 count/cronjobs.batch 0 2 count/deployments.apps 0 2 count/secrets 1 5 count/services 0 5 count/statefulsets.apps 0 2 fast-rbd.storageclass.storage.k8s.io/persistentvolumeclaims 0 6 fast-rbd.storageclass.storage.k8s.io/requests.storage 0 20Gi limits.cpu 2 4 limits.memory 512Mi 8Gi persistentvolumeclaims 0 6 pods 1 5 requests.cpu 500m 2 requests.memory 64Mi 4Gi requests.storage 0 20Gi
dev名称空间下的Pod资源限额已被先前的自主式Pod对象消耗了1/5,与此同时,计算资源请求和限制也各占用了一部分配额。随后,在dev名称空间中创建Pod资源时,requests.cpu、requests.memroy、limits.cpu、limits.memory和pods等任何一个限额的超出都将致使创建操作失败,如下面的命令及结果所示。
[root@k8s-master01 apps]# kubectl run testpod-2 --image="ikubernetes/demoapp:v1.0" \ > --requests="cpu=2,memory=1Gi" --limits="cpu=2,memory=1Gi" -n dev Error from server (Forbidden): pods "testpod-2" is forbidden: exceeded quota: resourcequota-demo, requested: requests.cpu=2, used: requests.cpu=500m, limited: requests.cpu=2 #cpu数超过最大的限制数量
每个ResourceQuota资源对象上还支持定义一组作用域,用于定义资源上的配额仅生效于这组作用域交集范围内的对象,目前适用范围包括Terminating、NotTerminating、BestEffort和NotBestEffort。
▪Terminating:匹配.spec.activeDeadlineSeconds的属性值大于等于0的所有Pod对象。
▪NotTerminating:匹配.spec.activeDeadlineSeconds的属性值为空的所有Pod对象。
▪BestEffort:匹配所有位于BestEffort QoS类别的Pod对象。
▪NotBestEffort:匹配所有非BestEffort QoS类别的Pod对象。
4、PodSecurityPolicy
Pod和容器规范中允许用户使用securityContext字段定义安全相关的配置,但允许任何用户随意以特权模式运行容器或者使用任意的Linux内核能力等,显然存在着难以预料的安全风险。API Server提供了PodSecurityPolicy资源让管理员在集群全局定义或限定用户在Pod和容器上可用及禁用的安全配置,例如是否可使用特权容器和主机名称空间,可使用的主机网络端口范围、卷类型和Linux Capabilities等。因此,本质上来说,PodSecurityPolicy资源就是集群全局范围内定义的Pod资源可用的安全上下文策略。同名的PodSecurityPolicy准入控制器负责观察集群范围内的Pod资源的运行属性,并确保它没有违反PodSecurityPolicy资源定义的约束条件。
PSP准入控制器会根据显式定义的PSP资源中的安全策略判定允许何种Pod资源的创建操作,若无任何可用的安全策略,它将阻止创建任何Pod资源。新部署的Kubernetes集群默认并不会自动生成任何PSP资源,因而该准入控制器默认处于禁用状态。PSP资源的API接口(policy/v1beta1/podsecuritypolicy)独立于PSP准入控制器,因此管理员可以先定义好必要的Pod安全策略,再设置kube-apiserver启用PSP准入控制器。不当的Pod安全策略可能会产生难以预料的副作用,因此请确保添加的任何PSP对象都经过了充分测试。
PodSecurityPolicy是标准的API资源类型,它隶属于policy群组,在spec字段中嵌套多种安全规则来定义期望的目标,资源规范及简要的使用说明如下所示。
apiVersion: policy/v1beta1 # PSP资源所属的API群组及版本 kind: PodSecurityPolicy # 资源类型标识 metadata: name <string> # 资源名称 spec: allowPrivilegeEscalation <boolean> # 是否允许权限升级 allowedCSIDrivers <[]Object> #内联CSI驱动程序列表,必须在Pod规范中显式定义 allowedCapabilities <[]string> # 允许使用的内核能力列表,“*”表示all allowedFlexVolumes <[]Object> # 允许使用的Flexvolume列表,空值表示all allowedHostPaths <[]Object> # 允许使用的主机路径列表,空值表示all allowedProcMountTypes <[]string> # 允许使用的ProcMountType列表,空值表示默认 allowedUnsafeSysctls <[]string> # 允许使用的非安全sysctl参数,空值表示不允许 defaultAddCapabilities <[]string> # 默认添加到Pod对象的内核能力,可被drop defaultAllowPrivilegeEscalation <boolean> # 是否默认允许内核权限升级 forbiddenSysctls <[]string> # 禁止使用的sysctl参数,空表示不禁用 fsGroup <Object> # 允许在SecurityContext中使用的fsgroup,必选字段 rule <string> # 允许使用的FSGroup规则,支持RunAsAny和MustRunAs ranges <[]Object> # 允许使用的组ID范围,需要与MustRunAs规则一同使用 max <integer> # 最大组ID号 min <integer> # 最小组ID号 hostIPC <boolean> # 是否允许Pod使用hostIPC hostNetwork <boolean> # 是否允许Pod使用hostNetwork hostPID <boolean> # 是否允许Pod使用hostPID hostPorts <[]Object> # 允许Pod使用的主机端口暴露其服务的范围 max <integer> # 最大端口号,必选字段 min <integer> # 最小端口号,必选字段 privileged <boolean> # 是否允许运行特权Pod readOnlyRootFilesystem <boolean> # 是否设定容器的根文件系统为“只读” requiredDropCapabilities <[]string> # 必须要禁用的内核能力列表 runAsGroup <Object> # 允许Pod在runAsGroup中使用的值列表,未定义表示不限制 runAsUser <Object> # 允许Pod在runAsUser中使用的值列表,必选字段 rule <string> # 支持RunAsAny、MustRunAs和MustRunAsNonRoot ranges <[]Object> # 允许使用的组ID范围,需要跟MustRunAs规则一同使用 max <integer> # 最大组ID号 min <integer> # 最小组ID号 runtimeClass <Object> # 允许Pod使用的运行类,未定义表示不限制 allowedRuntimeClassNames <[]string> # 可使用的runtimeClass列表,“*”表示all defaultRuntimeClassName <string> # 默认使用的runtimeClass seLinux <Object> # 允许Pod使用的selinux标签,必选字段 rule <string> # MustRunAs表示使用seLinuxOptions定义的值;RunAsAny表示可使用任意值 seLinuxOptions <Object> # 自定义seLinux选项对象,与MustRunAs协作生效 supplementalGroups <Object> # 允许Pod在SecurityContext中使用附加组,必选字段 volumes <[]string> # 允许Pod使用的存储卷插件列表,空表示禁用,“*”表示all
然而,即便在启用了PSP准入控制器的情况下,PSP对象依然不会生效,管理员还需要借助授权插件(例如RBAC)将use权限授权给特定的Role或ClusterRole,再为相关的User Account或Service Account分配这些角色才能让PSP策略真正生效。下面简单说明为Kubernetes集群设定的能支撑集群自身运行的框架性的Pod安全策略,以及允许非管理员使用的Pod安全策略,而后启用PSP准入控制器中使这些策略生效的方法。
4.1 设置特权及受限的PSP对象
通常,system:masters组内的管理员账户、system:node组内的kubelet账户,以及kube-system名称空间中的所有服务账户需要拥有创建各类Pod对象的权限,包括创建特权Pod对象。因此,启用PSP准入控制器之前需要先创建一个特权PSP资源,并将该资源的使用权赋予各类管理员账户以确保Kubernetes集群的基础服务可以正常运行。一个示例性的特权PSP资源清单(psp-privileged.yaml)如下,它启用了几乎所有的安全配置。
[root@k8s-master01 apps]# vim psp-privileged.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' spec: privileged: true allowPrivilegeEscalation: true allowedCapabilities: ['*'] allowedUnsafeSysctls: ['*'] volumes: ['*'] hostNetwork: true hostPorts: - min: 0 max: 65535 hostIPC: true hostPID: true runAsUser: rule: 'RunAsAny' runAsGroup: rule: 'RunAsAny' seLinux: rule: 'RunAsAny' supplementalGroups: rule: 'RunAsAny' fsGroup: rule: 'RunAsAny'
出于安全加强的需要,除了有特权需求的系统级应用程序及集群管理员账户之外,其他应用或普通账户默认不应该允许使用与安全上下文相关的任何配置。因而,系统内置的特殊组之外的其他普通账户或服务账户,绝大多数都不必使用安全配置,它们仅可使用受限的安全策略。下面的资源清单(psp-restrict.yaml)定义了一个完全受限的安全策略,它禁止了几乎所有的特权操作。
[root@k8s-master01 apps]#vim psp-restrict.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: restricted annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default' apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default' seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' spec: privileged: false allowPrivilegeEscalation: false allowedUnsafeSysctls: [] requiredDropCapabilities: - ALL # 允许使用的核心存储卷类型 volumes: ['configMap', 'emptyDir', 'projected', 'secret', 'secret', 'persistentVolumeClaim'] hostNetwork: false hostIPC: false hostPID: false runAsUser: rule: 'MustRunAsNonRoot' seLinux: rule: 'RunAsAny' supplementalGroups: rule: 'MustRunAs' ranges: # Forbid adding the root group. - min: 1 max: 65535 fsGroup: rule: 'MustRunAs' ranges: # Forbid adding the root group. - min: 1 max: 65535 readOnlyRootFilesystem: false
将上面两个资源清单中定义的PSP资源提交并创建到集群之上,随后便可授权特定的Role或ClusterRole资源通过use调用它们。PSP资源创建完成后才能授权特定的Role或ClusterRole资源通过use进行调用。我们这里首先使用如下命令将上面配置清单中定义的资源创建到集群之上。
[root@k8s-master01 apps]# kubectl apply -f psp-privileged.yaml -f psp-restrict.yaml podsecuritypolicy.policy/privileged created podsecuritypolicy.policy/restricted created
启用PodSecurityPolicy准入控制器后,仅被授权使用PSP资源的账户才能够在该资源中定义的策略框架下行使账户权限范围内的资源管理操作。因此,这里还需要显式授予system:masters、system:nodes和system:serviceaccounts:kube-system组内的用户可以使用podsecuritypolicy/privileged资源,其他成功认证后的用户能够使用podsecuritypolicy/restricted资源。RBAC权限模型中,任何Subject都不能直接获得权限,它们需要借助分配到的角色获得权限。因此,下面先创建两个分别能使用podsecuritypolicy/privileged和podsecuritypolicy/restricted资源的ClusterRole。
下面的资源清单(clusterrole-with-psp.yaml)中创建了两个ClusterRole资源,授权psp-privileged可以使用名为privileged的安全策略,psp-restricted可以使用名为restricted的安全策略。
[root@k8s-master01 apps]# clusterrole-with-psp.yaml kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: psp-restricted rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - restricted --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: psp-privileged rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - privileged
下面的资源清单(clusterrolebinding-with-psp.yaml)定义了两个ClusterRoleBinding对象:前一个为system:masters、system:node和system:serviceaccounts:kube-system组的账户分配集群角色psp-privileged,从而能够使用任何安全配置;后一个为system: authenticated组内的账户分配集群角色psp-restricted,以禁止它们在Pod和容器上使用任何安全配置。
[root@k8s-master01 apps]#vim clusterrolebinding-with-psp.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: privileged-psp-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: psp-privileged subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:masters - apiGroup: rbac.authorization.k8s.io kind: Group name: system:node - apiGroup: rbac.authorization.k8s.io kind: Group name: system:serviceaccounts:kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: restricted-psp-user roleRef: kind: ClusterRole name: psp-restricted apiGroup: rbac.authorization.k8s.io subjects: - kind: Group apiGroup: rbac.authorization.k8s.io name: system:authenticated
将上面两个资源清单中定义的ClusterRole和ClusterRoleBinding资源创建到集群上,即可为API Server启用PodSecurityPolicy准入控制器。
[root@k8s-master01 apps]# kubectl apply -f clusterrole-with-psp.yaml -f clusterrolebinding-with-psp.yaml clusterrole.rbac.authorization.k8s.io/psp-restricted created clusterrole.rbac.authorization.k8s.io/psp-privileged created clusterrolebinding.rbac.authorization.k8s.io/privileged-psp-user created clusterrolebinding.rbac.authorization.k8s.io/restricted-psp-user created
API Server的应用程kube-apiserver使用--enable-admission-plugins选项显式指定要加载的准入控制器列表,因此在该选项的列表中添加PodSecurityPolicy条目,并重启kube-apiserver程序便能启用PSP准入控制器。对于使用kubeadm部署的Kubernetes集群来说,编辑Master节点上的/etc/kubernetes/manifests/kube-apiserver.yaml配置清单,直接修改--enable-admission-plugins选项的值,并添加PodSecurityPolicy列表项即可,各列表项以逗号分隔。kubelet监控到/etc/kubernetes/manifests目录下的任何资源清单的改变时都会自动重建相关的Pod对象,因此编辑并保存kube-apiserver.yaml资源清单后,kubelet会通过重建相关的静态Pod而自动生效。
待kube-apiserver重启完成后,可通过监测API Server程序的运行状态及相关日志来判定PodSecurityPolicy准入控制器是否成功启用。以静态Pod运行kube-apiserver的日志同样可使用kubectl logs命令获取。如下面的命令及截取的结果所示,PodSecurityPolicy准入控制器已然成功加载。若Kubernetes的各系统类Pod资源运行状态正常,即表示安全策略已然成功启用。
[root@k8s-master01 apps]# kubectl logs kube-apiserver-k8s-master01.ilinux.io -n kube-system …… plugins.go:158] Loaded 13 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,…,PodSecurityPolicy,… plugins.go:161] Loaded 11 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurityPolicy,… …… 注意: 尽管PSP对已处于运行状态的Pod或容器没有影响,但对于正常运行中的Kubernetes集群来说,中途启用PodSecurityPolicy仍然可能会导致诸多难以预料的错误,尤其是没有事先为用到安全配置的Pod资源准备好可用的PSP资源时,这些Pod资源一旦重启便会因触发PSP策略而被阻止。
接下来,我们可通过能成功认证的普通账户测试其创建Pod资源时是否受限于restricted安全策略,以验证PodSecurityPolicy资源的生效状态。下面的命令尝试以dev名称空间的管理员mason用户创建一个使用了主机端口(hostPort)的Pod资源,但该操作被PodSecurityPolicy拒绝。
[root@k8s-master01 apps]# kubectl run pod-with-hostport --image="ikubernetes/demoapp:v1.0" \ --port=80 --hostport=32080 -n dev --context='mason@kubernetes' Error from server (Forbidden): pods "pod-with-hostport" is forbidden: unable to validate against any pod security policy: [spec.containers[0].hostPort: Invalid value: 32080: Host port 32080 is not allowed to be used. Allowed ports: []]