kubernetes API 访问控制之:认证

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kubernetes API 访问控制之:认证相关的知识,希望对你有一定的参考价值。

文章目录

API访问控制

可以使用kubectl、客户端库方式对REST API的访问,Kubernetes的普通账户和Service帐户都可以实现授权访问API。API的请求会经过多个阶段的访问控制才会被接受处理,其中包含认证、授权以及准入控制(Admission Control)等。如下图所示:

需要注意:认证授权过程只存在HTTPS形式的API中。也就是说,如果客户端使用HTTP连接到kube-apiserver,是不会进行认证授权的。所以说,可以这么设置,在集群内部组件间通信使用HTTP,集群外部就使用HTTPS,这样既增加了安全性,也不至于太复杂。


认证

开启TLS时,所有的请求首先需要认证。Kubernetes支持多种认证机制,并支持同时开启多个认证插件(只要有一个认证通过即可)。如果认证成功,则用户的username会传入授权模块做进一步授权验证;对于认证失败的请求则返回HTTP 401。

当TLS建立时,HTTP请求会进行身份认证步骤,如图中步骤1,集群管理器将apiserver配置为运行一个或多个认证器模块。

认证模块包含客户端证书,密码、Plain Tokens、Bootstrap Tokens、JWT Tokens(used for service accounts)。

我们可以指定多个认证模块,每个认证模块都会按顺序进行,直到其中一个成功。


kubernetes账户

所有Kubernetes集群有两类用户:由Kubernetes管理的Service Accounts (服务账户)和(Users Accounts) 普通账户。

普通账户是假定被外部或独立服务管理的,由管理员分配keys,用户像使用Keystone或google账号一样,被存储在包含usernames和passwords的list的文件里。

需要注意:在Kubernetes中不能通过API调用将普通用户添加到集群中。

Kubernetes只专注于做应用编排,其他的功能则提供接口集成,除了认证和授权,我们发现网络、存储也都如此。

这样做的好处也显而易见,用户账户信息与Kubernetes集群松耦合,便于集成企业已有的身份认证系统,如AD、LADP、Keycloak等。

相比之下,Service Accounts是由Kubernetes API管理的帐户。它们被绑定到特定的命名空间,并由APIserver自动创建或通过API调用手动创建。Service Accounts与存储为Secrets的一组证书相关联,这些凭据被挂载到pod中,以便集群进程与Kubernetes API通信。

普通帐户是针对(人)用户的,服务账户针对Pod进程。
普通帐户是全局性。在集群所有namespaces中,名称具有惟一性。
通常,群集的普通帐户可以与企业数据库同步,新的普通帐户创建需要特殊权限。服务账户创建目的是更轻量化,允许集群用户为特定任务创建服务账户。
普通帐户和服务账户的审核注意事项不同。
对于复杂系统的配置包,可以包括对该系统的各种组件的服务账户的定义。

静态密码认证

静态密码是最简单的认证方式,只需要在api-server启动时指定使用的密码本路径即可,通过配置-Basic-authfile=file选项实现,Basic认证凭证一直有效,并且如果没有重新启动API Server,密码将无法更改。

Basic Authentication文件csv也是格式文件,且必须包含:password, user, uid。在Kubernetes 1.6+版本中,可以指定一个可选的第四列,使用逗号来分隔group名,如果有多个组,必须使用双引号(“)。参考以下示例:

password,user,uid,"group1,group2,group3"

当从http客户端使用Basic Authentication时,API Server需要在请求头加入Basic BASE64ENCODED(USER:PASSWORD)。

通过静态密码的唯一优势是简单,其缺点也是非常明显:

静态密码是明文,非常不安全,还有可能被暴力破解。
非常不灵活,增加或者删除用户,必须手动修改静态密码文件并重启所有的api-server服务。

这种方式在实际场景中很少被使用,不建议生产环境使用。


x509证书认证

对称加密和非对称加密

  1. 对称加密

对称加密指的就是加密和解密使用同一个秘钥,所以叫做对称加密。对称加密只有一个秘钥,作为私钥。

常见的对称加密算法:DES,AES,3DES等等。

  1. 非对称加密

非对称加密指的是:加密和解密使用不同的秘钥,一把作为公开的公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。

常见的非对称加密算法:RSA,ECC

  1. 区别

对称加密算法相比非对称加密算法来说,加解密的效率要高得多。但是缺陷在于对于秘钥的管理上,以及在非安全信道中通讯时,密钥交换的安全性不能保障。所以在实际的网络环境中,会将两者混合使用.


双向TLS认证

