带你玩转kubernetes-k8s(第40篇:深入分析集群安全机制三[Admission Control, Service Account])

释放双眼,带上耳机,听听看~!

Adminssion Control

       突破了之前所说的认证和鉴权两道关卡之后,客户端的调用请求就能够得到API Server的真正响应了吗?答案是:不能!这个请求还需要通过Admission Control(准入控制)所控制的一个准入控制链的层层考验,才能获得成功的响应。Kubernetes官方标准的“关卡”有30多个,还允许用户自定义扩展。
Admission Control配备了一个准入控制器的插件列表,发送给API Server的任何请求都需要通过列表中每个准入控制器的检查,检查不通过,则API Server拒绝此调用请求。此外,准入控制器插件能够修改请求参数以完成一些自动化任务,比如ServiceAccount这个控制器插件。当前可配置的准入控制器插件如下。
◎ AlwaysAdmit:已弃用,允许所有请求。
◎ AlwaysPullImages:在启动容器之前总是尝试重新下载镜像。这对于多租户共享一个集群的场景非常有用,系统在启动容器之前可以保证总是使用租户的密钥去下载镜像。如果不设置这个控制器,则在Node上下载的镜像的安全性将被削弱,只要知道该镜像的名称,任何人便都可以使用它们了。

◎ AlwaysDeny:已弃用,禁止所有请求,用于测试。
◎ DefaultStorageClass:会关注PersistentVolumeClaim资源对象的创建,如果其中没有包含任何针对特定Storage class的请求,则为其指派指定的Storage class。在这种情况下,用户无须在PVC中设置任何特定的Storage class就能完成PVC的创建了。如果没有设置默认的Storage class,该控制器就不会进行任何操作;如果设置了超过一个的默认Storage class,该控制器就会拒绝所有PVC对象的创建申请,并返回错误信息。管理员必须检查StorageClass对象的配置,确保只有一个默认值。该控制器仅关注PVC的创建过程,对更新过程无效。
◎ DefaultTolerationSeconds:针对没有设置容忍node.kubernetes.io/not-ready:NoExecute或者node.alpha.kubernetes.io/unreachable:NoExecute的Pod,设置5min的默认容忍时间。
◎ DenyExecOnPrivileged:已弃用,拦截所有想在Privileged Container上执行命令的请求。如果你的集群支持Privileged Container,又希望限制用户在这些Privileged Container上执行命令,那么强烈推荐使用它。其功能已被合并到DenyEscalatingExec中。
◎ DenyEscalatingExec:拦截所有exec和attach到具有特权的Pod上的请求。如果你的集群支持运行有escalated privilege权限的容器,又希望限制用户在这些容器内执行命令,那么强烈推荐使用它。
◎ EventReateLimit:Alpha版本,用于应对事件密集情况下对API Server造成的洪水攻击。
◎ ExtendedResourceToleration:如果运维人员要创建带有特定资源(例如GPU、FPGA等)的独立节点,则可能会对节点进行Taint处理来进行特别配置。该控制器能够自动为申请这些特别资源的Pod加入Toleration定义,无须人工干预。
◎ ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。ImagePolicyWebhook需要使用一个配置文件(通过kube-apiserver的启动参数–admission-control-config-file设置)定义后端Webhook的参数。目前是Alpha版本的功能。

