Kubernetes API 安全机制详解

 前 言 

作为后台支撑,Kubernetes优势明显,咪付的蓝牙过闸系统和全态识别AI系统的后台支撑采用了Kubernetes。Kubernetes平台稳定运行一个重要因素是安全性的有效保障,其中API的访问安全是一个重要方面,本文讲解Kubernetes如何保证API访问的安全性。

本篇文章包含以下内容:

介绍Kubernetes API安全防护措施

如何对用户身份进行认证

如何对访问资源进行鉴权

介绍Kubernetes准入控制的作用

Kubernetes平台由6个组件组成:apiserver、scheduler、controller-manager、kubelet、kube-proxy、etcd,其中etcd是Kubernetes数据库,在生产环境中一般不允许直接访问。平台通过apiserver组件对外提供HTTPS协议服务接口,其它scheduler、controller-manager、kubelet、kube-proxy组件不提供交互接口。运维中对平台的管理操作都需要通过apiserver提供的功能接口完成,因此Kubernetes总体的API安全防护机制是对用户访问操作HTTPS服务接口的控制。

用户可通过客户端kubectl命令行工具或其它方式访问Kubernetes的API资源,每个访问API的请求,都要经过三个步骤校验:Authentication、Authorization、Admission Control,总体如下图所示:

Authentication(认证),对用户身份进行验证。认证方式现共有8种,可以启用一种或多种认证方式,只要有一种认证方式通过,就不再进行其它方式的认证。通常启用X509 Client Certs和Service Accout Tokens两种认证方式。

Authorization(授权),验证用户是否拥有访问权限。授权方式现共有6种,可以启用一种或多种授权方式,启用的任一种方式依据授权策略明确允许或拒绝请求,则立即返回,不再进行其它方式的授权。通常启用RBAC和Node授权方式。

Admission Control(准入控制),对API资源对象修改、校验。它有一个插件列表,所有请求需要经过这个列表中的每个准入控制插件修改、校验,如果某一个准入控制插件验证失败,就拒绝请求。

用户访问 Kubernetes API时,apiserver认证授权模块从HTTPS请求中获取用户的身份信息、请求资源路径和业务对象参数。身份信息是用来确认用户的身份是否合法,资源路径是用于判定用户是否拥有访问操作的权限,业务对象参数则在准入控制中接受修改或校验参数设置是否符合业务要求。

请求到达后,apiserver从请求的证书、Header中获取用户信息:Name(用户名)、UID(用户唯一标识)、Groups(用户分组)、Extra(用户额外信息)。认证通过后,通过Context将用户信息向下传播。

授权模块从Context、请求的Method及URI提取用户信息、请求资源属性。使用请求资源属性与授权策略匹配或向外部服务验证判断是否准予访问。

准入控制接收到HTTPS请求提交的Body内容、用户信息、资源信息,根据不同准入控制插件的功能对上述信息做修改或校验。

  认 证  

认证即身份验证,认证关注的是谁发送的请求,也就是说用户必须用某种方式证明自己的身份信息。Kubernetes集群有两种类型的用户:普通用户(normal users)、服务账户(service accounts),普通用户并不被Kubernetes管理和保存;而服务账户由Kubernetes存储在etcd中,并可通过apiserver API管理。

最新的1.14版本中已支持8种认证方式,从身份验证的方法上可分为4种类型:Token、证书、代理、密码。Kubernetes可以同时启用多个认证方式,在这种情况下,每个认证模块都按顺序尝试验证用户身份,直到其中一个成功就认证通过。如果启用匿名访问,没有被认证模块拒绝的请求将被视为匿名用户,并将该请求标记为system:anonymous 用户名和system:unauthenticated组。从1.6版本开始,ABAC和RBAC授权需要对system:anonymous用户或system:unauthenticated组进行明确授权。

启用多个认证方式时,验证顺序如下:

 Authenticating Proxy 

Authenticating Proxy(认证代理),apiserver只验证代理程序身份,不校验用户身份,验证通过后,apiserver通过指定的Header提取访问的用户信息。

