kubernetes学习笔记十(安全)

kubernetes学习笔记十(安全)

Scroll Down

机制说明:

Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server 是集群内部各个组件通信的中介,也是外部控制的入口。所以 Kubernetes 的安全机制基本就是围绕保护 API Server来设计的。Kubernetes 主要使用了认证(Authentication)鉴权(Authorization)准入控制(AdmissionControl)三步来保证API Server的安全

Kubernetes API 访问控制流程:

k8s_3a.jpg

由上边的访问控制流程图我们可以知道,客户端请求首先需要和API Server 建立一个安全的TLS连接,当请求到达API Server时,这个请求会经历多个阶段,首先是第一步的认证,认证通过之后是第二步的鉴权,鉴权通过之后就是第三步的准入控制,准入控制通过之后该请求才会被写入etcd数据库进行持久化:

第一步的认证用通俗的话来讲就是判断请求通信的客户端的身份,这一过程主要有两种形式,一种是集群内Pod对APIServer的访问(通过SA进行认证),一种是集群组件对APIServer的访问(Apiserver本地的组件访问APIServer直接通过非安全端口进行通信,非本地的组件需要使用基于https的双向认证)

认证模块包含客户端证书、密码、普通令牌、引导令牌和 JSON Web 令牌(JWT,用于服务账户)

可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。

如果请求认证不通过,服务器将以 HTTP 状态码 401 拒绝该请求。

认证完成之后还不能访问集群内的资源,认证解决的问题只是识别用户的身份,并没有判断用户所具有的权限;

所以就来到了第二步的鉴权,鉴权是通过一些组件来实现的,目前主流的插件就是RBAC,作用就是判断是否允许客户端的这次请求。请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。

k8s支持同时设置多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)

鉴权之后还需要进行准入控制;

所以就到了第三步的准入控制,准入控制是作用于 kubernetes 中的对象,通过合理的权限管理,能够保证系统的安全可靠。这才算一次完整的安全的访问k8s集群内资源的过程。

所以现有的比较稳定的安全访问的方案就是”第一步的https + 第二步的RBAC + 第三步的一些准入控制机制“

第零步:建立安全传输

在典型的 Kubernetes 集群中,API Server在 443 端口上提供服务,受 TLS 保护。 API Server出示证书。 该证书可以使用私有证书颁发机构(CA)签名,也可以基于链接到公认的 CA 的公钥基础架构签名。

如果集群使用了私有证书颁发机构,则需要在客户端的 ~/.kube/config 文件中提供该 CA 证书的副本, 以便你可以信任该连接并确认该连接没有被拦截。

你的客户端需要在此阶段出示 TLS 客户端证书

第一步:Authentication 认证

如上图步骤 1 所示,建立 TLS 后, HTTP 请求将进入认证(Authentication)步骤。

认证步骤的输入整个 HTTP 请求;但是,通常组件只检查头部或/和客户端证书。

认证模块包含客户端证书、密码、普通令牌、引导令牌和 JSON Web 令牌(JWT,用于服务账户)。

可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。

如果请求认证不通过,服务器将以 HTTP 状态码 401 拒绝该请求。 反之,该用户被认证为特定的 username,并且该用户名可用于后续步骤以在其决策中使用。 部分验证器还提供用户的组成员身份,其他则不提供。

HTTP Token 认证:通过一个 Token 来识别合法用户

HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 - Token 来表达客户的一种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token

HTTP Base 认证:通过用户名+密码的方式认证

用户名+:+密码用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 HeatherAuthorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码

以上两种都是基于单向认证,即服务端可以检测客户端的身份是否合法,而客户端不能检测服务端的身份,安全性上差了那么一点

最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式

而HTTPS基于证书的双向认证,在安全性上无疑是最好的

Ⅰ、HTTPS证书认证过程

k8sca.jpg

大概过程:在k8s集群中,我们使用的是自签证书颁发机构(CA),刚开始服务端和客户端都向CA申请证书,CA分别下发证书给它们,当客户端与服务端通信的时候,客户端会发送自己的证书给服务端,服务端会发送自己的证书给客户端,这是一个双向的认证过程,认证通过之后,双方便基于随机私钥来进行通信。

Ⅱ、需要认证的节点

k8scommunication.jpg

在kubernetes集群中,需要认证的资源分为两种类型:

一种是kubernetes自身的组件对API Server的访问:例如kubectl 、Controller Manager、Scheduler、kubelet、kube-proxy

一种是kubernetes管理的Pod对容器的访问:Pod( dashboard也是以Pod形式运行的 )

