mcpField
I
implementing-aws-nitro-enclave-security

implementing-aws-nitro-enclave-security

github.com/mukul975安全35.2k38 次安装8 周前更新
代码仓库

关于 implementing-aws-nitro-enclave-security

面向在 AWS Nitro Enclaves 上处理敏感数据的工程师,指导配置隔离环境、构建 enclave 镜像并设置基于证明的 KMS 访问控制。内容还涵盖 vsock 通信、证明验证与运行监控。

主要功能

  1. 01配置 Nitro Enclaves 环境
  2. 02构建并签名 EIF 镜像
  3. 03配置 KMS 证明策略
  4. 04实现 vsock 安全通信
  5. 05验证证明文档
  6. 06启动与监控 enclave

适用场景

  1. 01隔离处理个人或财务数据
  2. 02将 KMS 解密限制到已验证镜像
  3. 03通过证明让多个 enclave 相互认证
  4. 04把父实例上的解密迁入 enclave

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill implementing-aws-nitro-enclave-security

安装后在 Claude Code 中输入/implementing-aws-nitro-enclave-security确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE
references/
api-reference.md
scripts/
agent.py

详细介绍

实现 AWS Nitro Enclave 安全

何时使用

  • 处理必须与 EC2 实例操作员和管理员隔离的敏感数据(PII、PHI、财务记录、加密密钥)
  • 构建机密计算管道,即使拥有父实例的 root 级访问权限也无法读取 enclave 内存或状态
  • 实现加密证明工作流,将 KMS 解密权限绑定到特定且经过验证的 enclave 镜像哈希
  • 部署多方计算环境,其中两个或多个 enclave 在交换数据前通过证明相互认证
  • 加固现有工作负载:这些工作负载目前在父实例上解密密钥,可将解密迁移到 enclave 边界内

不要使用:当工作负载不处理需要硬件级隔离的敏感数据时,当实例类型不支持 Nitro Enclaves 时(需要至少 4 个 vCPU 的基于 Nitro 的实例),或当延迟约束使 vsock 通信开销不可接受时。

前提条件

  • 一个 AWS 账户,具有启动支持 Nitro 的 EC2 实例的权限(m5.xlarge 或更大,C5、R5、M6i 系列)
  • 在父 EC2 实例(Amazon Linux 2 或 AL2023)上安装 AWS CLI v2 和 nitro-cli 工具集
  • 在父实例上安装 Docker,用于构建 enclave 镜像文件(EIF)
  • 一个 AWS KMS 对称密钥,其密钥策略为 enclave 的 IAM 角色授予权限
  • 用于 enclave 侧 KMS 操作的 aws-nitro-enclaves-sdk-c 或 Python aws-encryption-sdk
  • 在 /etc/nitro_enclaves/allocator.yaml 中配置具有足够内存和 vCPU 分配的 Nitro Enclaves 分配器服务

工作流

步骤 1:配置 Nitro Enclaves 环境

设置父 EC2 实例以支持 enclave 启动:

  • 安装 Nitro Enclaves CLI:在 Amazon Linux 2 上,安装工具和分配器:
    sudo amazon-linux-extras install aws-nitro-enclaves-cli
    sudo yum install aws-nitro-enclaves-cli-devel -y
    sudo systemctl enable --now nitro-enclaves-allocator.service
    sudo systemctl enable --now docker
    sudo usermod -aG ne ec2-user
    sudo usermod -aG docker ec2-user
    
  • 配置内存和 CPU 分配:编辑 /etc/nitro_enclaves/allocator.yaml 为 enclave 预留资源。Enclave 需要从父实例中划出的专用内存:
    ---
    memory_mib: 4096
    cpu_count: 2
    
    重启分配器:sudo systemctl restart nitro-enclaves-allocator.service
  • 验证设置:运行 nitro-cli describe-enclaves 以确认 CLI 可以与 Nitro hypervisor 通信。空的 JSON 数组 [] 表示没有 enclave 正在运行,设置正确。
步骤 2:构建 Enclave 镜像文件(EIF)

