本文永久链接: https://www.xtplayer.cn/kubernetes/custom-serviceaccount/

Service account 主要是为了方便 Pod 中的进程调用 Kubernetes API 而设计的,为服务提供了一种方便的认证机制。但它不提供授权,需要配合 RBAC 来为 Service Account 鉴权。

用户帐户与服务帐户

Kubernetes 出于多种原因区分用户帐户和服务帐户的概念:

  • 用户帐户是为人类使用的,服务帐户用于在 Pods 中运行的进程。
  • 用户帐户是全局的,其名称在集群的所有命名空间中必须是唯一的,将来的用户资源将不会被命名空间。服务帐户有命名空间。
  • 通常,集群的用户帐户可能是从公司数据库同步的。在公司数据库中,新用户帐户的创建需要特殊的权限,并且与复杂的业务流程相关联。服务帐户的创建旨在更加轻量级,从而允许集群用户为特定任务(即最小特权原则)创建服务帐户。
  • 用户账户和服务帐户的审核注意事项可能有所不同。
  • 复杂系统的配置可以包括该系统组件的各种服务帐户的定义。因为可以临时创建服务帐户并使用命名空间名称,所以这种配置是可移植的。

服务帐户自动化

Kubernetes 通过三个独立组件的协作实现了服务帐户自动化控制:

  • 服务帐户准入控制器
  • 令牌控制器
  • 服务帐户控制器

服务帐户准入控制器

Pod 的修改是通过名为 Admission Controller 的插件实现的。它是 apiserver 的一部分,它在创建或更新 Pods 时同步地对其进行修改。当这个插件被激活时(在大多数发行版上它是默认的),当一个 Pod 被创建或修改时,它会执行以下操作:

  1. 如果 Pod 没有设置 ServiceAccount,它将 ServiceAccount 设置为 默认值
  2. 它确保 Pod 引用的 ServiceAccount 存在,否则将拒绝创建 Pod。
  3. 如果 Pod 不包含任何 ImagePullSecrets,则 ServiceAccountImagePullSecrets 被添加到 Pod 中。
  4. 它向 Pod 添加一个卷,其中包含用于 API 访问的令牌。
  5. 它向 Pod 的每个容器添加一个 volumeSource,并挂载在 /var/run/secrets/kubernet.io/serviceaccount 上。

从 v1.13 开始,当启用了 BoundServiceAccountTokenVolume 功能时,您可以将服务帐户卷迁移到 Projected Volume 。服务帐户令牌将在 1 小时后,或者 pod 被删除后过期。查看有关Projected Volume更多详细信息。

令牌控制器

Token Controller 作为控制管理器的一部分,它是异步运行。它:

  • 监听 serviceAccount 创建并创建一个对应的 Secret 以允许访问 API
  • 监听 serviceAccount 删除并删除所有相应的 ServiceAccountToken Secrets
  • 监听 Secrets 添加,并确保引用的 ServiceAccount 存在,并在需要时向该 Secrets 添加令牌。
  • 监听 Secrets 删除,并在需要时从相应的 ServiceAccount 中删除引用。

必须使用 --service-account-private-key-file 选项将 服务帐户 的私钥文件传递给控制管理器中的令牌控制器,私钥将用于对生成的服务帐户令牌进行签名。同样,您必须使用 --service-account-key-file 选项将相应的公钥传递给 kube-apiserver ,公钥将在身份验证期间用于验证令牌。

创建额外的 API 令牌

控制器循环确保每个服务帐户都存在一个带有 API 令牌的 Secrets。要为服务帐户创建额外的 API 令牌,需要创建一个类型为ServiceAccountTokensecret,并带有引用服务帐户的 annotation ,控制器将用生成的令牌更新它:

secret.json

{
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"name": "mysecretname",
"annotations": {
"kubernetes.io/service-account.name": "myserviceaccount"
}
},
"type": "kubernetes.io/service-account-token"
}
kubectl create -f ./secret.json
kubectl describe secret mysecretname

删除服务帐户令牌

kubectl delete secret mysecretname

服务账户控制器

服务帐户控制器管理命名空间内的 ServiceAccount,并确保每个活动命名空间中都存在一个名为 defaultServiceAccount

默认的 Service account

默认情况下,创建 Pod 时,如果未指定 Service account ,则会在同一命名空间中自动为其分配名为 default 的服务帐户。查看 Pod 的原始 jsonyaml(例如: kubectl get pods/<podname> -o yaml),则可以看到 spec.serviceAccountName 字段被自动设置为 default