启用此认证方式是通过设置apiserver参数--requestheader-username-headers和--requestheader-client-ca-file。

--requestheader-username-headers(必需,不区分大小写)。设定Header中的用户标识,第一个包含value的Header将被作为用户名。

--requestheader-client-ca-file(必需)。PEM 编码的CA证书。用来校验代理端客户端证书,防止 Header 欺骗。

--requestheader-allowed-names(可选)。证书名称(CN)列表。如果设置了,则必须提供名称列表中的有效客户端证书。如果为空,则允许使用任何名称客户端证书。

--requestheader-group-headers( 1.6 以上版本支持,可选,不区分大小写)。建议为 “X-Remote-Group”,设定Header中的用户组标识。指定 Header 中的所有值都将作为用户分组。

--requestheader-extra-headers-prefix (1.6 以上版本支持,可选,不区分大小写)。建议为 “X-Remote-Extra-”,标题前缀可用于查找用户的扩展信息,以任何指定的前缀开头的 Header删除前缀后其余部分将成为用户扩展信息的键值,而 Header值则是扩展的值。

例如下面的配置:

--requestheader-username-headers=X-Remote-User--requestheader-group-headers=X-Remote-Group--requestheader-extra-headers-prefix=X-Remote-Extra-

代理请求:

GET/HTTP/1.1X-Remote-User: mifpayX-Remote-Group: adminX-Remote-Extra-Channel: vpnX-Remote-Extra-Scopes: openidX-Remote-Extra-Scopes: profile

提取后将得到以下用户信息:

 Static Password File 

Static Password File(静态密码认证),启用此认证方式是通过设置apiserver参数--basic-auth-file=SOMEFILE。密码是无限期使用,更改密码后需要重启apiserver才能生效。

静态密码文件是CSV格式文件,至少包含3列:密码,用户名,用户ID。从Kubernetes 1.6版本开始,第四列(可选)为用户分组,如果有多个用户分组,则该列必须加双引号。例如:

password,name,uid,"group1,group2,group3"

当使用HTTPS客户端连接时,apiserver从HTTPS Header中,如Authorization: Basic BASE64ENCODED,提取出用户名和密码。BASE64ENCODED解码后格式为USER:PASSWORD。

从请求中获取到用户名和密码,与静态密码文件中的用户逐一进行比较,匹配成功则认证通过,否则认证失败。认证通过时从CSV中提取匹配的用户信息用于后续的授权等。

 X509 Client Certs 

X509 Client Certs(客户端证书认证),启用此认证方式是通过设置apiserver参数--client-ca-file=SOMEFILE。当用户访问API时,apiserver使用SOMEFILE(CA证书),通过TLS双向认证验证客户端证书是否由它签发。

证书主题属性与用户身份属性的对应关系如下所示:

一个用户属于多个分组,则客户端证书包含多个机构字段。

Static Token File

Static Token File(静态Token认证),启用此认证方式是通过设置apiserver参数--token-auth-file=SOMEFILE。Token长期有效,更改Token列表后需要重启apiserver才能生效。

SOMEFILE是CSV格式文件,至少包含3列:Token、用户名、用户的UID,其次是可选的分组。每一行表示一个用户。例如:

token,name,uid,"group1,group2,group3"

当使用HTTPS客户端连接时,apiserver从HTTPS Header中提取身份Token。Token必须是一个字符序列,在HTTPS Header格式如下:

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

从请求中提取身份Token,与静态Token文件中的用户逐一进行比较,匹配成功则认证通过,否则认证失败。认证通过后将匹配的用户信息用于后续的授权等。

 Service Account Tokens 

Service Account Tokens(服务账户)是一个自动启用的认证方式,采用Token验证请求。有两个可选参数:

--service-account-key-file 给Token签名的私钥。如果未指定,将使用apiserver配置参数--tls-private-key-file指定的私钥。

--service-account-lookup 如果启用,被Kubernetes API删除的Token将被废除。