◎ Initializers:Alpha。用于为动态准入控制提供支持,通过修改待创建资源的元数据来完成对该资源的修改。
◎ LimitPodHardAntiAffinityTopology:该插件启用了Pod的反亲和性调度策略设置,在设置亲和性策略参数requiredDuringSchedulingRequiredDuringExecution时要求将topologyKey的值设置为“kubernetes.io/hostname”,否则Pod会被拒绝创建。
◎ LimitRanger:这个插件会监控进入的请求,确保请求的内容符合在Namespace中定义的LimitRange对象里的资源限制。如果要在Kubernetes集群中使用LimitRange对象,则必须启用该插件才能实施这一限制。LimitRanger还能用于为没有设置资源请求的Pod自动设置默认的资源请求,该插件会为default命名空间中的所有Pod设置0.1CPU的资源请求。
◎ MutatingAdmissionWebhook:Beta。这一插件会变更符合要求的请求的内容,Webhook以串行的方式顺序执行。
◎ NamespaceAutoProvision:这一插件会检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就会自动创建命名空间。
◎ NamespaceExists:这一插件会检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就会拒绝这一创建过程。
◎ NamespaceLifecycle:如果尝试在一个不存在的Namespace中创建资源对象,则该创建请求将被拒绝。当删除一个Namespace时,系统将会删除该Namespace中的所有对象,包括Pod、Service等,并阻止删除default、kube-system和kube-public这三个命名空间。
◎ NodeRestriction:该插件会限制kubelet对Node和Pod的修改行为。为了实现这一限制,kubelet必须使用system:nodes组中用户名为system:node:<nodeName>的Token来运行。符合条件的kubelet只能修改自己的Node对象,也只能修改分配到各自Node上的Pod对象。在Kubernetes 1.11以后的版本中,kubelet无法修改或者更新自身Node的taint属性。在Kubernetes 1.13以后,这一插件还会阻止kubelet删除自己的Node资源,并限制对有kubernetes.io/或k8s.io/前缀的标签的修改。
◎ OnwerReferencesPermissionEnforcement:在该插件启用后,一个用户要想修改对象的metadata.ownerReferences,就必须具备delete权限。该插件还会保护对象的metadata.ownerReferences[x].blockOwnerDeletion字段,用户只有在对finalizers子资源拥有update权限的时候才能进行修改。
◎ PersistentVolumeLabel:弃用。这一插件自动根据云供应商(例如GCE或AWS)的定义,为PersistentVolume对象加入region或zone标签,以此来保障PersistentVolume和Pod同处一区。如果插件不为PV自动设置标签,则需要用户手动保证Pod和其加载卷的相对位置。该插件正在被Cloud controller manager替换,从Kubernetes 1.11版本开始默认被禁止。
◎ PodNodeSelector:该插件会读取命名空间的annotation字段及全局配置,来对一个命名空间中对象的节点选择器设置默认值或限制其取值。
◎ PersistentVolumeClaimResize:该插件实现了对PersistentVolumeClaim发起的resize请求的额外校验。
◎ PodPreset:该插件会使用PodSelector选择Pod,为符合条件的Pod进行注入。
◎ PodSecurityPolicy:在创建或修改Pod时决定是否根据Pod的security context和可用的PodSecurityPolicy对Pod的安全策略进行控制。
◎ PodTolerationRestriction:该插件首先会在Pod和其命名空间的Toleration中进行冲突检测,如果其中存在冲突,则拒绝该Pod的创建。它会把命名空间和Pod的Toleration进行合并,然后将合并的结果与命名空间中的白名单进行比较,如果合并的结果不在白名单内,则拒绝创建。如果不存在命名空间级的默认 Toleration和白名单,则会采用集群级别的默认Toleration和白名单。
◎ Priority:这一插件使用priorityClassName字段来确定优先级,如果没有找到对应的Priority Class,该Pod就会被拒绝。
◎ ResourceQuota:用于资配额理目的,作用于Namespace。该插件拦截所有请求,以确保在Namespace上的资源配额使用不会超标。推荐在Admission Control参数列表中将这个插件排最后一个,以免可能被其他插件拒绝的Pod被过早分配资源。
◎ SecurityContextDeny:这个插件将在Pod中定义的SecurityContext选项全部失效。SecurityContext在Container中定义了操作系统级别的安全设定(uid、gid、capabilities、SELinux等)。在未设置PodSecurityPolicy的集群中建议启用该插件,以禁用容器设置的非安全访问权限。
◎ ServiceAccount:这个插件将ServiceAccount实现了自动化,如果想使用ServiceAccount对象,那么强烈推荐使用它。
◎ StorageObjectInUseProtection:这一插件会在新创建的PVC或PV中加入kubernetes.io/pvc-protection或kubernetes.io/pv-protection的finalizer。如果想要删除PVC或者PV,则直到所有finalizer的工作都完成,删除动作才会执行。
◎ ValidatingAdmissionWebhook:在Kubernetes 1.8中为Alpha版本,在Kubernetes 1.9中为Beta版本。该插件会针对符合其选择要求的请求调用校验Webhook。目标Webhook会以并行方式运行;如果其中任何一个Webhook拒绝了该请求,该请求就会失败。