① 浏览器发送一个连接请求给安全服务器。
② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器。
③ 客户浏览器检查服务器送过来的证书是否是由自己信赖的CA中心(如沃通CA)所签发的。如果是,就继续执行协议;如果不是,客户浏览器就给客户一个警告消息:警告客户这个证书不是可以信赖的,询问客户是否需要继续。
④ 接着客户浏览器比较证书里的消息,例如域名和公钥,与服务器刚刚发送的相关消息是否一致,如果是一致的,客户浏览器认可这个服务器的合法身份。
⑤ 服务器要求客户发送客户自己的证书。收到后,服务器验证客户的证书,如果没有通过验证,拒绝连接;如果通过验证,服务器获得用户的公钥。
⑥ 客户浏览器告诉服务器自己所能够支持的通讯对称密码方案。
⑦ 服务器从客户发送过来的密码方案中,选择一种加密程度最高的密码方案,用客户的公钥加过密后通知浏览器。
⑧ 浏览器针对这个密码方案,选择一个通话密钥,接着用服务器的公钥加过密后发送给服务器。
⑨ 服务器接收到浏览器送过来的消息,用自己的私钥解密,获得通话密钥。
⑩ 服务器、浏览器接下来的通讯都是用对称密码方案,对称密钥是加过密的。

双向认证则是需要服务端与客户端提供身份认证,只能是服务端允许的客户能去访问,安全性相对于要高一些。SSL单向认证只要求站点部署了ssl证书就行,任何用户都可以去访问(IP被限制除外等),只是服务端提供了身份认证。

使用API Server启动时配置–client-ca-file = SOMEFILE选项来启用客户端证书认证。引用的文件必须包含,提交给API Server的客户端证书的证书颁发机构。如果客户端提交的证书通过,通用名称(common name)将被用作请求的用户名。

x509认证是默认开启的认证方式,api-server启动时会指定ca证书以及ca私钥,只要是通过ca签发的客户端x509证书,则可认为是可信的客户端。


kubectl 如何认证? 获取$HOME/config

在kubectl config中会有两个证书,分别为certificate-authority证书和client-certificate证书。client-certificate用于客户端认证,这显而易见。

在使用client-certificate客户端证书认证时,CN(commom Name)对应Kubernetes的User,O(organization)对应Kubernetes的Group。

签发客户端证书有两种方式,一种是基于CA根证书签发证书,另一个种是发起CSR(Certificate Signing Requests)请求。

使用x509证书相对静态密码来说显然会更安全,只要证书不泄露,可以认为是无懈可击的。但是虽然颁发证书容易,目前却没有很好的方案注销证书。想想如果某个管理员离职,该如何回收他的权限和证书。有人说,证书轮转不就解决了吗?但这也意味着需要重新颁发其他所有证书,非常麻烦。

所以使用x509证书认证适用于Kubernetes内部组件之间认证,普通用户认证并不推荐通过证书的形式进行认证。


令牌认证

通过一个不记名令牌 (Bear Token) 来识别用户是一种相对安全又被各种客户端广泛支持的认证策略。不记名令牌,代表着对某种资源,以某种身份访问的权利,无论是谁,任何获取该令牌的访问者,都被认为具有了相应的身份和访问权限。配合成熟的令牌授予机构,不记名令牌非常适于在生产环境中严肃使用。身份令牌(ID Token)就是一种形式的不记名令牌,它本身记录着一个权威认证机构对用户身份的认证声明,同时还可以包含对这个用户授予了哪些权限的声明,像极了古代官员佩戴的腰牌。Kubernetes 接受和识别的正是这种 ID Token。

1)静态令牌认证

