ServiceAccount 无法删除部署或服务,但能够创建它

ServiceAccount unable to delete a deployment or service, but is able to create it

Kubernetes v1.20.0,在 Ubuntu 20.04.1,docker 19.3.11

使用以下配置,我可以使用此服务帐户的令牌在命名空间“实时”中创建部署,但我无法使用相同的令牌删除部署。

正在尝试找到正确的角色以绑定到我的编程 ServiceAccount 用户,但我无法获得用于部署删除的正确角色映射。我试过使用和不使用斜杠、资源名称中的星号以及复数形式的资源。

这是我从 API 得到的错误:

  warnings.warn(
Exception (403)
Reason: Forbidden
HTTP response headers: HTTPHeaderDict({'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'X-Content-Type-Options': 'nosniff', 'X-Kubernetes-Pf-Flowschema-Uid': 'xxxx-bf2f-43b3-af5d-3ce15b086080', 'X-Kubernetes-Pf-Prioritylevel-Uid': 'xxxx-0d42-4b08-820e-396249f74107', 'Date': 'Mon, 28 Dec 2020 13:33:00 GMT', 'Content-Length': '408'})
HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"deployments.apps \"live-stream-deploy-testing123\" is forbidden: User \"system:serviceaccount:live:live-serviceaccount\" cannot delete resource \"deployments\" in API group \"apps\" in the namespace \"live\"","reason":"Forbidden","details":{"name":"live-stream-deploy-testing123","group":"apps","kind":"deployments"},"code":403}

这是我要删除的部署:

$ kubectl -n live get deploy
NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
live-stream-deploy-testing123   1/1     1            1           76m

这是我目前的角色定义(我已经尝试在资源名称中使用星号,这对创建无效,后来进行了多次迭代:):

$ kubectl -n live describe role
Name:         live-serviceaccount-role
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources          Non-Resource URLs  Resource Names  Verbs
  ---------          -----------------  --------------  -----
  deployment/        []                 []              [get watch create list delete]
  deployment         []                 []              [get watch create list delete]
  deployments/       []                 []              [get watch create list delete]
  deployments        []                 []              [get watch create list delete]
  service/           []                 []              [get watch create list delete]
  service            []                 []              [get watch create list delete]
  services/          []                 []              [get watch create list delete]
  services           []                 []              [get watch create list delete]
  services           []                 []              [get watch create list delete]
  deployment.apps/   []                 []              [get watch create list delete]
  deployment.apps    []                 []              [get watch create list delete]
  deployments.apps/  []                 []              [get watch create list delete]
  deployments.apps   []                 []              [get watch create list delete]
  service.apps/      []                 []              [get watch create list delete]
  service.apps       []                 []              [get watch create list delete]
  services.apps/     []                 []              [get watch create list delete]
  services.apps      []                 []              [get watch create list delete]

角色绑定:

$ kubectl -n live describe rolebindings
Name:         live-serviceaccount-rolebinding
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  Role
  Name:  live-serviceaccount-role
Subjects:
  Kind            Name                 Namespace
  ----            ----                 ---------
  ServiceAccount  live-serviceaccount  live

pip 包 kubernetes==12.0.1。我没有针对以前的版本对其进行测试。 导致错误的脚本:

import kubernetes.client
from kubernetes.client.rest import ApiException
import logging
logger = logging.getLogger(__name__)

DEPLOYMENT_NAME = "live-stream-deploy-testing123" 

configuration = kubernetes.client.Configuration()
configuration.api_key['authorization'] = "service account token here..."
configuration.api_key_prefix['authorization'] = 'Bearer'
configuration.verify_ssl = False

configuration.host = "https://my_k8s_API:6443"

with kubernetes.client.ApiClient(configuration) as api_client:

    try:
        api_instance = kubernetes.client.AppsV1Api(api_client)
        api_instance.delete_namespaced_deployment(
            name=DEPLOYMENT_NAME, namespace="live")
        logger.info("Deployment deleted. %s" % DEPLOYMENT_NAME)

    except ApiException as e:
        logger.error("Exception %s\n" % e)

一些额外的“can-i”操作:

$ kubectl -n live auth can-i  --as=system:serviceaccount:live:live-serviceaccount delete deploy
yes
$ kubectl auth can-i --as=system:serviceaccount:live:live-serviceaccount  --list -n live
Resources                                       Non-Resource URLs                     Resource Names   Verbs
selfsubjectaccessreviews.authorization.k8s.io   []                                    []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                                    []               [create]
deployment/                                     []                                    []               [get watch create list delete]
deployment                                      []                                    []               [get watch create list delete]
deployments/                                    []                                    []               [get watch create list delete]
deployments                                     []                                    []               [get watch create list delete]
service/                                        []                                    []               [get watch create list delete]
service                                         []                                    []               [get watch create list delete]
services/                                       []                                    []               [get watch create list delete]
services                                        []                                    []               [get watch create list delete]
deployment.apps/                                []                                    []               [get watch create list delete]
deployment.apps                                 []                                    []               [get watch create list delete]
deployments.apps/                               []                                    []               [get watch create list delete]
deployments.apps                                []                                    []               [get watch create list delete]
service.apps/                                   []                                    []               [get watch create list delete]
service.apps                                    []                                    []               [get watch create list delete]
services.apps/                                  []                                    []               [get watch create list delete]
services.apps                                   []                                    []               [get watch create list delete]
                                                [/.well-known/openid-configuration]   []               [get]
                                                [/api/*]                              []               [get]
                                                [/api]                                []               [get]
                                                [/apis/*]                             []               [get]
                                                [/apis]                               []               [get]
                                                [/healthz]                            []               [get]
                                                [/healthz]                            []               [get]
                                                [/livez]                              []               [get]
                                                [/livez]                              []               [get]
                                                [/openapi/*]                          []               [get]
                                                [/openapi]                            []               [get]
                                                [/openid/v1/jwks]                     []               [get]
                                                [/readyz]                             []               [get]
                                                [/readyz]                             []               [get]
                                                [/version/]                           []               [get]
                                                [/version/]                           []               [get]
                                                [/version]                            []               [get]
                                                [/version]                            []               [get]

这是生成上述输出的 YAML。它们都在 live 命名空间中。请原谅角色上的混乱,但我一直在尝试迭代以找到解决方案:

$ cat role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: live-serviceaccount-role
  namespace: live
rules:
- apiGroups: ["","apps"] 
  resources: ["services", "services/", "deployment", "deployment/", "service", "service/", "deployments", "deployments/"]
  resourceNames: [""]
  verbs: ["get", "watch", "create", "list", "delete"]
- apiGroups: [""] 
  resources: ["services"]
  resourceNames: [""]
  verbs: ["get", "watch", "create", "list", "delete"]

$ cat serviceaccount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: live-serviceaccount
  namespace: live

$ cat rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: live-serviceaccount-rolebinding
  namespace: live
subjects:
- kind: ServiceAccount
  name: live-serviceaccount # "name" is case sensitive
  namespace: live
roleRef:
  kind: Role #this must be Role or ClusterRole
  name: live-serviceaccount-role # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: "rbac.authorization.k8s.io"

以下是成功启动实时命名空间中部署的脚本示例:

import kubernetes.client
from kubernetes import client
import logging
logger = logging.getLogger(__name__)

DEPLOYMENT_NAME = "live-stream-deploy-testing123"
POD_APP_LABEL = "lab"

configuration = kubernetes.client.Configuration()

# Configure API key authorization: BearerToken
configuration.api_key['authorization'] = "service account token here..."
# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
configuration.api_key_prefix['authorization'] = 'Bearer'
configuration.verify_ssl = False

# Defining host is optional and default to http://localhost
configuration.host = "https://<myk8sapi>:6443"

# Enter a context with an instance of the API kubernetes.client
#with kubernetes.client.ApiClient(configuration) as api_client:
with kubernetes.client.ApiClient(configuration) as api_client:

    api_instance = kubernetes.client.AppsV1Api(api_client)

    def create_deployment_object():
        # Configureate Pod template container
        container = kubernetes.client.V1Container(
            name="lab-container",

            # redacted for privacy
            image="nginx:latest",

            termination_message_path="/var/log/messages",
            image_pull_policy="IfNotPresent",
            resources=client.V1ResourceRequirements(
                requests={"memory": "5Gi"},
                limits={"memory": "16Gi"}
            ),
            volume_mounts=[
                client.V1VolumeMount(mount_path="/etc/nginx/nginx.conf.template", name="nginxconftemplate", sub_path="nginx.conf")
            ]
        )
        volume1 = client.V1Volume(
            name="nginxconftemplate",
            config_map=client.V1ConfigMapVolumeSource(
                name="live-nginx-conf",
                default_mode=0o0777
            )
        )
        # Create and configurate a spec section
        template = client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(labels={"app": POD_APP_LABEL}),
            spec=client.V1PodSpec(containers=[container],
                                  volumes=[volume1],
                                  node_selector={"backend": "yes"},
                                  )
        )
        # Create the specification of deployment
        spec = client.V1DeploymentSpec(
            replicas=1,
            template=template,
            selector={'matchLabels': {'app': POD_APP_LABEL}})
        # Instantiate the deployment object
        deployment = client.V1Deployment(
            api_version="apps/v1",
            kind="Deployment",
            metadata=client.V1ObjectMeta(name=DEPLOYMENT_NAME),
            spec=spec)

        return deployment

    def create_deployment(api_instance, deployment):
        # Create deployement
        api_response = api_instance.create_namespaced_deployment(
            body=deployment,
            namespace="live")
        logger.info("Deployment created. status='%s'" % str(api_response.status))

    deployment = create_deployment_object()
    create_deployment(api_instance, deployment)


以下是适合我的正确角色定义:


apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: live-serviceaccount-role
  namespace: live
rules:
- apiGroups: ["","apps"] # "" indicates the core API group
  resources: ["deployments"]
  verbs: ["create", "delete"]

产生这个 can-i 输出:

$ kubectl auth can-i --as=system:serviceaccount:live:live-serviceaccount  --list -n live
Resources                                       Non-Resource URLs   Resource Names   Verbs
deployments                                     []                  []               [create delete]
deployments.apps                                []                  []               [create delete]
selfsubjectaccessreviews.authorization.k8s.io   []                  []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []               [create]
                                                [/api/*]            []               [get]
                                                [/api]              []               [get]
                                                [/apis/*]           []               [get]
                                                [/apis]             []               [get]
                                                [/healthz]          []               [get]
                                                [/healthz]          []               [get]
                                                [/livez]            []               [get]
                                                [/livez]            []               [get]
                                                [/openapi/*]        []               [get]
                                                [/openapi]          []               [get]
                                                [/readyz]           []               [get]
                                                [/readyz]           []               [get]
                                                [/version/]         []               [get]
                                                [/version/]         []               [get]
                                                [/version]          []               [get]
                                                [/version]          []               [get]

虽然您的回答涵盖了解决方案,但并未解释为什么您提供的新角色集与之前的角色相比运行成功。

can-i 命令在这种情况下有点误导。结果实际上是正确的,因为使用此角色您只能删除 chosen/mentioned 资源。由于您使用 resourceNames 和一个空列表 [""],您的角色不允许删除任何内容。

为了简单地测试这个,我将确切的部署名称放在 resourceNames:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: live-serviceaccount-role
  namespace: live
rules:
- apiGroups: ["","apps"] 
  resources: ["services", "services/", "deployment", "deployment/", "service", "service/", "deployments", "deployments/"]
  resourceNames: ["live-stream-deploy-testing123"]
  verbs: ["get", "watch", "create", "list", "delete"]

完成后,我启动了您的代码,部署已成功删除。 所以总结一下你应该用资源名称填充 resourceNames 或完全省略它。