百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

kubernetes集群安全机制 kubernetes 管理

lipiwang 2024-10-20 10:10 9 浏览 0 评论

https://zhuanlan.zhihu.com/p/468010077?utm_id=0

提到安全时就离不开认证和授权。

  • 认证(Authentication)是识别用户的过程,这个过程需要知道这个用户他到底是谁。
  • 授权(Authorization)是识别已认证用户的访问权限的过程,这个过程需要弄清楚他到底有没有针对某个资源的操作(增、删、改、查等等)权限。

接下来我们就来介绍以下kubernetes提供给我们的这套机制是怎么样的。

用户的概念

认证和授权的过程中最重要的一个概念是用户。所以在正式讲解之前,我们需要了解清楚k8s里面的用户的怎样的。

kubernetes有两种用户:一是集群内部的Service Account,二是外部的用户账户

为什么kubernetes中会有service account和用户账户这两种形式呢?回答这个问题之前我们要先搞清楚一个问题:谁发起的请求?回答是:与kubernetes交互的实际上有两类东西,一类是真实的人类,另一类就是程序。真实的人类使用的就是用户账户,而某一个程序访问kubernetes时就需要使用service acocunt了。

用户账户

就是个人用户,比如某个运维人员或外部应用的账户。但是kubernetes没有相应的资源对象或者api来支持常规的个人账户,拥有kubernetes集群的CA证书签名的有效证书,个人用户就可以访问kubernetes集群了,在这种情况下,证书中的Subject(主题)会被api-server解析成为一个用户。

举个例子,X509证书中Subject的内容如下:O = system:masters, CN = kubernetes:admin ,其中,kubernetes:admin会被解析为User,system:masters会被解析为User所在的Group。

Service Account

一个Pod必须要以某一个Service Account的身份去运行。一个Service Account对应着一个Secret,一个Secret保存着一个Token和公钥文件,而这些信息会被挂载到Pod中,我们随意运行一个busybox pod,来查询一下看看:

kubectl exec -it busybox -- ls /var/run/secrets/kubernetes.io/serviceaccount

此时会看到公钥文件和token等信息。

这个token实际上是一个jwttoken。

了解清楚用户的概念后,我们正式开始讲解k8s中的安全机制。

api-server的认证机制

不管是通过外部还是集群内部访问kubernetes集群,api-server都是一个入口,所以,k8s的认证和授权机制几乎都是api-server来实现的。

通过上述对用户的阐述,我们总结如下:任一kubernetes api的访问都是以下三种方式之一:

①、以证书方式访问的普通用户或进程,包括运维人员及kubectl、kubelets等进程;

②、以Service Account方式访问的Kubernetes的内部服务进程;

③、以匿名方式访问的进程。

认证方面,kubernetes提供了如下的认证方式:

①、HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式,比如运维人员操作kubectl时就是采用这种认证的方式,证书信息在$HOME/.kube/config中保存。

②、HTTP Bear Token认证:通过一个Bear Token识别合法用户,比如Service Account就是这种认证方式。我们都知道Service Account是和一个Secret紧密关联的,每一个关联的Secret都保存了一个Token。这个Token就是一个JWT Token。

③、OpenID Connect Token第三方认证:通过第三方OIDC协议进行认证。

④、Webhook Token认证:通过外部Webhook服务进行认证。

⑤、Authentication Proxy认证:通过认证代理进程序行认证。

HTTPS证书认证

https://kubernetes.io/docs/setup/best-practices/certificates/kubernetes.io/docs/setup/best-practices/certificates/

PKI certificates and requirements

PKI certificates and requirementskubernetes.io/docs/setup/best-practices/certificates/

kubernetes需要PKI(public key infrastructure,公钥基础设施)证书来基于TLS的安全的认证。

如果你使用kubeadm来初始化的集群,则kubeadm会帮助你自动生成集群所需要的各类证书。

kubeadm会将证书放置在/etc/kubernetes/pki目录下,而管理员(用户账户)的证书会放置到/etc/kubernetes目录下。