服务账户通常是Kubernetes API服务自动创建,并关联到pod。账户通过podspec里的serviceAccountName字段与pod进行关联。通常情况下,serviceAccountName不需要手动赋值,这是自动完成。

要手动创建服务账户,只需要使用 kubectl create serviceaccount (NAME) 命令。这将在当前的命名空间中创建一个服务账户和一个相关联的 secret,其包含apiserver的公共CA证书和JSON Web令牌(JWT)。JWT用于服务账户的身份验证。

JWT的内容如下:

Header:{"alg":"RS256","kid":""} Payload:{"iss":"kubernetes/serviceaccount","kubernetes.io/serviceaccount/namespace":"default","kubernetes.io/serviceaccount/secret.name":"test-token-z9tzx","kubernetes.io/serviceaccount/service-account.name":"test","kubernetes.io/serviceaccount/service-account.uid":"c12e8998-7896-11e9-80fb-000c296ce5e0","sub":"system:serviceaccount:default:test"} Signature:js1yoyrzmIn1zccyOc4KD1l887IoBIz-ulKvPnQD7wzn-KLBrQZ7_BzO1laRL71szIjA-iZH0salqTz20yZrXoyhsRzfpdda1o9IIoykblc15H_PUUBqdIPmB7u0XjDyBIraRHU5GO9OMaPwoeWNbt_uzKclckpe53lCtYwLnSfxM8G8kJEb8H3Mg7VnGJpxyuGII2ZG_ZEIO8Le-l_i3rZv-DcJYBpjrcG5DuXX7dcR0NfZM8Ex-VKOaUb9nuA0osXo9s02ATKBWn3zCUDb_O5KZ40fRISkCEmbioKJQLUWdJKvTUu7N-g9m0I9flufczwHO9hf6fC_X7x8-YhMew

服务账户验证是使用secret中的CA证书,对JWT Signature进行校验,验证通过后将用户名设置为 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT),被指定到组system:serviceaccounts和system:serviceaccounts:(NAMESPACE)。

 Bootstrap Tokens 

Bootstrap Tokens(Bootstrap令牌),此功能目前处于beta阶段。

为了简化新集群的引导,Kubernetes引用了一个动态管理令牌类型,称为Bootstrap令牌。Bootstrap令牌是一种简单的承载令牌,用于创建新集群或将新节点连接到现有集群。这些令牌作为Secrets存储在kube-system命名空间中,可以动态管理和创建它们。

controller-manager包含一个TokenCleaner控制器,可在过期时自动删除Bootstrap令牌。

使用--enable-bootstrap-token-auth参数在apiserver上启用Bootstrap Token 认证。通过--controllers 在controller-manager上启用TokenCleaner控制器。例如--controllers=*,tokencleaner。

令牌形式为[a-z0-9]{6}.[a-z0-9]{16}。第一部分是令牌ID,第二部分是令牌密钥。可以在HTTPS头信息中指定令牌,如下所示:

Authorization:Bearer781292.db7bc3a58fc5f07e

进行身份验证时,验证在kube-system命名空间中是否存在名称为bootstrap-token-的secret,如果存在则验证secret中的令牌ID、令牌密钥是否与请求中的一致,令牌是否过期。认证通过后使用system:bootstrap:作为用户名,并且是用户分组system:bootstrappers的成员。

secrets内包含的令牌信息如下:

 OpenID Connect Tokens 

OpenID Connect Tokens是OAuth2中的一种,被许多供应商支持,尤其是Azure Active Directory、Salesforce和 Google。该协议对OAuth2的主要扩展是返回一个额外的访问Token,叫ID Token。这个ID Token是JSON Web Token(JWT)。

在请求中,使用ID Token作为bearer token(而不是 access_token)。

要启用此插件,需要设置下面两个参数:

--oidc-issuer-url=https://xxx,用于让apiserver获取到OAuth2的公有密钥。

 --oidc-client-id 客户端的ID。


该方式通过OAuth2服务的公有密钥对JWT中Signature校验实现身份认证。

Webhook Token Authentication

