研究背景

传统方案的缺陷

  • 隐私性:每个验证者在验证容器的完整性状态时,需要收集整个ML(即便是来自别的容器)
    • 攻击者可以窃取其他用户的容器信息
    • 需求:验证者只搜集与度量目标相关的度量日志
  • 完整地度量:传统方案在一个容器被启动完毕后就结束度量,其后装载组件的完整性被忽视——一旦容器开始运行,其内部组件的完整性就无法得到保证
  • 额外组件的安全:vTPM的组件在用户空间内,不受到TPM的保护,这些安全模块可能被攻击
  • 效率问题

创新点

  • 从隐私角度,架构能够在不向远程验证者传递其他容器信息或不必要的底层主机信息的情况下,证明指定容器的完整性。
  • 从安全角度,架构能够收集涵盖指定容器所有组件及其依赖项的完整性证据,并提出 cPCR 以确保隐私保护的完整性证据受到基于硬件的 RoT 的保护。

新方法、理论

度量内容:需要度量什么?

  • 度量链

    • TPM硬件->OS kernel:先度量后加载的机制,主要是TPM度量 (本文采用了cPCR机制)
    • OS kernel->应用层:由IMA度量
  • 度量内容

    • Boot启动:主要包括BIOS、GRUB和OS kernel
    • 容器依赖服务:容器管理服务(docker service)、docker 守护进程
    • 容器Boot启动:镜像、配置
    • 容器应用
    • 宿主机应用

前三个是容器需要度量的
且要求验证者不能区分他的容器是否是运行在证明器上的唯一容器

容器度量的用例

  • 需求
    • 解决IMA日志隔离问题(通过NameSpace机制,每个NameSpace机制对应一个度量日志s-ML)
    • PCR问题:PCR很少,一共24个。但是可能启动的容器不止24个——通过cPCR机制(模拟多个TPM)来解决

度量架构

Container-IMA度量架构

  • Split Hook 检查系统调用 创建一个新的名字空间,并通知内核一个事件来拆分ML。
  • Namespace Parser提取当前进程的名称空间编号,以识别当前ME属于哪个分区;
  • cPCR模块负责通过维护cPCR,保护每个s-ML;cPCR是内核模拟PCR的数据结构

原本的度量:TPM硬件———>(物理的)PCR10——>ML
改造的:TPM硬件———>(物理的)PCR12——>cPCRs——->s - MLs
因为:我有多个ML需要度量(多个容器),但对多个容器ML的度量不可能都存储在一个PCR中,所以添加了cPCR来模拟PCR

度量机制