k8s使用x509证书中CN(Common Name)以及O(Organization)字段对应k8s中的user和group,将Authentication和RBAC Authorization结合到了一起,巧妙地将Control Plane中的各个核心User和Group、与操作权限(ClusterRole)进行了绑定(ClusterRoleBinding)。

其实每一个kubectl命令背后都有一个kubeconf文件在支持,关于kubeconf文件的详情,请看这里(我会抽时间将它翻译):

Mastering the KUBECONFIG fileahmet.im/blog/mastering-kubeconfig/

HTTP Bear Token认证Mastering the KUBECONFIG fileHTTP Bear Token认证

这种认证采用的数据结构一般是由JWT Token提供的,一般的形式是在HTTP Authroziation 头部添加Bear xxxxx的形式进行的。

比如Service Account采用的就是这种形式,Token采用的是RS256非对称加密的算法进行加密。在Kubernetes中,每个Bearer Token都对应一个用户名,存储在api-server能访问的一个文件中(Static Token file参见文档)。客户端发起API调用请求时,需要在HTTP Header里放入此Token,这样一来,api-server就能识别合法用户和非法用户了。

要使用这种认证方式,就需要为api-server服务设置一个保存用户信息和Token的文件,通过启动参数--token-auth-file=SOMEFILE指定文件路径。该文件为CSV文本文件格式,每行内容都由以下字段组成:

token,user,uid,[,groupnames]

  • token:必填,Token字符串。
  • user:必填,用户名。
  • uid:必填,用户ID。
  • groupnames:可选,用户组列表,如果有多个组,则必须使用双引号。

OpenID Connect第三方认证

略过

Webhook Token认证

略过

Authentication Proxy认证

略过

api-server授权管理

当用户通过认证(Authentication)后,下一步就是授权(Authorization)了。api-server目前支持以下授权策略:

1、AlwaysDeny:拒绝所有,用于测试。目前这个已经被废弃

2、AlwaysAllow:允许所有,如果集群不需要授权流程,就可以采用该套策略。这个基本不会出现在生产环境。

3、ABAC(Attribute Based Access Control)基于属性的访问控制,它是k8s 1.6之前的默认策略,现在已经被RBAC代替。

4、RBAC(Role Based Access Control)基于角色的访问控制,它是目前(截止到k8s 1.23)默认的授权策略。如果RBAC仍然不满足某些特定需求,则用户还可以自行编写授权逻辑并通过Webhook方式注册为Kubernetes的授权服务,以实现更加复杂的授权规则。

5、Webhook:通过调用外部的REST服务对用户进行授权。

6、Node:是对kubelet进行授权的一种特殊模式。

通过APIServer的启动参数--authorization-mode可配置多种授权策略,用逗号分隔即可。在通常情况下,我们会设置授权策略为Node,RBAC,APIServer在收到请求后,会读取该请求中的数据,生成一个访问策略对象,APIServer会将这个访问策略对象和配置的授权模式逐条进行匹配,第一个被满足或拒绝的授权策略决定了该请求的授权结果,如果匹配的结果是禁止访问,则APIServer会终止API调用流程,并返回客户端的错误调用码。

Node授权策略用于对kubelet发出的请求进行访问控制,与用户的应用授权无关,属于Kubernetes自身安全的增强功能。简单来说,就是限制每个Node只访问它自身运行的Pod及相关的Service、Endpoints等信息;也只能受限于修改自身Node的一些信息,比如Label;也不能操作其他Node上的资源。而之前用RBAC这种通用权限模型其实并不能满足Node这种特殊的安全要求,所以将其剥离出来定义为新的Node授权策略。

RBAC授权策略

因为AlwaysDeny、AlwaysAllow这两个基本不会出现在生产环境,而ABAC已经被RBAC所替代,所以,我们直接从RBAC开始。

RBAC在k8s 1.8版本时升级为GA稳定版本,并作为kubeadm安装方式下的默认选项。

RBAC具有如下优势:

①、对集群中的资源和非资源权限均有完整的覆盖。

②、RBAC的权限配置通过几个API对象即可完成,同其他API对象一样,可以用kubectl或API进行操作。

