本站文章大部分为作者原创,非商业用途转载无需作者授权,但务必在文章标题下面注明作者 刘世民(Sammy Liu)以及可点击的本博客地址超级链接 http://www.cnblogs.com/sammyliu/ ,谢谢合作

理解OpenShift(4):用户及权限管理

理解OpenShift(1):网络之 Router 和 Route

理解OpenShift(2):网络之 DNS(域名服务)

理解OpenShift(3):网络之 SDN

理解OpenShift(4):用户及权限管理

理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume

 

** 本文基于 OpenShift 3.11,Kubernetes 1.11 进行测试 ***

 

OpenShift 支持 RBAC(Role Based Access Controll),基于角色的访问控制。它涉及诸多概念,本文尝试着做一些概念上的梳理,很多细节还需要进一步研究。

1. 主要概念及其之间的联系

1.1 用户(User)

我试着把一个OpenShift 环境中的所有用户分为三大类:

  • 应用用户:部署在集群之中的应用自己的用户。一般来说每个应用都有自己的用户管理系统,与平台无关。也有一些应用,比如 Jenkins,支持与OpenShift 用户系统集成,也就是Jenkins允许用户在通过了OpenShift 用户认证后对其进行访问。这部分不是本文的讨论范围之内。
  • OpenShift 用户:访问OpenShift 资源的用户。根据其特征,又将其分为三个子类:
    • Regular user:代表一个自然人用户,比如部署应用的一个开发者。
    • System user:OpenShift 系统用户,大部分在集群创建时被自动创建,比如每个node都有一个system user。因为这部分主要和OpenShift自身系统相关,与一般用户关系不太大,因此本文不会具体介绍这部分。
    • Service account:服务账户。这是跟一个项目关联的特殊系统用户,每个用户被一个 ServiceAccount 对象表示,通常是指 pod 中运行主进程的用户。后文会有具体介绍。
  • 操作系统用户:访问操作系统资源的用户,又分为容器内的操作系统用户,和宿主机上的操作系统用户。

1.2 身份(Identity)与认证(Authentication)

认证是确认用户身份是否合法的过程。这可以有多种实现方式,比如用户名/密码、令牌(token)、证书等。不同类型的用户有不同的身份管理方式:

  • 对于 regular user,每个用户有一个身份(identity)用于认证。OpenShift  以插件形式支持多种 identity provider,比如在测试环境中常用的 htpasswd,生产环境中常用的 LDAP 等。这些 provider 中会保存用户身份信息,比如用户名和密码。useridentitymapping 对象将 user 对象和 identity 对象联系在一起。
  • 对于 service account,一方面它需要访问 OpenShift 集群资源比如 API 和内部镜像仓库中的镜像,另一方面它可能需要访问 pod 中和宿主机上的操作系统资源,比如宿主机上的文件或网络。对于前者,每个 service account 使用 secret 来进行身份认证,包括用户 API 访问的 token 和用于从镜像仓库拉取代码的 secret。对于后者,本来有 user namespace(用户命名空间)来支持,但是似乎OpenShift 还不支持该功能。

1.3 角色(Role)/权限(Permission)与授权(Authorization)

授权是对被认证了的用户访问受控制的资源的权限进行控制。按照资源类型,OpenShift 将授权管理方式分为两大类:

  • 对于 OpenShift 集群资源,比如 pod,deployment,route 等,通过 role (角色)进行控制。从范围(scope)上分,role 可分为集群角色(clusterRole)和项目角色(role)。每个角色定义了受控制的对象(subject)、允许的操作(verb)和范围(集群还是项目)。用户(user)和角色(role/clusterRole)之间通过 rolebinding/clusterrolebinding 对象进行绑定。
  • 对于操作系统资源,这只针对服务账户。宿主机上的用户访问宿主机上的资源,这由宿主机操作系统进行控制。pod 中的用户(serviceaccount)访问pod内和宿主机上操作系统资源,由 scc(security context constraints)进行控制。

2. 身份 (Identity) 与认证(Authentication)

就像人的身份证一样,identity 是一个 user 的身份信息,该信息用于用户认证。OpenShift 自己并没有实现用户身份信息库,而是通过灵活支持多种 identity provider,来实现各种不同的身份管理方案。每种实现都以不同的方式保存着用户的身份信息,比如保存在LDAP 中,保存在  htpasswd 文件中,保存在 OpenStack Keystone 中。

 

