13 min read

Kubernetes 认证、鉴权与准入控制器详解

kubernetes 认证、鉴权与准入控制器详解

1. 认证与鉴权基本介绍

几种常见的认证方式

  • 证书认证
  • 账户密码认证
  • token认证 (JWT token webhook Oauth2、OpenID token -> OIDC)
  • HTTP Basic认证 (key and pwd)
  • Authenticating Proxy (http X-Remote-Use)

关于认证的个人理解

认证的主要目的是通过检查对象提供的身份凭据,来确认输入对象的具体身份从而为后续的操作做信用保证。 认证是构建应用系统整个信用体系最基础的一个环节,只有通过认证的对象才有访问资源的基本资格。

鉴权的基本策略

  • ABAC 基于属性的访问控制权限,最常见的是DAC或者MAC,其中DAC全名叫做Discretionary Access Control,翻译为自主访问控制权限,其中比较经典的DAC实现为linux文件系统的默认访问控制权限,即基于用户的登录身份在资源的默认权限列表中进行检查,从而确认是否具有访问权限。另外一种是MAC,全名叫做Mandatory Access Control,比较经典的MAC实现为linux中的SELinux强制访问控制模块,MAC访问控制以特定的策略规则进行驱动,可以指定针对不同的进程对特定问文件资源进行强制权限控制,这里注意DAC与MAC并不是互斥关系,往往权限检查模块作为一种串行的检查关系,需要同时满足多个权限模块要求才能对资源进行操作。
  • RBAC
  • RBAC与ABAC最大不同在与,RBAC在用户的基础上抽象出了单独角色的概念,从而将访问控制从ABAC的用户权限鉴定变成了角色权限控制,这样做的好处是通过对角色权限的控制间接影响绑定了不同用户的权限,也可以通过切换角色来使用户权限进行变更。

2. Kubernetes 认证方式

认证插件处理源码流程

kubernetes的认证、授权、准入功能主要由apiserver提供,其中这三个模块分别叫做
authentication、authorized、admission 。其中前两个插件authn和authz则在apiserver的config中由默认的handler chain注册并完成对所有api资源的处理。具体流程如下:

func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
	handler := filterlatency.TrackCompleted(apiHandler)  
	handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)  
	handler = filterlatency.TrackStarted(handler, "authorization")
	// ...
	handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyRuleEvaluator, c.LongRunningFunc)
	// ...
	handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences)
}

这里可以看到通过apifilters结构将对应的组件Authz和Authn加入到所有资源的handler处理流程中,这里以Authn举例可与看到

func withAuthentication(handler http.Handler, auth authenticator.Request, failed http.Handler, apiAuds authenticator.Audiences, metrics recordMetrics) http.Handler {  
  
   return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {  
      // ... 
      resp, ok, err := auth.AuthenticateRequest(req)  
      defer func() {  
         metrics(req.Context(), resp, ok, err, apiAuds, authenticationStart, authenticationFinish)  
      }()  
      // ..
      if !audiencesAreAcceptable(apiAuds, resp.Audiences) {  
         err = fmt.Errorf("unable to match the audience: %v , accepted: %v", resp.Audiences, apiAuds)  
         klog.Error(err)  
         failed.ServeHTTP(w, req)  
         return  
      }  
      // authorization header is not required anymore in case of a successful authentication.  
      req.Header.Del("Authorization")  
   })  
}

这里有需要找到在哪里对apiserver的认证和授权方法进行的初始化,如下可以看到在kubeapiserver中的buildGenericConfig函数对关键的authn和authz进行了初始化

func buildGenericConfig(  
   s *options.ServerRunOptions,  
   proxyTransport *http.Transport,  
)(...){
	// ...
	// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
	if lastErr = s.Authentication.ApplyTo(&genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, genericConfig.OpenAPIV3Config, clientgoExternalClient, versionedInformers); lastErr != nil {  
	   return  
	}  
	  
	genericConfig.Authorization.Authorizer, genericConfig.RuleResolver, err = BuildAuthorizer(s, genericConfig.EgressSelector, versionedInformers)  
	if err != nil {  
	   lastErr = fmt.Errorf("invalid authorization config: %v", err)  
	   return  
	}
	// ...
}

