请选择 进入手机版 | 继续访问电脑版
MSIPO技术圈 首页 IT技术 查看内容

【kubernetes系列】Kubernetes之ServiceAccount

2023-07-13

概述

Service Account是什么呢,顾名思义,服务账号,一种给服务使用的账号,它不是给Kubernetes的集群的用户(系统管理员、运维人员、租户用户等)使用,而是给运行在Pod里的进程用的,它为Pod中的进程提供必要的身份证明。

当用户访问集群(例如使用kubectl命令)时,apiserver会将用户认证为一个特定的User Account(目前通常是admin,除非系统管理员自定义了集群配置)。Pod 容器中的进程也可以与apiserver联系,但Pod 中访问 Kubernetes API Server 服务的时候,是以 Service 方式访问服务名为 kubernetes 这个服务的,而kubernetes服务只在 HTTPS 安全端口 443 上提供服务,那么如何进行身份认证呢?其实它们之间是通过一个特定的Service Account(例如default)进行认证访问。

这是在用一种类似HTTP Token的新的认证方式–ServiceAccount Auth,Pod中的服务调用Kubernetes API的时候,在HTTP Header中传递了一个 Token 字符串,这类似于之前提到的HTTP Token认证方式,存在以下几个不同点:

  • 该Token(/run/secrets/kubernetes.io/serviceaccount/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对象,分别用于不同目的的认证活动。

user account 和service account的区别:

  • User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API而设计;
  • User account是跨namespace的,而service account则是仅局限它所在的namespace;
  • 每个namespace都会自动创建一个default service account
  • Token controller检测service account的创建,并为它们创建secret
  • 开启ServiceAccount Admission Controller后
    • 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非单独指定使用其他ServiceAccout)
    • 验证Pod引用的service account已经存在,否则拒绝创建
    • 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
    • 每个container启动后都会挂载该Service Account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/目录下

分析系统自动生成的serviceaccount

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

[root@k8s-m1 pki]# kubectl describe sa default 
Name:                default
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   default-token-glxls
Tokens:              default-token-glxls
Events:              <none>kubectl describe serviceaccounts
[root@k8s-m1 pki]# kubectl describe secrets default-token-glxls 

default-token-xxx包括三个数据项依次为:
ca.crt
namespace
token

而查看每个命名空间下任意一个没有具体指明使用其他serviceAccount的pod,如下

[root@k8s-m1 pki]# kubectl  get pod  my-nginx-7ff446c4f4-2d9gz  -o yaml|grep -i serviceaccount
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
  serviceAccount: default
  serviceAccountName: default

[root@k8s-m1 pki]# kubectl  get pod  my-nginx-7ff446c4f4-2d9gz  -o yaml|grep -i token
      name: default-token-glxls
  - name: default-token-glxls
      secretName: default-token-glxls

[root@k8s-m1 dashboard]# kubectl exec -it my-nginx-7ff446c4f4-2d9gz -- /bin/bash

root@my-nginx-7ff446c4f4-2d9gz:/# ls /var/run/secrets/kubernetes.io/serviceaccount/ -l
total 0
lrwxrwxrwx 1 root root 13 Jul  1 01:31 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Jul  1 01:31 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Jul  1 01:31 token -> ..data/token
root@my-nginx-7ff446c4f4-2d9gz:/# 

可以发现使用的 serviceAccount是default,token也是使用的名为defaul-token-***的token,且目录/var/run/secrets/kubernetes.io/serviceaccount/下有我们上面在default-token里面看到的三个数据项(ca.crt、namespace、token)。综上:每个namespace下有一个名为default的默认的ServiceAccount对象,这个ServiceAccount里有一个名为Tokens的可以作作为Volume一样被Mount到Pod里的Secret,当Pod启动时这个Secret会被自动Mount到Pod的指定目录下,可以使用的secret的相关信息让当前名称空间中所有的pod连接至apiserver,从而保证pod与apiserver之间的通信。

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

  • 名为Tokens的Secret用于访问API Server的Secret,也被称为ServiceAccountSecret;

  • 名为Image Pull secrets的Secret用于下载容器镜像时的认证过程,通常镜像库运行在Insecure模式下,所以这个Secret为空;

用户自定义的其他ServiceAccount,用于权限设置

默认的service account 仅仅只能获取当前Pod自身的相关属性,无法观察到其他名称空间Pod的相关属性信息。如果想要扩展Pod,假设有一个Pod需要用于管理其他Pod或者是其他资源对象(例如dashboard),是无法通过自身的名称空间的default service account进行获取其他Pod的相关属性信息的,此时就需要进行手动创建一个serviceaccount,并在创建Pod时进行定义使用。如下在部署kubernetes-dashboard时我们一般就创建了一个叫做kubernetes-dashboard的Service Account,同时还创建了一个与之对应的secret。

  ......
  containers:
    - name: dashboard-metrics-scraper
      image: kubernetesui/metrics-scraper:v1.0.4
      ports:
        - containerPort: 8000
          protocol: TCP
      livenessProbe:
        httpGet:
          scheme: HTTP
          path: /
          port: 8000
        initialDelaySeconds: 30
        timeoutSeconds: 30
      volumeMounts:
      - mountPath: /tmp
        name: tmp-volume
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsUser: 1001
        runAsGroup: 2001
  serviceAccountName: kubernetes-dashboard
