关于 Kubrenetes 系列其他文章的传送门
-
《 k8s 中的安全守护神 – 你不知道不代表它不在 》 您当前所在位置

一、k8s 是如何保护集群的数据安全¶
etcd 其实就是一个通用键值存储,但是在 k8s 中,不允许在 etcd 中随意存储数据,所以操作都需要通过 kube-apiserver 来完成的。
为了保证安全,k8s 必须为在其之上运行的所有的资源,在访问上构建出一个安全的访问模型来。
插件机制¶
k8s 的安全由三类插件来完成,他们的主要工作范围有:
- Auth:账号认证模块;
- Authorization:根据不同的账号赋予不同的权限的授权模块;如果只有写操作,那么就到此为止了;
- Addmission Contrl:如果涉及到写操作,那么就需要使用准入控制模块:
- 校验类控制器:检查用户要创建的资源是否被允许创建;
- 变异类控制器:将用户没有指定的参数进行默认值补全;
Auth 和 Authorization 类型都有很多插件可以用,激活则开启,不激活则不生效。当一个类型中有多个插件同时激活时,采用自上而下的验证顺序,执行 一票通过制。
- 所有插件,如果一个显式许可都没有,则进入匿名用户。
而 Addmission Contrl 类型的插件是自上而下,一票否决制 的验证顺序。
- 需要全部插件都允许,才算通过。
(Auth)认证模块¶
支持的认证有:证书认证(双向认证),密码认证,令牌认证,引导令牌,JWT 令牌。
- 证书认证:采用双向认证,并且每个客户端绝对不能使用同一个证书,因为要验证身份,把
CN字段作为用户名,O字段为组; - 引导令牌:如果使用 kubeadm 部署,当一个 node 节点加入集群时,会带着引导令牌自动向 kube-apiserver 申发证书签发,之后引导令牌就无效了,转而使用证书认证。而如果是手动部署而话,则需要一台一台手动去认证。
k8s 中的用户有两类:
- 常规用户(人类用户):
- 是 k8s 中不存在的账号,只要有证书,并通过认证即可;
- 服务账号(Service Account)
- k8s 中存在的账号。
每一个 Pod 也需要令牌,使用 kubectl describe pods 查看 Pod,可以看到在 Mounts 字段中,默认会自动挂载一个存储卷,这是用来连接至 kube-apiserver 查看自己运行状态时使用的令牌,每个 Pod 的令牌都不同。
当创建一个 Pod 时,k8s 可以借助一个准入控制器,自动为 Pod 赋予一个私有的务账号,并且会自动把这个服务账号的令牌,以存储卷的方式挂载至当前 Pod。所以当这个容器内的进程需要连接至 kube-apiserver 时,就使用这个认证信息登录。
认证相关的配置文件 – kubeconfig¶
k8s 认证相关的配置文件保存在 /etc/kubernetes/admin.conf,是 yaml 格式的配置文件,它通常由以下 几个配置段组成:
- users:用户账号及其认证信息列表,可以有多个用户;
- clusters:目标集群列表,可以有多个集群;
- contexts:user 和 cluster 的使用上下文,可以组合定义用哪个 user 接入哪个 clusters,且可以配置多组;
- current-context:定义启用哪组上下文,可以来回切换;
显示配置文件信息,会隐藏证书信息;可指定配置文件路径,不指定默认为下面的路径:
1 | kubectl --kubeconfig=/etc/kubernates/admin.conf config view |
查看 cluster 列表:
1 | kubectl --kubeconfig=/etc/kubernates/admin.conf config get-cluster |
查看上下文列表:
1 | kubectl --kubeconfig=/etc/kubernates/admin.conf config get-contexts |
显示当前激活的上下文:
1 | kubectl --kubeconfig=/etc/kubernates/admin.conf config current-context |
二、创建一个客户端账号(User Account)¶
客户端账号通常是人类用户。
创建一个名为 ilinux 的客户端证书¶
创建私钥文件 ilinux.key:
1 | cd /etc/kubernetes/pki/ |
为用户创建一个证书签署请求格式(scr)的文件,其中 CN 会被识别为用户名,O 为组,组在 k8s 中可以不存在:
1 | openssl req -new -key ilinux.key -out ilinux.csr -subj "/CN=ilinux/O=kubeusers" |
用这个 CA /etc/kubernetes/pki/ca.crt 为证书签名。
1 | cd /etc/kubernetes/pki |
签名完成后,会得到 ilinux.crt 证书文件,而证书签署请求文件 ilinux.scr 其实就没用了。
现在我们两个文件:
- 经过 CA 认证的证书文件
ilinux.crt - 自己的私钥 Key 文件
ilinux.key
其实就可以认证到集群并使用集群了,不过接下来我们将它配置为 kubeconfig 文件,方便以后使用。
创建集群的 kubeconfig 文件¶
创建 cluster¶
kubectl config set-cluster:使用这个命令创建集群项:
--server:apiServer 的服务器地址;--certificate-authority=:指明证书签署机构的证书;--embed-certs=true:把证书信息打包起来,去明文化;--kubeconfig=/tmp/ilinux.conf:指定把配置文件保存到哪,不指定的话默认存在自带的/etc/kubernetes/admin.conf中去。
1 | kubectl config set-cluster my_kube --server="https://192.168.50.14:6443" \ |
查看刚才创建的配置文件,在 /tmp/ilinux.conf 下,可以看到内部只有 cluster 的相关配置,下面创建用户配置。
创建 user¶
kubectl config set-credentials:使用这个命令创建用户项:
NAME:用户信息的标识;--client-certificate=:客户端证书的路径;--client-key=:客户端私钥的路径;--username=:用户的用户名;--password=:使用密码认证时指明 password;--token=:使用令牌认证时指明 token--embed-certs=true:把证书信息打包起来,去明文化;
1 | kubectl config set-credentials ilinux --client-certificate=/etc/kubernetes/pki/ilinux.crt \ |
查看配置文件 /tmp/ilinux.conf,应该就被增加了 user 的相关配置。这时应该有 cluster 和 user 的配置,下面把两者关联起来。
定义 user 和 cluster 的上下文关系(context)¶
kubectl config set-context NAME:使用这个命令创建 user 和 cluster 的关系上下文(context):
NAME:这个关系上下文的名字;--cluster=:指明哪个集群;--user=:用户是哪个;
1 | kubectl config set-context ilinux@my_kube --cluster=my_kube \ |
查看配置文件 /tmp/ilinux.conf,这时应该就有了 cluster 和 user 关系上下文的相关配置。
切换关系上下文¶
切换到名为 ilinux@my_kube 的关系上下文:
1 | kubectl config use-context ilinux@my_kube --kubeconfig=/tmp/ilinux.conf |
测试¶
使用我们创建的 ilinux 的用户的配置文件查看 Pod 时,可以看到因为没有权限,所以无法读取:
1 | kubectl get pods --kubeconfig=/tmp/ilinux.conf |
如果要使用管理员账户,则不指定配置文件路径即可,默认会使用 /etc/kubernetes/admin.conf:
1 | kubectl get pods |
三、创建一个服务账号(Service Account)¶
当 k8s 内部的某一个 Pod,或是之外的某一个守护进程,需要通过 apiserver 连入 k8s 的时候,也应该认证。
SA 的定义方式和其他 Pod 类似,通过 apiversion、metadata、kind 来配置基本信息,但是用户的认证信息是在 secrets 字段中保存的。
secrets 中可以使用的类型有 TLS、generic、docker-registry,而之前讲过的 TLS 类型是用来做 SSL 通信的,并不是作为 k8s 集群认证使用的,所以 SA 这里应该使用 generic 类型的 secrets。
在创建 SA 时,是不必自己手动去定义 secrets 的,因为在创建 SA 账号时,k8s 会自动生成一个 secrets,其中包括认证所需要的信息。之所以会自动生成,因为认证并不是最终目标,对这个 SA 账号的授权才是关键。
创建 SA 配置文件¶
1 | -> # vim serviceaccount-demo.yaml |
创建一个 SA¶
1 | # kubectl apply -f serviceaccount-demo.yaml |
查看¶
1 | # kubectl get sa |
可以看到对应的 secrets 也被创建好了¶
1 | # kubectl get secrets |
四、授权模块¶
授权模块的相关概念¶
授权模块有三种:
- ABAC:基于属性的访问控制;
- RBAC:基于角色的访问控制,定义“谁”能对“什么资源”做“哪些操作”;
- 1.8 版本之后默认使用,并强制生效,需要显式授权;
- Node:根据 Pod 对象调度的结果为 Node 进行授权。
查看当前 apiserver 启动时加载的授权插件都有哪些:
1 | cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep "authorization" |
查看自己对某个操作是否有权限:
1 | kubectl auth can-i create deployments --namespace dev |
关于 RBAC¶
RBAC 即:基于角色的访问控制 (Role-based access control)
RBAC 的逻辑架构:
- Subjects:主语:
- User Account;
- Service Account;
- Object:被操纵对象:
- Resources:比如 Pod、Service;
- SubResources:子资源;
- URL:非对象型资源;
- Role 和 RoleBinding:把 Subjects 与 Object 桥接的组件;
- Role:定义了用户的操纵(Verbs)可以被施加在哪个资源(Resources)上;
- RoleBinding:让 Subjects 扮演某个 Role,并拥有某个 Role 的权限;
可以用来使用的“操纵”选项:
| HTTP verb | request verb |
|---|---|
| POST | create |
| GET,HEAD | get,list |
| PUT | update |
| PATCH | patch |
| DELETE | delete |
在 k8s 中有两种级别的 Role:
- Role:名称空间级别的 Role;不包含 ClusterRole;
- ClusterRole:集群级别的 Role;包含 Role;
所以对应于 RoleBinding 也有两种级别:
- RoleBinding:把用户绑定至名称空间级别的 Role;
- ClusterRoleBinding:把用户绑定至集群级别的 ClusterRole;
两者可以交替绑定:
- 当使用 ClusterRoleBinding 把一个用户和 Role 绑定时,会获得 Cluster 级别的权限,在所有名称空间内都有权限(升权);
- 但是使用 RoleBinding 把一个用户和 ClusterRole 绑定时,那么所获得的只是 RoleBinding 所属的名称空间级别的权限(降权);
- 降权应用场景:假设 k8s 中有 30 个名称空间,如果要使用 RoleBinding 在每个名称空间内绑定 Role,那么 RoleBinding 和 Role 要分别在这 30 个名称空间内创建 30 个,并一一绑定;而如果使用 RoleBinding 绑定 ClusterRole,那么只需要创建一个 ClusterRole,然后创建 30 个 RoleBinding,就能解决问题了。
创建授权,并绑定给之前创建的 ilinux 用户¶
创建一个名称空间级别的 Role 规则,目标是只允许读 default 名称空间下的 Pods 和 Services:
1 | -> # vim recouse-reader.yaml |
使用 RoleBinding 把某个用户绑在 Role 上:
1 | -> # vim read-pods.yaml |
使配置生效:
1 | kubectl apply -f recouse-reader.yaml |
授权后,再次使用之前创建的 ilinux 用户尝试读取 Pods 和 Services,就会有权限读取了,只是现在没有 Pod 而已:
1 | kubectl get pods --kubeconfig=/tmp/ilinux.conf |
内键的 ClusterRole¶
k8s 内置了 4 个,集群级别的,核心组件角色 ClusterRole,意义如下:
| 默认 ClusterRole | 默认 ClusterRoleBinding | 描述 |
|---|---|---|
| cluster-admin | system:masters group | 允许超级用户对任何资源执行任何操作。当使用 ClusterRoleBinding 绑定授权时,它可以完全控制集群中所有名称空间中的每个资源;而使用 RoleBinding 绑定授权时,它可以完全控制角色绑定的名称空间中的每个资源,包括名称空间本身。 |
| admin | None | 使用 RoleBinding 在指定的名称空间中绑定授权后,则允许对名称空间内的大多数资源进行读/写访问,包括在命名空间中创建角色和角色绑定的能力。它不允许对资源配额或名称空间本身进行写访问。 |
| edit | None | 只允许对指定的命名空间内的大多数对象进行读/写访问,它不允许查看或修改角色或角色绑定。 |
| view | None | 只允许以只读的方式查看指定名称空间中的大多数对象,它不允许查看角色或角色绑定,它不允许查看 Secret。 |
五、准入控制¶
先对经常使用的插件做个介绍¶
- DefaultStorageClass:为没有定义 SC 的 PV 和 PVC 默认分配一个 SC;
- LimitRanger(限制范围):主要防止 单个 Pod 使用的资源过多。是一个准入控制器,它允许在某个名称空间上创建一个叫
LimitRange类型的资源,这是一个标准的 k8s 资源,并且是名称空间级别的。可以为这个名称空间内所有的 Pod,限制一个默认的资源使用的上、下限规则,过高或过低都不行。LimitRange资源只负责去检查用户的资源是否违反了限制,而真正执行的是 LimitRanger 控制器,所以要使用LimitRange资源的前提是 k8s 集群在启动时加载了 LimitRanger 控制器。 - ResourceQuota(资源配额):主要防止 单个名称空间 使用的资源总量过多。这也是一个标准的 k8s 资源,允许用户自已定义一个
ResourceQuota类型的资源,并生效在某个名称空间上,用于定义整个名称空间在集群内部最低使用多少资源。同样,ResourceQuots资源只负责去检查用户的资源是否违反了限制,而真正执行的是 ResourceQuots 控制器,所以要使用ResourceQuots资源的前提是 k8s 在启动时加载了ResourceQuots控制器。 - PodSecurityPolicy(Pod 安全策略):这也是一个标准的 k8s 资源类型,可以通过这个资源,限制运行在某个名称空间中的某个 Pod,必须明确符合在 PSP 中定义的 Pod 安全策略,任何违反的 Pod 都不允许创建。PSP 资源的生效也是需要使用 PSP 准入控制器的,并且 PSP 资源需要显式指明,任何没有明确允许的操作都是不允许的,所以 PSP 默认没有开启。
如何启用准入控制器:
- 使用 kubeadm 部署的集群,如果想定义启动哪些准入控制器,需要编辑 apiServer 的配置文件
/etc/kubernetes/manifests/kube-apiserver.yaml,在--enable-admission-plugins=NodeRestriction字段添加。可以看到默认这里定义开启了一个。其实一些这里没指明的准入控制器也默认开启了,比如 LimitRanger,不过不同版本的 k8s 对于默认启用哪些模块都不太一样,具体需要查看官方文档。
LimitRange(限制范围)¶
在名称空间的级别,为所创建的每一个 Pod 定义以下几个属性:
- 如果 Pod 没有定义默认的资源请求和资源限制,可以为 Pod 自动施加一个默认的 Request 和 Limit;
- 如果 Pod 自己定义了,那么默认的就不生效;
- 限制名称空间内的每个 Pod 或 Container 可使用资源的最小(
min)和最大值(max);- 如果 Pod 自己定义了,那么不可以超过此处定义的值的上下限。
示例:
limitrange-demo.yaml
1 | apiVersion: v1 |
创建,使用 -n 指定哪个名称空间,就对哪个生效:
1 | # kubectl apply -f limitrange-demo.yaml -n myns |
查看详情:
1 | # kubectl describe limitrange -n myns |
ResourceQuota(资源配额)¶
计算资源配额¶
用户可以对指定命名空间下的可被请求的 计算资源 总量进行限制。
配额机制所支持的资源类型:
| 资源名称 | 描述 |
|---|---|
limits.cpu |
所有非终止状态的 Pod 中,其 CPU 限额总量不能超过该值。 |
limits.memory |
所有非终止状态的 Pod 中,其内存限额总量不能超过该值。 |
requests.cpu |
所有非终止状态的 Pod 中,其 CPU 需求总量不能超过该值。 |
requests.memory |
所有非终止状态的 Pod 中,其内存需求总量不能超过该值。 |
cpu |
与 requests.cpu 相同。 |
memory |
与 requests.memory 相同。 |
存储资源配额¶
用户可以对给定命名空间下的 存储资源 总量进行限制。
此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。
| 资源名称 | 描述 |
|---|---|
requests.storage |
所有 PVC 请求的存储空间总和不能超过此值。 |
persistentvolumeclaims |
在名称空间中 PVC 的总数不能超过此值。 |
<storage-class-name>.storageclass.storage.k8s.io/requests.storage |
在所有与 <storage-class-name> 相关的持久卷申领中,所有 PVC 请求的存储空间总和不能超过此值。 |
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims |
在与 storage-class-name 相关的所有持久卷申领中,命名空间中可以存在的最大持久卷申领总数。 |
举例:如果一个操作人员针对 gold 类型存储与 bronze 类型存储设置配额,操作人员可以定义如下配额:
gold.storageclass.storage.k8s.io/requests.storage: 500Gibronze.storageclass.storage.k8s.io/requests.storage: 100Gi
在版本 1.8 后,添加了对 本地临时存储 的配额支持,已经是 Alpha 功能:
| 资源名称 | 描述 |
|---|---|
requests.ephemeral-storage |
在命名空间中的所有 Pod 中,本地临时存储请求的总和不能超过此值。 |
limits.ephemeral-storage |
在命名空间的所有 Pod 中,本地临时存储限制的总和不能超过此值。 |
ephemeral-storage |
与 requests.ephemeral-storage 相同。 |
对象数量配额¶
1.9 版本之后,增加了对 对象数量 配额设置。
可以使用以下语法,对名称空间内的所有资源类型进行配额分配:
count/<resource>.<group>:用于非核心(core)组的资源count/<resource>:用于核心组的资源
例如:限制使用的 PVC 的总数:
1 | count/persistentvolumeclaims |
配额作用域¶
每个配额都有一组相关的 scope(作用域),配额只会对作用域内的资源生效。配额机制仅统计所列举的作用域的交集中的资源用量。
当一个作用域被添加到配额中后,它会对作用域相关的资源数量作限制。如配额中指定了允许(作用域)集合之外的资源,会导致验证错误。
- 配额只会作用到指定的作用域上:
| 资源名称 | 描述 |
|---|---|
| Terminating | 匹配所有 spec.activeDeadlineSeconds 不小于 0 的 Pod。 |
| NotTerminating | 匹配所有 spec.activeDeadlineSeconds 是 nil 的 Pod。 |
| BestEffort | 匹配所有 Qos 是 BestEffort 的 Pod。 |
| NotBestEffort | 匹配所有 Qos 不是 BestEffort 的 Pod。 |
| PriorityClass | 匹配所有引用了所指定的优先级类的 Pods。 |
更多关于配额作用域的参数:https://kubernetes.io/zh/docs/concepts/policy/resource-quotas/#quota-scopes
示例¶
vim resoucequota-demo.yaml
1 | apiVersion: v1 |
创建并查看:
1 | -> # kubectl apply -f resoucequota-demo.yaml -n myns |
PodSecurityPolicy(Pod 安全策略)¶
与 PSP 相关的参数和详细教程:https://kubernetes.io/zh/docs/concepts/policy/pod-security-policy/
PSP 在生产环境中实现的模板:
先创建两个 PSP 清单,一个定义特权操作,一个定义受限操作。
特权 PSP 清单¶
1 | -> # cat psp-privileged.yaml |
受限的 PSP 清单¶
1 | -> # cat psp-restricted.yaml |
然后创建两个集群角色,一个为特权角色,并绑定特权 PSP 清单,另一个为受限的角色,并绑定受限的 PSP 清单:
1 | -> # vim clusterrole-with-psp.yaml |
之后将两个集群角色,使用 ClusterRoleBinging 绑定至合适的组即可。
1 | -> # vim clusterrolebinding-with-psp.yaml |