Secret足矣-普通Kubernetes (secret翻译成中文)
It is well known that Kubernetes secrets are simply base64-encoded strings stored in etcd alongside the rest of the cluster state. Ever since secrets were introduced in 2015, security experts have scoffed at this decision and clamored for different alternatives. I believe these folks are missing thepoint.
The secrets API was designed before Kubernetes v0.12. In a discussion that predates the initial design document, there is a line that hints at why people might be confused about secrets: "It's hard to evaluate these alternatives without a threat model."
This is exactly the issue. The naive approach to securing software is to blindly implement a laundry list of security features. But a deeper understanding of security quickly reveals that perfect security is impossible; you have to make tradeoffs and prioritize against the most likely scenarios. Creating a threat modelhelps you make these decisions.
Let's build a simple threat model for Kubernetes secrets and see what shakes out.
What are we protecting?
Secrets are typically used to store database passwords and private keys, which means they are a high-value target.
What does a security failure look like?
If an attacker is able to read a secret, they can use it to carry out further attacks, such as stealing data, modifying/deleting/ransoming data, or gaining the authorization to run operations like mining cryptocurrency in a pod.
Normally, we would use something like DREAD to rank the severity of different attacks, but in the case of exposing a secret it's a bit of a toss-up unless we have a specific secret in mind.
How could a secret be stolen? (What could go wrong?)
At a minimum, a secret will need to exist in plaintext in memory of whatever application needs it, and another process on the same node can (almost) always steal it with enough persistence. We also need to store the secret somewhere durable. In ourcase, secrets are stored in etcd, which is accessible from the Kubernetes API.
Since the secret has to exist in both of these places, it can be stolen through any of the following:
- Stealing the secret from memory of the process that is using it
- Gaining root access to the node where the secret is being used
- Gaining root access to the node where etcd is running
- Gaining access to etcd's persistent storage
- Exploiting a zero-day vulnerability in etcd
Some more exotic attacks, such as social engineering, malicious insiders, human error/misconfiguration, or hardware supply chain attacks, are certainly possible but are outside the scope of what Kubernetes can realistically address.
How do we prevent these attacks?
For attack 1: Stealing the secret out of memory of the application is a risk we have to tolerate. Applications may use auto-expiring tokens or multi-factor authentication, but since these features are specific to the application they are out of scope. For attacks 2 and 3: Root access to a node is a huge problem. This can be mitigated through regular server hardening, patching, and preventing privileged pods from running, but it is a very complex threat to address. For attack 4: Access to the physical server can be mitigated to some extent by encrypting the persistent disks at rest. It is critical to note that the encryption key must be stored in a separate secure enclave for this to have any security benefit. But since physical access usually means game over, you just need reasonably good physical security. For attack 5: Here, we have to make a bet about whether a zero-day exploit will materialize. We can improve our odds by choosing simpler and well-tested approaches, and there is nothing simpler than a plain Kubernetes secret.What the Threat Model Reveals
The threat model reveals the inconvenient fact that storing secrets is hard, because a plaintext version has to exist somewhere (contrast this with a password hash, for example). This is simply a problem of reversible encryption. Any approach to improving upon the current secret implementation must mitigate more attacks,and I don't believe any of the alternatives to plain Kubernetes secrets offer enough additional security to be worth the trouble.
Alternatives to Kubernetes Secrets
Let's take a look at some of the alternatives that exist and see how they measure up.
Etcd Static Encryption
I am somewhat shocked that this is still the 1 recommended alternative, given how absurdly broken it is. Etcd static encryption involves encrypting all secrets in etcd using a key that is stored on the same filesystem as etcd itself. This means that none of the four attacks in our threat model are mitigated. Not even the physical access attack, because the key is stored on the same disk! Or, at the very least, another disk that can be accessed from the same host (not even mentioned as an option in the documentation).
Etcd Encryption via KMS
You can replace the above approach with a key management service (KMS) from your favorite cloud provider. This addresses the issue of the encryption key being stored on the same filesystem as etcd, but it does not address any of the other attacks in our threat model.
Vault
Vault is a popular secrets management solution that can be integrated with Kubernetes. Vault offers a number of features that Kubernetes secrets do not, such as:
- Centralized management of secrets
- Audit logging
- Role-based access control
However, Vault also introduces a number of complexities, such as:
- A new dependency to manage
- Potential performance overhead
- A new attack surface to secure
In my opinion, the additional security benefits of Vault do not outweigh the added complexity.
Conclusion
Kubernetes secrets are not perfect, but they are a simple and effective way to store secrets in a Kubernetes cluster. Until a better alternative comes along, I recommend sticking with plain Kubernetes secrets.
01-Kubernetes 组件介绍
当你部署完 Kubernetes, 即拥有了一个完整的集群。 一个 Kubernetes 集群由一组被称作节点的机器组成。 这些节点上运行 Kubernetes 所管理的容器化应用。 集群具有至少一个工作节点。 工作节点托管作为应用负载的组件的 Pod 。 控制平面管理集群中的工作节点和 Pod 。 为集群提供故障转移和高可用性,这些控制平面一般跨多主机运行,集群跨多个节点运行。 本节概述了交付正常运行的 Kubernetes 集群所需的各种组件。 从上面的构架图可以看出来整个kubernetes集群分为control plane(master)和node节点两部份。 master组件是集群的“脑力”输出者。 它维护有kubernetesr 的所有对象记录,负责持续管理对象状态并响应集群中各种资源对象的管理操作,以及确保各资源对象的实际状态与所需状态相匹配。 主要由API Server(kube-apiserver)、Control Manager(kube-controller-manager)和Scheduler(kube-scheduler)这3个组件。 以及一个用于集群状态存储的etcd存储服务组成。 kube-apiserver API Server是 Kubernetes控制平台的前端。 支持不同类型应用的生命周期编排,包括部署、缩放和滚动更新等。 还是整个集群的网关接口,由kube-apiserver守护程序运行为服务。 通过HTTP/HTTPS协议将RESTful API公开给用户。 是发往集群的所有REST操作命令的接入点,并提供认证、授权、访问控制、API注册和发现等所有的REST请求。 并将结果状态持久存储于集群状态存储系统(etcd)中。 kube-apiserver 支持同时提供 https(默认监听在 6443 端口)和 http API(默认监听在 127.0.0.1 的 8080 端口),其中 http API 是非安全接口,不做任何认证授权机制,不建议生产环境启用。 两个接口提供的 REST API 格式相同kube-controller-manager Control Manager负责实现用户通过API Server提交的终态声明。 它通过一系列操作步骤驱动API对象的当前状态逼近或同于期望状态。 Kubernetes提供了驱动Node、Pod、Server、Endpoint、ServiceAccount和Token等数十种类型的API对象的控制器。 从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。 控制器包括:kube-scheduler Scheduler是指为API Server 接收到的每一个pod创建请求,并在集群中为其匹配出一个最佳的工作节点为。 调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限等特性。 etcd kubernetes集群的所有状态信息都需要持久存储于存储系统etcd中。 etcd是由CoreOS基于Raft协议开发的分布式键值存储。 可用于服务发现。 共享配置以及一致性保障。 生产环境中应该以etcd集群的方式运行以确保其服务可用性,并需要制周期备份策略以确保数据安全可靠。 etcd还为其存储的数据提供了监听(watch)机制。 用于监视和推送变更,API Server是kubernetes集群中唯一能够与etcd通信的组件。 封装了这种监听机制。 并借此同其他各组件高效协同。 Node组件是集群中的“体力”输出者,因而一个集群通常会有多个Node以提供足够的承载力来运行容器化应用和其他工作负载。 kubelet kubelet是运行于每个node节点之上的“节点代理”服务,负责维护容器的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理;其主要功能概括如下: 持续监听node的健康状态并向master汇报。 基于用户自定义的探针进行存活状态探测,并在任何Pod出现问题时将其重建为新实例。 准备pod所需的数据卷 返回pod的状态 在node节点执行容器的健康检测 Pod是一组容器组成的集合并包含这些容器的管理机制。 安并未额外定义进程的边界或其他更多抽象,因此真正负责运行容器的依然是底层的容器运行时。 kubelet通过CRI(容器运行时接口)可支持多种类型的OCI容器运行时,例如docker、 containerd、CRI-O、runC、fraki和kata Containers等kube-proxy kube-proxy也是需要运行于集群中每个节点之上的服务进程。 它把API Server上的Service资源对象转换为当前节点上的iptables或与ipvs规则。 这些规则能够将那些发往该Service对象ClusterIP的流量分发至它后端的Pod端点上。 kube-proxy是kubernetes的核心网络组件。 它本质上更象是Pod的代理及负载均衡器。 负责确保集群中Node、Service和Pod对象之间的有效通信 。 kube-proxy 不同的版本可支持三种工作模式 UserSpace: kubernetes V1.1之前使用,V1.2及以后就已淘汰 IPtables: Kubernetes 1.1版本开始支持。 1.2开始为默认 IPVS: kubernetes 1.9引入到1.11为正式版本,需要安装ipvadm、ipset工具包和加载ip_vs内核模块。 kubectl 概述 是一个通过命令行对kubernetes集群管理的工具 基于Web的用户接口,用于可视化k8s集群。 dashborad可用于获取集群中资源对象的详细信息,Dashboard提供GUI,作为运维人员,使用kubectl命令行工具管理足矣 CoreDNS负责为整个集群提供DNS服务,它自1.11版本起默认使用CoreDNS,一种灵活,可扩展的DNS服务,之前的版本用到的是kube-dns项目,SkyDNS则是更早一代的解决方案。
Kubernetes——安全认证
Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其中一个重要的任务,API Server是集群内部各个组件通信的中介,也是外部控制的入口。所以Kubernetes的安全机制基本就是围绕保护API Server来设计的。Kubernetes使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission Control)三步来保证API Server的安全。
kubeconfig文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥)、集群Context信息(集群名称、用户名)。Kubernetes组件通过启动时指定不同的kubeconfig文件可以切换到不同的集群。
Pod中的容器访问API Server,因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了,Kubernetes使用了Service Account解决Pod访问API Server的认证问题。
Kubernetes设计了一种资源叫做Secret,分为两类,一种是用于ServiceAccount的service-account-token,另一种是用于保存用户自定义保密信息的Oqaque,ServiceAccount中用到包含三个部分:Token、、namespace
上面的认证过程,只是确认通信的双方都确认对方是可信的,进而可以互相通信。 鉴权是确定请求方有哪些资源权限,API Server 内部通过用户认证后,然后进入授权流程。对合法用户进行授权并且随后在用户访问时进行鉴权,是权限管理的重要环节。API Server目前支持以下几种授权策略(通过API Server的启动参数 --authorization-mode 设置)
RBAC(Role-Based Access Control,基于角色的访问控制)在Kubernetes v1.5引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项,组建其重要程度。相对于其他的访问控制方式,RBAC具有如下优势。
要使用RBAC授权模式,则需要在API Server的启动参数中加上 --authorization-mode=RBAC 。
角色绑定
主体(subject)
BAC引入了4个新的顶级资源对象:Role、ClusterRole、RoleBinding、ClusterRoleBinding。同其他API资源对象一样,用户可以使用kubectl或者API调用方式操作这些资源对象。
需要注意的是Kubernetes并不会提供用户管理,那么User、Group、ServiceAccount指定的用户又是从哪里来的呢?Kubernetes组件(kubectl、kube-proxy)或是其他自定义的用户在向CA申请证书时,需要提供一个证书请求文件。
在RBAC API中Role表示一组规则权限,权限只会增加(累加权限),不存在一个资源一开始就有很多权限而通过RBAC对其进行减少的操作;Role可以定义在一个namespace中,如果想要跨namespace则可以创建ClusterRole
rules中的参数说明:
ClusterRole ClusterRole处理具有和Role一致的命名空间内资源的管理能力,因其集群级别的范围,还可以用于以下特殊元素的授权。
下面的ClusterRole可以让用户有权访问任意一个或所有命名空间的secrets(视其绑定方式而定):
RoleBinding可以将角色中定义的权限授予用户或用户组,RoleBinding包含一组权限列表(subjects),权限列表中包含有不同形式的待授予权限资源类型(users、groups、Service Account);RoleBinding同样包含对被Bind的Role引用,RoleBinding适用于某个命名空间内授权,ClusterRoleBinding适用于集群范围内授权。
下面例子中的RoleBinding将在default命名空间中把pod-reader角色授予用户jane,这一操作让jane可以读取default命名空间中的Pod:
RoleBinding也可以引用ClusterRole,来对当前namespace内用户、用户组或ServiceAccount进行授权,这种操作允许集群管理员在整个集群内定义一些通用ClusterRole,然后在不同的namespace中使用RoleBinding来引用 例如,以下RoleBinding引用一个ClusterRole,这个ClusterRole具有整个集群内对secrets的访问权限,但是其授权用户dave只能访问development空间中的secrets(因为RoleBinding定义在development命名空间)
集群角色绑定中的角色只能是集群角色,用于进行集群级别或者对所有命名空间都生效的授权。下面的例子允许manager组的用户读取任意namespace中的secret:
示例1:普通角色的资源列表:
解释:如上限定在default的命名空间的名为pod-and-pod-logs-reader的普通角色,对pod和pods/log资资源就有get和list的权限。
示例2:更精细粒度的资源控制,可通过resourceNamess指定特定的资源实例,以限制角色只能够对具体的某个实例进行访问控制。
解释:如上限定在default的命名空间的名为configmap-updater的普通角色,对名为my-configmap的configmaps类型的特定资源,具有update和get权限。
示例1:类型为user(用户)。
示例2:类型为group(组)。
示例3:查看default的服务账户。
Kubernetes系统通过三个独⽴的组件间的相互协作来实现服务账户的⾃动化,三个组件具体为:Admission Controllers准⼊控制器、令牌控制器(token controller)和Service Account账户控制器。Service Account控制器负责为名称空间管理相应的资源,并确保每个名称空间中都存在⼀个名为“default”的Service Account对象。
当请求通过了前面的认证和授权之后,还需要经过准入控制处理通过之后,apiserver 才会处理这个请求。Admission Control 有一个准入控制列表,我们可以通过命令行设置选择执行哪几个准入控制器。只有所有的准入控制器都检查通过之后,apiserver 才执行该请求,否则返回拒绝。
在kubernetes中,一些高级特性正常运行的前提条件为,将一些准入模块处于enable状态。总结下,对于kubernetes apiserver,如果不适当的配置准入控制模块,他就不能称作是一个完整的server,某些功能也不会正常的生效。
允许所有请求
拒绝所有请求
强制设置Pod拉取镜像策略为Always。这样能够保证私有镜像只能被有拉取权限的使用者使用。
它会拦截所有想在privileged container上执行命令的请求。(如果自己的集群支持privileged container,自己又希望限制用户在这些privileged container上执行命令,那么强烈推荐使用它。)
这个插件禁止那些通过主机执行而获得privileges去执行exec和attach Pod的命令。
通过webhook决定image策略,需要同时配置–admission-control-config-file
一个serviceAccount为运行在pod内的进程添加了相应的认证信息。当准入模块中开启了此插件(默认开启),如果pod没有serviceAccount属性,将这个pod的serviceAccount属性设为“default”;确保pod使用的serviceAccount始终存在;如果LimitSecretReferences 设置为true,当这个pod引用了Secret对象却没引用ServiceAccount对象,弃置这个pod;如果这个pod没有包含任何ImagePullSecrets,则serviceAccount的ImagePullSecrets被添加给这个pod;如果MountServiceAccountToken为true,则将pod中的container添加一个VolumeMount 。
它会观察所有的请求,确保在namespace中ResourceQuota对象处列举的container没有任何异常。如果在kubernetes中使用了ResourceQuota对象,就必须使用这个插件来约束container。(推荐在admission control参数列表中,这个插件排最后一个。)
实现配额控制。他会观察所有的请求,确保没有违反已经定义好的约束条件,这些条件定义在namespace中LimitRange对象中。如果在kubernetes中使用LimitRange对象,则必须使用这个插件。
禁止创建设置了 Security Context 的 pod。这个插件将会将使用了 SecurityContext的pod中定义的选项全部失效。关于 SecurityContext的描述:SecurityContext 在container中定义了操作系统级别的安全设定(uid, gid, capabilities, SELinux等等)。
确保处于termination状态的namespace不再接收新的对象创建请求,并拒绝请求不存在的namespace。
根据镜像的历史使用记录,为容器设置默认资源请求和限制
为PVC设置默认StorageClass
设置Pod的默认forgiveness toleration为5分钟
使用Pod Security Policies时必须开启
限制kubelet仅可访问node、endpoint、pod、service以及secret、configmap、PV和PVC等相关的资源(v1.7版本以上才支持)
参考:
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。