• Kubernetes-深入分析集群安全机制


    Kubernetes过一系列机制来实现集群的安全机制,包括API Server的认证授权、准入控制机制及保护敏感信息的Secret机制等。集群的安全性必须考虑以下的几个目标:

    1. 保证容器与其所在宿主机的隔离;
    2. 限制容器给基础设施及其他容器带来消极影响的能力;
    3. 最小权限原则,合理限制所有组件权限,确保组件只执行它被授权的行为,通过限制单个组件的能力来限制他所能达到的权限范围;
    4. 明确组件间边界的划分;
    5. 划分普通用户和管理员角色;
    6. 在必要的时候允许将管理员权限赋给普通用户;
    7. 允许拥有Secret数据(Keys、Certs、Passwords)的应用在集群中运行;

    下面分别从Authentication、Authorization、Admission Control、Secret和Service Account等方面来说明集群的安全机制。

    API Server认证

    Kubernetes集群中所有资源的访问和变更都是通过Kubernetes API Server的REST API来实现的,所以集群安全的关键点在于识别认证客户端身份(Authentication)以及访问权限的授权(Authorization)。

    Kubernetes提供管理三种级别的客户端身份认证方式:

    1. 最严格的HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式;
    2. HTTP Token认证:通过一个Token来识别合法用户;
    3. HTTP Base认证:通过用户名+密码的方式认证;

    SSL双向认证步骤:

    1. HTTPS通信双方的务器端向CA机构申请证书,CA机构是可信的第三方机构,它可以是一个公认的权威的企业,也可以是企业自身。企业内部系统一般都使用企业自身的认证系统。CA机构下发根证书、服务端证书及私钥给申请者;
    2. HTTPS通信双方的客户端向CA机构申请证书,CA机构下发根证书、客户端证书及私钥个申请者;
    3. 客户端向服务器端发起请求,服务端下发服务端证书给客户端。客户端接收到证书后,通过私钥解密证书,并利用服务器端证书中的公钥认证证书信息比较证书里的消息,例如域名和公钥与服务器刚刚发送的相关消息是否一致,如果一致,则客户端认为这个服务器的合法身份;
    4. 客户端发送客户端证书给服务器端,服务端接收到证书后,通过私钥解密证书,获得客户端的证书公钥,并用该公钥认证证书信息,确认客户端是否合法;
    5. 客户端通过随机秘钥加密信息,并发送加密后的信息给服务端。服务器端和客户端协商好加密方案后,客户端会产生一个随机的秘钥,客户端通过协商好的加密方案,加密该随机秘钥,并发送该随机秘钥到服务器端。服务器端接收这个秘钥后,双方通信的所有内容都都通过该随机秘钥加密;

    CA认证流程图:

    上述是双向SSL协议的具体通信过程,这种情况要求服务器和用户双方都有证书。单向认证SSL协议不需要客户拥有CA证书,对应上面的步骤,只需将服务器端验证客户端证书的过程去掉,以及在协商对称密码方案和对称通话秘钥时,服务器端发送给客户端的是没有加过密的(这并不影响SSL过程的安全性)密码方案。

    HTTP Token原理:HTTP Token的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串——Token来表明客户身份的一种方式。在通常情况下,Token是一个复杂的字符串,比如我们用私钥签名一个字符串的数据就可以作为一个Token,此外每个Token对应一个用户名,存储在API Server能访问的一个文件中。当客户端发起API调用请求时,需要在HTTP Header里放入Token,这样一来API Server就能够识别合法用户和非法用户了。

    HTTP Base:常见的客户端账号登录程序,这种认证方式是把“用户名+冒号+密码”用BASE64算法进行编码后的字符串放在HTTP REQUEST中的Header Authorization域里发送给服务端,服务端收到后进行解码,获取用户名及密码,然后进行用户身份的鉴权过程。

    API Server授权

    对合法用户进行授权(Authorization)并且随后在用户访问时进行鉴权,是权限与安全系统的重要一环。授权就是授予不同用户不同访问权限,API Server目前支持一下集中授权策略:

    • AlwaysDeny:拒绝所有请求,该配置一般用于测试;
    • AlwaysAllow:接收所有请求,如果集群不需要授权流程,可以采用该策略,此为Kubernetes默认的策略;
    • ABAC:(Attribute-Base Access Control)为基于属性的访问控制,表示使用用户配置的授权规则去匹配用户的请求;

    为了简化授权的复杂度,对于ABAC模式的授权策略,Kubernetes仅有下面四个基本属性:

    1. 用户名(代表一个已经被认证的用户的字符型用户名)
    2. 是否是只读请求(REST的GET操作是只读的)
    3. 被访问的是哪一类资源,例如Pod资源/api/v1/namespaces/default/pods
    4. 被访问对象所属的Namespace

    当API Server启用ABAC模式时,需要指定授权文件的路径和名字(--authorization_policy_file=SOME_FILENAME),授权策略文件里的每一行都是一个Map类型的JOSN对象,被称为访问策略对象,我们可以通过设置“访问策略对象”中的如下属性来确定具体的授权行为:

    • user:字符串类型,来源于Token文件或基本认证文件中的用户名字段的值;
    • readonly:true时表示该策略允许GET请求通过;
    • resource:来自于URL的资源,例如“Pod”;
    • namespace:表明该策略允许访问某个namespace的资源;

    eg:

    • {"user":"alice"}
    • {"user":"kubelet","resource":"Pods","readonly":true}
    • {"user":"kubelet","resource":"events"}
    • {"user":"bob","resource":"Pods","readonly":true,"ns":"myNamespace"}

    Admission Control准入控制

    通过认证和鉴权之后,客户端并不能得到API Server的真正响应,这个请求还需通过Admission Control所控制的一个“准入控制链”的层层考验,Admission Control配备有一个“准入控制器”的列表,发送给API Server的任何请求都需要通过列表中每个准入控制器的检查,检查不通过API Server拒绝此调用请求。此外,准入控制器还能够修改请求参数以完成一些自动化的任务。比如Service Account这个控制器,当前可配置的准入控制如下:

    • AlwaysAdmit:允许所有请求;
    • AlwaysPullmages:在启动容器之前总去下载镜像,相当于在每个容器的配置项imagePullPolicy=Always
    • AlwaysDeny:禁止所有请求,一般用于测试;
    • DenyExecOnPrivileged:它会拦截所有想在Privileged Container上执行命令的请求,如果你的集群支持Privileged Container,你又希望限制用户在这些Privileged Container上执行命令,强烈推荐你使用它;
    • Service Account:这个plug-in将ServiceAccount实现了自动化,默认启用,如果你想使用ServiceAccount对象,那么强烈你推荐使用它;
    • SecurityContextDeny:这个插件将使用SecurityContext的Pod中的定义全部失效。SecurityContext在Container中定义了操作系统级别的安全设定(uid,gid,capabilityes,SELinux等)
    • ResourceQuota:用于配额管理目的,作用于namespace上,它会观察所有请求,确保在namespace上的配额不会超标。推荐在Admission Control参数列表中这个插件排最后一个;
    • LimitRanger:用于配额管理,作用于Pod与Container,确保Pod与Container上的配额不会超标;
    • NamespaceExists(已过时):对所有请求校验namespace是否已存在,如果不存在则拒绝请求,已合并至NamespaceLifecycle。
    •  NamespaceAutoProvision(已过时):对所有请求校验namespace,如果不存在则自动创建该namespace,推荐使用NamespaceLifecycle。
    • NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒绝。当删除一个namespace时,系统将会删除该namespace中所有对象,保存Pod,Service等。

    在API Server上设置--admission-control参数,即可定制我们需要的准入控制链,如果启用多种准入控制选项,则建议的设置如下:

    • --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota

    下面着重介绍三个准入控制器:

    SecurityContextDeny

    Security Context时运用于容器的操作系统安全设置(uid、gid、capabilities、SELinux role等),Admission Control的SecurityContextDeny插件的作用是,禁止创建设置了Security Context的Pod,例如包含以下配置项的Pod:

    • spec.containers.securityContext.seLinuxOptions
    • spec.containers.securityContext.runAsUser

    ResourceQuota

    ResourceQuota不仅能够限制某个Namespace中创建资源的数量,而且能够限制某个namespace中被Pod所请求的资源总量。该准入控制器和资源对象ResourceQuota一起实现了资源的配额管理;

    LimitRanger

    准入控制器LimitRanger的作用类似于上面的ResourceQuota控制器,这对Namespace资源的每个个体的资源配额。该插件和资源对象LimitRange一起实现资源限制管理。

    Service Account

    Servuce Account是一种账号,但他并不是给Kubernetes的集群的用户(系统管理员、运维人员、租户用户等),而是给运行在Pod里的进程用的,它为Pod里的进程提供必要的身份证明。

    Pod中访问Kubernetes API Server服务的时候,是以Service方式访问服务名为kubernetes这个服务的,而kubernetes服务又只在HTTPS安全端口443上提供服务,那么如何进行身份认证呢?在Kubernetes的官方文档并没有清除的说明这个问题。

    通过查看源码获知这是在用一种类似HTTP Token的新的认证方式--ServiceAccount Auht,Pod中的客户端调用Kubernetes API的时候,在HTTP Header中传递了一个Token字符串,这类似于之前提到的HTTP Token认证方式,存在以下几个不同点:

    • 此处的Token的内容来源于Pod里指定路径下的一个文件(/run/secrets/kubernetes.io/serviceaccount/token),这种token是动态生成的,确切的说,是由KubernetesController进程用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-account-key-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从属于ServiceAccount资源对象,属于Service Account的一部分,一个ServiceAccount对象里面可以包括多个不同的Secret对象,分别用于不同目的的认证活动。

    下面通过命令来直观的加深对ServiceAccount的认识:

    查看系统中ServiceAccount对象,可以看到一个名为default的Service Account对象,包含一个名为default-token-xxx的Secret,这个Secret同时是“Mountable secrets”,表明他是需要被Mount到Pod上的。

    • kubectl describe serviceaccounts
    • kubectl describe secrets default-token-xxx

    default-token-xxx包括三个数据项:

    • token
    • ca.crt
    • namespace

    联想到“Mountable secrets”的标记,以及之前看到的Pod中的三个文件的文件名:每个namespace下有一个名为default的默认的ServiceAccount对象,这个ServiceAccount里有一个名为Tokens的可以作为Volume一样被Mount到Pod里的Secret,当Pod启动时这个Secret会被自动Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权过程。

    一个ServiceAccount可以包括多个Secrets对象:

    • 名为Tokens的Secret用于访问API Server的Secret,也被称为ServiceAccountSecret;
    • 名为Image Pull secrets的Secret用于下载容器镜像时的认证过程,通常镜像库运行在Insecure模式下,所以这个Secret为空;
    • 用户自定义的其他Secret,用于用户的进程;

    如果一个Pod在定义时没有指定spec.service.AccountName属性,则系统会自动为其赋值为“Default”,即使用同一namespace下默认的ServiceAccount,如果某个Pod需要使用非default的ServiceAccount,需要在定义时指定:

    apiVersion:v1

    kind:Pod

    metadata:

        name:mypod

    spec:

        containers:

        - name:mycontainer

          image:

        serviceAccountName:myserviceaccount

    Kubernetes之所以要创建两套独一的账号系统,原因如下:

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

    下面分析Service Account与Secret相关的一些运行机制:

     Controller manager创建了ServiceAccountController与Token Controllerl两个安全相关的控制器。其中ServiceAccountController一直监听Service Account和Namespace的事件,如果一个Namespace中没有default Service Account,那么Service Account Controller就会为该Namespace创建一个默认的(default)的Service Account,这就是我们之前看到的每个namespace下都有一个名为default的ServiceAccount的原因。

    如果Controller manager进程在启动时指定了API Server私钥(service-account-private-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的启动参数中包括下面的内容时:

    • --admission_control=ServiceAccount

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

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

    综上所述,ServiceAccount正常运行需要以下几个控制器:

    • Admission Controller
    • Token Controller
    • Service Account Controller

    Secret私密凭据

    Secret主要作用是保管私密数据,比如密码、OAuth Tokens、SSH Keys等信息。将这些私密信息放在Secret对象中比直接放在Pod或Docker Image中要更安全,也便于使用和分发。

    • 创建Secret

    secret.yaml

    apiVersion:v1

    kind:Secret

    metadata:

        name:mysecret

    type: Opaque

    data:

        password:dmfsdWUtMg0k

        username:dmfsdWUtMg0k

    kubectl create -f secret.yaml

    在上面的data域中的各子域的值必须为BASE64编码值,其中password域和username域BASE64编码前的值分别为value-1和value-2。一旦secret被创建,可以通过以下三个方式使用它:

    • 在创建Pod时,通过为Pod指定ServiceAccount来自动使用该Seret;
    • 通过挂载该Secret到Pod来使用它;
    • Docker镜像下载时使用,通过指定Pod的spec.ImagePullSecrets来引用它;
    • 第一种方式主要用在API Server鉴权方面;
    • 第二种方式如下:

    apiVersion:v1

    kind:Pod

    metadata:

        name:mypod

        namespace:myns

    spec:

        containers:

        - name:mycontainer

          image:redis

          volumeMounts:

          - name:foo

            mountPath:“/etc/foo”

            readOnly:true

    volumes:

    - name:foo

      secret:

          secretName:mysecret

    • 第三种方式如下:
      • 执行login登录私有registry
      • docker login localhost:5000(输入账户及密码,则会创建新用户,并把相关信息写入~/。dockercfg文件中)
      • 用BASE64编码dockercfg的内容;
      • cat ~/.dockercfg|grep base64
      • 将上一步命令的输出结果作为secret的“data.dockercfg”域的内容,由此来创建一个Secret:

     image-pull-secret.yaml:

    apiVersion:v1

    kind:Secret

    metadata:

        name:myregistrykey

    data:

        .dockercfg:xxx

    type:kubernetes.io/dockercfg

    • kubectl create -f image-pull-secret.yaml
    • 在创建Pod的时候引用该Secret:

    pods.yaml

    apiVersion:v1

    kind:Pod

    metadata:

        name:mypod2

    spec:

        containers:

        - name:foo

          image:xxxxxx:v1

    imagePullSecrets:

    - name:myregistrykey

    • kubectl create -f pods.yaml

    每个单独的Secret大小不能超过1M,Kubernetes不鼓励创建大尺寸的Secret,因为如果使用大尺寸的Secret,则将大量占用API Server和kubelet的内存。当然创建许多小的Secret也能耗尽API Server和kubelet的内存。

    在使用Mount方式挂载Secret时,Container中Secret的“data”域的各个域的key值作为目录中的文件,Value值被BASE64编码后存储在相应的文件中。前面的例子中创建的Secret,被挂载到一个叫做mycontainer的container中,在该container中可以通过命令查看所生产的文件和文件中的内容:

    • ls /etc/foo

    username

    password

    • cat /etc/foo/username

    value-1

    • cat /etc/foo/password

    value-2

    我们可以通过Secret保管其他系统的敏感信息(比如数据库用户名和密码),并以Mount的方式将Secret挂载到Container中,然后通过访问目录中的文件的方式获取该敏感信息。当Pod被API Server创建时,API Server不会校验该Pod引用的Secret是否存在。一旦这个Pod被调度,则Kubelet将试着获取Secret的值。如果Secret不存在或暂时无法连接到API Server,则kubelet将按一定的时间间隔定期重试获取该Secret,并发送一个Event来解释Pod没有启动的原因。一旦Secret被Pod获取,则Kubelet将创建并Mount包含Secret的Volume。只有所有的Volume被Mount后,Pod中的Container才会被启动。在kubelet启动Pod中container后,Container中和Secret相关的Volume将不会被改变,即使Secret本身被修改了。为了使用更新后的Secret,必须删除旧的Pod,并重新创建一个新的Pod,因此更新Secret的流程和部署一个新的Image是一样的。

  • 相关阅读:
    【视频开发】【CUDA开发】英伟达CUVID硬解,并通过FFmpeg读取文件
    【视频开发】【CUDA开发】英伟达CUVID硬解,并通过FFmpeg读取文件
    【视频开发】ffmpeg 的编译选项
    【视频开发】ffmpeg 的编译选项
    【视频开发】Nvidia硬解码总结
    【视频开发】 ffmpeg支持的硬解码接口
    【视频开发】 ffmpeg支持的硬解码接口
    【视频开发】【CUDA开发】FFMPEG硬件加速-nvidia方案
    【视频开发】【CUDA开发】FFMPEG硬件加速-nvidia方案
    【视频开发】【CUDA开发】ffmpeg nvenc编码
  • 原文地址:https://www.cnblogs.com/cf532088799/p/7977083.html
Copyright © 2020-2023  润新知