静态token认证和静态密码原理几乎完全一样,唯一不同的是静态token通过token-auth-file指定token文件,认证时头部格式为Authorization: Bearer T o k e n , 而 静 态 密 码 通 过 b a s i c − a u t h − f i l e 指 定 密 码 文 件 , 认 证 头 部 为 B a s i c b a s e 64 e n c o d e ( Token,而静态密码通过basic-auth-file指定密码文件,认证头部为Basic base64encode( TokenbasicauthfileBasicbase64encode(username😒password),本质都是一样的。

因此其优点和缺点也和静态密码完全一样,这里不再赘述。

2)Bootstrap Token认证

前面提到的静态token在运行时是固定不变的,并且不会在Kubernetes集群中存储,意味着除非修改静态文件并重启服务,token不会改变。

而bootstrap token则是由Kubernetes动态生成的,通过Secret形式存储,并且具有一定的生命周期,一旦过期就会失效。

启用参数:–enable-bootstrap-token-auth=true

为了简便我们使用kubeadm生成一个token:

Kubeadm token create

Kubeadm token list

kubectl get clusterrolebinding | grep boot

kubectl get secret bootstrap-token-$TOKEN_ID –n kube-system

过期的token会自动删除secret

Token有两个部分组成,由.分割,前面部分为Token ID,后面部分为Token Secret。Token默认TTL为一天,对应的group system:bootstrappers:kubeadm:default-node-token,对应User为system:bootstrap:$Token ID。

kubeadm创建一个Token会对应在Kubernetes的kube-system namespace创建一个secret,secret名为bootstrap-token-$TOKEN_ID。

授权: ClusterRoleBinding kubeadm:get-nodes

这种token主要用于临时授权使用,比如kubeadm初始化集群时会生成一个bootstrap token,这个token具有创建certificatesigningrequests权限,从而新Node能够发起CSR请求,请求客户端证书。

  1. service account token认证

service account是Kubernetes唯一由自己管理的账号实体,意味着service account可以通过Kubernetes创建,不过这里的service account并不是直接和User关联的,service account是namespace作用域,而User是全cluster唯一。service account会对应一个虚拟User,User名为system:serviceaccount: n a m e s p a c e : namespace: namespace:sa_name,比如在default namespace的test service account,则对应的虚拟User为system:serviceaccount:default:test。

和前面的bootstrap一样,service account也是使用Bearer Token认证的,不过和前面的Token不一样的是service account是基于JWT(JSON Web Token)认证机制,JWT原理和x509证书认证其实有点类似,都是通过CA根证书进行签名和校验,只是格式不一样而已,JWT由三个部分组成,每个部分由.分割,三个部分依次如下:

Header(头部): Token的元数据,如alg表示签名算法,typ表示令牌类型,一般为JWT,kid表示Token ID等。
Payload(负载): 实际存放的用户凭证数据,如iss表示签发人,sub签发对象,exp过期时间等。
Signature(签名):基于alg指定的算法生成的数字签名,为了避免被篡改和伪造。

为了便于HTTP传输,JWT Token在传递过程中会转成Base64URL编码,其中Base64URL相对我们常用的Base64编码不同的是=被省略、+替换成-,/替换成_,这么做的原因是因为这些字符在URL里面有特殊含义,更多关于JWT的介绍可参考阮一峰的JSON Web Token 入门教程。

JWT Token的颁发机构为kubernetes/serviceaccount,颁发对象为SA对应的虚拟用户system:serviceaccount:default:test,除此之外还存储着其他的SA信息,如SA name、namespace、uuid等。这里需要注意的是我们发现JWT Token中没有exp字段,即意味着只要这个SA存在,这个Token就是永久有效的。

kubectl create sa SA_NAME

通过如下方式配置kubeconfig:

TOKEN_NAME=$(kubectl get serviceaccounts $SA_NAME -o jsonpath=.secrets[0].name)

TOKEN=$(kubectl get secret "$TOKEN_NAME" -o jsonpath=.data.token | base64 -d)

kubectl config set-credentials "$USERNAME" --token="$TOKEN"

为了验证test-sa,在刚刚创建的int32bit-rolebinding的subjects增加了ServiceAccount test-sa。

Service account除了可以用于集群外认证外,其还有一个最大的特点是可以通过Pod.spec.serviceAccountName把token attach到Pod中。

此时Kubernetes会自动把SA的Token通过volume的形式挂载到/run/secrets/kubernetes.io/serviceaccount目录上,从而Pod可以读取token调用Kubernetes API.


如何在Pod自动添加ServiceAccount:

通过 Admission Controller插件来实现对pod修改,它是apiserver的一部分。创建或更新pod时会同步进行修改pod。当插件处于激活状态(在大多数发行版中都默认情况)创建或修改pod时,会按以下操作执行:

1.如果pod没有设置ServiceAccount,则将ServiceAccount设置为default。

2.确保pod引用的ServiceAccount存在,否则将会拒绝请求。

3.如果pod不包含任何ImagePullSecrets,则将ServiceAccount的ImagePullSecrets会添加到pod中。

4.为包含API访问的Token的pod添加了一个volume。

5.把volumeSource添加到安装在pod的每个容器中,挂载在/var/run/secrets/kubernetes.io/serviceaccount。

service account token的管理:Token Controller

TokenController作为controller-manager的一部分运行。异步行为:

观察serviceAccount的创建,并创建一个相应的Secret 来允许API访问。
观察serviceAccount的删除,并删除所有相应的ServiceAccountToken Secret
观察secret 添加,并确保关联的ServiceAccount存在,并在需要时向secret 中添加一个Token。
观察secret 删除,并在需要时对应 ServiceAccount 的关联

需要使用–service-account-private-key-file 参数选项将Service Account 密匙(key)文件传递给controller-manager中的Token controller。key用于 Service Account Token签名。同样,也需要使用–service-account-key-file 参数选项将相应的(public key)公匙传递给kube-apiserver ,公钥用于在认证期间验证Token。

针对一些需要和Kubernetes API交互的应用非常有用,比如coredns就需要监控endpoints、services的变化,因此关联了coredns SA,coredns又关联了system:coredns clusterrole。flannel需要监控pods以及nodes变化同样关联了flannel SA。

到这里为止,service account可能是Kubernetes目前最完美的认证方案了,既能支持集群外的客户端认证,又支持集群内的Pod关联授权。

但事实上,service account并不是设计用来给普通user认证的,而是给集群内部服务使用的。目前虽然token是永久有效的,但未来会改成使用动态token的方式,参考官方设计设计文档Bound Service Account Tokens,此时如果kubectl客户端认证则需要频繁更新token。

除此之外,SA虽然能够对应一个虚拟User,但不支持自定义Group,在授权体系中不够灵活。另外也不支持客户端高级认证功能,比如MFA、SSO等。


集成外部认证系统

前面已经介绍过Kubernetes集成简单的静态用户文件以及x509证书认证,Kubernetes最强大的功能是支持集成第三方Id Provider(IdP),主流的如AD、LADP以及OpenStack Keystone等,毕竟专业的人做专业的事。

Kubernetes 的认证策略有很多种(请参考 Kubernetes Authentication Strategy),其中,通过一个不记名令牌 (Bear Token) 来识别用户是一种相对安全又被各种客户端广泛支持的认证策略。不记名令牌,代表着对某种资源,以某种身份访问的权利,无论是谁,任何获取该令牌的访问者,都被认为具有了相应的身份和访问权限。配合成熟的令牌授予机构,不记名令牌非常适于在生产环境中严肃使用。身份令牌(ID Token)就是一种形式的不记名令牌,它本身记录着一个权威认证机构对用户身份的认证声明,同时还可以包含对这个用户授予了哪些权限的声明,像极了古代官员佩戴的腰牌。Kubernetes 接受和识别的正是这种 ID Token。

由上可知,想得到 ID Token,首先要经过某个权威机构的一套身份认证流程,OpenID Connect 简称 OIDC 就是这样一套认证、授予 ID Token 的协议。OIDC 是 OAuth2 协议的一种扩展,目前在各大主流厂牌的云服务中都被广泛支持和使用,例如 IBM IAM Service、Google Accounts、Azure Active Directory 等。


Kubernetes 使用 OIDC Token 的认证流程

Kubernetes 使用 OIDC 进行身份认证的流程,比上面介绍的 OAuth2 授权流程要简单不少。在整个过程中, Kubernetes 既作为资源(Resource)服务器,又作为用户代理 User-Agent 存在,但它并不提供引导用户到 Auth Server 进行认证的功能,相反,它要求用户先自行获取 ID Token,然后在访问 Kubernetes API Server 的时候直接提供 ID Token。因此,整个过程中,Kubernetes 其实不需要真正和 Authentication Server 发生任何交互。整个认证流程如下图 2 所示:

某个 Kubernetes Client 想访问 Kubernetes API。
用户先自行向 Auth Server (例如 KeyCloak 或 Google Accounts) 认证自己。
拿到 id_token、refresh_token。
用户把 token 配置到需要访问 Kubernetes api 的 client application 中(如 kubectl 或 dashboard)。
Kubernetes Client 就可以使用 tokens 以用户的身份访问 Kubernetes API 了。

那么问题来了,如果 Kubernetes 不和 Auth Server 发生交互的话,它怎么鉴定这些 Token 是合法的用户 token 呢?这里我们可以参考一下 Kubernetes Authentication OIDC Tokens 官方文档,其中它分 9 个步骤更详细描述了从获取 token 到在 kubectl 中使用它们进行访问的流程。其中绝大部分都被上图抽象概括,这里我们重点关注一下步骤 5:

5.The API server will make sure the JWT signature is valid by checking against the certificate named in the configuration.

能够认证 token 的合法性的关键在于,所有 JWT token 都被其颁发 Auth Service 进行了数字签名,我们只需在 Kubernetes API Server 中配置上我们所信任的这个 Auth Server 的证书(Certificate),并用它来验证收到的 id_token 中的签名是否合法,就可以验证 token 的真实性。使用这种基于 PKI 的验证机制,在配置完成后,认证过程中 Kubernetes 就无需和 Auth Server 有任何交互。

以上是关于kubernetes API 访问控制之:认证的主要内容,如果未能解决你的问题,请参考以下文章

kubernetes API 访问控制之:准入控制

kubernetes集群的认证授权准入控制

Kubernetes(k8s)集群安全机制之访问控制

kubernetes 核心组件之 APIServer

十一,k8s集群访问控制之ServicAccount

kubernetes API 访问控制之:授权