在API Server上设置参数即可定制我们需要的准入控制链,如果启用多种准入控制选项,则建议设置:在Kubernetes 1.9及之前的版本中使用的参数是–admission-control,并且其中的内容是顺序相关的;在Kubernetes 1.10及之后的版本中,该参数为–enable-admission-plugins,并且与顺序无关。
对Kubernetes 1.10及以上版本设置如下:


1
2
1--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultsStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
2

对Kubernetes 1.9及以下版本设置如下:


1
2
1--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,CalidatingAdmissionWebhook,ResourceQuota
2

Service Account

      Service Account也是一种账号,但它并不是给Kubernetes集群的用户(系统管理员、运维人员、租户用户等)用的,而是给运行在Pod里的进程用的,它为Pod里的进程提供了必要的身份证明。
在正常情况下,为了确保Kubernetes集群的安全,API Server都会对客户端进行身份认证,认证失败的客户端无法进行API调用。此外,在Pod中访问Kubernetes API Server服务时,是以Service方式访问名为Kubernetes的这个服务的,而Kubernetes服务又只在HTTPS安全端口443上提供,那么如何进行身份认证呢?这的确是个谜,因为Kubernetes的官方文档并没有清楚说明这个问题。
通过查看官方源码,我们发现这是在用一种类似HTTP Token的新认证方式—Service Account Auth,Pod中的客户端调用Kubernetes API时,在HTTP Header中传递了一个Token字符串,这类似于之前提到的HTTP Token认证方式,但有以下几个不同之处。

◎ 这个Token的内容来自Pod里指定路径下的一个文件(/run/secrets/kubernetes.io/serviceaccount/token),这种Token是动态生成的,确切地说,是由Kubernetes Controller进程用API Server的私钥(–service-account-private-key-file指定的私钥)签名生成的一个JWT Secret。
◎ 在官方提供的客户端REST框架代码里,通过HTTPS方式与API Server建立连接后,会用Pod里指定路径下的一个CA证书(/run/secrets/kubernetes.io/serviceaccount/ca.crt)验证API Server发来的证书,验证是否为CA证书签名的合法证书。
◎ API Server在收到这个Token以后,采用自己的私钥(实际上是使用service-accountkey-file参数指定的私钥,如果没有设置此参数,则默认采用tls-private-key-file指定的参数,即自己的私钥)对Token进行合法性验证。

     明白了认证原理,我们接下来继续分析在上面的认证过程中所涉及的Pod的以下三个文件。

◎ /run/secrets/kubernetes.io/serviceaccount/token。
◎ /run/secrets/kubernetes.io/serviceaccount/ca.crt。
◎ /run/secrets/kubernetes.io/serviceaccount/namespace(客户端采用这里指定的namespace作为参数调用Kubernetes API)。

        这三个文件由于参与到Pod进程与API Server认证的过程中,起到了类似secret(私密凭据)的作用,所以它们被称为Kubernetes Secret对象。Secret从属于Service Account资源对象,属于Service Account的一部分,在一个Service Account对象里面可以包括多个不同的Secret对象,分别用于不同目的的认证活动。
下面通过运行一些命令来加深我们对Service Account与Secret的直观认识。
首先,查看系统中的Service Account对象,看到有一个名为default的Service Account对象,包含一个名为default-token-kfmj7的Secret,这个Secret同时是Mountable secrets,表明它是需要被挂载到Pod上的:

带你玩转kubernetes-k8s(第40篇:深入分析集群安全机制三[Admission Control, Service Account])

接下来看看default-token-kfmj7都有什么内容:

带你玩转kubernetes-k8s(第40篇:深入分析集群安全机制三[Admission Control, Service Account])

从上面的输出信息中可以看到,default-token包含三个数据项,分别是token、ca.crt、namespace。联想到Mountable secrets的标记,以及之前看到的Pod中的三个文件的文件名,我们恍然大悟:在每个Namespace下都有一个名为default的默认Service Account对象,在这个Service Account里面有一个名为Tokens的可以当作Volume被挂载到Pod里的Secret,当Pod启动时,这个Secret会自动被挂载到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权。

如下图所示,一个Service Account 可以包含多个Secret对象。

