1、背景

使用阿里云ack托管版运行k8s集群,有时需要对团队内成员授权集群非管理员的权限,以及某些服务需要调用k8s相关api做特定操作,例如在指定的某namespace下创建某控制器(例如deployment)的权限

由于在ack托管版环境下,集群的master节点由阿里云托管,对租户不可见,因此除了基于k8sRBAC权限外,还需要授予阿里云的授权体系,即RAM

2、授权体系概述

2.1 k8s授权体系

先来回忆一下k8sRBAC(Role-Based Access Control)基于角色的访问控制授权体系

这是一种很常见并通用的授权体系,在k8s中,RBAC使用rbac.authorization.k8s.io API Group 来实现授权决策

Kubernetes1.6版本开始支持RBAC,集群管理员可以对用户或服务账号的角色进行更精确的资源访问控制。允许管理员通过Kubernetes API动态配置策略,在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色

如上图所示,左边对于Pod资源的getlist操作(称为请求动词)类似于常见的CRUD,对k8s的相关资源操作都是以REST API形式发起,每一类请求动词对应http请求如下表

HTTP 动词 请求动词
POST create
GET, HEAD get (针对单个资源)、list(针对集合)
PUT update
PATCH patch
DELETE delete(针对单个资源)、deletecollection(针对集合)

k8s中的资源和API Group关联,不同的资源属于不同的API Group,例如Pod属于Core API GroupDeployements属于apps API Group

资源操作的集合叫做RuleRBAC中的角色,在k8s中分为RoleClusterRole,其区别就是前者是命名空间级别下,后者不限命名空间(集群范围内生效)

上图最右边即用户,k8s中的用户可以分为三类:User、Group、Service Account,User即传统意义上的用户,这个用户一般由外部服务管理;Group用来关联多个账户;而Service Account(服务帐号)就是通过Kubernetes API来管理的一些用户帐号,适用于集群内部运行的应用程序。所有的用户称作一个Subject

有了角色和用户,就需要两者之间的绑定关系,由于角色有RoleClusterRole两类,因此绑定关系也分为RoleBindingClusterRoleBinding,即把声明的Subject和想要绑定的Role进行绑定(给某个用户绑定上某些操作的权限),二者作用范围和角色类似

2.2 阿里云RAM遇上RBAC

如文章封面图所示,ACK的授权体系包含对基础资源层的RAM授权和对ACK集群层的RBAC授权两部分

  • RAM授权对应ACK集群的运维操作,需要获取ACK产品及其所依赖阿里云云产品的OpenAPI操作权限,主要包括以下操作:
    • 集群:创建、查看、升级、删除
    • 节点池:创建、修改、扩缩容
    • 授权管理
    • 集群监控、日志、事件
  • RBAC授权对应的是运行于ACK集群中Kubernetes应用的运维操作,需要获取ACK集群及其命名空间的操作权限,主要包括对以下Kubernetes对象的增删改查操作:
    • 工作负载:Deployment、StatefulSet、DaemonSet、Job、CronJob、Pod、ReplicaSet、HPA等
    • 网络:Service、Ingress、NetworkPolicy等
    • 存储:PV、PVC、StorageClass等
    • Namespace、ConfigMap 、Secrets等

因此当RAM用户或RAM角色需要进行集群运维和应用运维时,需要依次对其进行RAM授权和RBAC授权

对于RAM这里不再深入,因为阿里云RAM关联了很多很多不同的云产品统一进行授权,因此很复杂,也不做过多研究

3、授权过程

上面对阿里云RAMk8sRBAC两个体系做了简要介绍和回顾,接下来根据一个实际场景简要介绍授权的整个过程以及需要注意的踩坑点

需求如下:

研发或集群外服务需要操作某集群,例如dev(开发)ack集群,具有dev命名空间的创建、删除deploymentserviceingress这三类资源的权限

若服务在集群内,那么就可以通过ServiceAccount方式,假设服务在集群外,目的是要生成一个给kubectl使用的config文件

3.1 创建RAM用户并授权

由于用户最终只能通过控制台才能获取config文件,因此需要创建一个可以登录控制台,并对ack具有只读权限的用户

然后对用户进行授权,记录下用户的密码

3.2 ACK集群中创建ClusterRole

在集群中创建相关授权通常是通过编写对应的yaml资源清单

这里推荐一个图形化的工具Permission manager,是一个简单便捷的RBAC管理界面工具,支持通过web界面创建用户,分配Namespace权限,并可以生成kubeconfig文件,项目地址https://github.com/sighupio/permission-manager

前面需求提到是指定命名空间下的权限,但这里是创建ClusterRole而不是Role,这个问题后面解释

ClusterRole的内容如下

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: project-rbac-role
rules:
  - apiGroups: ["extensions", "apps"]
    resources: ["deployments"]
    verbs: ["create", "delete"]
  - apiGroups: [""]
    resources: ["service"]
    verbs: ["create", "delete"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingress"]
    verbs: ["create", "delete"]

使用kubectl进行创建

➜  rbac git:(master) ✗ kubectl apply -f clusterrole.yaml

3.3 控制台授权绑定

ack的控制台对ram用户授权,点击到某个集群——>安全管理——>授权,选择上面创建的ram子账号,点击“管理权限”,选择集群(这里虽然已经进入到了特定集群,但依然可以选择其他集群,或许是ack产品设计上有点不合理),选择对应要授权的命名空间,访问权限选择“自定义”,然后下拉就能找到上面创建的名为project-rbac-roleClusterRole

上面提到了创建ClusterRole而不是Role,原因就在这里,因为这里下拉只能读取到集群中存在的ClusterRole,选择完成后点击下一步,授权成功

那么,既然已经使用了ClusterRole,又选定了命名空间,因此相当于还是基于Role的权限?

没错,实际上这里的操作是对应的在ack集群中创建了一个Rolebinding绑定集群的ClusterRole,正常来说应该是Rolebinding绑定集群的RoleClusterRolebinding绑定集群的ClusterRole,这里相当于交叉绑定了,但实际上这个ClusterRole的权限范围缩小了

可以通过如下方式查看集群中的Rolebinding,名称大致为“ram用户的uid-命名空间-clusterrole的名称-rolebinding”,关联的subjects用户正是ram用户的uid

➜  rbac git:(master) ✗ kubectl get rolebinding -n dev|grep 29xxxxxxxxxxx1
29xxxxxxxxxxx1-dev-project-rbac-role-rolebinding   ClusterRole/project-rbac-role    41m
➜  rbac git:(master) ✗ kubectl -n dev get rolebinding 29xxxxxxxxxxx1-dev-project-rbac-role-rolebinding -o yaml|kubectl neat
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: 29xxxxxxxxxxx1-dev-project-rbac-role-rolebinding
  namespace: dev
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: project-rbac-role
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: "29xxxxxxxxxxx1"

到这里授权就完成了

3.4 获取连接集群的config

用上面创建的readonly这个ram用户登录到ack的每个集群控制台,获取这个ram用户的集群连接信息即config内容,保存成文件即可

4、小结

使用ack托管版对集群授权需要关联ram用户和集群RBACClusterRole,无需在集群中手动创建其他资源,整个过程中有一定的小坑和个人感觉设计不太合理的地方仅供参考~

See you ~

参考

https://kubernetes.io/zh/docs/reference/access-authn-authz/authorization/

https://help.aliyun.com/document_detail/119596.html