③、可以在运行时进行调整,无须重新启动api-server。

在RBAC管理体系中,Kubernetes引入了4个资源对象:Role、ClusterRole、RoleBinding和ClusterRoleBinding。同其他API资源对象一样,用户可以使用kubectl或者API调用等方式操作这些资源对象。

角色(Role)和集群角色(ClusterRole)

一个角色就是一组权限的集合,在Role中设置的权限都是许可(Permissive)形式的,不可以设置拒绝(Deny)形式的规则。Role设置的权限将会局限于命名空间(namespace)范围内,如果需要在集群级别设置权限,就需要使用ClusterRole了。

Role示例

下面是一个Role的yaml定义:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
  labels:
    role: pod-reader
  annotation:
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","list","watch"]

上述定义的Role的name是pod-reader,这个Role的权限是可以对所有apigruop下面的资源名为“pod”的资源进行get、list以及watch的操作。但是需要注意的是限定在default的namespace范围内。

Role资源对象主要通过rules字段来描述它的功能,rules字段是一个rule的list,每一个rule包含如下几个关键字段:

apiGroups:api组,比如当我们使用kubectl api-resources来查询集群所支持的api资源时,会发现比如“apps/v1”这样的vesion,它的结构是apiVersion: $GROUP_NAME/$VERSION,所以,这里的api组就是apps。

resouces:和Role绑定的资源名称。

verbs:和Role绑定的动作,比如get、list等。

ClusterRole示例

从命名上来看,ClusterRole的处理范围要比Role大,因为Role的范围是namespace,而ClusterRole的范围是Cluster。

ClusterRole主要适用以下场景:

对集群范围内资源的授权,例如Node。

对非资源型的授权,例如/healthz。

对包含全部namespace资源的授权,例如pods(用于kubectl get pods --all-namespaces这样的操作授权)。

对某个命名空间中多种权限的一次性授权。

下面是一个ClusterRole的yaml定义:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # ClusterRole不受命名空间限制,所以不必绑定namespaces
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get","watch","list"]

上述ClusterRole定义的是一个name为secret-reader,拥有对所有apiGroup下的资源类型为secrets的资源进行get、watch和list的操作。并且他没有限定namespace。

角色绑定(RoleBinding)和集群角色绑定(ClusterRoleBinding)

RoleBinding示例

RoleBinding可以与属于相同命名空间的Role或者某个集群级别的ClusterRole绑定,完成对某个主体的授权。

这里的主体包括用户(User)、组(Group)以及Service Account

下面这个RoleBingding的例子是将上面我们定义的pod-reader这个Role进行了绑定:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
  labels:
    roleBinding: read-pods
  annotation:
subjects:
- kind: User
  name: wallee
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

RoleBinding有两个比较重要的根节点,一个是subjects,描述了需要绑定的主体,有user、group和service account;另一个是roleRef,描述了要绑定的Role。

RoleBinding本身会被namespace所影响,用于某个namespace内的授权,如果它适合Role进行绑定,就需要保持一致的namespace;而RoleBinding除了能够和Role绑定,也能和ClusterRole绑定,这个操作的含义是:对目标主体在其所在的命名空间授予在ClusterRole中定义的权限。如下这个例子展示了这个情况:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rolebinding-with-clusterole
  namespace: development
subjects:
- kind: User
  name: wallee
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io
 

secret-reader这个ClusterRole的定义在前面已给出。虽然secret-reader是一个集群角色,但因为RoleBinding的作用范围为命名空间development,所以用户wallee只能读取命名空间development中的secret资源对象,而不能读取其他命名空间中的secret资源对象。

集群角色(ClusterRoleBinding)绑定示例

ClusterRoleBinding用于进行集群级别或者对所有命名空间都生效的授权。下面的例子允许manager组的用户读取任意命名空间中的secret资源对象:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secret-global
subjects:
- kind: Group
  name: manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

在集群角色绑定(ClusterRoleBinding)中引用的角色只能是集群级别的角色(ClusterRole),而不能是命名空间级别的Role。