认证、授权和准入控制器组件分别在pkg/kubeapiserver/组件名称/下

func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, error) {  
   var authenticators []authenticator.Request  
   var tokenAuthenticators []authenticator.Token  
   securityDefinitions := spec.SecurityDefinitions{}  
  
   // front-proxy, BasicAuth methods, local first, then remote  
   // Add the front proxy authenticator if requested   if config.RequestHeaderConfig != nil {  
      requestHeaderAuthenticator := headerrequest.NewDynamicVerifyOptionsSecure(  
         config.RequestHeaderConfig.CAContentProvider.VerifyOptions,  
         config.RequestHeaderConfig.AllowedClientNames,  
         config.RequestHeaderConfig.UsernameHeaders,  
         config.RequestHeaderConfig.GroupHeaders,  
         config.RequestHeaderConfig.ExtraHeaderPrefixes,  
      )  
      authenticators = append(authenticators, authenticator.WrapAudienceAgnosticRequest(config.APIAudiences, requestHeaderAuthenticator))  
   }  
  
   // X509 methods  
   if config.ClientCAContentProvider != nil {  
      certAuth := x509.NewDynamic(config.ClientCAContentProvider.VerifyOptions, x509.CommonNameUserConversion)  
      authenticators = append(authenticators, certAuth)  
   }
   // ...
}

用户及用户组

kubernetes集群有两类用户,一种叫做普通账户,另一种叫做服务账户。

  • 普通账户
    一类用户由外部维护,kubernetes仅在认证阶段识别读取用户名称,kubernetes本身无法保存用户的任何信息,以证书认证举例,Kubernetes 使用证书中的 'subject' 的通用名称(Common Name)字段 (例如,"/CN=bob")来确定用户名,Organization 来确定用户组,然后基于内部的授权系统(默认一般为RBAC)来负责给该用户添加访问资源的权限。Proxy认证和BasicAuth则会从http header中读取用户和用户组关键字。

  • 服务账户
    另外一类账户为服务账户由kubernetes管理并保存用户的基本信息,服务账户一般含有命名空间的约束,并且可以被自动创建并将服务账户的凭据(一般是存储在secret的Token字段)挂载到Pod中。

  • 两个特殊用户
    特殊管理员用户: 集群默认提供一个system:admin的管理员账户的授权,该用户可以通过集权默认的管理员授权进行任何资源的访问。
    特殊匿名用户:当访问apiserver中提供的资源而不通过任何的身份认证时,该用户被视为匿名用户 system:anonymous

x509 证书认证

  • 证书认证基本概念
  • x509规范索引
  • 数据包分析证书交换
  • k8s基础组件证书认证流程*

横向分析证书文件目录
使用kubeadm社区部署工具部署生成的kubernetes集群默认生成的证书目录如下:

/etc/kubernetes/pki/

apiserver.crt apiServer的对外提供服务的服务端证书及私钥
apiserver-etcd-client.crt 访问etcd的客户端证书及私钥,这个证书是由etcd的CA证书签发,因此也需要在apiserver中配置etcd的CA证书
apiserver-kubelet-client.crt apiserver 访问 kubelet 所需的客户端证书及私钥
ca.crt 根证书
front-proxy-ca.crt front-proxy-client.crt 扩展apiserver为了能够和apiserver通讯,所以需要在apiserver中配置,假如不需要这个功能可以不配置该证书
sa.key sa.pub service Account密钥对,提供给 kube-controller-manager使用,kube-controller-manager 创建pod时通过 sa.key 对 token 进行签名,apiserver通过公钥 sa.pub 进行签名的验证

/etc/kubernetes/pki/etcd/