将敏感工作负载打包为已签名的 enclave 镜像:

  • 创建应用程序 Dockerfile:Enclave 运行一个最小化 Linux 环境。应用程序仅通过 vsock 通信:

    FROM amazonlinux:2
    
    RUN yum install -y python3 python3-pip && \
        pip3 install boto3 cbor2 cryptography requests
    
    COPY enclave_app.py /app/enclave_app.py
    
    WORKDIR /app
    CMD ["python3", "enclave_app.py"]
    
  • 使用 nitro-cli 构建 EIF:将 Docker 镜像转换为 enclave 镜像文件,并捕获 PCR 测量值:

    docker build -t enclave-app:latest .
    nitro-cli build-enclave \
      --docker-uri enclave-app:latest \
      --output-file enclave-app.eif
    

    输出包含三个关键 PCR 值:

    • PCR0:enclave 镜像文件的 SHA-384 哈希(完整镜像摘要)
    • PCR1:Linux 内核和引导过程的 SHA-384 哈希
    • PCR2:应用程序代码的 SHA-384 哈希 记录这些值;它们用于 KMS 密钥策略中,以实现基于证明的访问控制。
  • 构建已签名的 EIF(生产环境推荐):生成签名证书并使用它生成 PCR8:

    openssl ecparam -name secp384r1 -genkey -noout -out enclave_key.pem
    openssl req -new -key enclave_key.pem -sha384 \
      -nodes -subj "/CN=Enclave Signer" -out enclave_csr.pem
    openssl x509 -req -days 365 -in enclave_csr.pem \
      -signkey enclave_key.pem -sha384 -out enclave_cert.pem
    
    nitro-cli build-enclave \
      --docker-uri enclave-app:latest \
      --output-file enclave-app.eif \
      --private-key enclave_key.pem \
      --signing-certificate enclave_cert.pem
    

    PCR8(签名证书哈希)使 KMS 策略能够信任由特定证书签名的任何镜像,从而允许在不更改策略的情况下更新镜像。

步骤 3:配置基于 KMS 证明的密钥策略

创建一项 KMS 密钥策略,将解密操作限制为仅可由经过验证的 enclave 执行:

  • 使用 PCR0(镜像哈希)的策略:该策略将密钥锁定到特定的 enclave 构建。任何代码变更都会产生新的 PCR0,需要更新策略:
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AllowEnclaveDecrypt",
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::111122223333:role/EnclaveParentRole"
          },
          "Action": [
            "kms:Decrypt",
            "kms:GenerateDataKey"
          ],
          "Resource": "*",
          "Condition": {
            "StringEqualsIgnoreCase": {
              "kms:RecipientAttestation:ImageSha384": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
            }
          }
        }
      ]
    }
    
  • 使用 PCR8(签名证书)的策略:信任任何由特定证书签名的 enclave,从而无需更改策略即可实现镜像轮换:
    {
      "Condition": {
        "StringEqualsIgnoreCase": {
          "kms:RecipientAttestation:PCR8": "ab3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890abcdef"
        }
      }
    }
    
  • 用于纵深防御的多 PCR 策略:结合 PCR0(镜像)和 PCR1(内核),确保应用程序和启动环境都与预期值一致:
    {
      "Condition": {
        "StringEqualsIgnoreCase": {
          "kms:RecipientAttestation:PCR0": "<pcr0-hex>",
          "kms:RecipientAttestation:PCR1": "<pcr1-hex>"
        }
      }
    }
    
  • IAM 角色策略:父实例的 IAM 角色必须具有 kms:Decrypt 权限,但 KMS 密钥策略条件确保只有当请求来自附加了正确证明文档的有效 enclave 时,实际解密才会成功。
步骤 4:实现安全的 Vsock 通信