一旦通过创建RoleBinding或ClusterRoleBinding与某个Role或ClusterRole完成了绑定,用户就无法修改与之绑定的Role或ClusterRole了。只有删除了RoleBinding或ClusterRoleBinding,才能修改Role或ClusterRole。Kubernetes限制roleRef字段中的内容不可更改,主要有以下两个原因。

从逻辑上来说,与一个新的Role进行绑定实际上是一次全新的授权操作。通过删除或重建的方式更改绑定的Role,可以确保给主体授予新角色的权限(而不是在不验证所有现有主体的情况下去修改roleRef)。

使roleRef不变,可以授予某个用户对现有绑定对象(Bindingobject)的更新(update)权限,以便其管理授权主体(subject),同时禁止更改角色中的权限设置。

RBAC对资源的引用方式

在RBAC中引用资源的方式就是资源对象的名称,如pods、services、deloys等等。但是有一些资源是具有字资源的,例如pod的日志。对于这种资源,RBAC的引用方式是“资源/子资源”。例如pods/log。

此外,默认情况下RBAC引用的资源能够包含所有该资源的实例,如果只是想针对某个资源的某个特定的实例,可以通过resourcesName来指定:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # ClusterRole不受命名空间限制,所以不必绑定namespaces
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceName: "my-secrets"
  verbs: ["update","get"]

resourceName有一个限制,它对list、watch、create或者deletecollection这些操作是无效的。这是因为必须要通过URL进行鉴权,而资源名称在list、watch、create或deletecollection请求中只是请求Body数据的一部分。

ClusterRole的聚合

某些情况下需要多个ClusterRole合并适用,这种情况下适用聚合ClusterRole能够有效的减轻管理员的操作。

这个功能是通过aggregationRule这个字段来完成的。aggregationRole使用LABEL SElECTOR选择多个ClusterRole,由相关的控制器(Controller)来保证能够将多个适合的ClusterRole进行整合。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: clusterrole-with-aggregation
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] #系统自动填充合并的结果
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: clusterole-demo
  labels: 
    rbac.example.com/aggregate-to-monitoring: "true"
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","list","watch"]

clusterrole-with-aggregation会由系统进行控制,形成一个ClusterRole的聚合,我们可以通过kubectl describe clusterrole/clusterrole-with-aggregation来进行验证。

k8s内置了许多的clusterrole,有些clusterrole本身就是聚合的,比如veiw,它本身是一个聚合的clusterrole,然后它又被edit这个clusterrole聚合了。感兴趣的同学可以去看看。

RBAC的其他示例

这里继续罗列两个示例:

1、读取Node资源对象(属于Core Api Group)的信息,由于Node是集群资源对象,所以必须在ClusterRole中定义。并使用ClusterRoleBinding进行绑定:

rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get","list","watch"]

2、允许对非资源类型的/healthz端点(Endpoint)及其所有子路径进行GET和POST操作(必须使用ClusterRole和ClusterRoleBinding):

rules:
- nonResourceURLs: ["/heathz","/healthz/*"]
  verbs: ["get","post"]

授权目标主体(Subject)的命名规范

在RBAC系统中,通过角色绑定(RoleBinding或ClusterRoleBinding)的定义,将在角色(Role或ClusterRole)中设置的授权规则与某个目标主体(Subject)绑定。授权的目标主体可以是用户(User)、用户组(Group)和Service Account三者之一。

用户名由字符串进行标识,例如人名(alice)、Email地址(bob@example.com)、用户ID(1001)等,通常应该在客户端CA证书中进行设置。

需要注意的是,Kubernetes内置了一组系统级别的用户/用户组,以“system:”开头,用户自定义的名称不应该使用这个前缀。

用户组与用户名类似,由字符串进行标识,通常也应该在客户端CA证书中进行设置,并且要求不以“system:”为前缀。

Service Account在Kubernetes系统中的用户名会被设置成以“system:serviceaccount:”为前缀的名称,其所属的组名会被设置成以“system:serviceaccounts:”为前缀的名称。

kubernetes系统默认的授权规则

api-server会创建一组默认的ClusterRole和ClusterRoleBinding。这些通常以system:开头,一般不要对其进行修改。