安全性说明

Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用 API Server 的非安全端口访问,--insecure-bind-address=127.0.0.1

kubectl、kubelet、kube-proxy 访问 API Server 就都需要证书进行 HTTPS 双向认证

证书颁发

手动签发:通过 k8s 集群的跟 ca 进行签发 HTTPS 证书

自动签发:kubelet 首次访问 API Server 时,使用 token 做认证,通过后,Controller Manager 会为kubelet 生成一个证书,以后的访问都是用证书做认证了

Ⅲ、kubeconfig

kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群context 信息(集群名称、用户名)。Kubenetes 组件通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群

Ⅳ、ServiceAccount(SA)

Pod中的容器访问API Server使用的就是这种方式。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了,频繁创建证书太过繁琐还浪费集群资源。素以Kubenetes使用了Service Account(简称SA)解决Pod 访问API Server的认证问题

Ⅴ、Secret 与 SA (Service Account)的关系

Kubernetes 设计了一种资源对象叫做 Secret,其分为两类,一种是用于 ServiceAccount 的 service-account-token,另一种是用于保存用户自定义保密信息的 Opaque。ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace

token是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证

Json Web Token( JWT ) ,是为了在网络应用环境间传递声明而执行的一种基于Json的开放标准。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其他业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密

ca.crt,根证书。用于Client端验证API Server发送的证书

namespace, 标识这个service-account-token的作用域名空间

# 获得所有名称空间下的secret
kubectl get secret --all-namespace

# 描述某一个特定的secret
kubectl describe secret default-token-kh78r -n kube-system

describe_secret_allnamespace.jpg

默认情况下,每个 namespace 都会有一个 ServiceAccount,如果 Pod 在创建时没有指定 ServiceAccount,就会使用 Pod 所属的 namespace 的 ServiceAccount

k8s_authentication_summary.jpg

总结:

k8s的认证分为两种:

1、集群内Pod的认证

Pod访问ApiServer只能通过Service Account

2、集群内k8s组件的认证

Controller Manager、Scheduler等与ApiServer在一台机器上的情形下,直接访问ApiServer的非安全端口,insecure-bind-address=127.0.0.1

其它不在一台机器上的远程组件kubelet、kube-proxy访问ApiServer,以及用户使用 kubectl客户端库构造 REST 请求来访问 Kubernetes API 都需要证书进行HTTPS双向认证(需要手动颁发证书)

当认证通过之后,并不代表访问者已经具有访问k8s资源的权限,认证之后还需要进行鉴权,只有鉴权完成之后才有了访问对应k8s资源对象的能力

第二步:鉴权(Authorization)

上面的认证过程,只是验证了请求是来自哪个特定的用户,之后的鉴权则是确定请求方有哪些资源的权限。

请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。 如果现有策略声明用户有权完成请求的操作,那么该请求被鉴权通过。

API Server 目前支持以下几种授权策略( 通过 API Server 的启动参数 “--authorization-mode” 设置 )

✦ AlwaysDeny:表示拒绝所有的请求,一般用于测试

✦ AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略

✦ ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制。ABAC首先需要定义一长串的属性,并且修改之后不能即时生效,需要重启APIServer( 现已淘汰 )

✦ Webbook:通过调用外部 REST 服务对用户进行授权。Webook无法通过集群内部的kubectl进行操作

✦ RBAC(Role-Based Access Control):基于角色的访问控制,现行默认规则

Kubernetes 支持多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,他们配置应在 API Server中使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。

之前的默认策略是ABAC,而现在最常用以及最流行的就是RBAC模式

RBAC 授权模式

RBAC(Role-Based Access Control)基于角色的访问控制,在 Kubernetes 1.5 中引入,现行版本成为默认标准。相对其它访问控制方式,拥有以下优势:

✦ 对集群中的资源和非资源均拥有完整的覆盖,即RBAC模式下,可以基于角色对集群所有资源的设置访问控制

✦ 整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作

✦ 可以在运行时进行调整,无需重启 API Server

Ⅰ、RBAC的API资源对象说明

RBAC 引入了 4 个新的顶级资源对象:RoleClusterRoleRoleBindingClusterRoleBinding,4 种对象类型均可以通过 kubectl 与 API 操作

rbac.jpg