建立父实例到 enclave 的通信通道:

  • Vsock 架构:enclave 与外部世界通信的唯一方式是通过 vsock(虚拟套接字)。Vsock 使用 CID(上下文标识符)和端口号。父实例的 CID 始终为 3,而 enclave 的 CID 在启动时分配。
  • 父实例侧代理服务器:父实例运行一个代理,通过 vsock 将来自 enclave 的 KMS API 调用转发到 AWS KMS 端点:
    import socket
    import json
    import boto3
    
    VSOCK_CID = 3  # Parent CID
    VSOCK_PORT = 5000
    
    def start_proxy():
        sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
        sock.bind((VSOCK_CID, VSOCK_PORT))
        sock.listen(5)
    
        kms_client = boto3.client('kms', region_name='us-east-1')
    
        while True:
            conn, addr = sock.accept()
            data = conn.recv(65536)
            request = json.loads(data.decode())
    
            if request['action'] == 'decrypt':
                response = kms_client.decrypt(
                    CiphertextBlob=bytes.fromhex(request['ciphertext']),
                    Recipient={
                        'KeyEncryptionAlgorithm': 'RSAES_OAEP_SHA_256',
                        'AttestationDocument': bytes.fromhex(request['attestation_doc'])
                    }
                )
                conn.sendall(json.dumps({
                    'ciphertext_for_recipient': response['CiphertextForRecipient'].hex()
                }).encode())
            conn.close()
    
  • enclave 侧客户端:enclave 应用程序从位于 /dev/nsm 的 Nitro Security Module(NSM)设备请求证明文档,将其附加到 KMS 解密请求中,并接收使用 enclave 的临时公钥加密的数据:
    import socket
    import json
    from cryptography.hazmat.primitives.asymmetric import rsa, padding
    from cryptography.hazmat.primitives import hashes, serialization
    
    PARENT_CID = 3
    VSOCK_PORT = 5000
    
    def get_attestation_document(public_key_der):
        """Request attestation document from NSM device."""
        # Uses the aws-nitro-enclaves-nsm-api
        # NSM provides: module_id, digest (SHA384), timestamp, PCRs,
        # certificate (from Nitro PKI), cabundle, public_key, user_data, nonce
        import nsm_util
        nsm_fd = nsm_util.nsm_lib_init()
        attestation_doc = nsm_util.nsm_get_attestation_doc(
            nsm_fd,
            public_key=public_key_der,
            user_data=None,
            nonce=None
        )
        return attestation_doc
    
    def decrypt_via_parent(ciphertext_hex):
        """Send decrypt request through vsock to parent proxy."""
        private_key = rsa.generate_private_key(
            public_exponent=65537, key_size=2048
        )
        public_key_der = private_key.public_key().public_bytes(
            serialization.Encoding.DER,
            serialization.PublicFormat.SubjectPublicKeyInfo
        )
    
        attestation_doc = get_attestation_document(public_key_der)
    
        sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
        sock.connect((PARENT_CID, VSOCK_PORT))
        sock.sendall(json.dumps({
            'action': 'decrypt',
            'ciphertext': ciphertext_hex,
            'attestation_doc': attestation_doc.hex()
        }).encode())
    
        response = json.loads(sock.recv(65536).decode())
        sock.close()
    
        # KMS encrypted the plaintext to the enclave's public key
        # Only the enclave's private key can decrypt it
        ciphertext_for_recipient = bytes.fromhex(
            response['ciphertext_for_recipient']
        )
        plaintext = private_key.decrypt(
            ciphertext_for_recipient,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                algorithm=hashes.SHA256(),
                label=None
            )
        )
        return plaintext
    
步骤 5:验证证明文档

