Skip to content

Latest commit

 

History

History
401 lines (358 loc) · 13.6 KB

cluster_serviceaccount.md

File metadata and controls

401 lines (358 loc) · 13.6 KB

ServiceAccount 概述

ServiceAccount用于表示非人类用户(例如Pod等)的一种身份标识。ServiceAccount的格式样例如下:

system:serviceaccount:<namespace>:<service account name>

每一个namespace都有一个名为default的默认ServiceAccount(创建namespace时候自动创建):

$ kubectl -n default get serviceaccounts
NAME      SECRETS   AGE
default   0         36d

Pod可以被显示指定某一个ServiceAccount。如果不显示指定,则Pod使用当前namespace下的default默认ServiceAccount。 通过不同的ServiceAccount,可以实现Pod访问集群资源权限的精细控制。

每一个Pod只能使用相同namespace下的某一个ServiceAccount,不能使用其他namespace下的ServiceAccount

可以使用kubectl create serviceaccount <serviceaccount name> 命令创建新的ServiceAccount

$ kubectl -n default create serviceaccount foo
serviceaccount/foo created

# 查看创建的 foo ServiceAccount
$ kubectl -n default get serviceaccounts foo
NAME   SECRETS   AGE
foo    0         16s

# 查看 ServiceAccount 详细信息
$ kubectl -n default describe serviceaccounts foo
Name:                foo
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

新版本的k8s默认不会创建默认的secretsServiceAccount绑定。所以看到Mountable secrets字段和Tokens字段为<none>,可以创建单独secrets对象进行关联。

默认情况下,Pod可以挂载任意的Secret对象。但如果一个ServiceAccount的注解有:

kubernetes.io/enforce-mountable-secrets="true"

则使用此ServiceAccountPod只能挂载Mountable secrets指定的Secret对象。

ServiceAccount中有Image pull secrets字段,用于自动添加到使用此ServiceAccountPod中。这样的好处就是不需要每个Pod都需要单独添加imagePullSecrets字段。

Pod的模版定义中,可以通过spec.serviceAccountName字段指定Pod使用的ServiceAccount,例如:

apiVersion: v1
kind: Pod
metadata:
  name: curl-custom-sa
spec:
  # 指定服务账户
  serviceAccountName: foo
  containers:
  - name: main
    image: tutum/curl
    command: ["sleep", "9999999"]
  - name: ambassador
    image: luksa/kubectl-proxy:1.6.2

ServiceAccount的目的是用于权限控制,而k8s集群的权限的校验是通过RBAC(基于角色的访问控制)。

基于角色的访问控制概述

RBAC校验相关的有四种资源,分为两类:

  • RolesClusterRoles:决定在某种资源上可以执行的动作(what)。
  • RoleBindingsClusterRoleBindings:将上面的Roles绑定到具体的用户,组或者ServiceAccountwho)。

其中RolesRoleBindings表示命名空间的资源,ClusterRolesClusterRoleBindings表示集群层面资源。

下图总结了rolesrolesbindings大致关系:

rbac

使用 Roles 和 RoleBindings

运行一个Pod,并在Pod中访问Api Server组件以验证RBAC工作机制。

# 创建一个新的命名空间
$ kubectl create namespace foo
namespace/foo created

# 在新的 foo 命名空间运行 kubectl-proxy Pod
$ kubectl -n foo run test --image=luksa/kubectl-proxy
pod/test created

$ kubectl -n foo get pods
NAME   READY   STATUS    RESTARTS   AGE
test   1/1     Running   0          25s

# 在 test Pod 中访问 api server
$ kubectl -n foo exec -it test -- sh
/ # curl 127.0.0.1:8001/api/v1/namespaces/default/services
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "services is forbidden: User \"system:serviceaccount:foo:default\" cannot list resource \"services\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "kind": "services"
  },
  "code": 403
}/ #

可以看到在命名空间foo中,默认的defaultServiceAccount没有权限list相同namespace下的Service资源。

