简介
Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server 是集群内部各个组件通信的中介,也是外部控制的入口。所以 Kubernetes 的安全机制基本就是围绕保护 API Server 来设计的。Kubernetes 使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission Control)三步来保证API Server的安全。
认证Authentication
认证的方法有:
- HTTP Token 认证:通过一个 Token 来识别合法用户:HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 - Token 来表达客户的一
种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token。 - HTTP Base 认证:通过 用户名+密码 的方式认证。用户名+:+密码 用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 Heather;Authorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码
- 最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式
认证是包括两种类型的:
Kubenetes组件对API Server的访问:kubectl、Controller Manager、Scheduler、kubelet、kubeproxy- Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用API Server的非安全端口访问,
--insecure-bind-address=127.0.0.1 - kubectl、kubelet、kube-proxy 访问 API Server 就都需要证书进行 HTTPS 双向认证
- 实现方式:kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群context 信息(集群名称、用户名)。Kubenetes 组件通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群
- Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用API Server的非安全端口访问,
Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)。- Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了。Kubenetes使用了Service Account解决Pod 访问API Server的认证问题
- Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-accounttoken,另一种是用于保存用户自定义保密信息的 Opaque。ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace
- token是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证
- ca.crt,根证书。用于Client端验证API Server发送的证书
- namespace, 标识这个service-account-token的作用域名空间
认证总体上如下图:

鉴权Authorization
鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “—authorization-mode” 设置)
- AlwaysDeny:表示拒绝所有的请求,一般用于测试
- AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略
- ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
- Webbook:通过调用外部 REST 服务对用户进行授权
- RBAC(Role-Based Access Control):基于角色的访问控制,现行默认规则。相对其它访问控制方式,拥有以下优势:
- 对集群中的资源和非资源均拥有完整的覆盖
- 整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作
- 可以在运行时进行调整,无需重启 API Server
准入控制Admission Control
准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount。
列举几个插件的功能:
- NamespaceLifecycle: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除namespace 时,连带删除它的所有资源对象。
- LimitRanger:确保请求的资源不会超过资源所在 Namespace 的 LimitRange 的限制。
- ServiceAccount: 实现了自动化添加 ServiceAccount。
- ResourceQuota:确保请求的资源不会超过资源的 ResourceQuota 限制。
ServiceAccount
Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。它与User account不同
- User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API而设计;
- User account是跨namespace的,而service account则是仅局限它所在的namespace;
- 每个namespace都会自动创建一个default service account
- Token controller检测service account的创建,并为它们创建secret
开启ServiceAccount Admission Controller后
- 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
- 验证Pod引用的service account已经存在,否则拒绝创建
- 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
- 每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/
当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account。而pod和apiserver之间进行通信的账号,称为serviceAccountName。
当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account。而pod和apiserver之间进行通信的账号,称为serviceAccountName。
1 | [root@master ~]# kubectl get pods nginx-geekbang-76579f4dfd-54d85 -o yaml |grep serviceAccountName |
从上面可以看到每个Pod无论定义与否都会有个存储卷,这个存储卷为default-token-***的token令牌,这就是pod和serviceaccount认证信息。通过secret进行定义,由于认证信息属于敏感信息,所以需要保存在secret资源当中,并以存储卷的方式挂载到Pod当中。查看sa以及secret:
1 | [root@master ~]# kubectl get sa |
而默认的service account 仅仅只能获取当前Pod自身的相关属性,无法观察到其他名称空间Pod的相关属性信息。
从上可以得知,SA是会挂载到POD里面的,那具体是哪个位置呢?如下:
1 | [root@master ~]# kubectl exec -it nginx-geekbang-76579f4dfd-54d85 -- bash |
创建方法
SA的创建方法非常简单,直接 kubectl create serviceaccount admin 即可。我们在POD创建时可以使用spec.serviceAccountName 字段中将name设置为您想要用的 service account 名字即可。在 pod 创建之初 service account 就必须已经存在,否则创建将被拒绝。需要注意的是不能更新已创建的 pod 的 service account。
1 | [root@master ~]# kubectl create serviceaccount admin |
RBAC
RBAC 引入了 4 个新的顶级资源对象:Role、ClusterRole、RoleBinding、ClusterRoleBinding,4 种对象类型均可以通过 kubectl 与 API 操作。

