Azure:打造安全的容器,为“云原生”构筑坚实基础
近一段时间来,“云原生”已成为企业应用开发和IT运维领域妥妥的“网红级”概念。借助灵活的微服务架构和容器化运维方式,以及持续的自动化构建与发布能力、声明式API还有服务网格等技术的加持,云原生应用已经在很多方面彻底改变了企业设计、开发、发布和运维应用程序的方式,让上云的企业在灵活性、弹性、成本等诸多方面获得了显著收益。
其实“云原生”这个概念依然还比较新,并且随着技术和需求的演化,其相关定义依然在不断迭代更新。之前我们曾对微服务进行过简单的介绍,错过的童鞋可以点击这里回看 。本文,我们会一起看看容器的安全与保护问题,并通过分享安全设计实践,告诉大家如何加固容器本身的安全性,为云原生应用打造一个坚实、可靠的基础。
容器 | 使用和安全现状
首先来看看早前的一次统计结果:
彼时,Docker还占据主要份额。但不容忽视的是,容器化的Containerd份额在当时已经出现了大幅增加。目前,Kubernetes社区版本已经在1.20版之后,将对Docker的支持改成了“Deprecated”,并将从1.23版之后彻底移除对Docker的支持。
不过Azure Kubernetes Service的动作更快。早在1.19版本就开始使用Containerd取代Docker了。对用户来说,我们不可避免需要经历迁移过程,包括用criCtl工具替换Docker,以及日志位置和形式的变化等。
总结来说:RKT、LXC、Mesos等容器运行时已经很少见,Docker和Containerd基本成为容器运行时的主流实现。
另外,在编排工具方面,毫无悬念Kubernetes已占据榜首。如下图所示,可见加上OpenShift以及Rancher,其占有比例高达89%,Swarm则从2018年的11%下降到了5%。
从上述统计数据可以看出,容器和容器编排等主导技术栈的发展趋势已经很明显了,但容器安全方面并没有那么乐观。有一些非常触目惊心的数据值得我们每个人警醒:
60%的受访者所在组织在过去一年内碰到过安全事件
93%的受访者不是非常清楚问题的状况
82%的受访者认为,由于采用了容器,需要重新思考安全职责问题
那么对于容器技术来说,到底为我们造成了哪些安全挑战?我们该采取怎样的应对措施?
容器带来的安全挑战和应对策略
容器还是虚拟机,这是个问题:
容器是一种“软件包”,其中包含了能在任何环境中运行应用所需的全部元素。容器可以通过虚拟化操作系统在任何地方(私有数据中心、公有云、开发者的个人计算机)。但是它的虚拟化程度与虚拟机还有很大区别。
通过下图的对比可以看出:传统虚拟机会占用大量磁盘空间,除了虚拟机托管的应用程序外,还包含完整的操作系统和相关工具;容器则相对较轻,仅包含运行容器化应用程序所需的库和工具,因此比虚拟机更紧凑,并且启动速度更快。
虚拟机 容器
容器的主要技术实现:
以Docker容器为例,主要使用这三大特性来实现虚拟化与隔离:cgroup + namespace + docker image:
cgroup (资源限制):
驱动 :
cgroupfs:
要限制内存, CPU等资源限制写入pid对应的一个cgroup文件
systemd :
提供一个cgroup管理方式,所有的写cgroup操作都必须通过systemd的接口来完成,不能手动更改cgroup的文件。
常用显示项目:
CPU:计算
Memory:内存
Device:设备
Freezer:批任务冻结(为了安全)
Blkio:磁盘访问
Pid:创建进程数量
namespace (隔离):
mount : 文件系统的视图,是容器镜像提供的一个文件系统。
uts : 隔离hostname和domain。
pid namespace : 保证容器的init进程是以1号进程来启动。
network namespace : 除host网络这种模式,其他所有的网络模式都有一个networknamespace的文件。
user namespace : 控制用户UID和GID在容器内部和宿主机上的一个映射。
IPC namespace : 控制了进程兼通信的一些东西,比方说信号量。
docker image
基于联合文件系统
不同层可以被其他镜像复用
容器读写层作为最新的镜像最新一层
容器化所采用的主要安全技术:
容器在带来轻量与便利的同时,也带来一些安全问题。主要原因包括:
多个容器间使用的还是同一宿主机的操作系统内核;
Linux内核中很多资源和对象不能被Namespace化;
系统调用甄别变得复杂(虽然可以通过Seccomp等技术过滤和甄别容器内部发起的所有系统调用来进行安全加固,但多了一层对系统调用的过滤,会拖累容器性能。此外,默认情况下我们也很难知道到底该开启哪些系统调用,禁止哪些系统调用)。
随着大家对安全越来越重视,容器安全技术本身也在不断的加强和演进。具有代表性的技术包括:
-
默认利用Capability限制容器Root用户的能力。Docker默认容器的Capability包括(Docker的默认Secomp可参阅
https://github.com/moby/moby/blob/master/profiles/seccomp/default.json):
Seccomp系统调用过滤:
结合seLinux或Apparmor实现MAC访问控制:
打造安全的容器:
容器安全问题的本质是共享内核。为了解决该问题,逐渐发展出“安全容器”这一概念,其主要代表技术有Kata Containers和Gvisor。
Kata Containers的本质是一个轻量化虚拟机。当我们启动一个Kata Containers后,会看到一个正常的虚拟机在运行。这也就意味着:标准的虚拟机管理程序(Virtual Machine Manager, VMM)是运行Kata Containers的必备组件。
2018年,Google发布了gVisor项目。gVisor项目给容器进程配置了一个用Go语言实现,运行在用户态的极小“独立内核”。这个内核对容器进程暴露Linux内核,扮演“Guest Kernel”的角色,从而达到了将容器与宿主机隔离的目的。两者大致架构对比如下:
容器docker-bench-security:
在具体的生产运行环境中,我们可执行脚本得到最佳实践校验结果。运维人员可结合这些建议作出修改和备注。详细地址如下:https://github.com/docker/docker-bench-security
下图是执行结果,从中可以看到各个级别的条目:
镜像安全扫描:
容器的执行程序和文件系统是镜像,要保障容器安全,镜像的安全扫描也非常重要。开源的镜像扫描产品可选择Clair。
在Azure上,容器注册的Azure Defender包含一个漏洞扫描程序,可扫描基于Azure资源管理器的Azure Container Registry注册表中的镜像。该技术由业界领先的漏洞扫描供应商Qualys提供支持。
Kubernetes的安全问题
Kubernetes的安全问题可从几个不同层面展开。
首先,Kubernetes本身是一个资源对象管理平台,通过把不同对象进行抽象,形成平台能够识别的资源对象,例如Pod、Namespace、ReplicaSet等。访问这些对象需要统一的API,在API层面主要有三种安全机制:认证、授权,以及准入控制。
Kubernetes API访问控制:
其中准入控制又分成两种Hook:
Mutating admission webhook:对资源的定义声明作出相应修改;
Validating admission webhook:判断资源是否可以创建或更新。
在API认证方面,使用最普遍的是X509和ServiceAccount方式。
X509是通过指定相应Name和Group属性实现的:
Pod的ServiceAccount认证方式如下图所示:
在授权方面,则主要采取了基于角色的访问控制机制。Kubernetes中的角色可根据是否需要跨名称空间访问而分成Role和ClusterRole,同时绑定关系也可分成RoleBinding和ClusterRoleBinding,它们之间的关系如下图所示:
此外还要注意Kubernetes的安全上下文问题。由于Kubernetes底层还是通过调用容器运行时来对容器进行生命周期管理和安全限制,所以上述有关Docker的安全思路也适用于Kubernetes,借此可实现对运行时的控制。详细内容可参阅下图:
除了通过上述securityContext对运行时加以控制外,Kubernetes还引入了资源安全规则对象PodSecurityPolicy,借此实现对名称空间内Pod的统一安全控制。主要控制内容如下:
其工作流程如下图所示:
在流量访问控制方面,Kubernetes中进出站流量可通过NetworkPolicy加以定义。具体来说,我们可以定义如下的内容:
ServiceMesh:
如果需要多租户或企业级别的流量和安全管控,仅使用NetworkPolicy是不够的。此时需要引入ServiceMesh。各个ServiceMesh在Kubernetes这个基座之上都能很好地运行。
当前流行的ServiceMesh主要有Istio和Linkerd。下图展示了以Istio为代表地ServiceMesh整体架构图和组件构成。因为它本身也有一定的配置和管理复杂性,对性能损耗也不能忽略,因此对于技术团队不完整的公司,很多还处于观望状态。
OpenAgentPolicy
由于PodSecurityPolicy本身只能定义Pod的规则,而在云原生应用中,包括在各个公有云或私有云中,还会引入更多资源类型,如Service、Node等。
开源社区也逐步开始废弃PodSecurityPolicy,转为推荐通过OpenAgentPolicy框架实现对各种资源的约束。因为需要约束各种资源,因而有必要提供更大灵活性,进而引入了一种新的,表达能力更丰富的规则语言:Rego。实例请参照下图:
具体到Kubernetes平台,我们需要使用GateKeeper这个组件。
在Azure云平台中,则是通过Azure Policy来实现和替代这个功能的:
存储安全
Kubernetes可将密钥等机密信息存储在Secret资源之中,但查看etcd中的内容会发现一切都以明文方式实现,这就会导致一些安全问题,例如:
在Azure上提供专门保护加密密钥、证书(以及与证书关联的私钥)和机密(例如连接字符串和密码)等存储敏感数据和关键业务数据的服务--Azure Key Vault。它可以与Azure Kubernetes Service无缝集成,最大限度提高这些敏感信息的访问和存储安全性。
Azure Kubernetes Service安全
AKS中主要包含下图所示的Kubernetes功能模块,大致可分为控制平面和数据平面两部分:
Azure Kubernetes Service是基于Azure云平台的托管Kubernetes服务。它把控制平面变成了一种托管的平台服务,客户无需管理API Servers、Etcd等组件,只需要为自己的Agent node付费。同时在安全方面,Azure平台提供了Application Gateway、Azure Firewall、Azure Key Vault等服务来保证系统安全。
下图是一个比较常见的,以Azure Kubernetes Service为基础的整体架构图:
整个架构分为四大部分:
1.容器和镜像安全:
通过AAD认证保证容器认证安全
通过ACR镜像扫描保证镜像安全
2.节点和集群安全:
节点自动补丁更新
节点可以发布到私有网络中
3.Pod安全:
通过Azure Policy提供丰富的安全定义
4.流量安全:
通过存储加密
通过Key Vault保证秘钥等安全
通过Azure Application Gateway等保证访问安全
关于AKS的详细信息请参阅相关文档。同时这方面还有一些动手实验,详情可参阅https://github.com/sme-csu/aks-security-design-practice。
容器的安全性是一个很庞大的话题,限于篇幅,本文不可能一一进行详细介绍。希望上述内容能起到抛砖引玉的作用,帮助大家更好地了解相关技术,设计出更安全的容器环境。
如果希望进一步了解本文涵盖的相关话题,可前往https://github.com/sme-csu/App-Mod查看相关演示文档和视频教程。
如果有任何意见或建议,也欢迎通过留言进行交流。