server.crt 对外提供服务的服务器证书及私钥 ca.crt peer.crt etcd 节点之间相互进行认证的 peer 证书、私钥以及验证 peer 的 CA
healthcheck-client.crt  healthcheck-client.key  验证访问其服务的客户端的 CA,etcd探测其服务健康检查接口,客户端会下载服务端证书进行验证,服务端也会下载客户端证书验证即下面的客户端证书

/var/lib/kubelet/pki/kubelet

kubelet-client-2022-02-28-15-12-19.pem kubelet-client-current.pem kubelet-client证书由kube-controller-manager自动轮转 使用kubelet.conf进行认证
kubelet.crt kubelet和controller-manager 增加启动参数开启自动轮转 自动生成默认有效期为1年,当开启自动轮转时该证书不生效。总结:若未使用 kubelet.conf 则使用kubelet.crt

纵向看组件使用的证书

kubeadm 自动生成/etc/kubernetes/pki 下和 /etc/kubernetes/pki/etcd/下所有证书,默认生成逻辑是如果无对应证书则创建,如果有对应证书文件则直接使用
见参考[7]

看k8s集群的证书处理如下

    1. kubectl 使用kubeconfig与apiserver进行认证
    1. apiserver作为客户端访问etcd服务端,其中apiserver使用 apiserver-etcd-client.crt,etcd使用/etc/kubernetes/pki/etcd/server.crt
    1. etcd集群中间进行数据同步等操作,使用的是/etc/kubernetes/pki/etcd/peer.crt
    1. kubelet上报event事件和修改资源,使用的是kubelet-client-2022-02-28-15-12-19.pem与apiserver的服务端访问
    1. kubelet进行服务端使用kubelet.crt,apiserver作为客户端使用apiserver-kubelet-client.crt
    1. expand-apiserver作为服务端使用自定义证书或controller-manager签名证书,apiserver作为客户端使用front-proxy.crt
  • 7、8、9为pod部署,使用服务账户+token访问apiserver

  • kubernetes 自动证书轮转

    • kubernetes默认提供了对所有节点kubelet服务的证书实现过期自动轮转
    • kubernetes 证书更新方案
      • 管理面集群证书更新
        • 导入ca/生成ca
        • 生成ca bundle 并分发所有节点
        • 替换ca
        • 依次重启节静态pod、kubelet使ca bundle生效
        • 根据新/导入ca生成 kubeconfig、controller-manager.conf、kubelet.conf、sa-secret、cluster-info
        • 依节点依次重启该节点组件
        • kubeadm renew重新生成每一个节点证书、重启该节点pod
        • 遍历并依次renew所有节点
        • 各节点ca.bundle转换成新的ca.crt
        • 各节点依次重启apiserver、controller-manager、scheduler、kubelet
      • etcd集群证书更新
        • 生成etcd ca-bundle
        • 依节点重启 ca-bundle
        • 生成etcd peer.crt和healthcheck-client.crt
        • 依节点依次重启 etcd
        • 新的ca.key替换ca-bundle并依次分发
        • 依节点依次重启 etcd
      • 工作节点证书更新
        • 工作节点只包含kubelet组件,所有kubelet证书由controller-manager组件自动轮转

token 认证

bootstrap token属于Bearer Token的一种,主要包含token ID和 token secret, 默认使用的用户和用户组为system:bootstrap:<token id>、 system:bootstrappers ,其中也可以通过auth-extra-groups字段为 bootstrap token 指定额外的用户组 。
除了身份认证,token还可以用做签名configmap, 从而实现通过使用后被签名的configmap完成认证。其中最典型的就是使用kubeadm 部署集群时保留的cluster-info中含有这个该签名认证,签名算法使用的是JWS签名,具体流程见参考3

OpenID Conect Token 通过使用Oauth2的服务器对请求进行认证,然后将请求通过赋予Bear Token的方式在apiserver获得认证结果