Kubernetes采用 HTTP协议进行**CS的构建服务(ETCD也是采用HTTP协议进行CS的构建服务),其实也就是基于Restful 风格的编程接口,在这种接口下它会有一些操作的动作类型,比如create、get、update,比如create pod就是一个创建pod的操作,create、get、update这一系列的动作会被赋予一个Role以执行这个动作,这个赋予了一定权限的Role会被执行RoleBinding,即将角色的权限绑定到相应的User、Group或ServiceAccount。这就是执行一个动作的创建角色、角色绑定的一个过程。

clusterrolesample.jpg

在K8S集群中,Role、RoleBinding都是名称空间级别的资源,忘了可见这里而在设定RoleBinding必须要为其指定名称空间,表示为其分配的角色权限在某个名称空间下才起作用这样,假设每一个名称空间都需要一个只读权限的角色ReadOnlyRole,那么就需要在每一个名称空间下都创建一个只读角色ReadOnlyRole,并在使用Role时进一步设置RoleBinding。这就过于繁琐了,为了解决这个问题就引入了集群级别的资源ClusterRole、ClusterRoleBindingClusterRole、ClusterRoleBinding都是集群级别的资源,在使用ClusterRoleBinding的时候就不用为其指定名称空间了。

需要注意的是:ClusterRole也可以进行RoleBinding,如果用户被执行的是ClusterRole的RoleBinding(对ClusterRole的RoleBinding也需要指定名称空间),那就相当于用户获得的了ClusterRole权限中的对应某个特定名称空间的特定权限,而不是针对所有集群资源的该特定权限。

需要注意的是 Kubenetes 并不会提供用户管理,那么 User、Group、ServiceAccount 指定的用户又是从哪里来的呢? Kubenetes 组件(kubectl、kube-proxy)或是其他自定义的用户在向 CA 申请证书时,需要提供一个证书请求文件,在这个证书请求文件中,就已经包含了这些信息。

{
    "CN": "admin",
    "hosts": [],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "HangZhou",
            "L": "XS",
            "O": "system:masters",
            "OU": "System"
        }
    ]
}

在这个证书文件中,API Server会把客户端证书的CN字段作为User,把names.O字段作为Group

1、kubelet 使用 TLS Bootstaping 认证时,API Server 可以使用 Bootstrap Tokens 或者 Token authenticationfile 验证 =token,而无论哪一种,Kubenetes 都会为 token 绑定一个默认的 User 和 Group

2、Pod 使用 ServiceAccount 认证时,service-account-token 中的 JWT(Json Web Token,一种特定网络场景下使用的token) 会保存 User 信息,有了用户信息,再创建一对角色/角色绑定(集群角色/集群角色绑定)资源对象,就可以完成权限绑定了

Role & ClusterRole

RBACRoleClusterRole 中包含一组代表相关权限的规则。 这些权限是纯粹累加的,即不能做去除现有权限的操作(不存在拒绝某操作的规则)。

Role 总是用来在某个namespace内设置访问权限;在你创建 Role 时,你必须指定该 Role 所属的namespace

与之相对,ClusterRole 则是一个集群作用域的资源。这两种资源(Role 和 ClusterRole)的名字不同是因为 Kubernetes 对象要么是namespace级别作用域的,要么是集群级别作用域的, 不可两者兼具。

ClusterRole 有若干用法,你可以用它来:

定义对某namespace级别对象的访问权限,并使权限在所有名称空间下生效

namespace下的对象设置访问权限,使其可以跨名称空间去访问其他名称空间下资源(如Pods),比如可以使用ClusterRole来允许某特定用户执行 kubectl get pods --all-namespaces

为集群级别的资源定义访问权限

为非资源端点授予访问权限(比如/healthz)

如果希望在namespace内定义角色,应该使用 Role; 如果希望定义集群范围的角色,应该使用 ClusterRole

Role示例:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1  # apiVersion 其实就是一个apiGroup
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]   # 这里指定的是pod-reader这个Role所属的api版本,如果为空默认指定的是 core API group
  resources: ["pods"]  # Role所指定权限的对象是pod
  verbs: ["get", "watch", "list"]  # 指定的权限为,可以及进行get获取pod,watch监听pod,list列出pod

ClusterRole示例:

该示例可授予对某个特定名称空间中的Secret的读访问权限(RoleBinding);或授予对任一名称空间下的Secret的访问权限( ClusterRoleBinding )

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" 被忽略,因为 ClusterRoles 不受名称空间限制
  name: secret-reader
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Secret 对象的资源的名称为 "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

RoleBinding & ClusterRoleBinging:

角色绑定(Role Binding)是将角色中定义的权限赋予给一个用户或者一个组。 RoleBinding由两部分组成,一部分是可以将权限授予的对象subjects( users、groups、service account ) ,一部分是对指定的Role权限的引用 。官网的解释太拗口了,这里我就做了自己的解释。 RoleBinding只能在指定的名称空间中执行授权,而 ClusterRoleBinding可以在集群范围执行授权。

一个 RoleBinding 可以引用同一名称空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名称空间。 如果希望将某 ClusterRole 绑定到集群中所有名称空间,则要使用 ClusterRoleBinding。即ClusterRole既可进行RoleBinding( 只会具有某个特定名称空间的相应权限 )、也可进行ClusterRoleBinding(具有跨名称空间的相应权限)

RoleBinding示例

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许用户 "jane" 读取 "default" 名称空间中的 Pods
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 你可以指定不止一个“subject(主体),即进行RoleBind的主体可以有多个”
- kind: User
  name: jane # "name" 是不区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系,即权限来源为 Role 或者 ClusterRole
  kind: Role # 此字段必须是 Role 或 ClusterRole
  name: pod-reader     # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
  apiGroup: rbac.authorization.k8s.io

RoleBinding 也可以引用 ClusterRole,以将对应 ClusterRole 中定义的访问权限授予 RoleBinding 所在名称空间的资源。这种引用使得你可以跨整个集群定义一组通用的角色, 之后在多个名称空间中复用

例如,尽管下面的 RoleBinding 引用的是一个 ClusterRole,但是"dave"(这里的主体, 不区分大小写)只能访问 "development" 名称空间中的 Secrets 对象,因为 RoleBinding 所在的名称空间(由其 metadata 决定)是 "development"。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "default" 名称空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
  name: read-secrets
  # RoleBinding 的名称空间决定了访问权限的授予范围。
  # 这里仅授权在 "development" 名称空间内的访问权限。
  namespace: development
subjects:
- kind: User
  name: dave # 'name' 是不区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding 示例 :

要跨整个集群完成访问权限的授予,你可以使用一个 ClusterRoleBinding。 下面的 ClusterRoleBinding 允许 "manager" 组内的所有用户访问任何名称空间中的 Secrets。

apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名称空间中的 secrets
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # 'name' 是不区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

创建了绑定之后,你不能再修改绑定对象所引用的 Role 或 ClusterRole。 试图改变绑定对象的 roleRef 将导致合法性检查错误。 如果你想要改变现有绑定对象中 roleRef 字段的内容,必须删除重新创建绑定对象。

这种限制有两个主要原因:

  1. 针对不同角色的绑定是完全不一样的绑定。要求通过删除/重建绑定来更改 roleRef, 这样可以确保要赋予绑定的所有主体会被授予新的角色(而不是在允许修改 roleRef 的情况下导致所有现有主体胃镜验证即被授予新角色对应的权限)。
  2. roleRef 设置为不可以改变,这使得可以为用户授予对现有绑定对象的 update 权限, 这样可以让他们管理主体列表,同时不能更改被授予这些主体的角色。

命令 kubectl auth reconcile 可以创建或者更新包含 RBAC 对象的清单文件, 并且在必要的情况下删除和重新创建绑定对象,以改变所引用的角色。

对资源的引用

在 Kubernetes API 中,大多数资源都是使用对象名称的字符串表示来呈现与访问的。例如,对于 Pod 应使用 "pods"。RBAC 使用对应 API 端点的 URL 中呈现的名称来引用资源。同时 Kubernetes API 中某些资源也会包含子资源,例如 logs 资源就属于 pods 的子资源,API 中 URL 样例如下:

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

上例中,pods 对应名称空间作用域的 Pod 资源,而 logpods 的子资源。 在 RBAC 角色表达子资源时,使用斜线(/)来分隔资源和子资源。

如果要允许某主体读取 pods 同时访问这些 Pod 的 log 子资源,你可以这么写:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

对于某些请求,也可以通过 resourceNames 列表按名称引用资源。 在指定时,可以将请求限定为资源的单个实例。 下面的例子中限制可以 "get" 和 "update" 一个名为 my-configmapConfigMap:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: configmap-updater
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 ConfigMap 的资源的名称为 "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-configmap"]
  verbs: ["update", "get"]
  
# 说明:
# 不能针对 create 或者 deletecollection 请求来实施 resourceName 限制。 仅拿 create 操作而言,这是因为在鉴权时还不知道对象名称

Aggregated ClusterRole:

你可以将若干 ClusterRole 聚合(Aggregate) 起来,形成一个复合的 ClusterRole。 controller 作为集群控制层面的一部分运行,会监听带有 aggregationRule集 的 ClusterRole 对象。aggregationRule 会定义一个标签选择器aggregationRule.clusterRoleSelectors.matchLabels,控制器使用该标签选择器来匹配其他同样具有同一个标签选择器的ClusterRole对象,被匹配到的ClusterRole对象将被组合到一个Aggregated ClusterRole中。

aggregated ClusterRole的一个例子:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # The control plane 自动填充这里的规则

如果你创建了一个与现有Aggregated ClusterRolelabbel selector相同的 ClusterRole, 这将会触发规则将你新创建的ClusterRole添加到具有相同labbel selectorAggregated ClusterRole 中去。 下面的例子中,通过创建一个标签为 rbac.example.com/aggregate-to-monitoring: true 的 名为monitoring-endpoints的ClusterRole,可将其具有的权限规则添加到同样具有相同label`的名为"monitoring" ClusterRole 中:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# When you create the "monitoring-endpoints" ClusterRole,
# the rules below will be added to the "monitoring" ClusterRole.
rules:
- apiGroups: [""]
  resources: ["services", "endpoints", "pods"]
  verbs: ["get", "list", "watch"]

Role示例:

以下示例均为从 Role 或 CLusterRole 对象中截取出来,这里仅展示其 rules 部分:

允许读取在core apiGroup(因为apiGroups为空表示默认core apiGroup)下的“Pods:”

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Pod 的资源的名称为 "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

允许读/写在 "extensions" 和 "apps" 的apiGroup 中的 Deployment(在 HTTP 层面,对应 URL 中资源部分为 "deployments"):

rules:
- apiGroups: ["extensions", "apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

允许读取核心core apiGroup中的 "pods" 和读/写 "batch""extensions" apiGroup中的 "jobs":

rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch", "extensions"]
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

允许读取名称为 "my-config" 的 ConfigMap(需要通过 RoleBinding 绑定以 限制为某名称空间中特定的 ConfigMap):

rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]

允许读取在核心组中的 "nodes" 资源(因为 Node 是集群作用域的,所以需要 ClusterRole 绑定到 ClusterRoleBinding 才生效)

rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]

允许针对非资源端点 /healthz 和其子路径上发起 GET 和 POST 请求 (必须在 ClusterRole 绑定 ClusterRoleBinding 才生效)

rules:
  - nonResourceURLs: ["/healthz", "/healthz/*"] # nonResourceURL 中的 '*' 是一个全局通配符
    verbs: ["get", "post"]

对subjects的引用:

RoleBinding 或者 ClusterRoleBinding 可绑定角色到某 *主体(Subject)*上。 主体subject可以是group,user或者service accout ( 适用于pod与apiServer进行交互 )

Kubernetes 用字符串来表示用户名。 用户名可以是普通的用户名,像 "alice";或者是邮件风格的名称,如 "bob@example.com", 或者是以字符串形式表达的数字 ID。 Kubernetes 管理员负责配置 身份认证模块 以便后者能够生成你所期望的格式的用户名。

注意:

前缀 system: 是 Kubernetes 系统保留的,所以要确保 所配置的用户名或者组名不能出现上述 system: 前缀。 除了对前缀的限制之外,RBAC 鉴权系统不对用户名格式作任何要求。

在 Kubernetes 中,鉴权模块提供用户组信息。 与用户名一样,用户组名也用字符串来表示,而且对该字符串没有格式要求, 只是不能使用保留的前缀 system:

说明:

  • system:serviceaccount: (单数)是用于服务账号用户名的前缀;
  • system:serviceaccounts: (复数)是用于服务账号组名的前缀。

RoleBinding subjects示例:

下面示例是 RoleBinding 中的片段,仅展示其 subjects 的部分

subjects即为RoleBinding要将权限授予的对象,可以是user、group、sa

对于名称为 alice@example.com 的用户:

subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io

对于名称为 frontend-admins 的用户组:

subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io

对于 kube-system 名称空间中的默认服务账号:

subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

对于 "qa" 名称空间中所有的服务账号:

subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io

对于在任何名称空间中的服务账号:

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

对于所有已经过认证的用户:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io

对于所有未通过认证的用户:

subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

对于所有用户:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

默认Roles和 Role Bindings:

API Server会创建一组默认的 ClusterRoleClusterRoleBinding对象。 这其中许多是以 system: 为前缀的,用以标识对应资源是直接由集群的控制层面所管理的。 所有默认的 ClusterRoleClusterRoleBinding 都有 kubernetes.io/bootstrapping=rbac-defaults 标签。

注意:

在修改名称包含 system: 前缀的ClusterRoleClusterRoleBinding 时要格外小心。 对这些资源的更改可能导致集群无法继续工作

自动协商:

在每次启动时,API Server都会更新默认 ClusterRole 以添加缺失的各种权限,并更新 默认的ClusterRoleBinding以增加缺失的的各类主体。 这种自动协商机制允许集群去修复一些不小心发生的修改,并且有助于保证RoleRoleBinding 在新的发行版本中发生权限变更subjects变更时仍然保持最新。

如果要禁止自动协商功能,请将默认ClusterRole 以及 默认ClusterRoleBindingrbac.authorization.kubernetes.io/autoupdate 注解设置成 false。 注意,缺少默认权限和角色绑定的主体可能会导致集群无法正常工作。

如果基于 RBAC 的鉴权机制被启用,则自动协商功能默认是被启用的。

API发现角色:

无论是经过身份验证的还是未经过身份验证的用户,默认的RoleBinding都授权他们可以读取被认为 是可安全地公开访问的 API( 包括 CustomResourceDefinitions)。 如果要禁用匿名的未经过身份验证的用户访问,需要在APIServer配置中中添加 --anonymous-auth=false 的配置选项

通过运行命令 kubectl 可以查看这些角色的配置信息:

kubectl get clusterroles system:discovery -o yaml

getclusterroles.jpg

说明:

如果你编辑该 ClusterRole,你所作的变更会被 API Server在重启时自动覆盖,这是通过 自动协商机制完成的。要避免这类覆盖操作, 要么不要手动编辑这些角色,要么禁止自动协商机制。

默认 ClusterRole默认 ClusterRoleBinding描述
system:basic-usersystem:authenticated允许用户以只读的方式去访问他们自己的基本信息。在 1.14 版本之前,这个角色在 默认情况下也绑定在 system:unauthenticated
system:discoverysystem:authenticated允许以只读方式访问 API 发现端点,这些端点用来发现和协商 API 级别。 在 1.14 版本之前,这个角色在默认情况下绑定在 system:unauthenticated
system:public-info-viewersystem:authenticatedsystem:unauthenticated允许对集群的非敏感信息进行只读访问,它是在 1.14 版本中引入的

面向用户的角色:

一些默认的 ClusterRole 不是以前缀 system: 开头的。这些是面向用户的角色。 它们包括超级用户(Super-User)角色(cluster-admin)使用 ClusterRoleBinding 在集群范围内完成授权的角色(cluster-status)、 以及使用 RoleBinding 在特定名称空间中授予的角色(admin、edit、view)

面向用户的 ClusterRole 使用Aggregated ClusterRole以允许管理员为这些 ClusterRole 添加用于定制资源的规则。如果想要添加规则到 adminedit 或者 view, 可以创建带有以下一个或多个标签的 ClusterRole

metadata:
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-view: "true"

核心组件角色:

默认 ClusterRole默认 ClusterRoleBinding描述
cluster-adminsystem:masters允许超级用户在平台上的任何资源上执行所有操作。 当在 ClusterRoleBinding 中使用时,可以授权对集群中以及所有名称空间中的全部资源进行完全控制。 当在 RoleBinding 中使用时,可以授权控制 RoleBinding 所在名称空间中的所有资源,包括名称空间本身
admin允许管理员访问权限,旨在使用 RoleBinding 在名称空间内执行授权。 如果在 RoleBinding 中使用,则可授予对名称空间中的大多数资源的读/写权限, 包括创建角色和角色绑定的能力。 但是它不允许对资源配额或者名称空间本身进行写操作
edit允许对名称空间的大多数对象进行读/写操作。 它不允许查看或者修改角色或者角色绑定。 不过,此角色可以访问 Secret,以名称空间中任何 ServiceAccount 的身份运行 Pods, 所以可以用来了解名称空间内所有服务账号的 API 访问级别
view
system:kube-schedulersystem:kube-scheduler user允许访问 scheduler 组件所需要的资源
system:volume-schedulersystem:kube-scheduler user允许访问 kube-scheduler 组件所需要的卷资源
system:kube-controller-managersystem:kube-controller-manager user允许访问控制器管理器 组件所需要的资源。 各个控制回路所需要的权限在控制器角色 详述
system:node允许访问 kubelet 所需要的资源,**包括对所有 Secret 的读操作和对所有 Pod 状态对象的写操作。**你应该使用 Node 鉴权组件NodeRestriction 准入插件 而不是 system:node 角色。同时基于 kubelet 上调度执行的 Pod 来授权 kubelet 对 API 的访问。 system:node 角色的意义仅是为了与从 v1.8 之前版本升级而来的集群兼容
system:node-proxiersystem:kube-proxy user允许访问 kube-proxy 组件所需要的资源

1

其他组件角色:

默认 ClusterRole默认 ClusterRoleBinding描述
system:auth-delegator允许将身份认证和鉴权检查操作外包出去。 这种角色通常用在插件式 API Server上,以实现统一的身份认证和鉴权
system:heapsterHeapster 组件(已弃用)定义的角色
system:kube-aggregator0kube-aggregator 组件定义的角色
system:kube-dnskube-system 名称空间中的 kube-dns 服务账号kube-dns 组件定义的角色
system:kubelet-api-admin允许 kubelet API 的完全访问权限
system:node-bootstrapper允许访问执行 kubelet TLS 启动引导 所需要的资源
system:node-problem-detectornode-problem-detector 组件定义的角色
system:persistent-volume-provisioner允许访问大部分 动态卷驱动 所需要的资源

1

初始化与预防权限提升:

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

对创建角色(Role)或更新角色(Role)的限制

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

  1. 你已经拥有角色中包含的所有权限,且其作用域与正被修改的对象作用域相同。 (对 ClusterRole 而言意味着集群范围,对 Role 而言意味着相同名称空间或者集群范围)。
  2. 你被显式授权在 rbac.authorization.k8s.io API 组中的 rolesclusterroles 资源 使用 escalate 动词。

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

  1. 根据需要赋予他们一个角色,允许他们根据需要创建/更新 Role 或者 ClusterRole 对象。
  2. 授予他们在所创建/更新角色中包含特殊权限的权限:
    • 隐式地为他们授权(如果它们试图创建或者更改 Role 或 ClusterRole 的权限, 但自身没有被授予相应权限,API 请求将被禁止)
    • 通过允许他们在 Role 或 ClusterRole 资源上执行 escalate 动作显式完成授权。 这里的 rolesclusterroles 资源包含在 rbac.authorization.k8s.io API 组中

对创建角色绑定(RoleBinding/ClusterRoleBinding)或更新角色绑定的限制

只有你已经具有了所引用的角色中包含的全部权限时,或者你被授权在所引用的角色上执行 bind 动作时,你才可以创建或更新角色绑定。这里的权限与角色绑定的作用域相同。

例如,如果用户 user-1 没有list集群范围所有 Secret 的能力,则他不可以创建 ClusterRoleBinding 引用授予该许可权限的角色。 如要允许用户创建或更新角色绑定:

  1. 赋予他们一个角色,使得他们能够根据需要创建或更新 RoleBinding 或 ClusterRoleBinding 对象。
  2. 授予他们绑定某特定角色所需要的许可权限:
    • 隐式授权下,可以将角色中包含的许可权限授予他们;
    • 显式授权下,可以授权他们在特定 Role (或 ClusterRole)上执行 bind 动作的权限。

例如,下面的 ClusterRoleRoleBinding 将允许用户 user-1 把名称空间 user-1-namespace 中的 admineditview 角色赋予其他用户:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: role-grantor
# ClusterRole / Role 下的rules字段表示允许该角色执行的权限,可以同时有多个
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 Server启动时启用了不安全端口(使用 --insecure-port), 你也可以通过 该端口调用 API ,这样的操作会绕过身份验证或鉴权。

关于Role、ClusterRole、roleBinding、clusterrolebinding操作命令:

详见官网文档一些命令行工具

实践:创建一个用户devuser只能管理dev空间

简述过程:

首先k8s集群是没有用户管理的,所以首先需要在k8s master所在主机上创建一个devuser用户,然后以devuser的身份向CA请求证书,需要创建一个json格式的证书请求文件,然后使用cfssl为其生成证书,这样就有了证书请求文件和私钥文件。

然后就是设置一下集群参数:声明KUBE_APISERVER参数、设置config

然后设置客户端认证参数,即客户端证书、客户端认证私钥

然后设置上下文参数(作用就是将该特定用户绑定到名称空间),然后就可以创建rolebinding,将某一个clusterrole的dev名称空间下的权限rolebinding给devuser

第三步:准入控制

什么是准入控制?

准入控制(Admission Controller)是 Kubernetes API Server 用于拦截请求的一种手段。Admission部分可以做到对请求的资源对象进行校验,修改。

既然在 Kubernetes 中有了authn/authz,为什么还会引入 admission 这种机制?

authn/authzKubernetes 的认证和鉴权,运行在 filter 中,只能获取 http 请求 header 以及证书,并不能获取请求的 body。所以 authn/authz 只能对客户端进行认证和鉴权,不可以对请求的对象进行任何操作,因为这里根本还获取不到对象。

Admission 运行在 API Server 的增删改查 handler 中,可以自然地操作 API resource。它是在经过授权之后,资源持久化之前的一个处理 API server请求的步骤。Admission准入过程 能获取到和认证过程一致的信息(用户、URL 等),以及绝大多数 API 请求的完整报文。

准入阶段由不同的插件组成,每个插件都能 “各司其职”,并明确知道自己要检查的对象结构。例如:PodNodeSelector(影响调度决策),PodSecurityPolicy(防止升级的容器)和 ResourceQuota(为每个 Namespace 限制资源配额)

admission_stage.jpg

准入分为两个阶段:

  • 修改 (Mutation) 阶段 : 在对象持久化之前修改对象的主体内容以及拒绝 API 请求。
  • 验证 (Validation) 阶段 :在对象持久化之前进行校验以及拒绝 API 请求。

一个准入插件可以在这两个阶段应用,但是所有的修改阶段都发生在验证阶段之前。

修改 (Mutation)阶段:

Admission 的 Mutation 阶段允许在资源内容生成前进行修改。因为同一个字段在 Admission 链上可以被多次修改,因此 Admission 插件的执行顺序很重要。

准入修改插件(Mutating Admission Plugin)中的一个例子就是 PodNodeSelector,它使用 Namespace 的一个 annotation:namespace.annotations[“scheduler.alpha.kubernetes.io/node-selector”] 来查找标签选择器并将其添加到 pod.spec.nodeselector 字段。这一功能正向限制了特定 Namespace 中的 pod 能够落在哪个节点上,这与提供反向限制的 taints 正相反(也是通过 Admission 插件来实现的)

验证 (Validating)阶段:

我们可以在 Admisson 的验证阶段来检查特定 API 资源以保证其不变。验证阶段在所有的 mutators 完成之后运行,以确保资源在做完验证之后不会被再次改变。

准入验证插件(Validation Admission Plugin)的一个例子也是 PodNodeSelector 插件,它可以确保所有 pod 的 spec.nodeSelector 字段都能符合 Namespace 上节点选择器的约束。即使在 Mutating 链中运行 PodNodeSelector 之后,有其他修改插件试图更改 spec.nodeSelector 字段,验证链中的 PodNodeSelector 插件也会因验证失败而阻止 API 资源的创建。

这里对准入控制工作流做一番详解:

admisscontroll.jpg

API Server 接收到客户端请求后首先进行认证鉴权,认证鉴权通过后才会进行后续的 endpoint handler 处理。

  • 当 API Server 接收到对象后首先根据 http 的路径可以知道对象的版本号,然后将 request body 反序列化成 versioned object
  • versioned object 转化为 internal object,即没有版本的内部类型,这种资源类型是所有 versioned 类型的超集。只有转化为 internal 后才能适配所有的客户端 versioned object 的校验。
  • Admission Controller 具体的 admit 操作,可以通过这里修改资源对象,例如为 Pod 挂载一个默认的 Service Account 等。
  • API Server internal object validation,校验某个资源对象数据和格式是否合法,例如:Service Name 的字符个数不能超过 63 等。
  • Admission Controller validate,可以自定义任何的对象校验规则。
  • internal object 转化为 versioned object,并且持久化存储到 etcd。

如何使用准入控制:

Kubernetes 1.10 之前的版本可以使用 --admission-control 打开准入控制。同时 --admission-control 的顺序决定 Admission 运行的先后。其实这种方式对于用户来讲其实是挺复杂的,因为这要求用户对所有的准入控制器需要完全了解。

如果使用 Kubernetes 1.10 之后的版本,--admission-control 已经废弃,建议使用
--enable-admission-plugins--disable-admission-plugins 指定需要打开或者关闭的准入控制器。 同时用户指定的顺序并不影响实际准入控制器的执行顺序,对用户来讲非常友好。

值得一提的是,有些准入控制器可能会使用 Alpha 版本的 API,这时必须首先使能其使用的 API 版本。否则准入控制器不能工作,可能会影响系统功能