ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AMD SEV机密计算实战:从内存加密原理到SEV-SNP与宿主机隔离实践

AMD SEV机密计算实战:从内存加密原理到SEV-SNP与宿主机隔离实践 虚拟化圈子里有个老段子云平台运维手里握着宿主机root想不想看客户机里的数据只取决于他想不想不存在能不能的问题。因为在KVM这类方案里客户机的内存就是宿主机上的普通进程内存/proc/kcore、gdb、甚至一个粗心大意的dd命令都能把运行中的虚拟机内存全部导出来。有段时间我负责云平台的安全加固被业务方问过一句你们能不能彻底保证看不到我的内存我当时的回答是从流程上保证不看从技术上说做不到直到AMD SEVSecure Encrypted Virtualization铺开以后这个回答才终于能改一改。这篇文章就把SEV这东西掰开讲讲它能解决什么问题、背后的原理是什么、SEV/SEV-ES/SEV-SNP三代怎么演进、我在实际部署里踩过哪些坑以及它到底值不值得你为每一台虚拟机打开。适合虚拟化运维、平台安全工程师以及所有对机密计算感兴趣的同学看完应该能对安全虚拟化这件事有个完整的判断框架。1. 真问题虚拟机里的数据云厂商到底能不能看到1.1 一个令运维后背发凉的场景早年间我做KVM虚拟化平台维护有一次为了排查一个客户机内存泄漏的奇怪bug在宿主机上用gdb挂到一个QEMU进程上直接读取guest物理内存的某个偏移地址里面的数据干干净净地摊在那里——客户说那是他正在处理的数据库导出文件而我这边连个密码都没输入就看到了明文。这件事让我印象极其深刻。KVM的虚拟化模型决定了客户机内存对宿主机是完全透明的hypervisor要管理内存、要处理缺页、要做设备模拟就必须能看到这些内存。而能看到内存和能读内存之间根本没有技术屏障只有运维规范这一层单薄的约束。比特流数据存放在哪里就以明文形式存在哪里一旦能读取就如同用吸管喝豆浆只要管子够长碗底的东西都能吸上来。虚拟化环境下这个吸管天生就在操作系统层被接管了。1.2 现有的加密方案堵不住哪个洞你可能会说给虚拟机里的磁盘做加密不就行了比如LUKS、dm-crypt、Windows BitLocker这里要分清加密的层次数据落盘加密保护的是磁盘镜像文件或块设备数据在内存里仍然是明文。传输加密TLS/SSH保护的是网络包数据在两端机器的内存里仍然是明文。文件系统级加密eCryptfs、fscrypt保护的是文件内容运行中的进程访问时仍然解密进内存。这几层加起来逃不出一个共同点运行时的内存始终是明文的。而内存恰恰是数据最密集、最容易被窃取的地方——进程栈、堆、密钥、会话令牌、业务数据库的缓存页全都在内存里。更麻烦的是在虚拟化模型下查内存权限这个过程本身就不可控。hypervisor要负责把客户机的物理地址映射到宿主机的物理地址它就是内存管理的中枢让中枢自己看不见自己管理的内容从架构上看是反直觉的。AMD SEV正好把这个反直觉的事做成了现实让内存加密的密钥掌握在客户机手里而不是宿主机手里。2. SEV的根基AMD安全处理器与内存加密引擎怎么协作2.1 SME先给整台机器加上内存加密SEV不是凭空冒出来的它建立在AMD SMESecure Memory Encryption安全内存加密的基础上。咱们把SME搞懂SEV就懂了一半。SME做的事一句话概括内存控制器在写入内存时自动加密读出来时自动解密。这个过程发生在CPU与物理内存之间的内存控制器里用的是硬件AES引擎按64字节的行粒度加解密。对于操作系统来说SME是完全透明的进程不需要改代码内核也只需要在启动时做极少量的适配。但这里有个关键问题加解密的密钥放在哪如果密钥存在内存里的某个寄存器位置那攻击者直接读这块内存就把密钥拿走了加密等于白做。AMD的答案是在CPU里塞了一个独立的安全岛——AMD安全处理器AMD Secure Processor简称ASP。它是一颗独立的ARM核心有自己的固件、自己的内存区域x86主核心访问不到它的密钥存储区。SME的密钥就由这颗安全处理器生成和管理x86世界里的人根本摸不到这把钥匙。有了SME之后整台机器的内存理论上都不会以明文落盘到物理内存条上。那SEV又在SME之上做了什么2.2 从全机加密到每台虚拟机一把独立钥匙SME的问题是一把钥匙锁全楼——整台机器所有数据用同一把密钥加密。这在单机场景没问题但在虚拟化场景就尴尬了hypervisor和所有客户机共用一把钥匙要么都能解开要么都解不开。hypervisor还是能看到客户机的明文因为解密是硬件自动完成的CPU在访问那段内存时已经把密文还原成明文放在数据总线上了。SEV的核心改进是每个虚拟机分配一个独立的地址空间IDASID每个ASID对应一把独立的内存加密密钥。当某个客户机的CPU核心访问属于该客户机的guest物理内存时内存控制器用这把密钥解密当hypervisor的代码访问同一块物理内存时因为没有对应的密钥拿到的就是一坨密文。这个过程用生活类比比较好理解SME像是给一栋楼的所有房间都装同样型号的锁物业拿着一把万能钥匙能进每家每户SEV是把每户的锁芯换成了不同的物业手里那把钥匙只在公共区域有效进了任何一户都是寸步难行。密钥整个生命周期——生成、装载、销毁——都由AMD安全处理器管理。guest要启动时hypervisor会向安全处理器发起一个请求安全处理器为这个客户机生成或者加载密钥然后在客户机的整个生命周期里持有它。hypervisor全程接触不到密钥明文。2.3 启动过程是怎么被度量的SEV还有一个看起来很玄学但很重要的概念启动度量Launch Measurement。它做的事情是在虚拟机启动时安全处理器对启动过程中的固件、内核、initramfs等关键成份算出一串哈希值并记录在安全处理器内部。外部验证者可以通过远程证明attestation把这串哈希值取出来跟你预期的标准值比对确认这台虚拟机里跑的系统没被人调包过。打个比方你订了一份外卖骑手保温箱上有一个防拆封条封条上的编码是你下单时生成的。你收到的箱子封条编码和预期的一致说明箱子在路上没被打开过。SEV的启动度量就是这个封条hypervisor可以把它提交给外部证明服务外部服务确认这台机器的启动链路是可信的之后才能信任这台机上运行的业务。3. 从SEV到SEV-SNP三代演进各自解决了什么问题3.1 第一代SEV的核心价值与明显短板第一代SEV常说的SEV解决了客户机内存对宿主机保密的问题但远远没解决所有问题。它有一个非常明显的盲区内存虽然加密了CPU的寄存器状态却没有保护。KVM做虚拟化的一个基本流程是客户机在guest模式下运行遇到特权指令或者外部中断时CPU会从guest模式退出VM Exit回到hypervisor模式然后hypervisor读取并保存vCPU的寄存器状态等处理完事件再恢复寄存器重新进入guest。在这一进一出的过程中客户机vCPU所有寄存器的值包括通用寄存器、控制寄存器、甚至某些敏感状态都暴露给了hypervisor。恶意hypervisor完全可以在VM Exit的时候读走客户机的寄存器甚至修改某个寄存器的值再让guest继续跑从而实现注入攻击。第一代SEV对此基本无能为力。换句话说第一代SEV保护了内存这摊静态数据但对CPU上下文这个动态状态没做像样的防护。3.2 SEV-ES如何藏住vCPU寄存器状态SEV-ESEncrypted State加密状态就是冲着这个短板去的。它在硬件层面实现了客户机在VM Exit时vCPU寄存器自动打包加密保存到客户机自己的内存区域里hypervisor只能看到一个密文块完全不知道里面的值。而且SEV-ES还引入了一个机制叫Automatic ExitAEguest在遇到某些不需要hypervisor介入的异常时可以直接在guest内部处理掉压根不退出到hypervisor层。退出次数变少暴露面自然更小。在配置层面SEV-ES对应的policy值会发生变化后面实战部分我会给一个具体的配置示例。从这一代开始SEV从一个半透明保险柜变成了封死的黑匣子——内存密文、寄存器密文hypervisor手里只剩下调度权没有窥探权。3.3 SEV-SNP对页表和完整性攻击的补全SEV-ES保护了寄存器但还有一个隐秘的攻击面内存重映射攻击。因为页表仍然是hypervisor管理的恶意hypervisor可以偷偷修改guest的嵌套页表把客户机的某段guest物理内存重新映射到别的地方或者把它自己伪造的数据页映射给guest。客户机内核以为自己在读某个合法页面实际上读到的是hypervisor塞进来的脏数据。这种攻击不碰加密的东西而是利用地址映射来做手脚第一代SEV和SEV-ES都没法完全防住。SEV-SNPSecure Nested Paging安全嵌套分页在架构上补上了这个洞。它引入了反向映射表RMPReverse Map Table由硬件维护每个物理内存页的所有者到底是hypervisor还是某个客户机。客户机只能访问自己拥有的页面hypervisor也不能随意修改一个已分配给guest的页面的映射关系。同时SEV-SNP还提供了内存完整性保护硬件能检测出数据被篡改或重放的情况。简单说SEV-SNP要解决的是防篡改和防重放问题让客户机不仅能保证自己内存是密文还能保证自己读到的内容一定是自己当初写下的内容中间没人换过货。从保密Confidentiality到完整性IntegritySEV的防线才算真正闭环。3.4 一张表看清三代差异特性SEV第一代SEV-ESSEV-SNP内存加密支持支持支持vCPU寄存器加密不支持支持支持页表/嵌套页表保护不支持不支持支持防重映射攻击不支持不支持支持内存完整性/防重放不支持不支持支持远程证明能力有限增强完善扩展证明典型部署预警防偷看防偷看篡改现场防全套操纵日常沟通里很多人把所有SEV都叫SEV但如果你要跟伙伴讨论漏洞和威胁模型一定要先确认说的是哪一代。就像聊加密算法时不能把DES和AES混为一谈一样差的不是一个版本号是整整一个安全层级。4. 实战配置与踩坑记录把SEV在QEMU/KVM里跑起来理论讲完来看点能落地的。我在这块踩过的坑比预想的多得多先从平台确认说起。4.1 跑起来之前的平台确认清单SEV不是装个软件就能用的它对硬件和固件有硬性要求。我拿到一台新服务器之后按照以下流程检查确认CPU型号AMD EPYC 7001系列及以上基本都支持SEV7002/7003系列支持SEV-ES7003及以后的型号支持SEV-SNP。注意Ryzen桌面级CPU只支持SME不一定支持完整的SEV功能做实验别拿家用机硬扛。进BIOS开启相关选项这是最容易被忽略的一步。很多服务器的BIOS默认关闭SEV/SME要在CPU配置或者安全菜单里找到AMD SEV或者Secure Memory Encryption并打开。有些BIOS开启SME后还会要求你设置内存加密的最小密钥ID数默认值就可以但不要设成0。确认Linux内核支持建议用较新的发行版内核5.15并确认内核启用了CONFIG_AMD_MEM_ENCRYPT、CONFIG_AMD_MEM_ENCRYPT_ACTIVE、CONFIG_KVM_AMD_SEV等配置。可以通过zgrep -i sev /proc/config.gz快速检查。查看实际状态在设备目录里确认SEV设备存在ls -l /dev/sev dmesg | grep -i sev sevctl platformsevctl platform能看到平台版本、ASID数量、API版本等关键信息如果这里报错说明BIOS或内核配置还没到位。APIs的版本号挺重要老固件API版本太低的话QEMU会拒绝创建SEV guest。注意sevctl工具在CentOS/RHEL里可以通过yum install sevctl安装Debian/Ubuntu里是apt install sevctl。这是AMD官方提供的小工具强烈建议装上排查问题全靠它。4.2 QEMU与libvirt的关键配置项平台确认没问题后有两种方式让虚拟机启用SEV。如果你是直接用QEMU命令行可以在启动参数里增加类似这样的配置qemu-system-x86_64 \ -machine q35,confidential-guest-supportsev0 \ -object sev-guest,idsev0,cbitpos47,reduced-phys-bits5,policy0x01 \ ...用libvirt管理虚拟机的话对应的XML片段是这样的launchSecurity typesev cbitpos47/cbitpos reducedPhysBits5/reducedPhysBits policy0x01/policy /launchSecurity这几个参数的意义要搞明白不然跑不起来只能干瞪眼cbitpos47指物理地址中用作加密标记C-bit的比特位。AMD EPYC一般就是47表示物理内存地址的第47位用来标记这段内存的加密属性。填错了guest根本起不来。reducedPhysBits5表示客户机物理地址空间从52位EPYC默认缩减5位即客户机最多只能看到47位物理地址。因为最高位被加密功能占用了必须告诉QEMU给guest的地址空间留出余量。policy0x01这是个位图。bit0必须为1禁止调试模式bit1、bit2分别控制是否允许特定功能。要启用SEV-ES的话policy要设成0x05bit0和bit2置1。SEV-SNP的设置更复杂还要配合其它参数一般不建议直接手搓。启动之后在客户机内部可以通过dmesg | grep -i sev确认SEV特性是否生效能看到类似AMD Secure Encrypted Virtualization的日志就说明guest已经跑在加密环境里了。4.3 我踩过的几个具体坑坑1BIOS开了SEV之后宿主机起不来。有一次我在一台EPYC服务器上开了BIOS里的SME选项结果重启后停在GRUB之前直接黑屏。后来排查发现是因为内核启动参数里没加内存加密相关选项而BIOS已经把内存控制器设置为加密模式导致内核在早期初始化阶段无法正确读写内存。解决方法是进入内核命令行加上mem_encrypton或者根据发行版要求配合iommupt并且确认initramfs里包含了必要的加密模块。坑2cbitpos和reducedPhysBits填错导致guest启动即崩溃。这个参数跟具体硬件强相关我在一台比较老的EPYC 7001平台上按照7003的默认值cbitpos47配结果guest内核直接panic。后来查看该平台的物理地址位数并不是52位重新计算后使用了cbitpos47,reduced-phys-bits1才正常。建议每台机器上先跑一遍sevctl platform看看输出的物理地址位数和平台能力再用它反推这两个参数。坑3SDK/代理层不支持SEV但你以为支持了。很多IaaS平台自己封装了虚拟机调度系统底层直接调libvirt。如果你只是手动在宿主机上配好SEV但平台代码里创建虚拟机时没有把launchSecurity传给libvirt那实际创建的虚拟机还是普通VM。这个坑排查起来最耗时间建议先怀疑自己平台代码对launchSecurity参数的处理逻辑。坑4热迁移和快照直接罢工。这不是bug是特性。SEV的密钥绑死在物理平台的安全处理器里要把VM从A机迁到B机要么两机共享密钥——这会大幅扩大攻击面——要么就得在迁移前把guest内存数据解密出来那就违背了SEV的初衷。所以多数平台实现里SEV guest自动禁用标准热迁移。做快照也类似内存快照里全是密文恢复时需要重新度量密钥。如果业务依赖热迁移上SEV之前一定先把这点想清楚。5. 别把SEV当银弹它不保护什么代价又是什么5.1 SEV的威胁模型边界SEV不是把虚拟机放进一个刀枪不入的保险柜它有自己的威胁模型在模型之外的攻击路径它管不着。我总结了几类SEV明确不防的情况缓存侧信道攻击SEV加密了DRAM里的数据但CPU缓存里的数据加解密前必然存在hypervisor可以通过测量缓存访问时序来推断客户机的某些行为模式。SEV-SNP缓解了一部分这类攻击但没有根治。guest内部的漏洞利用如果客户机里的应用被打穿、内核提权SEV不会来救你。SEV保护的是外部的人看不进内存不是内部的人干不了坏事。客户机自己的人类攻击者照样能读它自己解密后的数据。拒绝服务攻击恶意hypervisor可以不调度客户机的vCPU、截断中断、故意把guest置于死循环这些干扰不泄露数据但能让业务不可用。SEV对此无能为力。物理攻击和供应链攻击如果攻击者能物理接触服务器、改BIOS固件、在内存总线上做手脚SEV的保护会被大幅削弱。它假设的是平台固件可信这一点在购买二手服务器时要格外留意。5.2 性能账到底损耗了多少内存加密当然有性能代价因为它改变了每一次内存访问的路径。我实测下来的大致数据是工作负载类型典型性能影响范围说明CPU密集型计算1%~3%内存访问相对较少损耗很小内存带宽密集型5%~15%数据库/大数据处理等高带宽场景损耗最明显网络/IO密集型3%~8%网络包处理涉及频繁的数据拷贝和加密混合业务Web服务2%~5%整体感知不大但长尾延迟可能变差做性能测试时建议用真实业务压测而不是只看基准测试工具的数字。我在一次数据库场景的压测中纯内存读测试掉了约12%但通过调整NUMA绑定和内存分配策略把损耗压回了7%左右。优化空间还是有的但你要接受每笔内存访问都多一道加解密这个事实。5.3 什么时候不该上SEV根据自己的实践我会建议以下情况不要盲目上SEV业务对延迟极其敏感尤其是微秒级响应的高频交易、实时流处理等场景SEV的额外延迟可能直接影响业务指标。业务需要频繁热迁移比如弹性伸缩要求虚拟机在物理机之间漂移的SEV会让这个流程变得非常痛苦甚至不可用。平台还有大量遗留虚拟机和老内核这些guest大概率对SEV支持不完整强行开启容易引入不稳定因素。威胁模型不清晰你并不清楚自己防的是谁那先别急着给所有VM上SEV。SEV解决的是hypervisor不可信这个特定矛盾如果只是合规要求做数据加密磁盘加密已经能覆盖大部分需求了。6. 从SEV延伸机密计算生态的现状与选择思路6.1 SEV和Intel SGX不是同一个赛道聊SEV的时候绕不开Intel的SGX但两者其实不是同类东西。SGX的粒度是应用程序的enclave——你只需要把代码里最敏感的函数放进一个受保护的内存区域其它部分照常运行。SEV的粒度是整个虚拟机——hypervisor完全不可信整个guest的内存和状态都被保护起来。带来的取舍也很明显SGX需要改造应用把敏感逻辑单独拆出来写工程量不小SEV对客户机里的应用几乎透明你不用改一行代码现有虚拟机直接就能迁移过去。反过来SGX因为粒度小可以做到更细粒度的安全策略SEV则是要么全保护要么全不保护没法只保护某个进程。Intel后来搞的TDXTrust Domain Extensions才是冲着SEV来的竞品——同样做整机加密、整VM保护。但那是Intel平台的事不在本文展开。如果你的平台是AMD EPYCSEV/SEV-SNP就是和TDX对标的那条路线。6.2 在云上使用SEV的实践路径在公有云上买一个机密计算实例并不等于自动安全。要真正发挥SEV的价值我的建议是走这套流程先明确信任边界SEV能防的只是云平台管理员偷看你内存。如果你还担心云服务商在你guest里装agent、篡改内核那SEV不够需要配合远程证明让云服务商证明自己没动手脚。做启动度量验证把guest的启动度量值定期外发给一个独立于云平台的验证服务比对你预期的镜像哈希确保开机过程没被干扰。密钥管理前置把业务需要的密钥数据库主密钥、TLS私钥在客户机内部封装起来绑定SEV的度量值。这样即使VM镜像被整体拖走没有对应的SEV环境也解不开。容器场景可以考虑Kata Containers配合SEV用轻量虚拟机承载容器让容器也享受到SEV的隔离保护这是目前机密容器比较务实的落地路径。6.3 我对SEV未来演进的一个观察从SEV到SEV-ES再到SEV-SNPAMD的思路很清晰先把看不了做扎实再把改不了做扎实最后把伪证不了做扎实。SNP带来更完整的远程证明体系之后SEV才有资格撑起多云环境下的敏感业务。可以预见SEV-SNP会逐渐成为EPYC平台上虚拟化安全的默认配置尤其在云原生和机密容器的场景里会越来越常见。回到开头那个问题云厂商到底能不能看到虚拟机里的数据有了SEV之后答案从不能保证变成了技术上真的看不到。作为运维人员我最大的感受是SEV并没有让运维工作变得更复杂它只是把安全边界从流程和规范硬生生地推到了芯片和硬件这一层。最后分享一个小技巧如果你刚接触SEV别急着在生产环境开SNP先拿一台EPYC服务器从policy0x01的第一代SEV玩起跑通启动度量和验证链路再逐步升级到SEV-ES和SNP。这个循序渐进的过程能帮你省下大量排查成本也能让你更清晰地理解每一代SEV分别堵住了哪个洞。
返回列表