在RBAC API中,一个角色包含了一套表示一组权限的规则。 权限以纯粹的累加形式累积(没有”否定”的规则)。 角色可以由命名空间(namespace)内的Role对象定义,而整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。
Role与ClusterRole
在RBAC API中,一个角色包含了一套表示一组权限的规则。 权限以纯粹的累加形式累积(没有”否定”的规则)。 角色可以由名字空间(namespace)内的Role对象定义,而整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。
一个Role对象只能用于授予对某一单一名字空间中资源的访问权限。
以下示例描述了”default”名字空间中的一个Role对象的定义,用于授予对pod的读访问权限:
1 | apiVersion: rbac.authorization.k8s.io/v1 |
ClusterRole对象可以授予与Role对象相同的权限,但属于集群级别的。可以用于:
- 集群级别的资源控制,如node的访问权限
- 非资源型的访问权限,如endpoints “/healthz”
- 所有命名空间的资源控制。即控制所有的namespace
下面示例中的ClusterRole定义可用于授予用户对某一特定名字空间,或者所有名字空间中的secret的读访问权限:
1 | apiVersion: rbac.authorization.k8s.io/v1 |
RoleBinding与ClusterRoleBinding
角色绑定将一个角色中定义的各种权限授予一个或者一组用户。角色绑定包含了一组相关主体(即subject, 包括用户——User、用户组——Group、或者服务账户——Service Account)以及对被授予角色的引用。 在名字空间中可以通过RoleBinding对象授予权限,而集群范围的权限授予则通过ClusterRoleBinding对象完成。
RoleBinding可以引用在同一名字空间内定义的Role对象。下面示例中定义的RoleBinding对象在”default”名字空间中将”pod-reader”角色授予用户”jane”。 这一授权将允许用户”jane”从”default”名字空间中读取pod。
1 | # 以下角色绑定定义将允许用户"jane",绑定pod-reader这个role |
RoleBinding对象也可以绑定一个ClusterRole对象。例如,尽管下面示例中的RoleBinding引用的是一个ClusterRole对象,但是用户”dave”(即角色绑定主体)还是只能读取”development” 名字空间中的secret(即RoleBinding所在的名字空间)。
1 | # 以下角色绑定允许用户"dave"读取"development"名字空间中的secret。 |
如果dave想要控制整个集群,那就上面的namespace删除掉就有了整个集群 secret-reader这个clusterRole的权限。
Subject角色绑定主体
RoleBinding或者ClusterRoleBinding将角色绑定到角色绑定主体(Subject)。 角色绑定主体可以是用户组(Group)、用户(User)或者服务账户(Service Accounts)。
- 用户名由字符串表示。可以是纯粹的用户名,例如”alice”、电子邮件风格的名字,如 “bob@example.com” 或者是用字符串表示的数字id。对于用户名,RBAC授权系统不要求任何特定的格式。然而,前缀
system:是 为Kubernetes系统使用而保留的,所以管理员应该确保用户名不会意外地包含这个前缀。 - Kubernetes中的用户组信息由授权模块提供。用户组与用户一样由字符串表示。Kubernetes对用户组 字符串没有格式要求,但前缀
system:同样是被系统保留的。 - Service Accounts拥有包含
system:serviceaccount:前缀的用户名,并属于拥有system:serviceaccounts:前缀的用户组。
一个名为”alice@example.com”的用户:
1 | subjects: |
对资源的控制引用
大多数资源由代表其名字的字符串表示,例如”pods”,就像它们出现在相关API endpoint的URL中一样。然而,有一些Kubernetes API还 包含了”子资源”,比如pod的logs。在Kubernetes中,pod logs endpoint的URL格式为:
1 | GET /api/v1/namespaces/{namespace}/pods/{name}/log |
在这种情况下,”pods”是名字空间资源,而”log”是pods的子资源。为了在RBAC角色中表示出这一点,我们需要使用斜线来划分资源 与子资源。如果需要角色绑定主体读取pods以及pod log,您需要定义以下角色:
1 | kind: Role |
这里的意思是rules的规则设置。
apiGroups定义可以使用api的版本,为空表示核心版本,如["extensions", "apps"]允许读写在”extensions”和”apps”的API。resources表示对哪些资源进行控制,可以为pods、deployments、nodes、secrets。。。。verbs表示允许的动作。如"get", "list", "watch", "create", "update", "patch", "delete"等。resourceNames表示一个资源的实例名,如一个pod的名字为abc,那加上这个,那这个rule就表示只对这个pod生效。
实例
普通用户管理集群
在实际生产环境中,有很可能是多个部门共用同一套K8S的环境,这就需要做权限的隔离,以下以devuser为例,创建一个普通用户只能管理namespace=dev这个环境。
1 | # 下载证书生成工具 |
配置完成之后,查看默认的配置:
1 | [devuser@master ~]$ kubectl config view |
创建一个只能访问某个namespace的用户
第1步:创建用户凭证
1 | # 给用户 fdm 创建一个私钥 |
现在我们可以使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context)
1 | [root@master user_login_kube-systecm]# kubectl config set-credentials fdm --client-certificate=fdm.crt --client-key=fdm.key |
第2步:创建角色
用户创建完成后,接下来就需要给该用户添加操作权限,我们来定义一个YAML文件,创建一个允许用户操作 Deployment、Pod、ReplicaSets 的角色。
1 | apiVersion: rbac.authorization.k8s.io/v1 |
其中Pod属于 core 这个 API Group,在YAML中用空字符就可以,而Deployment属于 apps 这个 API Group,ReplicaSets属于extensions。
第3步:创建角色权限绑定
1 | apiVersion: rbac.authorization.k8s.io/v1 |
第4步. 测试
现在我们应该可以上面的haimaxy-context上下文来操作集群了:
1 | # 默认查看的是kube-system的这个namespace空间 |
创建一个只能访问某个namespace的ServiceAccount
创建sa
1 | [root@master ~]# kubectl create sa fdm-sa -n kube-system |
创建role以及rolebinding
1 | apiVersion: rbac.authorization.k8s.io/v1 |
登陆验证测试
先找到fdm-sa这个sa的token。
1 | [root@master ~]# kubectl get secret -n kube-system fdm-sa-token-f6vpc |
用这个token去登陆Dashboard,会发现很多异常报错,这是因为当前登录用只被授权了访问 pod 和 deployment 的权限
、
参考资料
https://kubernetes.io/docs/reference/access-authn-authz/rbac/