带你读《云原生应用开发:Operator原理与实践》——2.3.2 认证

简介: 带你读《云原生应用开发:Operator原理与实践》——2.3.2 认证

2.3.2 认证


1. 认证中使用的用户

Kubernetes 集群中有两种用户:一种是由 Kubernetes 管理的 ServiceAccount ;另一种是普通用户。普通用户是由集群之外的服务管理的,比如存储在 Keystone 中的用户、存储在文件中的用户和存储在 ldap 中的用户等。 所有的 API 请求中都绑定了一个ServiceAccount 或普通用户,如果都未绑定,就是一个使用匿名用户的请求。ServiceAccount 与 它 关 联 的 请 求 中 的 用 户 是 什 么 样 的 呢? ServiceAccoount 在Kuberentes 中关联着一个 Secret,这个 Secret 中包括一个 JWT 格式的 Token,它会使用 Token 中的 Payload 中的 Sub 字段作为请求用户的名称。下面是对 Secret 中的 Token解析的一个示例,请求所使用的用户名是 system:serviceaccount:default:default,见代码清单 2-60。

代码清单 2-60

{
 "iss": "kubernetes/serviceaccount",
 "kubernetes.io/serviceaccount/namespace": "default",
 "kubernetes.io/serviceaccount/secret.name": "default-token-bxzg6",
 "kubernetes.io/serviceaccount/service-account.name": "default",
 "kubernetes.io/serviceaccount/service-account.uid": "a96a30e3-3ee0-44cf-
99b2-1ad4bdbc7632",
 "sub": "system:serviceaccount:default:default"
}

普通用户有多种方式携带认证信息去访问 APIServer。以客户端证书的方式为例,请求中如果携带了客户端证书,并且证书是由集群的 CA 证书(Certification Authority)签发的,那么这个请求的认证会通过,它会使用证书中的 Subject 字段作为用户的名称。代码清单 2-61 是一个证书的示例,示例中携带的用户名是 kcp (Subject: O=system:kcp,CN=kcp)。

代码清单 2-61

Certificate:
 Data:
 Version: 3 (0x2)
 Serial Number:
 08:e2:5b:3a:2c:8c:6c:06:80:f8:aa:1b:81:76:93:4f
 Signature Algorithm: sha256WithRSAEncryption
 Issuer: CN=kubernetes
 Validity
 Not Before: Mar 3 05:59:42 2021 GMT
 Not After : Jul 11 09:37:38 2030 GMT
 Subject: O=system:kcp, CN=kcp
......

2. 认证策略


(1) X509 客户端证书认证

客户端证书认证是双向认证,服务器端需要验证客户端证书的正确性,客户端需要验证服务器端证书的正确性。客户端证书认证方式可以通过 Kube-Apiserver 的 --clientca-file 参数启用,它指向的文件用来验证提供给 API 服务器的客户端证书。当请求中携带的客户端证书通过了认证时,请求关联的用户名就是证书中的 Subject 字段的内容。我们可以使用代码清单 2-62 查看证书内容。

代码清单 2-62

openssl x509 -in < 证书 > -text -noout

(2)静态 Token 文件

静态 Token 文件认证方式可以通过 --token-auth-file 参数启用,在这个文件中存放着没有时间限制的 Bearer Token,如果想变更 Token,则必须要重启 APIServer。代码清单 2-63 是一个 Token 文件的示例,每一行包括 Token、用户名和用户 ID。

代码清单 2-63

JGaOWpJuyBL8NXmeA9V341JOCkHJbOTf,system:kubectl-kcpm1,system:kubectl-kcpm1

访问 APIServer 时在 HTTP 请求头上加入 Authorization 的请求头,值的格式是Bearer ,上面的 Token 见代码清单 2-64。

代码清单 2-64

curl -H "Authorization: Bearer JGaOWpJuyBL8NXmeA9V341JOCkHJbOTf" -k 
https://127.0.0.1:6443/

(3)引导 Token

我们在创建集群或是将新节点加入集群时,新节点上的组件要与 APIServer 进行通信(如Kubelet),但通信需要证书,手动签发证书比较麻烦。为了简化这个过程,Kubernetes 1.4 之后的版本会通过新节点发送请求的方式为新节点签发证书。而发送请求获取证书的请求时使用的 Bearer Token 叫作 Bootstrap Token,这种 Token 在 Kube-System Namespace 下有一个对应的 Secret。Controller-Manager 中有一个 TokenCleaner 的 Controller,它会将那些已经过期的 Bootstrap Token 掉。