验证来自 enclave 的证明文档以建立信任:

  • 证明文档结构:该文档采用 CBOR 编码,并使用 COSE 签名(COSE_Sign1)。它包含:

    • module_id:NSM 模块的标识符
    • digest:哈希算法(SHA-384)
    • timestamp:文档创建时的 Unix 纪元毫秒数
    • pcrs:PCR 索引到测量值的映射(PCR0-PCR15)
    • certificate:NSM 的 x509 证书,由 Nitro PKI 签名
    • cabundle:从 NSM 证书到 AWS Nitro 根 CA 的证书链
    • public_key:enclave 的临时公钥(在请求证明时提供)
    • user_data:可选的应用程序自定义数据(最多 512 字节)
    • nonce:用于新鲜度验证的可选 nonce
  • 验证步骤:

    1. 解码 COSE_Sign1 结构并提取有效载荷和证书
    2. 使用嵌入证书中的公钥验证 COSE 签名
    3. 验证从 NSM 证书经由 CA 捆绑包到 AWS Nitro Attestation PKI 根证书的证书链(可在 https://aws-nitro-enclaves.amazonaws.com/AWS_NitroEnclaves_Root-G1.zip 获取)
    4. 检查根 CA 证书与预期的 AWS 根证书匹配:aws.nitro-enclaves CN
    5. 验证证书链中没有任何证书在文档的时间戳时已过期
    6. 将 PCR0、PCR1、PCR2 值与 enclave 构建输出中的预期测量值进行比较
    7. 如果提供了 nonce,则验证其匹配以防止重放攻击
  • 证明验证代码:

    import cbor2
    from cose import CoseMessage
    from cryptography import x509
    from cryptography.x509.oid import NameOID
    
    def validate_attestation(attestation_bytes, expected_pcrs, expected_nonce=None):
        cose_msg = CoseMessage.decode(attestation_bytes)
        payload = cbor2.loads(cose_msg.payload)
    
        # Verify certificate chain
        cert = x509.load_der_x509_certificate(payload['certificate'])
        cabundle = [x509.load_der_x509_certificate(c) for c in payload['cabundle']]
    
        # Check root CA is AWS Nitro
        root = cabundle[-1]
        cn = root.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
        assert cn == 'aws.nitro-enclaves', f'Unexpected root CA: {cn}'
    
        # Verify PCR measurements
        pcrs = payload['pcrs']
        for idx, expected_value in expected_pcrs.items():
            actual = pcrs.get(idx, b'').hex()
            assert actual == expected_value, f'PCR{idx} mismatch: {actual}'
    
        # Verify nonce freshness
        if expected_nonce:
            assert payload.get('nonce') == expected_nonce, 'Nonce mismatch'
    
        return payload
    
第 6 步:启动并监控 Enclave

运行 enclave 并实施运维监控:

  • 启动 enclave:

    nitro-cli run-enclave \
      --eif-path enclave-app.eif \
      --cpu-count 2 \
      --memory 4096 \
      --enclave-cid 16 \
      --debug-mode
    

    注意:--debug-mode 会为开发启用 enclave 控制台。在生产环境中请移除该选项,因为它允许读取 enclave 输出,从而破坏隔离保证。

  • 验证 enclave 状态:

    nitro-cli describe-enclaves
    

    预期输出包括 "State": "RUNNING"、分配的 EnclaveCID、内存、CPU 数量以及 enclave 标志。

  • 读取 enclave 控制台(仅调试模式):

    nitro-cli console --enclave-id <enclave-id>
    
  • 终止 enclave:

    nitro-cli terminate-enclave --enclave-id <enclave-id>
    
  • CloudWatch 监控:配置父实例上报 enclave 健康指标。由于 enclave 没有网络访问权限,健康检查必须通过 vsock 代理进行:

    # Parent-side health check over vsock
    def check_enclave_health(enclave_cid, port=5001):
        try:
            sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((enclave_cid, port))
            sock.sendall(b'HEALTH_CHECK')
            response = sock.recv(1024)
            sock.close()
            return response == b'OK'
        except (socket.timeout, ConnectionRefusedError):
            return False
    

关键概念

术语定义
Nitro Enclave由 Nitro Hypervisor 在基于 Nitro 的 EC2 实例上创建的隔离虚拟机,没有持久化存储、没有网络访问权限,也没有交互式访问权限,即使父实例的 root 用户也无法访问
Attestation Document由 Nitro Security Module 生成的 CBOR 编码、COSE 签名的文档,包含 PCR 度量值、到 AWS Nitro 根 CA 的证书链,以及可选的用户提供数据
PCR(Platform Configuration Register)SHA-384 哈希度量值,用于唯一标识 enclave 的镜像(PCR0)、内核/引导程序(PCR1)、应用程序(PCR2)、IAM 角色(PCR4)、实例 ID(PCR3)和签名证书(PCR8)
Vsock一种虚拟套接字,是父 EC2 实例与其 enclave 之间唯一的通信通道,使用 CID(Context Identifier)和端口寻址
EIF(Enclave Image File)由 nitro-cli 从 Docker 镜像构建的打包 enclave 镜像,包含内核、ramdisk 和应用程序,并在构建时生成 PCR 度量值
Nitro Security Module(NSM)enclave 内部的自定义 Linux 设备(/dev/nsm),提供 attestation document 生成和硬件随机数生成功能
COSE_Sign1CBOR Object Signing and Encryption 单签名者结构,用于使用 NSM 的私钥对 attestation document 进行签名
kms:RecipientAttestationAWS KMS 条件键前缀,使密钥策略能够强制要求只有在提供具有匹配 PCR 值的有效 attestation document 时,decrypt/generate 操作才能成功

工具与系统

  • nitro-cli:AWS CLI 工具,用于构建 enclave 镜像文件、启动/终止 enclave,以及读取 enclave 控制台输出
  • AWS KMS:Key Management Service,原生支持 Nitro Enclaves 的基于 attestation 的条件键,使用 enclave 的临时公钥加密响应
  • aws-nitro-enclaves-sdk-c:用于 enclave 侧 KMS 操作的 C SDK,处理 attestation document 生成和 vsock 代理通信
  • kmstool-enclave-cli:预构建的 CLI 工具(来自该 SDK),在 enclave 内运行,通过 attestation 执行 KMS Decrypt 和 GenerateRandom 操作
  • Nitro Enclaves ACM:AWS Certificate Manager 集成,在 enclave 内配置 TLS 证书以建立 HTTPS 端点
  • CloudTrail:记录 KMS API 调用,包括带有 Recipient 参数的 Decrypt 和 GenerateDataKey 操作,从而支持对源自 enclave 的加密操作进行审计

常见场景

场景:在 Nitro Enclave 中实现 PII 令牌化服务

背景:一家医疗健康 SaaS 公司处理包含 PHI 的患者记录。法规要求 PHI 的解密和令牌化绝不能在运维人员可访问的实例上进行。该公司部署了一个 Nitro Enclave,它接收加密的患者记录,在 enclave 内使用带证明的 KMS 进行解密,对 PII 字段进行令牌化,并仅通过 vsock 返回令牌化后的记录。

方法:

  1. 将令牌化应用构建为 Docker 镜像,其中包含令牌化逻辑、kmstool-enclave-cli 二进制文件,以及一个接受加密记录的 vsock 服务器
  2. 使用 nitro-cli build-enclave 构建 EIF,并从构建输出中记录 PCR0、PCR1、PCR2
  3. 创建带有密钥策略的 KMS 密钥,该策略包含与 PCR0 匹配的 kms:RecipientAttestation:ImageSha384 条件,仅允许此特定 enclave 构建解密患者记录
  4. 为父实例部署具有该密钥 kms:Decrypt 权限的 IAM 角色,但 KMS 条件确保解密仅在经过证明的 enclave 内成功
  5. 父应用通过 HTTPS 接收加密的患者记录,通过 vsock 端口 5000 将其传递给 enclave,并接收返回的令牌化记录
  6. enclave 向 NSM 请求证明文档,将其附加到 KMS Decrypt 调用,接收使用其临时 RSA 密钥加密的明文,在本地解密,对 PII(SSN、DOB、姓名)进行令牌化,并返回 {ssn: "tok_a8f3...", dob: "tok_b2e1...", name: "tok_c9d4..."}
  7. CloudTrail 日志显示带有 RecipientAttestation 参数的 Decrypt 调用,确认所有解密都发生在 enclave 边界内

陷阱:

  • 在生产环境中以调试模式运行 enclave,这会允许控制台访问并破坏监管机构要求的机密性保证
  • 将 KMS 密钥策略设置为仅使用 IAM 角色而不带证明条件,这会允许父实例直接解密而无需 enclave
  • 未在 allocator.yaml 中预留足够内存,导致 enclave 在启动时以晦涩的“资源不可用”错误失败
  • 未实现 vsock 消息分帧,导致大型记录在 64KB 套接字缓冲区边界处被截断
  • 忘记 PCR0 会随每次代码重建而变化,需要为每次部署更新 KMS 策略;在生产环境中使用 PCR8(签名证书)以将构建与策略更新解耦

输出格式

## Nitro Enclave Security Assessment

**Enclave Image**: enclave-tokenizer.eif
**Build Date**: 2026-03-19T14:30:00Z
**Instance Type**: m5.2xlarge
**Allocated Resources**: 2 vCPUs, 4096 MiB memory

### PCR Measurements
| PCR | Value | Bound in KMS Policy |
|-----|-------|---------------------|
| PCR0 (Image) | a1b2c3d4e5f6... | Yes |
| PCR1 (Kernel) | f6e5d4c3b2a1... | Yes |
| PCR2 (Application) | 1a2b3c4d5e6f... | No |
| PCR8 (Signing Cert) | 9f8e7d6c5b4a... | Yes (production) |

### KMS Key Policy Verification
- Key ARN: arn:aws:kms:us-east-1:111122223333:key/mrk-abc123
- Attestation condition: kms:RecipientAttestation:ImageSha384 = PCR0
- Signing cert condition: kms:RecipientAttestation:PCR8 = <cert-hash>
- Parent role: arn:aws:iam::111122223333:role/EnclaveParentRole
- Direct decrypt from parent: BLOCKED (attestation required)
- Decrypt from verified enclave: ALLOWED

### Security Posture
- [PASS] Debug mode disabled in production launch command
- [PASS] Vsock is the only communication channel (no network interface)
- [PASS] Attestation document nonce verification implemented
- [PASS] Certificate chain validates to AWS Nitro root CA
- [WARN] PCR0 used in policy; consider PCR8 for deployment flexibility
- [FAIL] Health check endpoint does not verify enclave attestation freshness

评论

支持 Markdown