• 万字长文 | 使用 RBAC 限制对 Kubernetes 资源的访问

    云和安全管理服务专家 新钛云服 祝祥翻译

    通过本文,我们将学习如何从头开始重新创建 Kubernetes RBAC 授权模型,并了解Roles、ClusterRoles、ServiceAccounts、RoleBindings 和 ClusterRoleBindings 之间的关系。

    随着集群中应用程序和资源数量的增加,我们可能需要查看并限制他们相关的权限,从而避免一些危险操作。这些危险操作往往会严重影响生产环境,有时候,甚至会引起长时间的停机,比如过大的权限导致正常运行的服务被删除。

    正常情况下,我们可能希望限制生产系统仅仅允许少部分人管理以及访问。

    或者,我们可能希望向部署在集群中的维护人员授予一组有限的权限。

    我们可以通过Kubernetes 中的基于角色的访问控制 (RBAC) 来实现上述的需求。

    Kubernetes API

    在讨论RBAC之前,让我们看看授权模型在图中的位置。

    假设我们希望将以下 Pod 部署到 Kubernetes 集群:

    apiVersion: v1
    kind: Pod
    metadata:
    name: my-pod
    spec:
    containers:
    - name: sise
      image: learnk8s/app:1.0.0
      ports:
      - containerPort: 8080
    

    我们可以使用以下命令将 Pod 部署到集群:

    $ kubectl apply -f pod.yaml
    
    

    当我们输入时kubectl apply,会触发以下的操作。

    kubectl 会:

    1、从KUBECONFIG读取配置

    2、从 API 中发现 API 和对象

    3、验证资源客户端(是否有明显的错误?)

    4、将带有有效负载的请求发送到kube-apiserver

    当kube-apiserver收到请求时,它不会立即将其存储在 etcd 中。

    首先,它必须验证请求者是否合法。

    换句话说,它必须对请求进行身份验证。(https://kubernetes.io/docs/reference/access-authn-authz/authentication/)

    一旦通过身份验证,请求者是否有权创建资源?

    身份和权限不是一回事。

    仅仅因为我们可以访问集群并不意味着我们可以创建或读取所有资源。

    授权通常使用基于角色的访问控制 (RBAC)(https://kubernetes.io/docs/reference/access-authn-authz/rbac/)来完成。

    借助基于角色的访问控制 (RBAC),我们可以分配精细的权限并限制用户或应用程序可以执行的操作。

    在更实际的情况下,API 服务器按顺序执行以下操作:

    1、收到请求后,对用户进行身份验证。

    a. 当验证失败时,通过返回来拒绝请求401 Unauthorized。

    b, 否则,进入下一阶段。

    2、用户已通过身份验证,但他们是否有权访问资源

    a. 如果他们没有权限,请通过返回来拒绝请求403 Forbidden。

    b. 否则,继续。

    在本文中,我们将重点关注授权部分。

    将用户和权限与 RBAC 角色解耦

    RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。

    如下图:

    有人会问为什么不直接给用户分配权限,还多此一举的增加角色这一环节呢?

    其实是可以直接给用户分配权限,只是直接给用户分配权限,少了一层关系,扩展性弱了许多,适合那些用户数量、角色类型少的平台。

    对于通常的系统,比如:存在多个用户拥有相同的权限,在分配的时候就要分别为这几个用户指定相同的权限,修改时也要为这几个用户的权限进行一一修改。有了角色后,我们只需要为该角色制定好权限后,将相同权限的用户都指定为同一个角色即可,便于权限管理。

    对于批量的用户权限调整,只需调整用户关联的角色权限,无需对每一个用户都进行权限调整,既大幅提升权限调整的效率,又降低了漏调权限的概率。

    要了解它是如何工作的,让我们想象一下,我们必须从头开始设计一个授权系统。

    我们该如何确保用户对特定资源具有写入权限?

    一个简单的实现可能涉及编写一个如下所示的列表:

    | User | Permission | Resource |
    | ----- | ---------- | -------- |
    | Bob   | read+write |   app1   |
    | Alice |   read   |   app2   |
    | Mo   |   read   |   app2   |
    

    在这个例子中:

    · Bob 有读写权限,可以访问app1但无权访问app2

    · Mo 和 Alice对app2有只读权限,但是无权访问app1

    上表适用于少数用户和资源,但一旦开始扩展它就会显示一些限制。

    让我们假设Mo和Alice在同一个Team中,并且他们被授予对app1的读取权限。

    我们必须将以下条目添加到表中:

    | User     | Permission | Resource |
    | --------- | ---------- | -------- |
    | Bob       | read+write |   app1   |
    | Alice     |   read   |   app2   |
    | Mo       |   read   |   app2   |
    | Alice     |   read   |   app1   |
    | Mo       |   read   |   app1   |
    

    这很好,但Alice和Mo是否有相同的访问权限并不明显,因为他们是同一Team的成员。

    • 在典型的授权系统中我们有用户访问资源
    • 我们可以直接向用户分配权限,并定义他们可以使用的资源
    • 这些权限直接映射资源。注意它们是如何作用于用户的
    • 如果我们决定让第二个用户具有相同的权限,则必须关联相同的权限

    我们可以通过在表中添加“Team”列来解决这个问题,但更好的替代方法是分解关系:

    1、我们可以为权限定义一个通用属性:角色。

    2、我们可以为定义的组织“Team”进行授权,而不是直接将权限分配给用户。

    3、最后,我们可以用户加入到定义组织”Team“。

    让我们看看这有什么不同。

    现在你有两个权限映射关系表:

    · 在第一个表中,权限映射到角色

    · 在第二个表中,角色与身份相关联

    | Role     | Permission | Resource |
    | -------- | ---------- | -------- |
    | admin1   | read+write |   app1   |
    | reviewer |   read   |   app2   |
    
    | User |   Roles |
    | ----- | -------- |
    | Bob   | admin1 |
    | Alice | reviewer |
    | Mo   | reviewer |
    

    当我们希望 Mo 成为 app1 的管理员时那又该如何做了?

    我们可以像这样将角色添加到用户:

    | User |       Roles       |
    | ----- | ------------------- |
    | Bob   |       admin1       |
    | Alice |       reviewer     |
    | M

    我们已经可以想象,将用户与权限与角色分离可以促进拥有大量用户和权限的大型组织中的安全管理。

    • 使用 RBAC 时,我们拥有用户、资源和角色
    • 权限不会直接分配给用户。相反,他们被包括在这个角色中
    • 用户通过绑定关联到角色
    • 由于角色是通用的,当新用户需要访问相同资源时,您可以使用现有角色并将其新绑定关联

    Kubernetes 中的 RBAC

    Kubernetes 实现了一个 RBAC 模型(以及其他几个模型)(https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules) 来保护集群中的资源。

    基于角色(Role)的访问控制(RBAC)是一种基于组织中用户的角色来调节控制对 计算机或网络资源的访问的方法。

    RBAC 鉴权机制使用 rbac.authorization.k8s.io API 组 来驱动鉴权决定,允许你通过 Kubernetes API 动态配置策略。

    要启用 RBAC,在启动 API 服务器 时将 –authorization-mode 参数设置为一个逗号分隔的列表并确保其中包含 RBAC。

    kube-apiserver --authorization-mode=Example,RBAC --<其他选项> --<其他选项>
    

    因此 Kubernetes 使用了前面解释过的相同的三个概念:身份、角色和绑定。

    它只是用稍微不同的名字来称呼它们。

    例如,让我们检查以下授予对 Pod、Service等资源的访问权限所需的 YAML 定义:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: serviceaccount:app1
    namespace: demo-namespace
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: role:viewer
    namespace: demo-namespace
    rules:          # Authorization rules for this role
    - apiGroups:  # 1st API group
        - ''      # An empty string designates the core API group.
      resources:
        - services
        - pods
      verbs:
        - get
        - list
    - apiGroups: # 2nd API group
        - apiextensions.k8s.io
      resources:
        - customresourcedefinitions
      verbs:
        - list
    - apiGroups: # 3rd API group
        - cilium.io
      resources:
        - ciliumnetworkpolicies
        - ciliumnetworkpolicies/status
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: rolebinding:app1-viewer
    namespace: demo-namespace
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: Role
    name: role:viewer
    subjects:
    - kind: ServiceAccount
      name: serviceaccount:app1
      namespace: demo-namespace
    

    该文件分为三个部分:

    1、ServiceAccount——这是访问资源的用户的身份

    2、Role——包含访问资源的权限

    3、将身份(ServiceAccount)关联到权限(Role)的 RoleBinding

    配置提交到集群后,允许使用服务帐户的应用程序向以下端点发出请求:

    # 1. Kubernetes builtin resources
    /api/v1/namespaces/{namespace}/services
    /api/v1/namespaces/{namespace}/pods
    
    # 2. A specific API extention provided by cilium.io
    /apis/cilium.io/v2/namespaces/{namespace}/ciliumnetworkpolicies
    /apis/cilium.io/v2/namespaces/{namespace}/ciliumnetworkpolicies/status
    

    结果很成功,但是我们忽略了很多细节。

    我们究竟授予了哪些资源访问权限?

    什么是服务帐户?身份不只是集群中的“用户”吗?

    为什么 Role 包含 Kubernetes 对象列表?

    为了理解它们是如何工作的,让我们抛开 Kubernetes RBAC 模型并尝试从头开始重建它。

    我们将重点关注三个要素:

    1、识别和分配身份

    2、’授予权限

    3、将身份与权限相关联

    分配身份:用户,组,robot账户

    假设我们的新同事希望登录 Kubernetes 界面。

    在这种情况下,我们应该有一个“帐户”或“用户”的实体,每个实体都有一个唯一的名称或 ID(例如电子邮件地址)。

    我们应该如何将用户存储在集群中?

    Kubernetes 没有代表常规用户帐户的对象。

    无法通过 API 调用将用户添加到集群中。

    相反,任何提供由集群的证书颁发机构 (CA) 签名的有效证书的参与者都被视为已通过身份验证。(https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user)

    在这种情况下,Kubernetes 从证书的“subject”中的通用名称字段中分配用户名(例如,“/CN=bob”)。

    创建一个临时用户信息对象并将其传递给授权 (RBAC) 模块。

    深入研究代码会发现一个结构映射了从 Authentication 模块收集的所有详细信息。

    type User struct {
       name string   // unique for each user
       ...           // other fields
    }
    

    请注意,User用于集群外的人员或应用

    如果要识别集群中的应用,则应改用服务帐户。

    该帐户与普通用户非常相似,但不同之处在于Kubernetes管理它。

    服务帐户通常分配给 pod 以授予权限。

    例如,我们可以让以下应用程序从集群内部访问资源:

    · cilium-agent必须列出特定节点上的所有 pod 资源· ingress nginx控制器必须列出服务的所有后端端点

    对于这些应用,我们可以定义一个 ServiceAccount (SA)。

    由于服务帐户是在集群中管理的,因此我们可以使用 YAML 创建它们:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: sa:app1             # arbitrary but unique string
    namespace: demo-namespace
    

    为了方便 Kubernetes 管理,我们还可以定义一组User 或ServiceAccount。

    通过这样,我可以很方便的引用特定namespace中的所有 ServiceAccount

    现在我们已经定义了如何访问资源,是时候讨论权限了。

    此时,我们有一种机制来确定谁可以访问资源。

    它可能是一个人、一个robot账户或一群人。

    但是他们在集群中访问什么资源呢?

    对资源的访问进行建模

    在Kubernetes中,我们感兴趣的是控制对资源的访问,比如Pod、Services、Endpoints等。

    这些资源通常存储在数据库 (etcd) 中,并通过内置 API 访问,例如:

    /api/v1/namespaces/{namespace}/pods/{name}
    /api/v1/namespaces/{namespace}/pods/{name}/log
    /api/v1/namespaces/{namespace}/serviceaccounts/{name}
    

    限制对这些资源的访问的最佳方法是控制这些 API 端点的请求方式。

    为此,我们将需要两件事:

    1、资源的 API 端点

    2、授予访问资源的权限类型(例如只读、读写等)

    对于权限,我们将使用verbs,例如get, list, create, patch, delete 等。

    想象一下,你想要get, list 以及 watchPods、logs和Services等资源。

    我们可以将这些资源和权限组合在一个列表中,如下所示:

    resources:
    - /api/v1/namespaces/{namespace}/pods/{name}
    - /api/v1/namespaces/{namespace}/pods/{name}/log
    - /api/v1/namespaces/{namespace}/serviceaccounts/{name}
    verbs:
    - get
    - list
    - watch
    

    根据以下信息,我们可以更简化定义上述信息:

    · 基本 URL/api/v1/namespaces/对所有人都是通用的。也许我们可以省略它

    · 我们可以假设所有资源都在当前namespace中并删除{namespace}路径

    最终我们可以简化为:

    resources:
    - pods
    - pods/logs
    - serviceaccounts
    verbs:
    - get
    - list
    - watch
    

    该定义更人性化,我们可以很清晰的了解相关权限信息。

    不过,权限配置不仅仅只有上面的内容,还有更多的内容。

    除了 pod、endpoint、service等内置对象的 API 外,Kubernetes 还支持 API 扩展。

    例如,当使用安装 Cilium CNI 时,脚本会创建一个CiliumEndpoint自定义资源 (CR):

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
    name: ciliumendpoints.cilium.io
    spec:
    group: cilium.io
    names:
      kind: CiliumEndpoint
    scope: Namespaced
    # truncated...

    这些对象存储在集群中,可通过 kubectl 获得:

    $ kubectl get ciliumendpoints.cilium.io -n demo-namespace
    NAME   ENDPOINT ID   IDENTITY ENDPOINT STATE   IPV4
    IPV6
    app1   2773         1628124 ready           10.6.7.54
    app2   3568         1624494 ready           10.6.7.94
    app3   3934         1575701 ready           10.6.4.24
    $
    

    可以通过 Kubernetes API 类似地访问自定义资源:

    /apis/cilium.io/v2/namespaces/{namespace}/ciliumendpoints
    /apis/cilium.io/v2/namespaces/{namespace}/ciliumendpoints/{name}
    

    如果要将它们映射到 YAML 文件中,可以编写以下内容:

    resources:
    - ciliumnetworkpolicies  
    - ciliumnetworkpolicies/status
    verbs:
    - get
    

    但是,Kubernetes 怎么知道资源是自定义的呢?

    它如何区分使用自定义资源和内置的 API?

    不幸的是,从 API endpoint删除 URL 并不是一个好主意。

    我们可以通过稍作更改来恢复它。

    我们可以在顶部定义它并稍后使用它来扩展资源的 URL。

    apiGroups:
    - cilium.io     # APIGroup name
    resources:
    - ciliumnetworkpolicies  
    - ciliumnetworkpolicies/status
    verbs:
    - get

    对于没有namespace API的POD之类的资源呢?

    Kubernetes “”空API组是一个特殊的组,它引用内置对象。因此,前面的定义应该扩展到:

    apiGroups:
    - '' # Built-in objects
    resources:
    - pods  
    - pods/logs  
    - serviceaccounts
    verbs:
    - get  
    - list  
    - watch
    

    Kubernetes 读取 API 组并自动将它们扩展为:

    · 如果为空,则将“”转换为/api/v1/xxx

    · 否则为/apis/{apigroup_name}/{apigroup_version}/xxx

    既然我们知道了如何映射资源和权限,现在终于到了将对多个资源的访问组合在一起的时候了。在Kubernetes中,resources和verbs的集合称为rules,您可以将规则分组到列表中:

    rules:
    - rule 1  
    - rule 2
    

    每条规则都包含我们上述所提到的apiGroups,resources和verbs:

    rules: # Authorization rules
    - apiGroups: # 1st API group
        - '' # An empty string designates the core API group.
      resources:
        - pods
        - pods/logs
        - serviceaccounts
      verbs:
        - get
        - list
        - watch
    - apiGroups: # another API group
        - cilium.io # Custom APIGroup
      resources:
        - ciliumnetworkpolicies
        - ciliumnetworkpolicies/status
      verbs:
        - get

    规则的集合在 Kubernetes 中具有特定的名称,称为角色。

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: viewer
    rules: # Authorization rules
    - apiGroups: # 1st API group
        - '' # An empty string designates the core API group.
      resources:
        - pods
        - pods/logs
        - serviceaccounts
      verbs:
        - get
        - list
        - watch
    - apiGroups: # another API group
        - cilium.io # Custom APIGroup
      resources:
        - ciliumnetworkpolicies
        - ciliumnetworkpolicies/status
      verbs:
        - get

    到目前为止,我们定义了:

    · 具有用户、服务帐户和组的身份。

    · 对具有角色的资源的权限。

    最后,缺少的部分是将两者联系起来。

    向用户授予权限

    RoleBinding 将角色中定义的权限授予用户、服务帐户或组。

    让我们看一个例子:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: role-binding-for-app1
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: Role
    name: viewer
    subjects:
    - kind: ServiceAccount
      name: sa-for-app1
      namespace: kube-system
    

    该定义有两个重要字段:

    · 引用viewer角色的roleRef

    · 关联到sa-for-app1服务帐户的subjects

    将资源提交到集群后,使用服务帐户的应用程序或用户将有权访问角色中列出的资源。

    如果我们删除绑定,应用程序或用户将失去对这些资源的访问权限(但该角色将随时准备被其他绑定使用)。

    请注意该subjects字段是一个包含kind,namespace和name的列表。

    该kind属性是从服务帐户和组中识别用户所必需的。

    但是namespace了?

    将集群拆分为namespace,并将对namespace资源的访问限制为特定帐户,这通常很有帮助。

    在大多数情况下,Role和RoleBinding与namespace关联,并授予对特定namespace的访问权。

    然而,这两种资源是可以混合使用的——稍后我们将描述该如何混合。

    在我们总结理论并从实践开始之前,让我们看一下subjects的几个例子:

    subjects:
    - kind: Group
      name: system:serviceaccounts
      apiGroup: rbac.authorization.k8s.io
       # when the namespace field is not specified, this targets all service accounts in all namespace
    

    我们还可以将多个组、用户或服务帐户作为subjects:

    subjects:
    - kind: Group
      name: system:authenticated # for all authenticated users
      apiGroup: rbac.authorization.k8s.io
    - kind: Group
      name: system:unauthenticated # for all unauthenticated users
      apiGroup: rbac.authorization.k8s.io
    

    回顾一下到目前为止所学的内容,让我们看看如何授予应用程序访问某些自定义资源的权限。

    首先,让我们介绍一下场景:你有一个应用程序需要访问Cilium暴露的资源。

    • 想象一下,在集群中部署了一个需要通过 API 访问自定义资源的应用程序
    • 如果不授予对这些API的访问权限,请求将失败,并显示403禁止的错误消息

    如何授予访问这些资源的权限?

    使用服务帐户、角色和角色绑定。

    • 首先,我们应该为我们的工作负载创建一个身份。在 Kubernetes 中,这意味着创建一个服务帐户。
    • 然后,我们需要定义权限并将其包含到角色中
    • 最后,我们希望通过RoleBinding将身份(服务帐户)关联到权限(角色)
    • 下次应用程序向Kubernetes API发出请求时,它将被授予访问Cilium资源的权限

    Namespaces和cluster-wide的资源

    当我们讨论资源时,您了解到endpoint的结构如下面一样:

    /api/v1/namespaces/{namespace}/pods/{name}
    /api/v1/namespaces/{namespace}/pods/{name}/log
    /api/v1/namespaces/{namespace}/serviceaccounts/{name}
    

    但是没有namespace的资源,比如持久卷和节点呢?

    namespace资源只能在namespace内创建,并且该namespace的名称包含在 HTTP 路径中。

    如果资源是全局的,比如节点,namespace名称就不会出现在 HTTP 路径中。

    /api/v1/nodes/{name}
    /api/v1/persistentvolume/{name}
    

    我们可以将它们添加到角色中吗?

    是的,我们可以添加。

    毕竟,在引入 Roles 和 RoleBindings 时,我们没有讨论任何namespace的限制。

    下面是一个例子:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: viewer
    rules: # Authorization rules
    - apiGroups: # 1st API group
        - '' # An empty string designates the core API group.
      resources:
        - persistentvolumes
        - nodes
      verbs:
        - get
        - list
        - watch
    

    但是,如果我们尝试提交该配置并将其关联到服务帐户,大家可能会意识到它不起作用。

    持久卷和节点是集群范围的资源。

    但是,角色可以授予对namespace范围内资源的访问权限。

    如果我们想使用适用于整个集群的 Role,我们可以使用 ClusterRole(以及相应 ClusterRoleBinding的为它分配subject)。

    之前的配置应改为:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: viewer
    rules:          # Authorization rules
    - apiGroups:  # 1st API group
        - ''      # An empty string designates the core API group.
      resources:
        - persistentvolumes
        - nodes
      verbs:
        - get
        - list
        - watch

    请注意,唯一的变化是kind属性,而其他一切都保持不变。

    我们可以使用 ClusterRoles 授予对所有资源的权限——例如,集群中的所有 Pod。

    此功能不限于集群范围的资源。

    Kubernetes 已经附带了一些角色和集群角色。

    让我们来看一下。

    $ kubectl get roles -n kube-system | grep "^system:"
    NAME
    system::leader-locking-kube-controller-manager
    system::leader-locking-kube-scheduler
    system:controller:bootstrap-signer
    system:controller:cloud-provider
    system:controller:token-cleaner
    # truncated output...
    

    system:前缀表示资源由集群控制平面直接管理。

    此外,所有默认的 ClusterRoles 和 ClusterRoleBindings 都标有kubernetes.io/bootstrapping=rbac-defaults。

    让我们也列出 ClusterRoles:

    $ kubectl get clusterroles -n kube-system | grep "^system:"
    NAME
    system:aggregate-to-admin
    system:aggregate-to-edit
    system:aggregate-to-view
    system:discovery
    system:kube-apiserver
    system:kube-controller-manager
    system:kube-dns
    system:kube-scheduler
    # truncated output...
    

    我们可以使用以下命令检查每个 Role 和 ClusterRole 的详细信息:

    $ kubectl get role <role> -n kube-system -o yaml
    # or
    $ kubectl get clusterrole <clusterrole> -n kube-system -o yaml
    

    至此,我们了解了 Kubernetes RBAC 的基本内容。

    通过上述内容,我们学习到了如下内容:

    1、如何使用用户、服务帐户和组创建身份

    2、如何为具有角色的namespace中的资源分配权限

    3、如何使用 ClusterRole 为集群资源分配权限

    4、如何将 Roles 和 ClusterRoles 关联到subject

    最后让我们再研究一下几个案例:RBAC的几个不寻常的边缘案例。

    理解 Roles、RoleBindings、ClusterRoles 和 ClusterBindings

    在较高级别上,Roles 和 RoleBindings集群内部关联,并授予对特定namespace的访问权限,而ClusterRoles和ClusterRoleBinding不属于namespace,并授予对整个集群的访问权限。

    但是,可以混合使用这两种类型的资源。

    例如,当 RoleBinding 将帐户关联到 ClusterRole 时会发生什么?

    接下来让我们通过一些实践来探索这个问题。

    让我们使用 minikube 创建一个本地集群:

    $ minikube start
     minikube v1.24.0
    ✨ Automatically selected the docker driver
     Starting control plane node in cluster
     Pulling base image ...
     Creating docker container (CPUs=2, Memory=4096MB) ...
     Preparing Kubernetes v1.22.3 on Docker 20.10.8 ...
      ▪ Generating certificates and keys ...
      ▪ Booting up control plane ...
      ▪ Configuring RBAC rules ...
     Verifying Kubernetes components...
      ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
     Enabled addons: storage-provisioner, default-storageclass
     Done! kubectl is now configured to use the cluster and "default" namespace by default
    

    首先,创建四个namespace:

    $ kubectl create namespace test
    namespace/test created
    $ kubectl create namespace test2
    namespace/test2 created
    $ kubectl create namespace test3
    namespace/test3 created
    $ kubectl create namespace test4
    namespace/test4 created
    

    最后,在namespace中创建一个服务帐户test:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: myaccount
    namespace: test
    

    我们可以通过以下方式提交资源:

    $ kubectl apply -f service-account.yaml
    serviceaccount/myaccount created
    

    此时,我们的集群应如下所示:

    场景 1:Role和RoleBinding在同一个namespace

    让我们从创建 Role 和 RoleBinding 来授予服务帐户对test namespace的访问权限开始:

    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: testadmin
    namespace: test
    rules:
    - apiGroups: ['*']
      resources: ['*']
      verbs: ['*']
    ---
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: testadminbinding
    namespace: test
    subjects:
    - kind: ServiceAccount
      name: myaccount
      namespace: test
    roleRef:
    kind: Role
    name: testadmin
    apiGroup: rbac.authorization.k8s.io
    

    我们可以通过以下方式提交配置:

    $ kubectl apply -f scenario1.yaml
    role.rbac.authorization.k8s.io/testadmin createdrolebinding.rbac.authorization.k8s.io/testadminbinding created
    

    我们的集群如下所示:

    所有资源(Service Account, Role以及 RoleBinding)都在namespace中。

    Role 授予对所有资源的访问权限,并且 RoleBinding 将 Service Account 和 Role 关联起来。

    我们如何测试服务帐户是否可以访问资源?

    我们可以结合 kubectl 的两个功能:

    1、使用kubectl–as=jenkins进行用户模拟访问(https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation)

    2、使用kubectl auth can-i验证API访问(https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access)

    请注意,我们的用户应该拥有impersonateverb作为允许此操作的权限。

    要作为myaccount服务帐户发出请求,并检查是否可以在namespace中列出Pod,可以使用以下命令:

    $ kubectl auth can-i get pods -n test --as=system:serviceaccount:test:myaccount
    yes
    

    让我们详解一下命令:

    · auth can-i需要查询授权模型(RBAC)

    · get pods是verb和resource

    · -n test是我们要发出命令的namespace

    · –as=system:serviceaccount:test:myaccount用于模拟myaccount服务帐户

    请注意–as=标志需要一些额外的提示来识别服务帐户。

    整个部分可以分解为:

    --as=system:serviceaccount:{namespace}:{service-account-name}
        ^^^^^^^^^^^^^^^^^^^^^
        This should always be included for Service Accounts.

    通过这个 Role+ServiceAccount+RoleBindings组合,我们可以访问test namespace中的所有资源。

    让我们往下继续看一个更复杂的例子。

    场景 2:不同namespace中的 Role 和 RoleBinding

    让我们在namespace中创建一个新的 Role 和 RoleBinding test2。

    注意RoleBinding如何关联test2中的角色和test中的服务帐户:

    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    namespace: test2
    name: testadmin
    rules:
    - apiGroups: ['*']
      resources: ['*']
      verbs: ['*']
    ---
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: testadminbinding
    namespace: test2
    subjects:
    - kind: ServiceAccount
      name: myaccount
      namespace: test
    roleRef:
    kind: Role
    name: testadmin
    apiGroup: rbac.authorization.k8s.io
    

    我们可以通过以下方式提交资源:

    $ kubectl apply -f scenario2.yaml
    role.rbac.authorization.k8s.io/testadmin created
    rolebinding.rbac.authorization.k8s.io/testadminbinding created
    

    我们的集群如下所示:

    让我们测试位于test中的服务帐户是否可以访问test2中的资源:

    $ kubectl auth can-i get pods -n test2 --as=system:serviceaccount:test:myaccount
    yes
    

    我们成功授予服务帐户访问它创建的namespace之外的资源。

    值得注意的是,RoleBinding中的roleRef(https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#roleref-v1-rbac-authorization-k8s-io) 属性没有namespace字段。

    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: testadminbinding
    namespace: test2
    subjects:
    - kind: ServiceAccount
      name: myaccount
      namespace: test
    roleRef:
    kind: Role
    name: testadmin
    apiGroup: rbac.authorization.k8s.io
    

    这意味着 RoleBinding 只能引用同一namespace中的 Role。

    场景 3:使用有 RoleBinding 的 ClusterRole

    如前所述,ClusterRoles 不属于namespace。

    这意味着 ClusterRole 不会将权限范围限定为单个namespace。

    但是,当 ClusterRole 通过 RoleBinding 关联到服务帐户时,ClusterRole 权限仅适用于创建 RoleBinding 的namespace。

    让我们看一个例子。

    在namespace中创建 RoleBindingtest3并将服务帐户关联到 ClusterRole cluster-admin:

    cluster-admin是 Kubernetes 中内置的 ClusterRoles 之一。

    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    name: testadminbinding
    namespace: test3
    subjects:
    - kind: ServiceAccount
      name: myaccount
      namespace: test
    roleRef:
    kind: ClusterRole
    name: cluster-admin
    apiGroup: rbac.authorization.k8s.io
    
    

    我们可以通过以下方式提交资源:

    $ kubectl apply -f scenario3.yaml
    rolebinding.rbac.authorization.k8s.io/testadminbinding created
    
    

    我们的集群如下所示:

    让我们测试位于test中的服务帐户是否可以访问test3中的资源:

    $ kubectl auth can-i get pods -n test3 --as=system:serviceaccount:test:myaccount
    yes
    
    

    但它无权访问其他namespace:

    $ kubectl auth can-i get pods -n test4 --as=system:serviceaccount:test:myaccount
    no
    $ kubectl auth can-i get pods --as=system:serviceaccount:test:myaccount
    no
    

    在这种情况下,当我们使用 RoleBindings 将服务帐户关联到 ClusterRole 时,ClusterRole 的行为就像是常规角色一样。

    它仅向 RoleBinding 所在的当前namespace授予权限。

    场景 4:使用 ClusterRole 和 ClusterRoleBinding 授予集群范围的访问权限

    在最后一个场景中,我们将创建一个 ClusterRoleBinding 以将 ClusterRole 关联到服务帐户:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: testadminclusterbinding
    subjects:
    - kind: ServiceAccount
      name: myaccount
      namespace: test
    roleRef:
    kind: ClusterRole
    name: cluster-admin
    apiGroup: rbac.authorization.k8s.io
    

    请注意roleRef缺少namespace字段。

    这意味着 ClusterRoleBinding 无法识别要关联的角色,因为角色属于namespace,并且 ClusterRoleBindings(连同它们引用的 ClusterRolnamespace有namespace。

    我们可以通过以下方式提交资源:

    $ kubectl apply -f scenario3.yaml
    rolebinding.rbac.authorization.k8s.io/testadminbinding created
    
    

    我们的集群如下所示:

    尽管 ClusterRole 和 ClusterRole 都没有定义任何namespace,但服务帐户现在可以访问所有内容:

    $ kubectl auth can-i get pods -n test4 --as=system:serviceaccount:test:myaccount
    yes
    $ kubectl auth can-i get namespaces --as=system:serviceaccount:test:myaccount
    Warning: resource 'namespaces' is not namespace scoped
    yes
    
    

    从这些示例中,我们可以观察到 RBAC 资源的一些行为和限制:

    · Roles 和 RoleBindings 必须存在于同一个namespace中

    · RoleBindings 可以存在于服务帐户的单独namespace中

    · RoleBindings 可以关联 ClusterRoles,但它们只授予对 RoleBinding 所在 namespace的访问权限

    · ClusterRoleBindings 将帐户关联到 ClusterRoles 并授予对所有资源的访问权限

    · ClusterRoleBindings 不能引用角色

    这里最有趣的意思可能是,当RoleBinding引用时,ClusterRole可以定义在单个namespace中表示的公共权限。这样就不需要在namespace中有重复的角色。

    附加 #1:使 RBAC 策略更简洁

    Role 或 ClusterRole的典型rules部分如下所示:

    rules:
    - apiGroups:
      -"
    resources:
    - pods
    - endpoints
    - namespaces
      verbs:
    - get
    - watch
    - list
    - create
    - delete
    

    但是,可以使用以下格式重写上述配置:

    - apiGroups: ['']
    resources: ['services', 'endpoints', 'namespaces']
    verbs: ['get', 'list', 'watch', 'create', 'delete']
    
    

    这种方式明显减少了行数并且更简洁。

    但是,当我们从数据库中检索内容时,Kubernetes 仍将内容作为 YAML 列表进行管理。

    所以每次拿到 Role 时,数组都会被渲染成一个列表:

    $ kubectl get role pod-reader -o yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    rules:
    - apiGroups:
    -""
    resources:
    - pods
    # truncated output...
    

    附加 #2:使用服务帐户创建 Kubernetes 帐户

    服务帐户通常由 API 服务器自动创建,并与集群中运行的 pod 相关联。

    三个独立的组件完成了这项任务:

    1、在 Pod 定义中注入 Service Account 属性的ServiceAccount准入控制器

    2、一个创建伴随 Secret 对象的Token 控制器

    3、ServiceAccount 控制器在每个namespace中创建默认服务帐户

    服务帐户可以在集群外部使用,为希望与 Kubernetes API 对话的用户或长期存在的作业创建身份。

    要手动创建服务帐户,我们可以使用以下命令:

    $ kubectl create serviceaccount demo-sa
    serviceaccount/demo-sa created
    
    $ kubectl get serviceaccounts demo-sa -o yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: demo-sa
    namespace: default
    resourceVersion: "1985126654"
    selfLink: /api/v1/namespaces/default/serviceaccounts/demo-sa
    uid: 01b2a3f9-a373-6e74-b3ae-d89f6c0e321f
    secrets:
    - name: demo-sa-token-hrfq2
    

    我们可能会注意到secrets服务帐户 YAML 定义末尾的一个字段。

    那是什么?

    每次创建服务帐户时,Kubernetes 都会创建一个 Secret。

    Secret 保存服务帐户的令牌,我们可以使用该令牌调用 Kubernetes API。

    它还包括 API 服务器的公共证书颁发机构 (CA):

    $ kubectl get secret demo-sa-token-hrfq2 -o yaml
    apiVersion: v1
    data:
    ca.crt: (APISERVER'S CA BASE64 ENCODED)
    namespace: ZGVmYXVsdA==
    token: (BEARER TOKEN BASE64 ENCODED)
    kind: Secret
    metadata:
    # truncated output ...
    type: kubernetes.io/service-account-token
    

    令牌是一个签名的JWT,可以用作承载令牌,以针对kube-apiserver服务进行身份验证。通常,这些secret被挂载到POD中,用于访问API服务,但可以从集群外部使用。

    附加 #3:Kubernetes 默认的角色

    有一些角色是kubernetes内置的一些默认角色,我们无需进行修改。

    默认的角色

    默认 ClusterRole

    默认 ClusterRoleBinding

    描述

    cluster-admin

    system:masters 组

    允许超级用户在平台上的任何资源上执行所有操作。当在 ClusterRoleBinding 中使用时,可以授权对集群中以及所有名字空间中的全部资源进行完全控制。当在 RoleBinding 中使用时,可以授权控制 RoleBinding 所在名字空间中的所有资源,包括名字空间本身。

    admin

    允许管理员访问权限,旨在使用 RoleBinding 在名字空间内执行授权。如果在 RoleBinding 中使用,则可授予对名字空间中的大多数资源的读/写权限, 包括创建角色和角色绑定的能力。此角色不允许对资源配额或者名字空间本身进行写操作。此角色也不允许对 Kubernetes v1.22+ 创建的 Endpoints 进行写操作。更多信息参阅“Endpoints 写权限”小节。

    edit

    允许对名字空间的大多数对象进行读/写操作。它不允许查看或者修改角色或者角色绑定。不过,此角色可以访问 Secret,以名字空间中任何 ServiceAccount 的身份运行 Pods, 所以可以用来了解名字空间内所有服务账户的 API 访问级别。此角色也不允许对 Kubernetes v1.22+ 创建的 Endpoints 进行写操作。更多信息参阅“Endpoints 写操作”小节。

    view

    允许对名字空间的大多数对象有只读权限。它不允许查看角色或角色绑定。此角色不允许查看 Secrets,因为读取 Secret 的内容意味着可以访问名字空间中 ServiceAccount 的凭据信息,进而允许利用名字空间中任何 ServiceAccount 的 身份访问 API(这是一种特权提升)。

    核心组件角色: (https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#core-component-roles)

    默认 ClusterRole

    默认 ClusterRoleBinding

    描述

    system:kube-scheduler

    system:kube-scheduler 用户

    允许访问 scheduler 组件所需要的资源。

    system:volume-scheduler

    system:kube-scheduler 用户

    允许访问 kube-scheduler 组件所需要的卷资源。

    system:kube-controller-manager


    system:kube-controller-manager
     用户

    允许访问控制器管理器 组件所需要的资源。各个控制回路所需要的权限在控制器角色 详述。

    system:node

    允许访问 kubelet 所需要的资源,包括对所有 Secret 的读操作和对所有 Pod 状态对象的写操作。你应该使用 Node 鉴权组件 和 NodeRestriction 准入插件 而不是 system:node 角色。同时基于 kubelet 上调度执行的 Pod 来授权 kubelet 对 API 的访问。system:node 角色的意义仅是为了与从 v1.8 之前版本升级而来的集群兼容。

    system:node-proxier

    system:kube-proxy 用户

    允许访问 kube-proxy 组件所需要的资源。

    其他组件角色

    (https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#other-component-roles)

    默认 ClusterRole

    默认 ClusterRoleBinding

    描述

    system:auth-delegator

    允许将身份认证和鉴权检查操作外包出去。这种角色通常用在插件式 API 服务器上,以实现统一的身份认证和鉴权。

    system:heapster

    为 Heapster 组件(已弃用)定义的角色。

    system:kube-aggregator

    为 kube-aggregator 组件定义的角色。

    system:kube-dns

    在 kube-system 名字空间中的 kube-dns 服务账户

    为 kube-dns 组件定义的角色。

    system:kubelet-api-admin

    允许 kubelet API 的完全访问权限。

    system:node-bootstrapper

    允许访问执行 kubelet TLS 启动引导 所需要的资源。

    system:node-problem-detector

    为 node-problem-detector 组件定义的角色。

    system:persistent-volume-provisioner

    允许访问大部分 动态卷驱动 所需要的资源。

    system:monitoring

    system:monitoring 组

    允许对控制平面监控端点的读取访问(例如:kube-apiserver 存活和就绪端点(/healthz、/livez、/readyz), 各个健康检查端点(/healthz/、/livez/、/readyz/*)和 /metrics)。请注意,各个运行状况检查端点和度量标准端点可能会公开敏感信息。

    内置控制器的角色

    (https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#controller-roles)

    Kubernetes 控制器管理器 运行内建于 Kubernetes 控制面的控制器。当使用 –use-service-account-credentials 参数启动时, kube-controller-manager 使用单独的服务账户来启动每个控制器。每个内置控制器都有相应的、前缀为 system:controller: 的角色。如果控制管理器启动时未设置 –use-service-account-credentials, 它使用自己的身份凭据来运行所有的控制器,该身份必须被授予所有相关的角色。这些角色包括:

    · system:controller:attachdetach-controller

    · system:controller:certificate-controller

    · system:controller:clusterrole-aggregation-controller

    · system:controller:cronjob-controller

    · system:controller:daemon-set-controller

    · system:controller:deployment-controller

    · system:controller:disruption-controller

    · system:controller:endpoint-controller

    · system:controller:expand-controller

    · system:controller:generic-garbage-collector

    · system:controller:horizontal-pod-autoscaler

    · system:controller:job-controller

    · system:controller:namespace-controller

    · system:controller:node-controller

    · system:controller:persistent-volume-binder

    · system:controller:pod-garbage-collector

    · system:controller:pv-protection-controller

    · system:controller:pvc-protection-controller

    · system:controller:replicaset-controller

    · system:controller:replication-controller

    · system:controller:resourcequota-controller

    · system:controller:root-ca-cert-publisher

    · system:controller:route-controller

    · system:controller:service-account-controller

    · system:controller:service-controller

    · system:controller:statefulset-controller

    · system:controller:ttl-controller

    附加 #4:初始化与预防权限升级

    RBAC API 会阻止用户通过编辑角色或者角色绑定来提升权限。由于这一点是在 API 级别实现的,所以在 RBAC 鉴权组件未启用的状态下依然可以正常工作。

    对角色创建或更新的限制

    (https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#%E5%AF%B9%E8%A7%92%E8%89%B2%E5%88%9B%E5%BB%BA%E6%88%96%E6%9B%B4%E6%96%B0%E7%9A%84%E9%99%90%E5%88%B6)

    只有在符合下列条件之一的情况下,你才能创建/更新角色:

    1、你已经拥有角色中包含的所有权限,且其作用域与正被修改的对象作用域相同。(对 ClusterRole 而言意味着集群范围,对 Role 而言意味着相同名字空间或者集群范围)。

    2、你被显式授权在 rbac.authorization.k8s.io API 组中的 roles 或 clusterroles 资源 使用 escalate 动词。

    例如,如果 user-1 没有列举集群范围所有 Secret 的权限,他将不能创建包含该权限的 ClusterRole。若要允许用户创建/更新角色:

    1、根据需要赋予他们一个角色,允许他们根据需要创建/更新 Role 或者 ClusterRole 对象。

    2、授予他们在所创建/更新角色中包含特殊权限的权限:

    1. · 隐式地为他们授权(如果它们试图创建或者更改 Role 或 ClusterRole 的权限, 但自身没有被授予相应权限,API 请求将被禁止)。· 通过允许他们在 Role 或 ClusterRole 资源上执行 escalate 动作显式完成授权。这里的 roles 和 clusterroles 资源包含在 rbac.authorization.k8s.io API 组中。

    对角色绑定创建或更新的限制

    (https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#%E5%AF%B9%E8%A7%92%E8%89%B2%E7%BB%91%E5%AE%9A%E5%88%9B%E5%BB%BA%E6%88%96%E6%9B%B4%E6%96%B0%E7%9A%84%E9%99%90%E5%88%B6)

    只有你已经具有了所引用的角色中包含的全部权限时,或者你被授权在所引用的角色上执行 bind 动词时,你才可以创建或更新角色绑定。这里的权限与角色绑定的作用域相同。例如,如果用户 user-1 没有列举集群范围所有 Secret 的能力,则他不可以创建 ClusterRoleBinding 引用授予该许可权限的角色。如要允许用户创建或更新角色绑定:

    1、赋予他们一个角色,使得他们能够根据需要创建或更新 RoleBinding 或 ClusterRoleBinding 对象。

    2、授予他们绑定某特定角色所需要的许可权限:

    1. · 隐式授权下,可以将角色中包含的许可权限授予他们;· 显式授权下,可以授权他们在特定 Role (或 ClusterRole)上执行 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 意味着允许绑定任何 ClusterRole
    resourceNames: ["admin","edit","view"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: role-grantor-binding
    namespace: user-1-namespace
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: role-grantor
    subjects:
    - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: user-1
    

    当启动引导第一个角色和角色绑定时,需要为初始用户授予他们尚未拥有的权限。对初始角色和角色绑定进行初始化时需要:

    · 使用用户组为 system:masters 的凭据,该用户组由默认绑定关联到 cluster-admin 这个超级用户角色。

    · 如果你的 API 服务器启动时启用了不安全端口(使用 –insecure-port), 你也可以通过 该端口调用 API ,这样的操作会绕过身份验证或鉴权。

    概括

    Kubernetes 中的 RBAC 是一种机制,使我们能够配置细粒度的特定权限集,这些权限集定义给定用户或用户组如何与集群或特定集群namespace中的 Kubernetes 对象进行交互。

    在本文中,我们了解到:

    · RBAC 如何通过更灵活的模型将权限与用户分离

    · RBAC 如何与 Kubernetes API 集成

    · 如何使用用户、服务帐户和组识别 RBAC 的subject

    · 如何使用verbs和 API 组将资源映射到规则中

    · 如何将规则分组到角色中并使用 RoleBindings 将这些角色关联到身份

    · Roles、RoleBindings、ClusterRoles 和 ClusterRole Bindings 之间的关系

    参考:

    1. https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control?hl=zh-cn
    2. https://zh.m.wikipedia.org/zh/%E4%BB%A5%E8%A7%92%E8%89%B2%E7%82%BA%E5%9F%BA%E7%A4%8E%E7%9A%84%E5%AD%98%E5%8F%96%E6%8E%A7%E5%88%B6

    原文:https://learnk8s.io/rbac-kubernetes

    «
    »
以专业成就每一位客户,让企业IT只为效果和安全买单

以专业成就每一位客户,让企业IT只为效果和安全买单