这个 default Service account 由 K8S 自动创建,默认没有绑定任何权限。

priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300

创建自定义 ServiceAccount

有两种方法创建 ServiceAccount

  1. 通过 yaml 文件创建 ServiceAccount

    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: <ServiceAccount-name>
    namespace: <ServiceAccount-namespace>

    保存以上内容为 ServiceAccount.yaml,然后运行以下命令创建 ServiceAccount:

    kubectl apply -f ServiceAccount.yaml
  2. 直接通过 kubectl 命令行创建

    namespace_name=xxx
    serviceaccount_name=xxx

    kubectl create serviceaccount --namespace=${namespace_name} ${serviceaccount_name}

服务账户权限控制

前文已说到服务账户本身不提供授权功能,如果要通过 ServiceAccount 去访问集群中某些资源,那么需要通过额外的授权插件和策略来实现授权。常用的授权模式,比如:基于角色(Role)的访问控制(RBAC)

基于角色(Role)的访问控制(RBAC)是一种基于组织中各个用户的角色来调节对计算机或网络资源的访问的方法。RBAC 授权使用 rbac.authorization.k8s.io API Groups 来驱动授权决策,允许您通过 Kubernetes API 动态配置策略。

Role 和 ClusterRole

RBAC Role 或 ClusterRole 包含一组权限规则,权限可以累加。只有允许规则,没有 禁止 规则,默认全部禁止

  • Role 具有命名空间的限制,当您创建一个角色,你必须指定所属的命名空间。一个 Role 只可以用来对某一命名空间中的资源赋予访问权限。
  • 相比之下,ClusterRole 是一种没有命名空间限制的资源。ClusterRole 可以授予的权限和 Role 相同, 但是因为 ClusterRole 属于集群范围,所以它也可以授予以下访问权限:
    • 集群范围资源 (比如 nodes
    • 非资源端点(比如 “/healthz“)
    • 跨命名空间访问的有名字空间作用域的资源(如 Pods),比如运行命令 kubectl get pods --all-namespaces 时需要此能力

Role 示例

这是 default 命名空间的示例 Role,可用于授予对 Pods 的读取访问权限。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]

ClusterRole 示例

这是一个 ClusterRole 示例,可用于授予对以下内容的读取访问权限: secrets 在任何特定的命名空间中,或所有命名空间中。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: secret-reader
rules:
- apiGroups: [""]
#
# at the HTTP level, the name of the resource for accessing Secret
# objects is "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]

RoleBinding 和 ClusterRoleBinding

RoleBinding 将角色中定义的权限授予一个用户或一组用户。它包含一个主题列表(用户、组或服务帐户)和一个对被授予角色的引用。RoleBinding 授予特定命名空间中的权限,而 ClusterRoleBinding 则在整个集群范围内授予访问权限。

RoleBinding 可以引用同一命名空间中的任意 Role,也可以将 ClusterRole 绑定到 RoleBinding 的命名空间。

RoleBinding 示例

以下是 RoleBinding 示例,该示例将 pod-reader 角色授予 default 命名空间中的用户 jane。这允许 jane 读取 default 命名空间中的Pod

apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
name: jane # "name" is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" specifies the binding to a Role / ClusterRole
kind: Role #this must be Role or ClusterRole
name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io

RoleBinding 还可以引用 ClusterRole,以将该 ClusterRole 中定义的权限授予 RoleBinding 命名空间内的资源。这种引用允许您在集群中定义一组通用角色,然后在多个命名空间中重用它们。

例如以下的例子,即使以下 RoleBinding 引用了 ClusterRoledave(名称区分大小写)也只能读取 development 命名空间中的Secrets,因为 RoleBinding 的命名空间(在 metadata 中)是 developmentRoleBinding 具有命名空间限制。

apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "dave" to read secrets in the "development" namespace.
# You need to already have a ClusterRole named "secret-reader".
kind: RoleBinding
metadata:
name: read-secrets
# The namespace of the RoleBinding determines where the permissions are granted.
# This only grants permissions within the "development" namespace.
namespace: development
subjects:
- kind: User
name: dave # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding 示例

要在整个集群上授予权限,可以使用 ClusterRoleBinding。以下 ClusterRoleBinding 允许 manager 组(Group)中的任意用户读取任意命名空间中的 secret。

apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

更多的 rbac 使用方法,请参考:https://kubernetes.io/docs/reference/access-authn-authz/rbac/

参考链接

1.service-accounts-admin
2.configure-service-account
3.access-authn-authz-rbac