Token 的 格 式 是 [a-z0-9]{6}.[a-z0-9]{16},Token 的 前 半 部 分 是 Token ID, 通过前半部分能找到 UI 应的 Secret ;第二部分是 Token Secret,保存在 Secret 中。以Kubeadm 创建的 tokencwb0ly.cqdj5l0k2qa19evv 为例,在 Kube-System 的 Namespace下会有一个名字为 bootstrap-token-cwb0ly 的 Secret,内容见代码清单 2-65。

代码清单 2-65

apiVersion: v1
data:
 auth-extra-groups: system:bootstrappers:kubeadm:default-node-token 
 expiration: 2021-03-13T13:44:23+08:00
 token-id: cwb0ly
 token-secret: cqdj5l0k2qa19evv
 usage-bootstrap-authentication: true
 usage-bootstrap-signing: true
kind: Secret
metadata:
 manager: kubeadm
 operation: Update
 name: bootstrap-token-cwb0ly
 namespace: kube-system

使用该 Token 作为 Authorization 请求头访问 APIServer,请求认证通过后关联到的用户名的格式是 bootstrap-token-,即 system:bootstrap:cwb0ly,请求示例见代码清单 2-66。

代码清单 2-66

curl -H "Authorization: Bearer cwb0ly.cqdj5l0k2qa19evv" -k https://127.0.0.1:6443/

要想使用引导 Token 认证,需要在 APIServer 启动的时候添加 --enable-bootstraptoken-auth 启动参数,如果想使用 Token Clear Controller,需要在 Controller-Manager的启动参数中添加 --controllers=*,tokencleaner。

(4) ServiceAccount Token

ServiceAccount 认证是自动开启的认证方式,它使用签过名的 Bearer Token 去验证请求,这个认证模块包括两个可配置项。

① --service-account-key-file:包含签名 Token 的 PEM 格式的密钥文件,如果不指定这个参数,将使用 APIServer 的 TLS 私钥。

② --service-account-lookup:如果被设置为 True,从 API 请求中删除的 Token 将被收回。ServiceAccount 由 APIServer 自动创建,Pod 在运行时通过 Admission Controller关联 ServiceAccount。Bearer Toen 挂载到 Pod 的特定目录上,并允许集群内的进程与APIServer 通信。 可以使用 PodSpec 的 ServiceAccountName 字段将账户与 Pod 进行关联。ServiceAccount 中包含一个 Secret,示例见代码清单 2-67。

代码清单 2-67

apiVersion: v1
kind: ServiceAccount
metadata:
 name: default
 namespace: default
 resourceVersion: "361"
secrets:
- name: default-token-h29t7
# Secret 中包含了 APIServer 公开的 CA 证书和一个 JWT 格式的 Token
 apiVersion: v1
data:
 ca.crt: < 证书内容 >
 namespace: ZGVmYXVsdA==
 token: <JWT Token>
kind: Secret
metadata:
 name: default-token-h29t7
 namespace: default
type:

(5) OpenID Connect Tokens

OpenID Connect 是一套基于 OAuth2 协议的认证规范,由提供商实现,比如 Azure Active Directory、Salesforce 和 Google。这个认证模块的使用流程:用户先从认证服务器上获取一个 ID Token,这个 Token 是一个 JWT 格式的 Token,用户收到这个 Token 后访问 APIServer。

这个认证模块使用从 OAuth2 中获取的 id_token 进行认证,认证的过程如图 2-10 所示。

image.png

图 2-10 OIDC 认证流程

① 登录到用户身份认证服务提供商。

② 用户身份认证服务提供商返回 access_token、id_token 和 refresh_token。

③ 用户使用 Kubectl 工具时通过 --token 参数指定 id_token 或将它写入 kubeconfig 中。

④ Kubectl 将 id_token 作为认证信息放在请求头中调用 APIServer。

⑤ APIServer 将通过指定的证书检查 JWT 中的签名的正确性。

⑥ 检查 id_token 是不是已经过期了。

⑦ 确保用户请求的资源有操作权限。

⑧ 一旦鉴权通过,APIServer 将返回一个响应给 Kubectl。

⑨ Kubectl 工具箱用户提供反馈。

用来对用户身份进行认证的所有数据都在 id_token 中,在上述整个流程中 Kubernetes 不需要与身份认证服务交互。在一个都是无状态请求的模型中,这种工作方式为身份认证提供了一种更容易处理大规模请求的解决方案。

要使用 OIDC(OpenID Connect)认证模块,需要在 APIServer 中配置如下参数。

