(1) ServiceAccountToken
ServiceAccount认证是自动开启的认证方式,它使用签过名的BearerToken去验证请求,这个认证模块包括两个可配置项。
① --service-account-key-file:包含签名 Token的 PEM 格式的密钥文件,如果不指定这个参数,将使用 APIServer的 TLS私钥。
② --service-account-lookup:如果被设置为 True,从 API 请求中删除的 Token将被收回。
ServiceAccount由 APIServer自动创建,Pod在运行时通过 Admission Controller关联 ServiceAccount。BearerToen挂载到Pod的特定目录上,并允许集群内的进程与APIServer通信。可以使用PodSpec的ServiceAccountName字段将账户与Pod进行关联。
ServiceAccount中包含一个 Secret,示例见代码清单 2-67。
apiVersion:v1
kind:ServiceAccountmetadata:
name:defaultnamespace:defaultresourceVersion:"361"
secrets:
-name:default-token-h29t7
# Secret中包含了APIServer公开的 CA证书和⼀个JWT格式的 TokenapiVersion: v1
data:
ca.crt: < 证书内容 >namespace:ZGVmYXVsdA==token: <JWTToken>
kind:Secretmetadata:
name:default-token-h29t7namespace:default
type:kubernetes.io/service-account-token
(2) OpenIDConnectTokens
OpenIDConnect是一套基于 OAuth2协议的认证规范,由提供商实现,比如 AzureActiveDirectory、Salesforce和 Google。这个认证模块的使用流程:用户先从认证服务器上获取一个 IDToken,这个 Token是一个JWT格式的 Token,用户收到这个 Token后访问 APIServer。
这个认证模块使用从OAuth2中获取的id_token进行认证,认证的过程如图2-10所示。
图2—10ODC认证流程
① 登录到用户身份认证服务提供商。
② 用户身份认证服务提供商返回 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(OpenIDConnect)认证模块,需要在 APIServer中配置如下参数。
① --oidc-issuer-url:认证服务提供商的地址,允许 APIServer发现公开的签名密钥服务的 URL。只接受 https://的 URL。此值通常设置为服务的发现URL,不含路径。
② --oidc-client-id:发放 Token的 ClientID。
③ --oidc-username-claim:使用 JWT中的哪个字段作为用户名,默认的是 sub。
④ --oidc-username-prefix:为了防止不同认证系统的用户冲突, 给用户名添加一个前缀,如果使用的用户名不是 email,那么用户名将是 <IssuerURL>#<UsernameClaim>。如果设置为“-”,将不会使用前缀。
⑤ --oidc-groups-claim:使用 JWT 中的哪个字段作为用户的组名。
⑥ --oidc-groups-prefix:组名的前缀,所有的组都将以此值为前缀,以避免与其他身份认证策略发生冲突。
⑦ --oidc-required-claim:键值对描述 IDToken 中的必要声明,如果设置了这个值,则验证声明是否存在于 IDToken 中且具有匹配值,重复设置可以指定多个声明。
⑧ --oidc-ca-file:签署身份认证提供商的 CA证书的路径,默认的是主机的根 CA证书的路径。
OIDC 认证实现见代码清单2-68。
func(v*IDTokenVerifier)Verify(ctxcontext.Context,rawIDTokenstring)
(*IDToken,error){
jws,err:=jose.ParseSigned(rawIDToken)
iferr!=nil{
returnnil,fmt.Errorf("oidc:malformedjwt:%v",err)
}
//解析 JWT的 Payload部分,JWT分为三段,以逗号作为分隔符,第⼆段是 Payload部分,是 JSON格式,使⽤ base64进⾏编码
payload,err:=parseJWT(rawIDToken)iferr!=nil{
returnnil,fmt.Errorf("oidc:malformedjwt:%v",err)
err)
}
vartokenidToken
iferr:=json.Unmarshal(payload,&token);err!=nil{
returnnil,fmt.Errorf("oidc:failedtounmarshalclaims:%v",
}
distributedClaims:=make(map[string]claimSource)forcn,src:=rangetoken.ClaimNames{
ifsrc==""{
returnnil,fmt.Errorf("oidc:failedtoobtainsourcefrom
claimname")
}
s,ok:=token.ClaimSources[src]if!ok{
returnnil,fmt.Errorf("oidc:sourcedoesnotexist")
}
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
ifv.config.Now!=nil{now=v.config.Now
}
nowTime:=now()
ift.Expiry.Before(nowTime){
returnnil,fmt.Errorf("oidc:tokenisexpired(TokenExpiry:%v)",t.Expiry)
}
iftoken.NotBefore!=nil{
nbfTime:=time.Time(*token.NotBefore)leeway:=1*time.Minute
ifnowTime.Add(leeway).Before(nbfTime){
returnnil,fmt.Errorf("oidc:currenttime%vbeforethenbf(notbefore)time:%v",nowTime,nbfTime)
}
}
}
switchlen(jws.Signatures){case0:
returnnil,fmt.Errorf("oidc:idtokennotsigned")case1:
default:
returnnil,fmt.Errorf("oidc:multiplesignaturesonidtokennotsupported")
}
sig:=jws.Signatures[0]
supportedSigAlgs:=v.config.SupportedSigningAlgs
iflen(supportedSigAlgs)==0{supportedSigAlgs=[]string{RS256}
}
.......
t.sigAlgorithm=sig.Header.Algorithm
//校验签名是否正确
gotPayload,err:=v.keySet.VerifySignature(ctx,rawIDToken)iferr!=nil{
returnnil,fmt.Errorf("failedtoverifysignature:%v",err)
}
if!bytes.Equal(gotPayload,payload){
returnnil,errors.New("oidc:internalerror,payloadparseddidnotmatchpreviouspayload")
}
returnt,nil
}