使用RBAC Impersonation简化Kubernetes资源访问控制
客座文章最初由Juanjo Ciarlante在Bitnami上发表
https://docs.bitnami.com/tutorials/simplify-kubernetes-resource-access-rbac-impersonation/
介绍
Kubernetes,像任何其他安全系统一样,支持以下概念:
- 身份验证(Authentication):验证和证明用户、组和服务帐户的身份
- 授权(Authorization):允许用户使用Kubernetes资源执行特定的操作
- 会计(Accounting):存储主题操作,通常用于审计(auditing)目的
授权--处理用户对资源访问的过程--总是一个挑战,特别是当访问由团队成员身份或项目成员身份控制时。两个关键挑战是:
由于Kubernetes组(group)成员关系是由身份提供程序(Identity Provider,IdP)从外部处理到API本身的,因此集群管理员需要与身份提供程序管理员交互来设置这些组成员关系,这使得工作流可能很麻烦。身份提供者可能根本不提供组成员关系,从而迫使集群管理员按用户处理访问,即Kubernetes RoleBindings包含允许最终用户(end-user)的“完整”列表。
在本教程中,我们提出了一种使用现有Kubernetes授权特性“扮演”组成员身份的方法--可以通过团队、项目或你可能需要的任何其他聚合。
假设和前提条件
本文假设你:
- 了解一般的最终用户安全概念
- 有一些关于RBAC角色和绑定的知识和经验
- 理解身份验证和授权之间的区别
- 配置集群时启用Kubernetes RBAC,自1.6发行版以来默认设置
Kubernetes身份验证概述
身份验证是任何集群管理员都应该遵循的策略的关键部分,以保护Kubernetes集群基础设施,并确保只有被允许的用户才能访问它。
下面简要介绍一下Kubernetes如何进行身份验证。主要有两类用户:
- ServiceAccounts(SAs):
- ID由Kubernetes本身在集群内管理。
- 每个ServiceAccount都有一个身份验证令牌(JWT),作为它的凭据
- 用户(外部角色或机器人用户):
- ID是外部提供的,通常由IdP提供。有许多机制可以提供这个ID,例如:
- x509证书
- 静态令牌或用户/密码文件
- 通过外部身份提供者(IdP)的OpenID连接令牌(OpenID Connect Tokens,OIDC)
- Webhook令牌
- 托管的Kubernetes提供商(例如GKE, AKS, EKS)与他们自己的云认证机制集成
- ID是外部提供的,通常由IdP提供。有许多机制可以提供这个ID,例如:
用户ID包含在对Kubernetes API的每次调用中,而该API又是由访问控制机制授权的。
采用OIDC进行身份验证很常见,因为它提供了单点登录(Single-Sign-On,SSO)体验,不过有些组织可能仍然使用最终用户x509证书,因为无需任何外部IdP干预就可以颁发这些证书。然而,这些共同的方法带来了以下挑战:
- x509证书:尽管它们很容易设置,但用户最终拥有一个无法撤消的x509包(密钥和证书)。这迫使集群所有者指定较短的过期时间,这显然取决于人员流动性。此外,用户的组被写入x509证书本身。这迫使集群管理员在用户每次更改成员资格时都重新颁发证书,同时无法撤消以前的证书(即,用户将继续保持旧组的成员身份,直到以前的证书过期)。
- OIDC身份验证:使用组织使用的IdP提供SSO很方便。当提供的身份缺少组成员关系,或者组成员关系(由组织设置)不能直接映射到用户的Kubernetes工作负载需求的团队或项目成员关系时,就会出现问题。
用户现在已经通过身份验证,我们需要看看如何授权他们使用Kubernetes集群。
Kubernetes授权和RBAC概述
在网上有许多关于Kubernetes RBAC的资源。如果你不完全熟悉这些概念,我推荐这个关于在Kubernetes中揭开RBAC神秘面纱的很棒的教程。要了解关于如何在集群中配置RBAC的更多信息,请参阅本教程。Kubernetes RBAC允许指定:
A)允许的SUBJECTS,对 B)资源种类进行VERBS(可以选择缩小到特定的资源名称)
在上面的模型中,B)被实现为一个Kubernetes Role(或ClusterRole),而A)-> B)的绑定被建模为一个Kubernetes RoleBinding(或ClusterRoleBinding),如下图所示:
使用扮演的(impersonated)“虚拟用户”来控制访问
Kubernetes RBAC包含一个特殊的impersonate(扮演)动词,可用于允许Subjects(即Users、Groups、ServiceAccounts)获得其他Kubernetes用户或组身份。
由于这些获得的身份不一定需要存在--还记得Kubernetes控制平面本身没有用户或组存储--我们将在本文中将它们称为“虚拟用户(virtual-users)”。此特性允许将“虚拟用户”设置为“角色帐户”安全主体。例如:
alice@example.com,作为应用前端(app-fe)团队的成员,可以扮演虚拟用户app-fe-user
bob@example.com,作为应用程序后端(app-be)团队的成员,可以扮演虚拟用户app-be-user
可以创建RBAC规则来允许这些“虚拟用户”访问他们需要的Kubernetes资源,如下图所示:
如上所示,利用现有的Kubernetes RBAC特性,授权处理分为:
- 团队成员:RBAC ClusterRoles和ClusterroleBindings,它们表示允许用户扮演其团队的虚拟用户。
- 团队职责:RBAC角色和角色绑定,说明团队的虚拟用户可以访问哪些实际的Kubernetes资源。
实际的扮演操作是通过在Kubernetes API调用的头文件指定的,这是方便的由kubectl通过:
kubectl --as <user-to-impersonate> ...
kubectl --as <user-to-impersonate> --as-group <group-to-impersonate> ...
提示:没有-as参数的kubectl -as -group…是无效的。为了简化CLI的使用,本文建议使用上面的第一种形式,通过将用户扮演为表示用户组或团队成员的“虚拟用户”进行建模。
如下图所示的例子,用户“alice@example.com”可以通过重载kubectl CLI使用下面的命令轻松扮演虚拟团队用户“app-fe-user”:
kubectl --as app-fe-user ...
使用RBAC规则的工作示例
现在已经“创建”了虚拟用户,让我们看看RBAC规则在实践中的一个工作示例。对于这个用例,假设以下场景:
- 用户alice@example.com和alanis@example.com已经通过某种形式的SSO认证(例如OIDC连接器到谷歌IdP)。
- 他们是app-fe(即应用前端)团队的成员。
- Kubernetes集群有三个与app-fe工作负载相关的命名空间:开发(development)、登台(staging)和生产(production)。
- 将创建一个名为app-fe-user的虚拟用户,允许Alice和Alanis扮演它。
- app-fe用户将被授予以下访问权限:
- dev-app-fe NS:完全管理
- staging-app-fe NS:编辑访问
- prod-app-fe NS:仅查看访问
提示:为了简单起见,我们将使用现有的Kubernetes ClusterRoles(可用于命名空间作用域的角色绑定)来实现上述访问规则。
步骤1:准备RBAC清单
下面的例子使用k14s/ytt作为模板语言实现了这个想法(你可以找到下面的ytt源代码和生成的YAML):
https://get-ytt.io/
https://github.com/bitnami-labs/k8s-training-resources/blob/master/rbac-impersonate/fe_team.yml
https://github.com/bitnami-labs/k8s-training-resources/blob/master/rbac-impersonate/generated/fe_team.yaml
#@ load("impersonate.lib.yml",
#@ "ImpersonateCRBinding", "ImpersonateCRole", "RoleBinding"
#@ )
https://github.com/k14s/ytt
#@ members = ["alice@example.com", "alanis@example.com"]
#@ prod_namespace = "prod-app-fe"
#@ stag_namespace = "staging-app-fe"
#@ dev_namespace = "dev-app-fe"
#@ team_user = "app-fe-user"
#! Add impersonation bindings <members> -> team_user
--- #@ ImpersonateCRBinding(team_user, members)
--- #@ ImpersonateCRole(team_user)
#! Allow *team_user* virtual-user respective access to below namespaces
--- #@ RoleBinding(team_user, prod_namespace, "view")
--- #@ RoleBinding(team_user, stag_namespace, "edit")
--- #@ RoleBinding(team_user, dev_namespace, "admin")
得到的YAML输出可以通过kubectl进行推送,像往常一样执行以下命令:
$ ytt -f . | kubectl apply -f- [ --dry-run=client ]
用户身份(本例中为“alice@example.com”)可以由本文开头讨论的任何身份验证机制提供。
步骤2:测试
在将RBAC资源推到集群之后,alice@example可以使用kubectl auth can-i…命令来验证设置。例如:
$ kubectl auth can-i delete pod -n dev-app-fe
no
$ kubectl --as app-fe-user auth can-i delete pod -n dev-app-fe
yes
$ kubectl --as foo-user auth can-i get pod
Error from server (Forbidden): users "foo-user" is forbidden: User "alice@example.com" cannot impersonate resource "users" in API group "" at the cluster scope
这完全是为Kubernetes的sudo的感觉,不是吗?
步骤3:将扮演设置保存到Kubernetes配置文件中
为了预先设置扮演的配置,可以在用户的KUBECONFIG文件的“user:”条目中添加一些没有广泛文档化的字段:
- name: alice@example.com@CLUSTER
user:
as: app-fe-user
auth-provider:
config:
client-id: <...>.apps.googleusercontent.com
client-secret: <...>
id-token: <... JWT ...>
idp-issuer-url: https://accounts.google.com
refresh-token: 1//<...>
name: oidc
这个持久的设置是有用的,因为它避免了需要:
- 在每次调用时向kubectl提供-as…参数
- 需要其他的Kubernetes工具来支持扮演,例如helm就是一个明显的缺少这个特性的例子 https://github.com/helm/helm/issues/5303
审计跟踪
Kubernetes扮演在审计跟踪方面设计得很好,因为API调用使用完整的原始身份(user)和扮演用户(impersonatedUser)进行日志记录。下面的代码片段显示了一个kube-audit日志跟踪条目,它是由kubectl –as app-fe-user get pod -n dev-app-fe触发的:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "Request",
"auditID": "032beea1-8a58-434e-acc0-1d3a0a98b108",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/dev-app-fe/pods?limit=500",
"verb": "list",
"user": {
"username": "alice@example.com",
"groups": ["system:authenticated"]
},
"impersonatedUser": {
"username": "app-fe-user",
"groups": ["system:authenticated"]
},
"sourceIPs": ["10.x.x.x"],
"userAgent": "kubectl/v1.18.6 (linux/amd64) kubernetes/dff82dc",
"objectRef": {
"resource": "pods",
"namespace": "dev-app-fe",
"apiVersion": "v1"
},
"responseStatus": {
"metadata": {},
"code": 200
},
"requestReceivedTimestamp": "2020-07-24T21:25:50.156032Z",
"stageTimestamp": "2020-07-24T21:25:50.161565Z",
"annotations": {
"authorization.k8s.io/decision": "allow",
"authorization.k8s.io/reason": "RBAC: allowed by RoleBinding "rb-app-fe-user-admin" of ClusterRole "admin" to User "app-fe-user""
}
}
挑剔的读者还会注意到,上面的“user”字段只有与用户身份相关的“username”,因为“system:authenticated”显然是一个通用的组值。
总结
通过现有的Kubernetes RBAC特性,集群管理员可以创建由角色用户扮演的虚拟用户安全主体,以建模“角色帐户”授权方案。这种方法提供了与Kubernetes安全配置相关的许多好处,如下所示:
- 它要求认证机制只提供用户身份数据(即不需要组)。
- 它允许Kubernetes集群管理员使用现有的Kubernetes RBAC扮演特性构建团队成员模式。
- 它允许Kubernetes集群管理员创建RBAC规则来针对这些扮演的“虚拟用户”访问Kubernetes资源(Kubernetes Rolebinding “subjects”,通常只有一个条目)。
- 它将成员关系从实际的资源访问规则中解耦,从而允许创建更清晰的RBAC条目。这样的条目更容易维护和审计,减少了集群管理员的复杂性和工作负载。
- 它有利于组织,因为集群管理员可以更精确地实现团队和项目控制,减轻员工的上岗/下岗以及相关的安全方面。
有用的链接
- https://en.wikipedia.org/wiki/Role-based_access_control
- https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/
- https://kubernetes.io/docs/reference/access-authn-authz/rbac/
- https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation
- https://get-ytt.io/
- flash player10.1 + FMS4中的p2p功能
- fms4 p2p:图片分享
- 老域名新用的优缺点分析
- 不用临时变量,交换二个整型变量的值
- monoTouch开发(1):win7 + vmware下安装mac os
- 数据结构C#版笔记--队列(Quene)
- 数据结构C#版笔记--堆栈(Stack)
- MySQL基础入门-第一课 新建数据库(linux版本)
- 2017年度最不安全密码报告,看看你的密码安全吗?
- 数据结构C#版笔记--顺序表(SeqList)
- 数据结构C#版笔记--单链表(LinkList)
- 操作系统 页式存储 页与块之间的关系详解
- 数据结构C#版笔记--双向链表(DbLinkList)
- 斐波那契数列与IE9
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- linux之网络
- linux vim编辑器之常用指令
- saltstack手册(含官方pdf)
- 详解Go变量类型的内存布局
- linux vim 编辑器之多文件多窗口编辑
- 深入解析 Go 中 Slice 底层实现
- 2017年的golang、python、php、c++、c、java、Nodejs性能对比(golang python php c++ java Nodejs Performance)
- linux文件时间属性 查看和修改文件时间
- Linux硬连接和软连接详解
- Linux 压缩,解压缩,打包指令
- Linux下使用python脚本执行BCP导入导出操作
- 通用高效字符串匹配--Sunday算法
- Golang fmt Printf 格式化参数手册/详解/说明
- 正则表达式(RegEx)官方手册/权威指南【Python】
- 玩透二叉树(Binary-Tree)及前序(先序)、中序、后序【递归和非递归】遍历