该认证方式类似于Static Token File,但它提供扩展Token身份验证的能力,具体的Token合法性验证由外部系统完成,Kubernetes平台将不再管理或配置具体的用户信息。


要启用此插件,需要设置下面两个参数:

--authentication-token-webhook-config-file 一个kubeconfig文件描述如何访问远程webhook服务。

--authentication-token-webhook-cache-ttl 用于设置缓存时间,默认为两分钟。

当客户端尝试使用承载令牌向API服务器进行身份验证时,身份验证webhook将包含令牌的JSON序列化对象POST 发送到远程服务。

POST正文将采用以下格式:

{"apiVersion":"authentication.k8s.io/v1beta1","kind":"TokenReview","spec": {"token":"(BEARERTOKEN)"  }}

认证成功将返回:

{"apiVersion":"authentication.k8s.io/v1beta1","kind":"TokenReview","status": {"authenticated":true,"user": {"username":"[email protected]","uid":"42","groups": ["quantai","ms"      ],"extra": {"extrafield1": ["extravalue1","extravalue2"        ]      }    }  }}

认证不成功将返回:

{"apiVersion":"authentication.k8s.io/v1beta1","kind":"TokenReview","status": {"authenticated":false  }}

授权

在Kubernetes中,授权作为一个独立的步骤。根据所有授权策略匹配请求资源属性,决定允许或拒绝请求。

授权包括六种方式:AlwaysDeny、AlwaysAllow、ABAC、RBAC、Webhook、Node。配置多个授权时,将按顺序检查每个授权,任何一个匹配的授权策略允许或拒绝请求时,则立即返回该决定,并且不会再检查其他授权策略;所有授权策略都没有允许或拒绝时,最终则拒绝该请求。

通过设置apiserver配置参数(--authorization-mode)启用授权插件。启用多个时,以逗号分隔。例如:

--authorization-mode=AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC,Node

启用多个授权时,授权验证顺序由配置参数中的顺序决定。

授权只依据以下的属性进行判断:

其中API、Resource、Subresource、Namespace、APIGroup是从请求的URI中解析获取得到。

 AlwaysDeny 

阻止所有的请求,仅用于测试。

 AlwaysAllow 

允许所有的请求。

 ABAC模式 

ABAC(基于属性的访问控制)定义了访问控制范例,通过将属性组合在一起的策略向用户授予访问权限。

要使用ABAC模式,还需要指定策略文件参数:--authorization-policy-file=SOMEFILE。

策略文件的格式为每行一个json对象。每一个对象为一个授权策略。授权策略json的属性包括apiVersion、kind、spec,其中spec的属性包括:user、group、readonly、apiGroup、namespace、resource、nonResourcePath。使用"*"匹配相应属性的任何值。

例如用户Alice可以对所有资源做任何操作:

{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec": {"user":"Alice","namespace":"*","resource":"*","apiGroup":"*"}}

进行授权验证时,将请求的属性与策略文件中的每一个策略进行匹配。至少与一条策略能够匹配的才能被授权。

 RBAC模式 

RBAC(Role Based Access Control):基于角色访问控制授权。允许管理员通过Kubernetes API动态配置授权策略。RBAC就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般是多对多的关系。具体关系如下图:

RBAC包括四种类型:Role、ClusterRole、RoleBinding、ClusterRoleBinding。Role、ClusterRole包含一组权限规则,其中ClusterRole能应用于所有的命名空间(配置中没有命名空间选项),常用于没有命名空间的资源,如nodes;而Role只能包含单个命名空间的权限规则。RoleBinding、ClusterRoleBinding是将角色中定义的权限授予用户、用户组或服务账户。

Node模式

Node模式(节点授权)是一种特殊用途的授权模式,专门授权由kubelet发出的API请求。在进行认证时,先通过用户名、用户分组验证是否是集群中的Node节点。只有是Node节点的请求才能使用Node模式授权。

节点授权允许kubelet执行API操作如下:

读操作:services、endpoints、nodes、pods、secrets、configmaps、persistent volume claims、绑定到kubelet节点的pod相关的持久卷。

写操作:节点、节点状态、pod、pod状态、事件。

认证相关的操作:对certificationsigningrequests API的读/写权限;创建tokenreviews和subjectaccessreviews的权限。

在未来版本中,节点授权将支持添加或删除权限,以确保kubelet具有正确操作所需的最小权限集。

为了获得Node授权者的授权,kubelet必须使用一个凭证来标识其用户名为system:node:,并且该用户属于system:nodes分组。值 必须与kubelet注册的节点名称完全匹配。

 Webhook模式 

Webhook模式,到指定的外部服务验证用户是否拥有权限。启用此模式,还需要设置apiserver参数--authorization-webhook-config-file=SOMEFILE,配置文件的示例如下:

# clusters refers to the remote service.clusters:  - name: name-of-remote-authz-service    cluster:certificate-authority: /path/to/ca.pem# CA for verifying the remote service.server: https://authz.example.com/authorize# URL of remote service to query. Must use 'https'. # users refers to the API Server's webhook configuration.users:  - name: name-of-api-server    user:client-certificate: /path/to/cert.pem# cert for the webhook plugin to useclient-key: /path/to/key.pem# key matching the cert # kubeconfig files require a context. Provide one for the API Server.current-context: webhookcontexts:- context:    cluster: name-of-remote-authz-service    user: name-of-api-sever  name: webhook

Kubernetes API接收到请求后会向Webhook服务发送一个POST请求来验证是否有权限访问API。请求的内容是api.authorization.v1beta1.SubjectAccessReview对象的JSON序列化对象。

远程服务将返回允许或禁止访问的授权结果,如:

{"apiVersion":"authorization.k8s.io/v1beta1","kind":"SubjectAccessReview","status": {"allowed":true  }}

准入控制

准入控制(Admission Control)是Kubernetes apiserver用于拦截请求的一种方式,运行在认证、授权之后,是权限认证链上的最后一环,对请求API资源对象进行修改(Mutation)和校验(Validating)。


准入控制功能分为两大部分:

修改 (Mutation):修改API对象的主体内容。

验证 (Validation):校验API对象内容并决定是否接受请求。

在Kubernetes 1.10+之后,用户在使用参数--enable-admission-plugins配置启用准入控制时不需要关注它们的排列顺序,apiserver在注册准入控制插件时,已经定义好了所有插件的执行顺序。

 核心接口  

在Kubernetes v1.14版本中准入控制有31个插件,每个插件实现不同的功能,如AlwaysDeny、PodNodeSelector这些具有单一功能职责的插件。它们都实现了两个关键接口MutationInterface、ValidationInterface的三个函数Handles()、Admit()、Validate()。

Handles(),在执行Admit()和Validate()之前,校验对资源的操作类型属于CREATE、UPDATE、DELETE、CONNECT这些类型,否则不执行Admit()和Validate()。

Admit(),实现对请求参数及API对象的校验、对API对象修改,如果返回error则拒绝请求。

Validate(),实现对请求参数及API对象的校验,如果返回error则拒绝请求。

Admission主要插件列表

 Webhook Admission 

Kubernetes向用户提供了Webhook Admission扩展机制,方便用户接入自己的准入控制器,Service Mesh Istio是通过该方式向Pod中注入Sidecar容器。Webhook Admission的应用场景很多,如修改Pod对象、多租户平台限定Pod的namespace、自定义资源的验证方法等。


接入Webhook Admission方式如下:

由Webhook Admission插件将数据AdminssionReview通过HTTPS协议请求Remote Service处理,在Remote Service服务内完成对数据内容修改或者验证后,返回约定的v1beta1.AdmissionResponse对象,其Allowed属性表示请求是否被允许。

目前Kubernetes对API的访问安全已提供了完整的认证授权体系,在实际的生产环境中,可以根据各自的情况采用合适自己的认证授权方式,也可以通过相应的接口扩展Kubernetes认证授权能力。

你可能感兴趣的:(Kubernetes API 安全机制详解)