① --oidc-issuer-url: 认证服务提供商的地址,允许 APIServer 发现公开的签名密钥服务的 URL。只接受 https:// 的 URL。此值通常设置为服务的发现 URL,不含路径。

② --oidc-client-id:发放 Token 的 Client ID。

③ --oidc-username-claim:使用 JWT 中的哪个字段作为用户名,默认的是 sub。

④ --oidc-username-prefix:为了防止不同认证系统的用户冲突,给用户名添加一个前缀,如果使用的用户名不是 email,那么用户名将是 #。如果设置为“-”,将不会使用前缀。

⑤ --oidc-groups-claim: 使用 JWT 中的哪个字段作为用户的组名。

⑥ --oidc-groups-prefix:组名的前缀,所有的组都将以此值为前缀,以避免与其他身份认证策略发生冲突。

⑦ --oidc-required-claim: 键值对描述 ID Token 中的必要声明,如果设置了这个值,则验证声明是否存在于 ID Token 中且具有匹配值,重复设置可以指定多个声明。

⑧ --oidc-ca-file:签署身份认证提供商的 CA 证书的路径,默认的是主机的根 CA 证书的路径。

OIDC 认证实现见代码清单 2-68。

代码清单 2-68

func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) 
(*IDToken, error) {
 jws, err := jose.ParseSigned(rawIDToken)
 if err != nil {
 return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
 }
 // 解析 JWT 的 Payload 部分,JWT 分为三段,以逗号作为分隔符,第二段是 Payload 部分,
是 JSON 格式,使用 base64 进行编码
 payload, err := parseJWT(rawIDToken)
 if err != nil {
 return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
 }
 var token idToken
 if err := json.Unmarshal(payload, &token); err != nil {
 return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", 
err)
 }
 distributedClaims := make(map[string]claimSource)
 for cn, src := range token.ClaimNames {
 if src == "" {
 return nil, fmt.Errorf("oidc: failed to obtain source from 
claim name")
 }
 s, ok := token.ClaimSources[src]
 if !ok {
 return nil, fmt.Errorf("oidc: source does not exist")
 }
 distributedClaims[cn] = s
 }
 t := &IDToken{
 Issuer: token.Issuer,
 Subject: token.Subject,
 Audience: []string(token.Audience),
 Expiry: time.Time(token.Expiry),
 IssuedAt: time.Time(token.IssuedAt),
 Nonce: token.Nonce,
 AccessTokenHash: token.AtHash,
 claims: payload,
 distributedClaims: distributedClaims,
 }
 ......
 // 检查 Token 是否过期
 if !v.config.SkipExpiryCheck {
 now := time.Now
 if v.config.Now != nil {
 now = v.config.Now
 }
 nowTime := now()
 if t.Expiry.Before(nowTime) {
 return nil, fmt.Errorf("oidc: token is expired (Token 
Expiry: %v)", t.Expiry)
 }
 if token.NotBefore != nil {
 nbfTime := time.Time(*token.NotBefore)
 leeway := 1 * time.Minute
 if nowTime.Add(leeway).Before(nbfTime) {
 return nil, fmt.Errorf("oidc: current time %v before 
the nbf (not before) time: %v", nowTime, nbfTime)
 }
 }
 }
 switch len(jws.Signatures) {
 case 0:
 return nil, fmt.Errorf("oidc: id token not signed")
 case 1:
 default:
 return nil, fmt.Errorf("oidc: multiple signatures on id token not 
supported")
 }
 sig := jws.Signatures[0]
 supportedSigAlgs := v.config.SupportedSigningAlgs
 if len(supportedSigAlgs) == 0 {
 supportedSigAlgs = []string{RS256}
 }
 .......
 t.sigAlgorithm = sig.Header.Algorithm
 // 校验签名是否正确
 gotPayload, err := v.keySet.VerifySignature(ctx, rawIDToken)
 if err != nil {
 return nil, fmt.Errorf("failed to verify signature: %v", err)
 }
 if !bytes.Equal(gotPayload, payload) {
 return nil, errors.New("oidc: internal error, payload parsed did 
not match previous payload")
 }
 return t, nil
}

(6) WebHook Token 认证

WebHook 认证就是一种回调机制,用来验证 Bearer Token 的正确性,要使用这种认证方式需要配置如下参数。

① --authentication-token-webhook-config-file:这是一个配置文件,用于描述如何访问远程的 WebHook 服务。

② --authentication-token-webhook-cache-ttl:缓存认证时间,默认是 2 分钟。

③ --authentication-token-webhook-version:使用哪个版本发送和接收 WebHook的消息,TokenReview 可以使用 authentication.k8s.io/v1beta1 或 authentication.k8s.io/v1,默认的是 authentication.k8s.io/v1beta1。

当客户端使用一个 Bearer Token 去访问 APIServer 时,WebHook 认证模块会使用Token Review 对象的 JSON 格式向远端服务器发送请求,这个对象中包含了 Token。远端服务器为返回给 TokenReview 对象的 Status 字段填充内容,内容包含此次请求认证是否通过。

(7)认证代理

可以为 Kubernetes 设置一个认证代理,这个认证代理将信息放在请求头中发送给APIServer,APIServer 从请求头中识别用户。启用认证代理需要设置以下几个参数。

① --requestheader-username-headers:用于指定用户名列表,不区分大小写,按照顺序检查用户身份。

② --requestheader-group-headers:用于指定组列表,不区分大小写,按照顺序检查用户组的名称。

③ --requestheader-extra-headers-prefix:指定额外的列表,不区分大小写。

④ --requestheader-client-ca-file:指定有效的客户端的证书,在检查请求头中的用户名之前,必须在指定的文件中提供有效的客户端证书并针对证书颁发机构进行验证。为防止请求头攻击,在检查请求头之前,代理客户端要为 APIServer 提供有效的客户端证书进行校验。

(8)匿名认证

启用后,未被其他配置身份验证方模块拒绝的请求将被视为匿名请求,并被赋予用户名 system:anonymous 和组 system:unauthenticated。在配置了 Token 身份验证且启用了匿名访问的服务器上,提供无效 Bearer Token 的请求将收到 401 未经授权错误。而不提供 Bearer Token 的请求将被视为匿名请求。

在 Kubernetes 1.6 之后的版本,如果鉴权模式不是 AlwaysAllow,则匿名访问默认是启用的。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
相关文章
|
2月前
|
缓存 Java API
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
|
2月前
|
Kubernetes Cloud Native 开发工具
带你读《云原生应用开发:Operator原理与实践》精品文章合集
带你读《云原生应用开发:Operator原理与实践》精品文章合集
|
2天前
|
JSON SpringCloudAlibaba Cloud Native
SpringCloudAlibaba:4.3云原生网关higress的JWT 认证
SpringCloudAlibaba:4.3云原生网关higress的JWT 认证
8 1
|
1月前
|
负载均衡 Cloud Native 安全
云原生最佳实践系列 6:MSE 云原生网关使用 JWT 进行认证鉴权
本文档介绍了如何在 MSE(Microservices Engine)云原生网关中集成JWT进行全局认证鉴权。
|
2月前
|
Kubernetes Cloud Native 微服务
作者推荐|剖析云原生服务框架中服务发现机制的核心原理与实现机制
作者推荐|剖析云原生服务框架中服务发现机制的核心原理与实现机制
45 0
|
2月前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
44 0
|
2月前
|
运维 Cloud Native 持续交付
云原生技术的未来展望:如何塑造下一代应用开发
【2月更文挑战第30天】 随着云计算的不断发展,云原生技术已经成为推动现代应用开发的重要力量。本文将深入探讨云原生技术的核心概念,分析其在提高开发效率、降低运维成本以及支持复杂业务场景中的作用。同时,文章还将预测云原生技术的发展趋势,并讨论如何在不断变化的技术环境中保持应用的敏捷性和可靠性。
|
2月前
|
消息中间件 存储 Cloud Native
【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程
【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程
|
19天前
|
消息中间件 Cloud Native 开发者
电子好书发您分享《阿里云云原生开源开发者沙龙北京站 PPT 合集 》
**阿里云开源沙龙PPT合集:北京站聚焦云原生技术** 探索云原生领域的深度与广度,[阿里云](https://developer.aliyun.com/ebook/8334/116563?spm=a2c6h.26392459.ebook-detail.5.da096cf6t38G15)分享了北京开发者沙龙的精彩内容,涵盖微服务、消息队列等主题,助力开发者洞悉行业趋势。![image](https://ucc.alicdn.com/pic/developer-ecology/cok6a6su42rzm_67b12f6cad6e4b2786859b3a668b3351.png)
18 3
|
2月前
|
人工智能 监控 Cloud Native
iLogtail 2.0 来了;通义灵码下载量破百万丨阿里云云原生 2 月产品月报
iLogtail 2.0 来了;通义灵码下载量破百万丨阿里云云原生 2 月产品月报

热门文章

最新文章