为了解决上述的权限问题,首先定义一个Role资源。

# 文件名是 service_reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: foo  # 指定 Role 资源所在的命名空间
  name: service-reader
rules:
  - apiGroups: [""] # "" 表示 core apiGroup,“*” 表示所有 API 组
    verbs: ["get", "list"]  # 给 get 和 list 权限
    resources: ["services"] # 操作的资源类型

定义的Role资源针对foo命名空间下的Service资源,允许做getlist操作。下图显示了Role资源操作权限示意图:

role权限

将上述定义的Role资源部署到集群:

$ kubectl -n foo apply -f service_reader.yaml
role.rbac.authorization.k8s.io/service-reader created

# 查看 Role 资源信息
$ kubectl -n foo get roles.rbac.authorization.k8s.io service-reader
NAME             CREATED AT
service-reader   2024-10-25T08:45:13Z

$ kubectl -n foo describe roles.rbac.authorization.k8s.io service-reader
Name:         service-reader
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  services   []                 []              [get list]

下一步,需要将创建的Role资源绑定到foo命名空间的名为defaultServiceAccount,也就是Pod默认使用的ServiceAccount

$ kubectl -n foo create rolebinding test --role=service-reader --serviceaccount=foo:default
rolebinding.rbac.authorization.k8s.io/test created

# 查看 rolebinding
$ kubectl -n foo get rolebindings.rbac.authorization.k8s.io test
NAME   ROLE                  AGE
test   Role/service-reader   4s

$ kubectl -n foo describe rolebindings.rbac.authorization.k8s.io test
Name:         test
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  Role
  Name:  service-reader
Subjects:
  Kind            Name     Namespace
  ----            ----     ---------
  ServiceAccount  default  foo

绑定完成后结果示意图如下:

绑定role

查看RoleBindings资源的yaml信息如下:

$ kubectl -n foo get rolebindings.rbac.authorization.k8s.io test -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  creationTimestamp: "2024-10-25T09:14:33Z"
  name: test
  namespace: foo
  resourceVersion: "727762"
  uid: d8f8ba8b-46d9-49db-955c-f0f266969990
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: service-reader
subjects:
- kind: ServiceAccount
  name: default
  namespace: foo

其中roleRef属性表示单个需要绑定的Role资源对象。subjects表示要绑定的用户,可以是多个

最后在Pod中重新访问api server

$ kubectl -n foo exec -it test -- sh
/ # curl 127.0.0.1:8001/api/v1/namespaces/foo/services
{
  "kind": "ServiceList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "729031"
  },
  "items": []
}/

可以看到此时Pod可以成功访问相同命名空间下的Service资源。

一个命名空间下的Role资源也可以被绑定到另一个命名空间下的ServiceAccount,或其他Subjects。示意说明如下:

rolebinding用于多命名空间

可以更改foo命名空间下的RoleBindings资源:

$ kubectl edit rolebinding test -n foo

# 添加如下内容
subjects:
- kind: ServiceAccount
 name: default
 namespace: bar

使用 ClusterRoles 和 ClusterRoleBindings

  • ClusterRolesClusterRoleBindings是集群层面的资源。针对某些没有namespace概念的资源(比如NodesPersistentVolumesNamespaces等) 以及某些URL没有对应的具体资源(例如/healthz),不能使用基于namespaceRoleBindingsRoles赋予权限。

  • 而且使用ClusterRoleBindingsClusterRoles资源,可以在所有namespace共享,不需要创建多份。

首先Pod访问PersistentVolumes资源(集群层面资源,不像Pod属于命名空间的资源):

$ kubectl -n foo exec -it test -- sh
/ # curl 127.0.0.1:8001/api/v1/persistentvolumes
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "persistentvolumes is forbidden: User \"system:serviceaccount:foo:default\" cannot list resource \"persistentvolumes\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "persistentvolumes"
  },
  "code": 403
}

可以看到,默认的名为defaultServiceAccount没有权限访问PersistentVolumes资源。