带你玩转kubernetes-k8s(第40篇:深入分析集群安全机制三[Admission Control, Service Account])

其中:
(1)名为Tokens的Secret用于访问API Server的Secret,也被称为Service Account Secret。
(2)名为imagePullSecrets的Secret用于下载容器镜像时的认证过程,通常镜像库运行在Insecure模式下,所以这个Secret为空。
(3)用户自定义的其他Secret,用于用户的进程。
如果一个Pod在定义时没有指定spec.serviceAccountName属性,则系统会自动为其赋值为default,即大家都使用同一个Namespace下的默认Service Account.。 如果某个Pod需要使用费default的Service Account,则需要在定义时指定:


1
2
3
4
5
6
7
8
9
10
1apiVersion: v1
2kind: Pod
3metadata:
4  name: mypod
5spec:
6  containers:
7  - name: mycontainter
8    image: nginx:v1
9  serviceAccountName: myserviceaccount
10

Kubernetes之所以要创建两套独立的账号系统,原因如下。
◎ User账号是给人用的,Service Account是给Pod里的进程使用的,面向的对象不同。
◎ User账号是全局性的,Service Account则属于某个具体的Namespace。
◎ 通常来说,User账号是与后端的用户数据库同步的,创建一个新用户通常要走一套复杂的业务流程才能实现,Service Account的创建则需要极轻量级的实现方式,集群管理员可以很容易地为某些特定任务创建一个Service Account。
◎ 对于这两种不同的账号,其审计要求通常不同。
◎ 对于一个复杂的系统来说,多个组件通常拥有各种账号的配置信息,Service Account是Namespace隔离的,可以针对组件进行一对一的定义,同时具备很好的“便携性”。

接下来深入分析Service Account与Secret相关的一些运行机制。

      我们知道Controller manager创建了ServiceAccount Controller与Token Controller这两个安全相关的控制器。其中ServiceAccount Controller一直监听Service Account和Namespace的事件,如果在一个Namespace中没有default Service Account,那么ServiceAccount Controller会为该Namespace创建一个默认(default)的Service Account,这就是我们之前看到在每个Namespace下都有一个名为default的Service Account的原因。
如果Controller manager进程在启动时指定了API Server私钥(service-accountprivate-key-file参数),那么Controller manager会创建Token Controller。Token Controller也监听Service Account的事件,如果发现在新创建的Service Account里没有对应的Service Account Secret,则会用API Server私钥创建一个Token(JWT Token),并用该Token、CA证书及Namespace名称等三个信息产生一个新的Secret对象,然后放入刚才的Service Account中;如果监听到的事件是删除Service Account事件,则自动删除与该Service Account相关的所有Secret。此外,Token Controller对象同时监听Secret的创建、修改和删除事件,并根据事件做不同的处理。

   当我们在API Server的鉴权过程中启用了Service Account类型的准入控制器,即在kube-apiserver启动参数中包括下面的内容时:


1
2
1--adminssion_control=ServiceAccount
2

 则针对Pod新增或修改的请求,Service Account准入控制器会验证Pod里的Service Account是否合法。

(1)如果spec.serviceAccount域没有被设置,则Kubernetes默认为其指定名称为default的Service accout。
(2)如果Pod的spec.serviceAccount域指定了default以外的Service Account,而该Service Account没有被事先创建,则该Pod操作失败。
(3)如果在Pod中没有指定ImagePullSecrets,那么这个spec.serviceAccount域指定的Service Account的ImagePullSecrets会被加入该Pod中。
(4)给Pod添加一个特殊的Volume,在该Volume中包含Service Account Secret中的Token,并将Volume挂载到Pod中所有容器的指定目录下(/var/run/secrets/kubernetes.io/serviceaccount)。

综上所述,Service Account的正常工作离不开以下几个控制器。
(1)Admission Controller。
(2)Token Controller。
(3)ServiceAccount Controller。

小结:

       本小节主要讲解Admission Control,Service Account的原理。

       理论和操作都很重要,希望大家认认真真的看。

       谢谢大家的浏览与支持,谢谢!(ps:多多点关注)

 

 

给TA打赏
共{{data.count}}人
人已打赏
安全运维

故障复盘的简洁框架-黄金三问

2021-9-30 19:18:23

安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索