.....

service account的创建

可以通过kubectl explain sa查看serviceAccount的yaml文件格式。

#yaml文件
[root@k8s-m1 k8s-serviceaccount]# cat serviceaccount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin
  namespace: default
#创建
[root@k8s-m1 k8s-serviceaccount]# kubectl apply  -f serviceaccount.yaml 
serviceaccount/admin created
#查看
[root@k8s-m1 k8s-serviceaccount]# kubectl get sa admin 
NAME    SECRETS   AGE
admin   1         21s
[root@k8s-m1 k8s-serviceaccount]# kubectl get secrets 
NAME                                  TYPE                                  DATA   AGE
admin-token-r7w27                     kubernetes.io/service-account-token   3      36s


#查看详细信息
[root@k8s-m1 k8s-serviceaccount]# kubectl get sa admin -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"admin","namespace":"default"}}
  creationTimestamp: "2023-07-12T03:22:21Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:secrets:
        .: {}
        k:{"name":"admin-token-r7w27"}:
          .: {}
          f:name: {}
    manager: kube-controller-manager
    operation: Update
    time: "2023-07-12T03:22:21Z"
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2023-07-12T03:22:21Z"
  name: admin
  namespace: default
  resourceVersion: "86037692"
  selfLink: /api/v1/namespaces/default/serviceaccounts/admin
  uid: d4568461-5942-433b-a1c5-228e1b2e8125
secrets:
- name: admin-token-r7w27
#可以看到secret就是上面看到的admin开头的secret

可以发现有一个 secret已经被自动创建(其实secret也可以自己手动创建,可参考部署dashboard时使用的yaml文件),使用时需要在 pod 的spec.serviceAccountName 字段中将name设置为您想要用的 service account 名字即可。在 pod 创建之初 service account 就必须已经存在,否则创建将被拒绝。并且需要注意的是不能更新已创建的 pod 的 service account。

serviceaccount的引用

上面在default名称空间创建了一个serviceaccount为admin,可以看到已经自动生成了一个secret(包含token信息) admin-token-r7w27,下面展示如何使用自定义的serviceaccount

[root@k8s-m1 k8s-serviceaccount]# cat use-sa-pod.yml 
apiVersion: v1
kind: Pod
metadata:
  name: use-sa-pod
  namespace: default
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - name: http
      containerPort: 80
  serviceAccountName: admin

[root@k8s-m1 k8s-serviceaccount]# kubectl apply -f use-sa-pod.yml 
pod/use-sa-pod created

[root@k8s-m1 k8s-serviceaccount]# kubectl describe po use-sa-pod 
......
Volumes:
  admin-token-r7w27:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  admin-token-r7w27
    Optional:    false
......

User Account的定义及使用

在K8S集群当中,每一个用户对资源的访问都是需要通过apiserver进行通信认证才能进行访问的,那么在此机制当中,对资源的访问凭证可以是用token保存,也可以是通过配置文件的方式进行保存,kubectl命令行工具使用kubeconfig文件来查找选择群集并与群集的APIserver进行通信。可以通过kubectl config进行查看编辑kubeconfig配置文件,配置文件路径$HOME/.kube/config文件,eg:/root/.kube/config 如下:

[root@k8s-m1 k8s-serviceaccount]# kubectl config view 
apiVersion: v1
clusters:  #集群列表
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://192.168.2.250:8443
  name: kubernetes
contexts: #上下文列表
- context: #定义哪个集群被哪个用户访问
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
- context:
    cluster: kubernetes
    user: margu
  name: margu@kubernetes
current-context: kubernetes-admin@kubernetes #当前上下文
kind: Config
preferences: {}
users:   #用户列表
- name: kubernetes-admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
- name: margu
  user:
    client-certificate-data: REDACTED

在上面的配置文件当中,定义了集群、上下文以及用户。其中Config也是K8S的标准资源之一,在该配置文件当中定义了一个集群列表,指定的集群可以有多个;用户列表也可以有多个,指明集群中的用户;而在上下文列表当中,是进行定义可以使用哪个用户对哪个集群进行访问,以及当前使用的上下文是什么。如图:定义了用户kubernetes-admin可以对kubernetes该集群的访问,用户kubernetes-user1对Cluster1集群的访问。
在这里插入图片描述

自建证书和账号进行访问apiserver演示

可用于对不同群体对集群的访问权限控制。过程请参考https://blog.csdn.net/margu_168/article/details/131564406

关于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的启动参数中包括下面的内容时(kubeadm部署的):

–enable-admission-plugins=ServiceAccount

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

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

所以,通过上面的介绍我们知道ServiceAccount正常运行需要以下几个控制器:
Admission Controller
Service Account Controller
Token Controller

更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出

相关阅读

热门文章

    手机版|MSIPO技术圈 皖ICP备19022944号-2

    Copyright © 2024, msipo.com

    返回顶部