boot启动过程:由可信启动进行测量,并由PCR0 - 7进行保护。它们与其他ML划分是固有分离的

  • 概念

    • s-MLs(分离的测量日志):内核需要维护多个双链表来表示从主测量日志(ML)中分离出来的测量日志(s-MLs)。
    • cPCRs(容器基于的PCR):为了保护这些s-MLs的完整性,架构模拟了一组PCR,称为容器基于的PCR(cPCRs)。每个cPCR有其值、一个独特的秘密(secret)和对应的命名空间编号。
  • 初始化

    • Split Hook捕捉系统运行docker run
    • Namespace Parser解析namespace编号$ns$
    • cPCR 置0($AllZero$)、创建密钥secret
      • cPCR Module请求TPM生成一个随机值作为这个cPCR的秘密,然后创建一个新的cPCR和一个新的s-ML。
      • 一旦容器成功启动,其对应的secret就会被反馈给用户,并且从那一刻起,内核不会再将这个secret暴露给用户空间。
      • $cPCR-list:=cPCR-list\cup\{AllZero,ns,secre t\}$
      • $s-MLs:=s-MLs\cup \{<\{\},n s>$
  • 测定时

    • 找存储的cPCR和ml是哪个:当一个ME被触发时,需要找到与当前进程的命名空间编号(nsnum)相匹配的cPCR和s-ML。这两个概念是通过查询cPCR-list和s-MLs来定位特定的cPCR和s-ML,以便将新的测量结果扩展到正确的cPCR并记录到对应的s-ML中。
    • 进行扩展:CPCR模块将当前ME扩展到目标cPCR中,并将其添加到目标s - ML中(等价于PCR_Extend的操作)
      • 在对应的$cPCR_i$进行扩展:$target-cPCR.value:=HASH(target-cPCR.value,ME.node_{hash})$
        • ($ME.node_{hash}$就是对应度量日志的template-hash)
      • 在对应的$ML_i$进行增加: $target-ml.value:=target-ml.value\cup measure(ME)$
  • 将cPCR绑定到TPM PCR中

    • 记录所有cPCR的摘要(digest):初始化一个临时PCR(tempPCR),将其设置为cPCR0.value与cPCR0.secret的异或结果。对于cPCR-list中的所有其他cPCR,将它们扩展到tempPCR中。最终,tempPCR将保存所有cPCR值的摘要值。(对于每个cPCR,计算tempValue,然后对tempPCR不断扩展)
      • $tempValue:=cPCR_{i}.value ⊕ cPCR_i.secret$
      • $tempPCR:=HASH(tempPCR\parallel tempValue)$
    • 然后扩展到物理上的TPM PCR
  • 发送给远程证实者的cPCR集合信息:

    • 发送的是和密钥异或的cPCR值(集合)。
    • $sendcPCRs=\{cPCR_i.value ⊕ cPCR_i.secret\mid\\cPCR_i\in cPCR-list\}$
    • 验证方法:
      • 远程证实方自行模拟出sendcPCR的内容,自行扩展historyPCR,如果与解密后的PCR12值相等,那么就可以确定sendcPCRs是可信的
      • 如果验证者拥有cPCRu的秘密值(cPCRu.secret),他可以通过将cPCRu.secret与sendcPCRs中相应的条目进行异或操作来恢复cPCRu.value

对容器依赖和BOOT的度量

前者没有创建新的名字空间,因此上述机制无法识别它们。

对于后者,容器的镜像和引导配置是用户空间中的概念。因此,很难让内核对其进行解析和记录

依赖项的完整性

  • 问题:容器的依赖项,如容器管理服务和它们所需的文件或库,不会创建新的命名空间,因此无法通过现有的基于命名空间的测量机制来识别和记录。
  • 解决方案:引入一个特殊的引导程序(bootstrap program),其任务是为应用创建新的命名空间。当使用这个引导程序启动依赖项时,会触发命名空间注册过程,从而记录和保护这些依赖项的完整性。
  • cPCR0的使用所有容器都依赖于这些服务,因此选择cPCR0来记录容器依赖项的测量结果,并且cPCR0的秘密(secret)被设置为一个已知值(例如全零),以便所有验证者都能识别和验证这些依赖项的完整性。
  • 具体解决
    • Docker社区版(Docker-ce)使用unshare系统调用来为容器分配一个新的命名空间,所以以unshare命令作为引导程序
      • 度量unshare确保了用于创建容器命名空间的工具本身没有被篡改,这是他的依赖项
    • 监测对象
        1. 系统调用程序本身的完整性
          • 公式1(针对上一节初始化时候进行修改,增加了createProcess本身的度量,而不再初始化为空):
          • 2:$s-MLs:=s-MLs\cup\{<\{measure(createProcess)\},ns>\}$
        1. 程序是否真的从”系统调用“启动
          • 如果一个容器是用真正的依赖关系启动的,那么依赖关系的创建过程的pid可以在这个容器的pid中找到——记录createProcess的进程id和其父id(下称pids)
          • 公式:$measure(P)=$
            • P是createProcess

启动项完整性

  • 问题:容器的启动信息,包括镜像、配置和启动参数,是在应用层的概念,内核无法直接获取这些信息。
  • 解决方案:利用已经记录和保护的容器依赖项的完整性,让它们收集容器启动时的信息。在原型中,修改了容器运行时(例如runc)来测量这些信息,并将测量结果扩展到另一个物理PCR中,例如PCR11。
  • s-ML的记录:每个容器的启动信息被记录在s-ML中,包括容器ID、命名空间编号、镜像的哈希值、配置的哈希值、配置文件的路径和节点哈希值。
    • 为了隐私,只为其他容器(不需要被证实的容器)传递nodehash,而不是整个信息

IMA模板说明

证实机制

集群中存在一个有效的用户管理系统,例如kubernetes,以防止未授权的验证者启动证明机制。因此,我们不考虑识别验证者。

  • 初始化信息
    • 远程证明方发送请求:request: <nonce, containerID>
    • 代理证明者收集信息
      • $Sign\{AIK, nonce ||PCRs\}$:PCR包含0~7(kernel本身boot度量)、11(容器启动项,包括配置项、镜像文件)、12(保护cPCR)
        • AIK是远程证明密钥
      • $sendcPCRs$(发送给远程证实者的cPCR集合信息)、historyPCR(PCR12)
        • 以供验证sendcPCR的准确性
      • $s-ML$
        • 宿主机启动项审计日志
        • 容器依赖项审计日志:
        • 容器启动项审计日志:镜像文件、配置项的度量
        • 容器应用层审计日志
        • 注:宿主机应用层审计日志存在自身的IMA文件中,不属于验证内容,无需发送给远程方。若需要也可以进行验证(PCR10)
  • 开始证明
    • 验证PCR:将接收到的PCR值与正确的TPM签名进行匹配,确认了PCR值的正确性。此外,还可以确定nonce的可信性,并且验证者知道该响应的新鲜性
      • 使用AIK的私钥对PCR值和nonce(一个随机数)进行签名。
      • 验证者可以验证PCR和nonce的有效性
    • 验证$s-ML$:通过模拟PCR _ Extend操作,并将模拟结果与可信PCR进行比较,可以检验这些s - ML的真实性。
      • 宿主机启动项审计日志:通过PCR0~7认证
      • 容器启动项审计日志:通过PCR11认证
    • 验证sendcPCRs的真实性:
      • 用sendcPCRs计算tempPCR,并对historyPCR进行扩展操作,若等于PCR12,那么sendcPCR正确
        • 根据哈希性质,若篡改,很难找到另一个恰好能等于PCR12的。
      • 然后可以通过sendcPCRs获取到$cPCR_i.value$(如果验证者有权限获取到这个($cPCR_i.secret$)
      • 容器依赖项审计日志+容器应用层审计日志:查询对应的$s-ML$,并模拟扩展,确定对应的$cPCR_0$、$cPCR_i$

分析

安全隐私

隐私

  • Container-IMA确保在远程验证过程中,只有必要的信息发送给验证者
    • $s-ML$
      • 容器依赖项审计日志+容器应用层审计日志:只有指定的容器信息
      • 容器启动项审计日志:只有指定的容器信息,其他信息只发送template_hash
    • $cPCR$
      • 每个cPCR都有一个与之关联的秘密值(secret),用于在远程验证过程中隐藏其他容器的cPCR值。只有拥有正确秘密值的验证者才能恢复和验证特定容器的cPCR值。
      • 方案1(通过可信通道传送):一旦容器成功启动,其对应的secret将通过安全通信渠道反馈给用户,确保secret不会在不安全的通道中传输。
      • 方案2(非对称加密):
        • 证明代理生成pk、sk,pk公开
        • 用户(远程证实方)使用一个随机数n,并pk加密
        • 证明代理解密,然后将随机数与secret进行异或,结果加密返回给远程方
        • 远程方通过nonce就可以得到secret
  • 可以采用一些策略,如添加无意义的值或更新PCR值,以混淆攻击者,防止他们通过分析PCR值来推断是容器的活动还是宿主机的活动。

安全

  • 两点安全
    • 覆盖了容器的整个CoT
    • 由基于硬件的RoT保护

宿主机的启动(PCR0~7)、容器依赖项(PCR 12)、容器启动(PCR 11)和容器应用(PCR 12)

效率

todo

局限性

  • Container-IMA如何处理容器迁移的场景,即容器从一个宿主机迁移到另一个宿主机时
  • Container-IMA在处理共享命名空间的容器时,并不会为每个容器创建单独的s-ML(分离的测量日志)。即:这些容器共享同一个s-ML,虽然这种处理方式不会妨碍隐私保护,但攻击者可能从这些s-ML中窥探其他容器的活动。

图解

整个系统安全链图解

验证过程图解

上两个图的宿主机最底层描述稍微有问题,宿主机Kernel没有(有一条boot_aggregate)IMA度量日志,是TPM度量


原文USENIX-2019
容器启动过程