Static token 这以apiserver 的参数 --token-auth-file=file 指定一个csv文件包含具体的token列表,api请求通过携带列表中符合列表的token来进行认证,一般格式为{token id, group, user}

basic auth 认证

通过在启动的时候提供 --basic-authfile=file来实现,basic auth file主要是包含一系列的包含用户名、密码的文件列表

3. kubernetes 鉴权方式

ABAC

通过 --authorization-policy-file=file指定策略文件,通过指定用户对资源的权限列表,在用户访问时进行授权。其中使用ABAC需要考虑服务账户等配置,服务账户的默认名称如下

system:serviceaccount:<namespace>:<serviceaccountname>

RBAC

NODE节点鉴权

节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的 API 请求进行鉴权。节点鉴权器会检查kubelet对apiserver的访问请求,其中包括读取 services、endpoints、nodes、pods、secrets、configmaps、pvc以及绑定到pod相关的其他持久卷,写入则包括节点及节点状态、Pod及Pod状态、事件,其中Node授权方式主要和NodeRestriction准入控制器配合使用,NodeRestriction通过实现对访问ip的检查来限制只允许本地节点执行本地的权限。

AlwaysAllow --authorization-mode=AlwaysAllow 开启后可以允许通过所有的请求,跳过鉴权机制

AlwaysDeny --authorization-mode=AlwaysDeny 开启后默认拒绝素有的请求,仅用于测试

Webhook WebHook 是一种 HTTP 回调模式,允许使用远程 REST 端点管理鉴权`

查看资源访问权限的方式

方式一: 检查pod确认用户->确认用户组->找到对应的权限绑定(clusterRolebinding、rolebinding) -> 找到对应角色 -> 确认资源权限

方式二:kubectl auth can-i verb resource --as=system:serviceaccount:default:default

4. kubernetes 准入控制器

准入控制器在kubernetes中位于认证和鉴权流程之后,通过使用准入控制器可以指定整个kubernetes的统一安全基线配置,如设置在集群中禁用某项不安全的pod选项,列入对所有的通过的请求进行额外的审计记录等功能。

PSS

在kubernetes 1.25以后指定了一项Pod安全标准[Pod Security Standards],用于针对不同的场景对Pod使用不同的隔离级别,这些标准可以严格限制使用非安全选项的Pod创建。并且在kubernetes 1.22 版本以后开放了安全控制管理器PSA,PSA的全名叫做PodSecurityAdmission,用于对不同命名空间的Pod进行准入控制。在kubernetes 1.22以前,Pod级别的准入控制器为PSP[Pod Security Policy],由于PSP安全策略中容易引起的一些错误配置,并导致准入控制器的权限检查范围放大,因此在1.22以后kubernetes 社区建议使用PSA替换对应的PSP控制器,并将PSP特性标记为废弃。
Pod安全标准 PSS 设定了三个安全级别 privileged baseline restricted,使用时在 namespace 资源通过添加对应级别的标签来预设置命名空间下所有 Pod 的安全级别。同时标准还规定了安全豁免策略,通过准入控制器配置指定豁免标准,对满足条件的指定pod进行安全豁免。

NamespaceLifecycle

指定命名空间的生命周期,对于过期的命名空间不允许创建新的资源对象

LimitRanger

默认情况limitRange可以限制命名空间中资源分配,指定该命名空间中每个Pod的最小最大资源使用量等

PodNodeSelector

强制Pod在指定节点运行

AlwaysDeny

默认启用的准入控制器插件列表

CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook

参考

[1] Authenticating | Kubernetes
[2] 一文带你彻底厘清 Kubernetes 中的证书工作机制 - 知乎 (zhihu.com)
[3] 使用启动引导令牌(Bootstrap Tokens)认证 | Kubernetes
[4] Kubernetes 认证 _ Kubernetes(K8S)中文文档_Kubernetes中文社区
[5] 使用 ABAC 鉴权 | Kubernetes
[6] 使用准入控制器 | Kubernetes
[7] 使用准入控制器 | Kubernetes