集群安全机制
一、概述
kubernetes通过API Server 的认证授权、准入控制机制及保护敏感信息的 Secret 机制等。
有以下几个目标:
- 保证容器与其所在的宿主机的隔离。
- 限制容器给基础设施及其他容器带来消极影响的能力。
- 最小权限原则一一合理限制所有组件的权限,确保组件只执行它被授权的行为,通过限制单个组件的能力来限制它所能到达的权限范围。
- 明确组件间边界的划分。
- 划分普通用户和管理员的角色。
- 在必要时允许将管理员权限赋给普通用户。
- 允许拥有“
Secret
”数据( Keys Certs Passwords )的应用在集群中运行。
二、API Server 认证管理(Authentication)
Kubernetes 集群中所有资源的访问和变更都是通过kubernetes API Server
的REST API 来实现的,所以集群安全的关键点就在于如何识别并认证客户端身份( Authentication ),以及随后访问权限的授权( Authorization )
Kubernetes 集群提供了3种级别的客户端身份认证方式。
- HTTPS 证书认证:基于 CA 根证书签名的双向数字证书认证方式
- HTTP Token 认证:通过 Token 来识别合法用户。
- HTTP Base 认证:通过用户名+密码的方式认证。
HTTPS 证书认证
需要有 CA 证书,CA是PKI 系统中通信双方都信任的实体,被称为可信第三方( Trusted Third Party
, TTP )。CA 作为可信第三方的重要条件之 就是 CA 的行为具有非否认性。必须能让信任者有追究自己责任的能力。CA通过证书证实他人的公钥信息,证书上有 CA 的签名。用户如果因为信任证书而有了损失,追究 CA 的法律责任。
在证书中绑定了公钥数据和相应私钥拥有者的身份信息,并带有 CA 的数字签名:证书中也包含了 CA 的名称,以便于依赖方找到 CA 的公钥,验证证书上的数字签名
CA 认证涉及诸多概念,比如根证书、自签名证书、密钥、私钥、加密算法及 HTTPS 等,
CA认证大概包括以下几个步骤:

- HTTPS 通信双方的服务器端向 CA 机构申请证书, CA 机构下发根证书、服务端证书及私钥给申请者
- HTTPS 通信双方的客户端向 CA 机构申请证书, CA 机构下发根证书、客户端证书及私钥给申请者。
- 客户端向服务器端发起请求,服务端下发服务端证书给客户端。客户端接收到证书后,通过私钥解密证书 ,并利用服务器端证书中的公钥认证证书信息比较证书里的消息,例如域名和公钥与服务器刚刚发送的相关消息是否一致,如果一致,则客户端认可这个服务器的合法身份。
- 客户端发送客户端证书给服务器端,服务端接收到证书后,通过私钥解密证书,获得客户端证书公钥,并用该公钥认证证书信息,确认客户端是否合法。
- 客户端通过随机密钥加密信息,并发送加密后的信息给服务端 。服务器端和客户端协商好加密方案后,客户端会产生一个随机的密钥,客户端通过协商好的加密方案,加密该随机密钥,井发送该随机密钥到服务器端。服务器端接收这个密钥后,双方通信的所有内容都通过该随机密钥加密。
双向认证 SSL协议要求服务器和用户双方都有证书。单向认证 SSL 协议不需要客户拥有 CA 证书,只需将服务器端验证客户证书的过程去掉,以及在协商对称密码方案和对称通话密钥时,服务器发送给客户的是没有加过密的(这并不影响 SSL 过程的安全性)密码方案。
HTTP Token 的认证
是用一个很长的特殊编码方式的并且难以被模仿的字符串Token表明客户身份的一种方式。
Token是一个很复杂的字符串 ,比如我们用私钥签名一个字符串后的数据就可以当作Token 。每个 Token 对应一个用户名,存储在 API Server
能访问的一个文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token,这样一来, API Server 就能识别合法用户和非法用户了。
HTTP Base 认证
浏览器和 Web 服务器之间可以通过 Cookie 来进行身份识别。桌面应用程序一般不会使用 Cookie,用到了 HTTP Base 认证,这种认证方式是把“用户名+冒号+密码”用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的Header Authorization 域里发送给服务端,服务端收到后进行解码,获取用户名及密码,然后进行用户身份的鉴权过程
三、API Server 授权管理(Authorization)
发起 API Server 调用时, API Server 内部要先进行用户认证,然后执行用户授权流程,即通过“授权策略”来决定一个 API 调用是否合法。对合法用户进行授权(Authorization)并且随后在用户访问时进行鉴权。
API Server 目前支持以下几种授权策略(--authorization-mode
):
属性 | 策略 |
---|---|
AlwaysDeny | 拒绝所有的请求,一般用于测试。 |
AlwaysAllow | 允许接收所有请求 |
ABAC (Attribute-Based Access Control) | 基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制。 |
Webhook | 通过调用外部REST 服务对用户进行授权。 |
RBAC | Role-Based Access Control ,基于角色的访问控制 |
API Server 在接收到请求后,会读取该请求中的数据,生成一个访问策略对象,如果该请求中不带某些属性(如 Namespace ),则这些属性的值将根据属性类型的不同,设置不同的默认值(例如为字符串类型的属性设置一个空字符串:为布尔类型的属性设置 false ;为数值类型的属性设置 )。然后将这个访问策略对象和授权策略文件中的所有访问策略对象逐条匹配,如果至少有一个策略对象被匹配,则该请求将被鉴权通过,否则终止 API 调用流程,并返回客户端的错误调用码。
四、Admission Control (准入控制)
认证和鉴权之后,这个请求还需要通过 Admission Control
所控制的 个“准入控制链”的层层考验。
Admission Control
配备有一个“准入控制器”的插件列表,发送给 API Server
的任何请求都需要通过列表中每个准入控制器的检查,检查不通过,则 API Server 拒绝此调用请求 此外,准入控制器插件还能够修改请求参数以完成一些自动化的任务,比如 ServiceAccount 这个控制器插件。
五、Service Account
Service Account
是一种账号 但它并不是给 Kubernete 集群的用户(系统管理员、运维人员、租户用户等) 使用的 ,而是给运行在 Pod 里的进程用的, 它为 Pod 里的进程提供必要的身份证明。
为了确保 Kubernetes 集群的安全, API Server 都会对客户端进行身份认证,认证失败的客户端无法进行 API 调用。此外, Pod 中访问 Kubernetes API Server 时,是以 Service 方式访问服务名为 Kubernetes 的这个服务的, 而kubernetes服务又只在 HTTPS安全端口 443 上提供服务,那么如何进行身份认证呢?
现这是在用一种类似 HTTP Token 的新的认证方式 ,Service Account Auth
, Pod 中的客户端调用 Kubernetes API 时,在 HTTP Header 中传递了 Token字符串,但不同的是:
- Token 的内容来自 Pod 里指定路径下的一个文件(
/run/secrets/kubemetes.io/s erviceaccount/token
),这种 Token 是动态生成的,确切地说,是由 Kubernetes Controller 进程用 API Server 的私钥(--service-account-private-key- file
指定的私钥)签名生成的一个JWT Secret
- 官方提供的客户端 REST 框架代码里,通过 HTTPS 方式与 API Server 建立连接后,会用Pod 里指定路径下的 CA 证书(
/run/secrets/kubernetes.io/serviceaccount/ca.crt
) 验证 API Server 发来的证书 ,验证是否是被 CA 证书签名的合法证书 - API Server 收到这个 Token 以后,采用自己的私钥(实际是使用参数
service-account-key-file
的私钥,如果此参数没有设置,则默认采用tls-private-key-file
指定的参数即自己的私钥)对 Token 进行合法性验证。
涉及Pod中的三个文件:
1 | /run/secrets/kubernetes.io/serviceaccount/token |
这3个文件被称为Kubernetes Secret对象。Secret 从属于 Service Account 资源对象,一个Service Account 对象里面可以包括多个不同的 Secret 对象,
以下是一些命令关于Secret
1 | #查看系统中 Service Account 对象,可以看到default的Service Account对象,包含一个default-token-nsw8n的Secret |
每个 Namespace 下有一个名为 default 默认的 SerivceAccount ,这个 Serivce Account 里面有一个名为 Tokens 的可以当作 Volume 一样被 Mount到Pod 里的 Secret ,当 Pod 启动时,这个 Secret 会自动被 Mount到Pod 的指定目录下,用来协助完成 Pod 中的进程访问 API Server 时的身份鉴权过程。
一个 Service Account 可以包括多个 Secret对象。

