租户隔离
基于Kubernetes的租户隔离
在规划和实施多租户集群时,我们首先可以利用的是Kubernetes自身的资源隔离层,包括集群本身,命名空间,节点,pod和容器均是不同层次的资源隔离模型。当不同租户的应用负载能够共享相同的资源模型时,就会存在彼此之间的安全隐患。为此,我们需要在实施多租时控制每个租户能够访问到的资源域,同时在资源调度层面尽可能的保证处理敏感信息的容器运行在相对独立的资源节点内;如果出于资源开销的角度,当有来自不同租户的负载共享同一个资源域时,可以通过运行时刻的安全和资源调度控制策略减少跨租户攻击的风险。
在实施多租户架构时首先需要确定对应的应用场景,包括判断租户内用户和应用负载的可信程度以及对应的安全隔离程度。在此基础上以下几点是安全隔离的基本需求:
- 开启Kubernetes集群的默认安全配置
- 开启RBAC鉴权,禁止匿名用户访问
- 开启secrets encryption能力,增强敏感信息保护
- 基于CIS kubernetes benchmarks进行相应的安全配置
- 开启NodeRestriction,AlwaysPullImages, PodSecurityPolicy等相关admission controllers
- 通过PSP限制pod部署的特权模式,同时控制其运行时刻SecurityContext
- 配置NetworkPolicy
- Docker运行时刻开启Seccomp/AppArmor/SELinux配置
- 尽量实现监控、日志等服务的多租隔离
而对于如SaaS、KaaS等服务模型下,或者我们无法保证租户内用户的可信程度时,我们需要采取一些更强有力的隔离手段,比如:
- 使用如OPA等动态策略引擎进行网络或Object级别的细粒度访问控制
- 使用安全容器实现容器运行时刻内核级别的安全隔离
- 完备的监控,日志,存储等服务的多租隔离方案
场景分析
内部共享集群的软隔离
该场景下集群的所有用户均来自企业内部,这也是当前很多K8s集群客户的使用模式,因为服务使用者身份的可控性,相对来说这种业务形态的安全风险是相对可控的;我们可以通过命名空间对不同部门或团队进行资源的逻辑隔离,同时定义以下几种角色的业务人员:
- 集群管理员:具有集群的管理能力(扩缩容、添加节点等操作)、负责为租户管理员创建和分配命名空间、负责各类策略(RAM/RBAC/networkpolicy/quota…)的CRUD
- 租户管理员:至少具有集群的RAM只读权限、管理租户内相关人员的RBAC配置
- 租户内用户:在租户对应命名空间内使用权限范围内的K8s资源
在建立了基于用户角色的访问控制基础上,我们还需要保证命名空间之间的网络隔离,在不同的命名空间之间只能够允许白名单范围内的跨租户应用请求。另外,对于业务安全等级要求较高的应用场景,我们需要限制应用容器的内核能力,可以配合seccomp/AppArmor/SELinux等策略工具达到限制容器运行时刻capabilities的目的。当然Kubernetes现有的命名空间单层逻辑隔离还不足以满足一部分大型企业应用复杂业务模型对隔离需求,我们可以关注Virtual Cluster,它通过抽象出更高级别的租户资源模型来实现更精细化的多租管理,以此弥补原生命名空间能力上的不足。
SaaS & KaaS服务模型下的多租户
在SaaS多租场景下,kubernetes集群中的租户对应为SaaS平台中各服务应用实例和SaaS自身控制平面,该场景下可以将平台各服务应用实例划分到彼此不同的命名空间中。而服务的最终用户是无法与Kubernetes的控制平面组件进行交互,这些最终用户能够看到和使用的是SaaS自身控制台,他们通过上层定制化的SaaS控制平面使用服务或部署业务(如下左图所示)。例如,某博客平台部署在多租户集群上运行。在该场景下,租户是每个客户的博客实例和平台自己的控制平面。平台的控制平面和每个托管博客都将在不同的命名空间中运行。客户将通过平台的界面来创建和删除博客、更新博客软件版本,但无法了解集群的运作方式。
KaaS多租场景常见于云服务提供商,该场景下业务平台的服务直接通过Kubernetes控制平面暴露给不同租户下的用户,最终用户可以使用K8s原生API或者服务提供商基于CRDs/controllers扩展出的接口。出于隔离的最基本需求,这里不同租户也需要通过命名空间进行访问上的逻辑隔离,同时保证不同租户间网络和资源配额上的隔离。
与企业内部共享集群不同,这里的最终用户均来自非受信域,他们当中不可避免的存在恶意租户在服务平台上执行恶意代码,因此对于SaaS/KaaS服务模型下的多租户集群,我们需要更高标准的安全隔离,而kubernetes现有原生能力还不足以满足安全上的需求,为此我们需要如安全容器这样在容器运行时刻内核级别的隔离来强化该业务形态下的租户安全。
访问控制
AuthN & AuthZ & Admission
ACK集群的授权分为RAM授权和RBAC授权两个步骤,其中RAM授权作用于集群管理接口的访问控制,包括对集群的CRUD权限(如集群可见性、扩缩容、添加节点等操作),而RBAC授权用于集群内部kubernetes资源模型的访问控制,可以做到指定资源在命名空间粒度的细化授权。
ACK授权管理为租户内用户提供了不同级别的预置角色模板,同时支持绑定多个用户自定义的集群角色,此外支持对批量用户的授权。
NetworkPolicy
NetworkPolicy可以控制不同租户业务pod之间的网络流量,另外可以通过白名单的方式打开跨租户之间的业务访问限制。
您可以在使用了Terway网络插件的容器服务集群上配置NetworkPolicy,这里可以获得一些策略配置的示例。
PodSecurityPolicy
PSP是K8s原生的集群维度的资源模型,它可以在创建pod请求的admission阶段校验其行为是否满足对应PSP策略的要求,比如检查pod是否使用了host的(网络,文件系统,指定端口,PID namespace)等,同时可以限制租户内的用户开启特权(privileged)容器,限制挂盘类型,强制只读挂载等能力;不仅如此,PSP还可以基于绑定的策略给pod添加对应的SecurityContext,包括容器运行时刻的uid,gid和添加或删除的内核capabilities等多种设置。
关于如何开启PSP admission和相关策略及权限绑定的使用,可以参阅这里
OPA
OPA(Open Policy Agent)是一种功能强大的策略引擎,支持解耦式的policy decisions服务并且社区已经有了相对成熟的与kubernetes的集成方案。当现有RBAC在命名空间粒度的隔离不能够满足企业应用复杂的安全需求时,可以通过OPA提供object模型级别的细粒度访问策略控制。
同时OPA支持七层的NetworkPolicy策略定义及基于labels/annotation的跨命名空间访问控制,可以作为K8s原生NetworkPolicy的有效增强。
资源调度
Resource Quotas & Limit Range
在多租户场景下,不同团队或部门共享集群资源,难免会有资源竞争的情况发生,为此我们需要对每个租户的资源使用配额做出限制。其中ResourceQuota用于限制租户对应命名空间下所有pod占用的总资源request和limit,LimitRange用来设置租户对应命名空间中部署pod的默认资源request和limit值。另外我们还可以对租户的存储资源配额和对象数量配额进行限制。
关于资源配额的详细指导可以参见这里
Pod Priority/Preemption
从1.14版本开始pod的优先级和抢占已经从beta成为稳定特性,其中pod priority标识了pod在pending状态的调度队列中等待的优先级;而当节点资源不足等原因造成高优先的pod无法被调度时,scheduler会尝试驱逐低优先级的pod来保证高优先级pod可以被调度部署。
在多租户场景下,可以通过优先级和抢占设置确保租户内重要业务应用的可用性;同时pod priority可以和ResouceQuota配合使用,完成租户在指定优先级下有多少配额的限制。
Dedicated Nodes
恶意租户可以规避由节点污点和容忍机制强制执行的策略。以下说明仅用于企业内部受信任租户集群,或租户无法直接访问Kubernetes控制平面的集群。
通过对集群中的某些节点添加污点,可以将这些节点用于指定几个租户专门使用。在多租户场景下,例如集群中的GPU节点可以通过污点的方式保留给业务应用中需要使用到GPU的服务团队使用。集群管理员可以通过如effect: “NoSchedule"这样的标签给节点添加污点,同时只有配置了相应容忍设置的pod可以被调度到该节点上。
当然恶意租户可以同样通过给自身pod添加同样的容忍配置来访问该节点,因此仅使用节点污点和容忍机制还无法在非受信的多租集群上保证目标节点的独占性。
关于如何使用节点污点机制来控制调度,请参阅这里。
敏感信息保护
secrets encryption at REST
在多租户集群中不同租户用户共享同一套etcd存储,在最终用户可以访问Kubernetes控制平面的场景下,我们需要保护secrets中的数据,避免在访问控制策略配置不当情况下的敏感信息泄露。为此可以参考K8s原生的secret加密能力,请参阅这里。