所有系统默认的ClusterRole和RoleBinding都会用标签”http://kubernetes.io/bootstrapping=rbac-defaults“进行标记。

授权规则的自动恢复(Auto-reconciliation)功能从Kubernetes1.6版本开始引入。该自动恢复功能在启用RBAC授权模式后自动开启。在集群启动时,会检测并更新缺失的默认规则。可以为一个默认的ClusterRole或者ClusterRoleBinding设置annotation“http://rbac.authorization.kubernetes.io/autoupdate=false”来取消自动恢复。

更多的相关细节请参考kubernetes官方文档。

预防权限提升和授权初始化

RBACAPI防止用户通过编辑Role或者RoleBinding获得权限的提升。这一限制是在API级别生效的,因此即使没有启用RBAC,也仍然有效。

1、创建或更新Role或ClusterRole的限制用户要对角色(Role或ClusterRole)进行创建或更新操作,需要满足下列至少一个条件:

(1)用户已拥有Role中包含的所有权限,且与该角色的生效范围一致(如果是集群角色,则是集群范围;如果是普通角色,则可能是同一个命名空间或者整个集群)。

(2)用户被显式授予针对Role或ClusterRole资源的提权(Escalate)操作权限。

例如,用户user-1没有列出集群中所有Secret资源的权限,就不能创建具有这一权限的集群角色。要让一个用户能够创建或更新角色,则需要:

(1)为其授予一个允许创建或更新Role或ClusterRole资源对象的角色。

(2)为其授予允许创建或更新角色的权限,有隐式和显式两种方法。

①隐式:为用户授予这些权限。用户如果尝试使用尚未被授予的权限来创建或修改Role或ClusterRole,则该API请求将被禁止。

②显式:为用户显式授予rbac.authorization.k8s.ioAPIGroup中的Role或ClusterRole的提权(Escalate)操作权限。

2、创建或更新RoleBinding或ClusterRoleBinding的限制仅当我们已经拥有被引用的角色(Role或ClusterRole)中包含的所有权限(与角色绑定的作用域相同)或已被授权对被引用的角色执行绑定(bind)操作时,才能创建或更新角色绑定(RoleBinding或ClusterRoleBinding)。

例如,如果用户user-1没有列出集群中所有Secret资源的权限,就无法为一个具有这样权限的角色创建ClusterRoleBinding。要使用户能够创建或更新角色绑定,则需要进行以下操作。

(1)为其授予一个允许创建和更新RoleBinding或ClusterRoleBinding的角色。

(2)为其授予绑定特定角色的权限,有隐式或显式两种方法。

①隐式:授予其该角色中的所有权限。

②显式:授予在特定角色或集群角色中执行绑定(bind)操作的权限。

例如,通过下面的ClusterRole和RoleBinding设置,将允许用户user-1为其他用户在user-1-namespace命名空间中授予admin、edit及view角色的权限:

apiVersion: rbac.authorization.k8s.io/v1
Kind: ClusterRole
metadata:
  name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterroles"]
  verbs: ["bind"]
  resourceNames: ["edit","admin","view"]
  ---
  apiVersion: rbac.authorization.k8s.io
  kind: RoleBinding
  metadata:
    name: role-grantor-binding
    namespace: user-1-namespace
  subjects:
  - kind: User
    name: user-1
    apiGroup: rbac.authorization.k8s.io
  roleRef:
    kind: ClusterRole
    name: role-grantor
    apiGroup: rbac.authorization.k8s.io

在系统初始化过程中启用第1个角色和角色绑定时,必须让初始用户具备其尚未被授予的权限。要进行初始的角色和角色绑定设置,有以下两种办法。

(1)使用属于system:masters组的凭据,这个组默认具有cluster-admin这个超级用户的权限。

(2)如果APIServer以--insecure-port参数运行,则客户端通过这个非安全端口进行接口调用,通过这个非安全端口的访问没有认证鉴权的限制。

对Service Account的授权管理

稍后补充

Node授权模式详解