- 名为 Tokens的Secret 用于访问 API Server Secret ,也被称为 Service Account Secret
- 名为
Image pull secrets
的 Secret 用于下载容器镜像时的认证过程,通常镜像库运行在Insecure 模式下,所以这个 Secret 空。 - 用户自定义的其他 Secret ,用于用户的进程。
如果 Pod 在定义时没有指定 spec.serviceAccountName
属性,则系统会自动为其赋值为default ,即大家都使用同一个 Namespace 下默认的 Service Account 如果某个 Pod 要使用非default的Service Account ,
Kubernetes 之所以要创建两套 的账号系统:
- User账号是给人用的, Service Account 是给 Pod 的进程使用的,面向的对象不同
- User 账号是全局性的, Service Account 则属于某个具体的 Namespace
- User 账号是与后端的用户数据库同步的,创建一个新用户通常要走一套复杂的业务流程才能实现, Service Account 的创建则 需要极轻量级的实现方式, 集群管理员可以很容易为某些特定任务创建 Service Account
- 多个组件通常拥有各种账号的配置信息, Service Account 是Namespace 隔离的,可以针对组件进行一对一的定义,同时具备很好的“便携性”。
分析 Service Account 与 Secret的一 些运行机制
Controller manager
建了 ServiceAccount Controller
和 Token Controller
两个安全的控制器,ServiceAccount Controller
直监昕Service Account
和 Namespace
的事件,如果 Namespace
没有 default Service Account
,那ServiceAccount Controller
就会为该 Namespace 创建一个默认( default )的 Service Account。
如果 Controller manager 进程在启动时指定了 API Server 私钥 ,那么 Controller manager
会创建 Token Controller
Token Controller
监昕 Service Account
的事件,如果发现新创建的 Service Account
里没有对应的 Service Account Secret
,则会用 API Server 私钥创建一个 Token ( JWT Token) 并用该 Token 、CA 证书及 Namespace 名称等 个信息产生一个新的 Secret 象,然后放入刚才的 Service Account 中
六、Secret 私密凭据
Secret 主要作用是保管私密数据 ,比如密码、 OAuth Tokens、SSH Keys 等信息。将这些私密信息放在 Secret 对象中比直接放在 Pod或Docker Image 更安全,也更便于使用和分发。
1 | #secrets.yaml |
data 域的各子域的值必须为 BASE64 编码值,其中 password 域和 usemame域BASE64 编码前的值分别为“value-I ”和“value-2”
Secret 被创建,则可以通过下面3种方式使用它
- 在创建 Pod 时, 通过为 Pod 指定Service Account 自动使用该 Secret
- 通过挂载该 Secret到Pod 来使用它。
- Docker 镜像下载时使用,通过指定 Pod 的
spc.ImagePulJSecrets
来引用它。
第一种方法用在API Server鉴权方面
第二种方法:
1 | apiVersion: vl |
第二种方法:
执行login 命令 登录私有 Registry:
1
docker login localhost:5000
输入用户名密码 ,第1次登录系统,则会创建新用户, 关信息会写入
~/.dockercfg
文件中。
用 BASE64 编码
dockercfg
的内容:1
cat ~/.dockercfg I base64
将上一步命令 结果作为 Secret 的“
data.dockercfg
”域的内容,创建一个Secret:1
2
3
4
5
6
7
8#image-pull-secret.yaml:
apiVersion : vl
kind : Secret
metadata :
name: myregistrykey
data :
.dockercfg: eyAiaHROcHM6Ly9pbmRleC5kb2NrZXIuaW8vd]EvijogeyAiYXVOaCI6ICJabUZyWlhCaGMzTjNiM0prTVRJSyisICJlbWFpbCI6ICJqZG91QGV4YWlwbGUuY29tIB9IHOK
type: kubernetes.io/dockercfg在 Pod 时引用 Secret
1
2
3
4
5
6
7
8
9
10
11#pods.yaml:
apiVersion : vl
kind : Pod
metadata :
name : mypod2
spec :
containers :
- name : foo
image : janedoe/awesomeapp :v l
imagePullSecrets:
- name: myregistrykey