为了解决上述权限问题,首先创建一个名为pv-readerClusterRoles资源:

$ kubectl -n foo create clusterrole pv-reader --verb=get,list --resource=persistentvolumes
clusterrole.rbac.authorization.k8s.io/pv-reader created

# 查看 pv-reader 定义的信息
$ kubectl -n foo get clusterrole pv-reader -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: "2024-10-25T10:14:08Z"
  name: pv-reader
  resourceVersion: "733264"
  uid: 94bf8c4e-56b3-480a-8f20-5b3f80cab9de
rules:
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list

接下来将创建的ClusterRoles绑定到名为defaultServiceAccount

$ kubectl -n foo create clusterrolebinding pv-test --clusterrole=pv-reader --serviceaccount=foo:default
clusterrolebinding.rbac.authorization.k8s.io/pv-test created

# 查看 ClusterRoleBindings pv-test
$ kubectl -n foo get clusterrolebindings.rbac.authorization.k8s.io pv-test -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: "2024-10-25T10:19:55Z"
  name: pv-test
  resourceVersion: "733795"
  uid: 98fbfa86-2d2b-4c5f-87c1-50e402147a69
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pv-reader
subjects:
- kind: ServiceAccount
  name: default
  namespace: foo

对于集群层面的资源(类似Pod属于命名空间的资源)访问权限,必须使用ClusterRoleBindingsClusterRoles。下图总结如下:

clusterrolebinding

此时在Pod中访问PersistentVolumes资源:

$ kubectl -n foo exec -it test -- sh
/ # curl 127.0.0.1:8001/api/v1/persistentvolumes
{
  "kind": "PersistentVolumeList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "735094"
  },
  "items": []
}

可以正常访问。

然后对于api server暴露的非资源URLs,也需要相关的权限才可以访问。一般来说,可以自动通过名为system:discoveryClusterRole和名为system:discoveryClusterRoleBinding分配权限。

查看名为system:discoveryClusterRole信息如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2024-09-18T07:49:13Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:discovery
  resourceVersion: "74"
  uid: 3771415a-bd9f-4c4c-9e93-ccc6138e54d8
rules:
- nonResourceURLs:
  - /api
  - /api/*
  - /apis
  - /apis/*
  - /healthz
  - /livez
  - /openapi
  - /openapi/*
  - /readyz
  - /version
  - /version/
  verbs:
  - get

查看名为system:discoveryClusterRoleBinding信息如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2024-09-18T07:49:13Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:discovery
  resourceVersion: "139"
  uid: 4c206bc7-548f-41f3-9fad-d9d1e87e3910
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:discovery
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:authenticated

可以看到,system:discoveryClusterRole具有众多非资源URLsget权限,且被绑定到组system:authenticated

system:authenticated表示所有被校验成功的用户。

也就是说所有被校验成功的Pod都可以访问system:discoveryClusterRole指定的非资源URLs。例如在Pod中可以直接访问/apiURL

$ kubectl -n foo exec -it test -- sh
/ # curl 127.0.0.1:8001/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.211.55.9:6443"
    }
  ]
}

最后对于命名空间的资源(例如PodConfigMaps等),如果有ClusterRole类型的资源,则可以使用ClusterRoleBinding或者RoleBinding两种绑定。 二者区别如下示意图所示:

clusterrolebinding和rolebinding

如果使用ClusterRoleBinding绑定,则Pod可以访问所有命名空间的资源。如果使用RoleBinding绑定,则Pod只能访问所在命名空间的资源。

总结如下:

资源类型 使用的Role类型 使用的绑定类型
Cluster-level资源(NodesPersistentVolumes等) ClusterRole ClusterRoleBinding
非资源的URLs/api/healthz等) ClusterRole ClusterRoleBinding
所有命名空间下的命名空间资源(Pod等) ClusterRole ClusterRoleBinding
某一个命名空间下的命名空间资源(Pod等),共享资源 ClusterRole RoleBinding
某一个命名空间下的命名空间资源(Pod等) Role RoleBinding