$ kubectl describe clusterrole/system:node
Name:         system:node
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources                                       Non-Resource URLs  Resource Names  Verbs
  ---------                                       -----------------  --------------  -----
  leases.coordination.k8s.io                      []                 []              [create delete get patch update]
  csinodes.storage.k8s.io                         []                 []              [create delete get patch update]
  nodes                                           []                 []              [create get list watch patch update]
  certificatesigningrequests.certificates.k8s.io  []                 []              [create get list watch]
  events                                          []                 []              [create patch update]
  pods/eviction                                   []                 []              [create]
  serviceaccounts/token                           []                 []              [create]
  tokenreviews.authentication.k8s.io              []                 []              [create]
  localsubjectaccessreviews.authorization.k8s.io  []                 []              [create]
  subjectaccessreviews.authorization.k8s.io       []                 []              [create]
  pods                                            []                 []              [get list watch create delete]
  configmaps                                      []                 []              [get list watch]
  secrets                                         []                 []              [get list watch]
  services                                        []                 []              [get list watch]
  runtimeclasses.node.k8s.io                      []                 []              [get list watch]
  csidrivers.storage.k8s.io                       []                 []              [get list watch]
  persistentvolumeclaims/status                   []                 []              [get patch update]
  endpoints                                       []                 []              [get]
  persistentvolumeclaims                          []                 []              [get]
  persistentvolumes                               []                 []              [get]
  volumeattachments.storage.k8s.io                []                 []              [get]
  nodes/status                                    []                 []              [patch update]
  pods/status                                     []                 []              [patch update]

Admission Control

突破了之前所说的认证和鉴权两道关卡之后,客户端的调用请求就能够得到APIServer的真正响应了吗?答案是:不能!这个请求还要通过AdmissionControl(准入控制)所控制的一个准入控制链的层层考验,才能获得成功的响应。Kubernetes官方标准的“关卡”有30多个,还允许用户自定义扩展。

AdmissionControl配备了一个准入控制器的插件列表,发送给APIServer的任何请求都需要通过列表中每个准入控制器的检查,检查不通过,APIServer就会拒绝此调用请求。此外,准入控制器插件能够修改请求参数以完成一些自动化任务,比如Service Account这个控制器插件。

这里就不单独介绍每种Admission Control插件了。有兴趣的同学请翻阅官方文档。

在api-server上设置参数即可定制我们需要的准入控制链,如果启用了多种准入控制选项,则建议这样设置:在Kubernetes1.9及之前的版本中使用的参数是--admission-control,其中的内容是顺序相关的;在Kubernetes1.10及之后的版本中使用的参数是--enable-admission-plugins,并且与顺序无关。

除了静态编译的Admission插件,也可以通过Webhook方式对接外部的AdmissionWebhook服务,实现与Admission插件一样的功能。但Webhook方式更加灵活,能够在api-server运行时修改和配置动态更新控制策略。

不过,相对于Admission Control插件来说,使用Admission Webhook要复杂得多,除了需要开发一个Admission Webhook Server实现HTTP回调的逻辑,还需要创建一个对应的ValidatingWebhookConfiguration资源对象配置文件,如果Admission Webhook需要与api-server进行认证,则还需要创建对应的AdmissionConfiguration配置文件。

Service Account

ServiceAccount也是一种账号,但它并不是给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/http://kubernetes.io/serviceaccount/token),该Token是动态生成的,确切地说,是由Kubernetes Controller进程用api-server的私钥(--service-account-private-key-file指定的私钥)签名生成的一个JWT Token。