因此,OpenShift 对 user 身份的校验是通过这些配置了的 identity provider 进行的。因此,还需要 OpenShift user 和这些 provider 里面的 identity 的映射关系。OpenShfit 支持四种映射管理,claim,lookup,generate,add。具体请参考官网 https://docs.openshift.com/container-platform/3.11/install_config/configuring_authentication.html#identity-providers_parameters

上图中以 htpasswd 这种 identity provider 为例,说明了从零创建一个 openshift user,在 htpasswd 中创建 user 及其密码,然后创建 openshift identity 对象以及 useridentitymapping 对象的过程。

用户获取用于身份认证的token的过程:

  • 所有申请 OAuth token 的请求都要发到 <master>/oauth/authorize和<master>/oauth/token。每个申请 OAuth token 的请求中都必须带有 OAuth client 标识,这是一个 OAuthClient  对象。具体请参阅官方文档。
  • OpenShift master 节点上内置有一个 OAuth server。用户从 OAuth 获取 token 后再用它去访问 API 就可以认证通过了。当一个 user 申请一个 OAuth token 时,OAuth 使用配置的 identity provider 去确定该申请用户的身份。它在确定该用户所映射到的 identity后,会为该用户创建一个 token,然后返回该token。

3. 角色(Role)和授权(Authorization)

前文说了,角色用于控制对 OpenShift 资源的访问权限,它分为项目角色和集群角色。

OpenShift 系统默认会创建很多的集群角色。常用的角色的简单描述如下: 

admin
  • project manager
  • 如果用于本地 rolebinding,那么用户将能查看和修改所在项目中的所有资源
basic-user
  • user 可以获取关于项目和用户的基本信息
cluster-admin
  • 用户可以在任何项目中做任何操作(超级用户)
  • 如何用于本地 rolebinding,那么用户将拥有所在project的所有权限,包括控制quota和role
cluster-status
  • 用户可以获取集群的基本状态信息
edit
  • 用户可以修改项目中的大部分对象;但是不能查看或修改 role 和 rolebinding
self-provisioner
  • 所有用户的默认role,可以创建自己的project
view
  • 用户可以查看项目中的大部分对象,除了 role 和 rolebinding
  • 用户不能做任何修改任何对象 

可以查看所有角色,比如 system:router 角色:

可以使用 oc adm policy 命令向用户或用户授予角色:

user、group、role、rolebinding 之间的关系:

更多对 role 的说明,可参见官方文档。 

4. Service Account 用户

 

OpenShift 的 service account 比较复杂,和很多概念都有关联。官方文档在  https://docs.openshift.com/container-platform/3.11/dev_guide/service_accounts.html。该文档对为什么需要这个概念的说明是:当一个自然人用户访问 OpenShfit API 时,OpenShift 对它进行用户认证和权限控制。但是,有时候做操作的并不是自然人用户,比如:

  • Replication Controller 调用 API 去创建或者删除 pod
  • 容器中的应用调用 API
  • 外部应用调用 API 去进行监控或者整合

为了这种访问,OpenShift 创造了 Service Account (简称sa)概念。每个项目(project)默认都会自动创建3个sa user: 

Service AccountUsage

builder

用于 build pod。它被授予了 system:image-builder 角色,因此被允许向内部镜像仓库中的任意 image stream 上push 镜像。

deployer

用于 deployment pod。被授予了 system:deployer 角色,因此被允许查看和修改集群中的 RC 及其 pod。

default

当一个 pod 没有显式指定 service account 默认都会使用该 sa user。

4.1 身份

sa 利用 secret(token)来保存其身份信息。默认情况下,每个 sa 用户都有两种token,即访问 OpenShift API 的token和访问内部镜像仓库的token。以系统默认的 『builder』 sa user 为例,它包含一个用于拉取镜像的token secret,两个访问API 的token secret,三个secret 中只有两个能被以卷的形式挂接给pod:

可以按需求修改一个 sa 账户的这些token。可参考 https://docs.openshift.com/enterprise/3.0/dev_guide/image_pull_secrets.html 为service account 创建和添加新的用于拉取镜像的token secret。具体请参考官方文档。

其中,sa 的 API token 会被挂接到 pod 的 目录的 token 文件,从而使得 pod 中的应用可以读取该 token 去访问 OpenShift API:

image pull secret 是如何挂载到 pod 的,我还没有找到。而且在 pod spec 中,只看到 API token secret 的 mountpoint,而并没有 imagePullSecret 的 mountpoint:

4.2 权限 - 访问OpenShift 集群资源的权限

和自然人 user 类似,对 sa 用户访问OpenShift 集群资源的权限控制是通过 role 进行的。前面的表格表明,项目的两个默认 sa builder 和 deployer 都被授予了相应的 role,从而能访问指定的资源。而默认的 sa 用户,只被授予了 /system:image-puller 角色。这意味着默认的 sa 用户只能拉取镜像,而不能访问集群其它资源。

下图是项目 stage 中的 rolebinding:

用户组 system:serviceaccounts:stage 中包括该项目中的所有 sa 用户。利用 default sa user 来做下实验。先获取其 API token,然后登录进 OpenShift 集群:

调用 API 获取 pod,结果失败:

向 default sa user 授予 cluster-reader 角色:

然后就可以做集群资源查询操作了。 

4.3 权限 - 访问系统资源的权限

pod 中的应用除了有访问 OpenShift API 和内部镜像仓库之外,还有一些系统资源访问要求。比如:

  • 要求以任意用户甚至是 root 来运行 pod 中的主进程
  • 要求访问宿主机上的文件系统
  • 要求访问宿主机上的网络

对于这些操作系统资源的访问权限,OpenShift 利用 scc 来进行控制。这就要求:

  • 在 scc 中进行权限控制。这部分在后面介绍。
  • 在 servie account 和 sa 之间建立联系。每个 scc 都有指定使用它的用户列表。所有通过身份认证了的用户都只在 restricted 这个 scc 的用户列表之中,包括 service account。因此,pod 默认使用的是 restricted scc。要使它使用其它的scc,就要将它的 service account user 加入到要使用的 scc 的用户列表之中。这在 scc 部分具体介绍。

5. Security Context Constraint(SCC)

前面说过,SCC 用于控制访问系统资源的权限,那说明只有  service account  才需要使用 scc。没在文档中看到自然人用户 user 使用 scc 的例子。

Linux 中的权限控制很是复杂,这里就不说明了,我自己也没怎么弄清楚。OpenShift scc 将系统权限分为几大类,具体见上图中的『权限』部分,然后可以创建 scc 对象来精细地控制对每种权限的控制。

5.1 OpenShift默认的 scc

因为这非常繁琐,因此 OpenShift 默认创建了几个典型的 scc,列表如下。上图中的『系统预定义scc』部分有简要说明,这里不再重复。

其中,如果 pod 所使用的 scc 的 runAsUser 策略被设置为了 MustRunAsRange,那么 pod 所使用的 uid 和 supplemental-group id 必须在某区间之内。

集群管理员可以在 scc 中设置区间,比如:

但是,OpenShift 默认提供的 scc 都没有设置该区间,而是使用 pod 所在的 project 中定义的区间之内。比如:

[root@master2 cloud-user]# oc describe project dev
Name:                   dev
Created:                3 weeks ago
Labels:                 <none>
Annotations:            openshift.io/description=
                        openshift.io/display-name=
                        openshift.io/requester=demo
                        openshift.io/sa.scc.mcs=s0:c16,c0
                        openshift.io/sa.scc.supplemental-groups=1000000000/10000
                        openshift.io/sa.scc.uid-range=1000000000/10000

每个 project 会被分配不同的 ID 区间。当未指定 uid 和 supplemental gid 时,会默认使用区间的最小值。

每个 pod 中,运行主进程的用户都有三个属性:

  • uid 即 user id,上面例子中uid 为 100000000,使用的是 project 定义的 uid 区间的第一个值。其策略分为三种:
    • MustRunAs - 需要配置一个区间,要么在 project 中配置,要么在 scc 中指定。默认使用区间的第一个值。指定特定值的话,会检查是否在该区间内。
    • MustRunAsNonRoot - 要求指定非 root user id,不提供默认值。
    • RunAsAny - 允许所有用户,不提供默认值。
  • gid 即用户的primary group id(主组ID)。上面例子中 gid 为 0,组名字为 root。从 https://github.com/kubernetes/enhancements/issues/213 上看,目前 Kubernetes 还不支持设置 gid,其值固定为 0.
  • fsGroup 定义 pod 的 文件系统 group ID,用于块存储,比如 Ceph RDB 和 iSCSI。supplementalGroups ID 用于共享存储,也是组/group ID 的一种,用于共享存储,比如 NFS 和 GlusterFS。上面例子中的 supplementalGroup ID 为 10000000,取的也是默认值。这两种 group ID 支持 MustRunAs 和 RunAsAny 两种策略。会在下一篇存储部分详细介绍。

备注:Supplemental group(附加组)也是Linux 组的一种。当一个进程运行在 Linux 中时,它会拥有一个 UID,一个 GID,以及一个或多个附加组ID。具体请查阅Linux 相关文档。

如果某 scc 设置的 RunAsUser 策略为 MustRunAsRange,它会要求配置合法的 uid 区间(要么在 project 中配置,要么在 scc 中指定)。如果你要指定特定的 uid ,你可以在 pod 定义 yaml 中使用 securityContext: runAsUser <uid> 来指定特定的 user id,但是该 id 必须在区间之内。否则会报错:

Error from server (Forbidden): error when creating "testcontainervolume-restricted.yaml": pods "test-pod-volume-restricted2" is forbidden: unable to validate against any security context constraint: [spec.containers[0].securityContext.securityContext.runAsUser: Invalid value: 65534: must be in the ranges: [1000000000, 1000009999]]

5.2 容器使用的默认 scc

根据创建 pod 的用户不同,pod 使用不同的默认 scc:

  • 非 cluster admin 角色的 openshift 用户创建的没有显式指定 service account 和 scc 的 pod,其默认使用的 sa user 为 default,默认使用的 scc 为 restricted。
  • cluster admin 用户,根据 scc 优先级,anyuid 将是这种 pod 默认使用的 scc。

SCC 优先级:一个 sa user 可以被加到多的 scc 当中。当一个 service account user 有多个 SCC 可用时,其 scc 按照下面的规则排序

  • 最高优先级的scc排在最前面。默认地,对于 cluster admin 角色的用户的 pod,anyuid scc 会被授予最高优先级,排在最前面。这会允许集群管理员能够以任意 user 运行 pod,而不需要指定 pod 的 SecurityContext 中的 RunAsUser 字段。
  • 如果优先级相同,scc 将从限制最高的向限制最少的顺序排序。
  • 如果优先级和限制都一样,那么将按照名字排序。

每个 scc 有其用户/用户组列表。以 restricted 为例,所有通过身份验证的用户都在列表中;而 anyuid,只有 cluster-admins 用户组中的用户在里面。

  

5.3 修改容器使用的scc

要授权 pod 有除了 restricted 定义的权限之外的权限,主要有两种做法:

  • 一是将其 service account 放到目标 scc 的用户列表中。此时建议创建一个新的 service account,而不要使用已有的,考虑到对现有 pod 的影响。比如因为 registry 和 router 服务的 pod 需要使用 host networkinig 网络模式,因此有为它们创建单独的 sa user 并且加入到了 hostnetwork scc 的用户列表中。

  • 二是将 service account 加入到目标 scc 的用户组中。

官方建议采用第一种。一个很常用的例子是运行要使用 root 用户的容器。很多的Docker 镜像都使用的是 root 用户。但是,openshift restricted scc 不允许使用 root 用户,而要使用一个用户区间内的用户:

修复步骤:

$ oc create serviceaccount useroot
$ oc adm policy add-scc-to-user anyuid -z useroot
$ oc patch dc/myAppNeedsRoot --patch '{"spec":{"template":{"spec":{"serviceAccountName": "useroot"}}}}'

备注:

  • 第二个语句中的 -z userroot 用于指定当前project 中的 sa user,它和使用 system:serviceaccount:<project>:userroot 效果相同。
  • 在 pod 中设置 service account,其属性名字为 serviceAccount,不是 dc 中的 serviceAccount。

说明:

  • 第一步创建名为 userroot 的 sa user
  • 第二步将该 sa user 加入到 anyuid scc 的 user 列表中
  • 第三步在应用的 DeploymentConfig 中指定 serviceAccountName 为 userroot

 

感谢您的阅读,欢迎关注我的微信公众号:

 

posted on 2018-12-08 10:07  SammyLiu  阅读(17688)  评论(0编辑  收藏  举报