在官方提供的客户端REST框架代码里,通过HTTPS方式与api-server建立连接后,会用Pod里指定路径下的一个CA证书(/run/secrets/http://kubernetes.io/serviceaccount/ca.crt)验证api-server发来的证书,验证是否为CA证书签名的合法证书。

api-server在收到这个Token以后,会采用自己的私钥(实际上是使用service-account-key-file参数指定的私钥,如果没有设置此参数,则默认采用tls-private-key-file指定的参数,即自己的私钥)对Token进行合法性验证。

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

/run/secrets/http://kubernetes.io/serviceaccount/token。

/run/secrets/http://kubernetes.io/serviceaccount/ca.crt。

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

这三个文件由于参与到Pod进程与api-server认证的过程中,起到了类似secret(私密凭据)的作用,所以被称为Kubernetes Secret对象。Secret从属于Service Account资源对象,属于Service Account的一部分,在一个Service Account对象里面可以包括多个不同的Secret对象,分别用于不同目的的认证活动。

下面通过运行一些命令来加深我们对Service Account与Secret的直观认识。

首先,查看系统中的ServiceAccount对象,看到有一个名为default的Service Account对象,包含一个名为default-token-8flhf的Secret,这个Secret同时是Mountable secrets,表明它是需要被挂载到Pod上的:

$ kubectl describe sa/default
Name:                default
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   default-token-8flhf
Tokens:              default-token-8flhf
Events:              <none>

接下来看看default-token-8flhf这个secret有什么内容:

Name:         default-token-8flhf
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 23278255-5020-4ddf-a0fa-81e0d0458c41

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1099 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6InBGR3phb0lpWDhIRGR4SFF3RjQyX......................

结论就是:在每个命名空间中都有一个名为default的默认Service Account对象,在这个Service Account里面有一个名为Tokens的可以作为Volume被挂载到Pod里的Secret,Pod启动时,这个Secret会自动被挂载到Pod的指定目录下,用来协助完成Pod中的进程访问api-server时的身份鉴权。

另外,一个Service Account可以包含多个secret:

service account可以和多个secret关联

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

如果一个Pod在定义时没有指定spec.serviceAccountName属性,则系统会自动将其赋值为default,即大家都使用同一个命名空间中的默认Service Account。如果某个Pod需要使用非default的Service Account,则需要在定义时指定:

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
  labels:
    name: busybox
    app: busybox
  annotation:
spec:
  containers:
  - name: busybox
    image: busybox:1.28
    command:
    - sleep
    - "3600"
  serviceAccountName: "non-default"

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

Service Account的正常工作离不开以下控制器:Service Account Controller、Token Controller、Admission Controller。

Service Account Controller的工作相对简单,它会监听Service Account和Namespace这两种资源对象的事件,如果在一个Namespace中没有默认的Service Account,那么它会为该Namespace创建一个默认的ServiceAccount对象,这就是在每个Namespace下都有一个名为default的Service Account的原因。

Token Controller也监听Service Account的事件,如果发现在新建的Service Account里没有对应的Service Account Secret,则会用APIServer私钥(--service-account-private-key-file指定的文件)创建一个Token,并用该Token、api-server的CA证书等三个信息产生一个新的Secret对象,然后放入刚才的Service Account中。如果监听到的事件是Service Account删除事件,则自动删除与该Service Account相关的所有Secret。此外,Token Controller对象也会同时监听Secret的创建和删除事件,确保与对应的Service Account的关联关系正确。

接下来就是Admission Controller的重要作用了,当我们在api-server的准入控制链中启用了Service Account类型的准入控制器时(这也是默认的设置),则针对Pod新增或修改的请求,Admission Controller会验证Pod里的Service Account是否合法,并做出如下控制操作:

如果spec.serviceAccount域没有被设置,则Kubernetes默认为其指定名称为default的Serviceaccout。

如果Pod的spec.serviceAccount域指定了不存在的Service Account,则该Pod操作会被拒绝。

如果在Pod中没有指定ImagePullSecrets,那么这个spec.serviceAccount域指定的ServiceAccount的ImagePullSecrets会被加入该Pod中。

给Pod添加一个特殊的volumeSource,在该Volume中包含ServiceAccountSecret中的Token。

给Pod里的每个容器都增加对应的VolumeSource,将包含Secret的Volume挂载到Pod中所有容器的指定目录下(/var/run/secrets/http://kubernetes.io/serviceaccount)。

在Kubernetes1.6版本以后,我们可以禁止自动创建ServiceAccount对应的Secret了,在ServiceAccount的YAML文件中增加automountServiceAccountToken:false属性即可,同时可以在某个Pod的YAML文件中增加此属性,以实现同样的效果。

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: